From 79d160b9f1691c5c7fd8b6513976897a2ad6c7c3 Mon Sep 17 00:00:00 2001 From: louispaulb Date: Thu, 4 Jun 2026 11:09:16 -0400 Subject: [PATCH] =?UTF-8?q?Ops:=20page=20Copilote=20dispatch=20(chat=20+?= =?UTF-8?q?=20voix=20+=20s=C3=A9lecteur=20de=20politique)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nouvelle page /copilote : chat texte/voix (Web Speech API fr-CA) vers le copilote Gemini Flash (impact d'absence + propositions de réassignation), + sélecteur de politique de reprise (réassign/SMS/escalade) persistée. Route + nav (icône Sparkles ; ajout CalendarRange/CalendarClock manquantes dans la map d'icônes). Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/ops/src/api/roster.js | 5 ++ apps/ops/src/config/nav.js | 1 + apps/ops/src/layouts/MainLayout.vue | 3 +- apps/ops/src/pages/CopilotePage.vue | 122 ++++++++++++++++++++++++++++ apps/ops/src/router/index.js | 1 + 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 apps/ops/src/pages/CopilotePage.vue diff --git a/apps/ops/src/api/roster.js b/apps/ops/src/api/roster.js index b1da4d0..28a7391 100644 --- a/apps/ops/src/api/roster.js +++ b/apps/ops/src/api/roster.js @@ -37,6 +37,11 @@ export const generate = (start, days = 7, weights) => jpost('/roster/generate', export const publish = (assignments) => jpost('/roster/publish', { assignments }) export const publishWeek = (start, days, assignments, notify) => jpost('/roster/publish-week', { start, days, assignments, notify }) export const updateTemplate = (name, patch) => jput('/roster/template/' + encodeURIComponent(name), patch) + +// ── Copilote (Gemini Flash) + politique de reprise ── +export const askAssistant = (message, history) => jpost('/roster/assistant', { message, history }) +export const getPolicy = () => jget('/roster/policy') +export const savePolicy = (policy) => jpost('/roster/policy', policy) export async function deleteShiftTemplate (name) { const r = await fetch(HUB + '/roster/template/' + encodeURIComponent(name), { method: 'DELETE' }) if (!r.ok) throw new Error('Suppression modèle: ' + r.status) diff --git a/apps/ops/src/config/nav.js b/apps/ops/src/config/nav.js index 2a2b0ae..db73e21 100644 --- a/apps/ops/src/config/nav.js +++ b/apps/ops/src/config/nav.js @@ -6,6 +6,7 @@ export const navItems = [ { path: '/dispatch', icon: 'Truck', label: 'Dispatch', requires: 'view_all_jobs' }, { path: '/planification', icon: 'CalendarRange', label: 'Planification', requires: 'view_all_jobs' }, { path: '/rdv', icon: 'CalendarClock', label: 'Rendez-vous', requires: 'view_all_jobs' }, + { path: '/copilote', icon: 'Sparkles', label: 'Copilote', requires: 'view_all_jobs' }, { path: '/tickets', icon: 'Ticket', label: 'Tickets', requires: 'view_all_tickets' }, { path: '/equipe', icon: 'UsersRound', label: 'Équipe', requires: 'manage_users' }, { path: '/rapports', icon: 'BarChart3', label: 'Rapports', requires: 'view_dashboard_kpi' }, diff --git a/apps/ops/src/layouts/MainLayout.vue b/apps/ops/src/layouts/MainLayout.vue index f9b1609..5ecf02f 100644 --- a/apps/ops/src/layouts/MainLayout.vue +++ b/apps/ops/src/layouts/MainLayout.vue @@ -124,12 +124,13 @@ import { navItems as allNavItems } from 'src/config/nav' import { LayoutDashboard, Users, Truck, Ticket, UsersRound, BarChart3, Gift, Settings, LogOut, PanelLeftOpen, PanelLeftClose, Mail, + CalendarRange, CalendarClock, Sparkles, } from 'lucide-vue-next' import ConversationPanel from 'src/components/shared/ConversationPanel.vue' import { useConversations } from 'src/composables/useConversations' import FlowEditorDialog from 'src/components/flow-editor/FlowEditorDialog.vue' -const icons = { LayoutDashboard, Users, Truck, Ticket, UsersRound, BarChart3, Gift, Settings, LogOut, PanelLeftOpen, PanelLeftClose, Mail } +const icons = { LayoutDashboard, Users, Truck, Ticket, UsersRound, BarChart3, Gift, Settings, LogOut, PanelLeftOpen, PanelLeftClose, Mail, CalendarRange, CalendarClock, Sparkles } const { panelOpen, activeCount: convCount } = useConversations() function toggleConvPanel () { panelOpen.value = !panelOpen.value } diff --git a/apps/ops/src/pages/CopilotePage.vue b/apps/ops/src/pages/CopilotePage.vue new file mode 100644 index 0000000..63a9fd6 --- /dev/null +++ b/apps/ops/src/pages/CopilotePage.vue @@ -0,0 +1,122 @@ + + + diff --git a/apps/ops/src/router/index.js b/apps/ops/src/router/index.js index 3df2b8a..f9da577 100644 --- a/apps/ops/src/router/index.js +++ b/apps/ops/src/router/index.js @@ -40,6 +40,7 @@ const routes = [ { path: 'dispatch', component: () => import('src/pages/DispatchPage.vue') }, { path: 'planification', component: () => import('src/pages/PlanificationPage.vue') }, { path: 'rdv', component: () => import('src/pages/RendezVousPage.vue') }, + { path: 'copilote', component: () => import('src/pages/CopilotePage.vue') }, { path: 'agent-flows', component: () => import('src/pages/AgentFlowsPage.vue') }, { path: 'network', component: () => import('src/pages/NetworkPage.vue') }, // Gift campaigns — list, new wizard (CSV upload + matching), per-campaign detail with live SSE updates