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/
81 lines
2.2 KiB
JavaScript
81 lines
2.2 KiB
JavaScript
import { computed, onMounted } from 'vue';
|
|
import { useMapGetter, useStore } from 'dashboard/composables/store';
|
|
import { useAccount } from 'dashboard/composables/useAccount';
|
|
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
|
import TasksAPI from 'dashboard/api/captain/tasks';
|
|
|
|
/**
|
|
* Cleans and normalizes a list of labels.
|
|
* @param {string} labels - A comma-separated string of labels.
|
|
* @returns {string[]} An array of cleaned and unique labels.
|
|
*/
|
|
const cleanLabels = labels => {
|
|
return labels
|
|
.toLowerCase()
|
|
.split(',')
|
|
.filter(label => label.trim())
|
|
.map(label => label.trim())
|
|
.filter((label, index, self) => self.indexOf(label) === index);
|
|
};
|
|
|
|
export function useLabelSuggestions() {
|
|
const store = useStore();
|
|
const { isCloudFeatureEnabled } = useAccount();
|
|
const appIntegrations = useMapGetter('integrations/getAppIntegrations');
|
|
const currentChat = useMapGetter('getSelectedChat');
|
|
const conversationId = computed(() => currentChat.value?.id);
|
|
|
|
const captainTasksEnabled = computed(() => {
|
|
return isCloudFeatureEnabled(FEATURE_FLAGS.CAPTAIN_TASKS);
|
|
});
|
|
|
|
const aiIntegration = computed(
|
|
() =>
|
|
appIntegrations.value.find(
|
|
integration => integration.id === 'openai' && !!integration.hooks.length
|
|
)?.hooks[0]
|
|
);
|
|
|
|
const isLabelSuggestionFeatureEnabled = computed(() => {
|
|
if (aiIntegration.value) {
|
|
const { settings = {} } = aiIntegration.value || {};
|
|
return !!settings.label_suggestion;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
const fetchIntegrationsIfRequired = async () => {
|
|
if (!appIntegrations.value.length) {
|
|
await store.dispatch('integrations/get');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Gets label suggestions for the current conversation.
|
|
* @returns {Promise<string[]>} An array of suggested labels.
|
|
*/
|
|
const getLabelSuggestions = async () => {
|
|
if (!conversationId.value) return [];
|
|
|
|
try {
|
|
const result = await TasksAPI.labelSuggestion(conversationId.value);
|
|
const {
|
|
data: { message: labels },
|
|
} = result;
|
|
return cleanLabels(labels);
|
|
} catch {
|
|
return [];
|
|
}
|
|
};
|
|
|
|
onMounted(() => {
|
|
fetchIntegrationsIfRequired();
|
|
});
|
|
|
|
return {
|
|
captainTasksEnabled,
|
|
isLabelSuggestionFeatureEnabled,
|
|
getLabelSuggestions,
|
|
};
|
|
}
|