Copy the Chatwoot (v4.14.0) frontend runnable subset into frontend/ for customization: - app/javascript/ (Vue SPA: dashboard, widget, sdk, portal, superadmin) - app/views/ (ERB templates for vite-plugin-ruby entrypoint resolution) - app/helpers/, app/assets/ (Rails view helpers, static assets) - enterprise/ (Enterprise edition frontend overlay) - config/vite.json, vite.config.ts, bin/vite (Vite-Rails toolchain) - package.json, pnpm-lock.yaml, tailwind/postcss/eslint configs - Gemfile, Gemfile.lock (vite_rails gem for bin/vite binstub) Excluded Rails backend: controllers, models, services, jobs, mailers, policies, db, lib, spec, public, node_modules. Update references to the new frontend location: - .gitignore: exclude frontend build artifacts (node_modules, tmp, packs), keep frontend/bin/ and frontend/vendor/ via negation - backend/scripts/parity_frontend_smoke.sh: CHATWOOT_DIR default reference/chatwoot -> ../frontend - backend/scripts/parity_frontend_browser_smoke.mjs: same default update - AGENTS.md: add frontend section with Rails/Vite coupling notes - README: architecture tree includes frontend/
90 lines
2.4 KiB
Vue
90 lines
2.4 KiB
Vue
<script setup>
|
|
import { computed, toRef } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useMapGetter } from 'dashboard/composables/store.js';
|
|
import GroupedAvatars from 'widget/components/GroupedAvatars.vue';
|
|
import AvailabilityText from './AvailabilityText.vue';
|
|
import { useAvailability } from 'widget/composables/useAvailability';
|
|
|
|
const props = defineProps({
|
|
agents: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
showHeader: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
showAvatars: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
textClasses: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
});
|
|
|
|
const { t } = useI18n();
|
|
|
|
const availableMessage = useMapGetter('appConfig/getAvailableMessage');
|
|
const unavailableMessage = useMapGetter('appConfig/getUnavailableMessage');
|
|
|
|
// Pass toRef(props, 'agents') instead of props.agents to maintain reactivity
|
|
// when the parent component's agents prop updates (e.g., after API response)
|
|
const {
|
|
currentTime,
|
|
hasOnlineAgents,
|
|
isOnline,
|
|
inboxConfig,
|
|
isInWorkingHours,
|
|
} = useAvailability(toRef(props, 'agents'));
|
|
|
|
const workingHours = computed(() => inboxConfig.value.workingHours || []);
|
|
const workingHoursEnabled = computed(
|
|
() => inboxConfig.value.workingHoursEnabled || false
|
|
);
|
|
const utcOffset = computed(
|
|
() => inboxConfig.value.utcOffset || inboxConfig.value.timezone || 'UTC'
|
|
);
|
|
const replyTime = computed(
|
|
() => inboxConfig.value.replyTime || 'in_a_few_minutes'
|
|
);
|
|
|
|
// If online or in working hours
|
|
const isAvailable = computed(
|
|
() => isOnline.value || (workingHoursEnabled.value && isInWorkingHours.value)
|
|
);
|
|
|
|
const headerText = computed(() =>
|
|
isAvailable.value
|
|
? availableMessage.value || t('TEAM_AVAILABILITY.ONLINE')
|
|
: unavailableMessage.value || t('TEAM_AVAILABILITY.OFFLINE')
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex items-center justify-between gap-2">
|
|
<div class="flex flex-col gap-1">
|
|
<div v-if="showHeader" class="font-medium text-n-slate-12">
|
|
{{ headerText }}
|
|
</div>
|
|
|
|
<AvailabilityText
|
|
:time="currentTime"
|
|
:utc-offset="utcOffset"
|
|
:working-hours="workingHours"
|
|
:working-hours-enabled="workingHoursEnabled"
|
|
:has-online-agents="hasOnlineAgents"
|
|
:reply-time="replyTime"
|
|
:is-online="isOnline"
|
|
:is-in-working-hours="isInWorkingHours"
|
|
:class="textClasses"
|
|
class="text-n-slate-11"
|
|
/>
|
|
</div>
|
|
|
|
<GroupedAvatars v-if="showAvatars && isOnline" :users="agents" />
|
|
</div>
|
|
</template>
|