diff --git a/.gitignore b/.gitignore index 14de1ea..8467616 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ services/targo-hub/templates/*.bak-*.html # Legacy refresh creds (prod-only, never commit) .refresh.env **/.refresh.env + +# Python bytecode +__pycache__/ +*.pyc diff --git a/apps/ops/src/api/roster.js b/apps/ops/src/api/roster.js index a176ff0..621fef5 100644 --- a/apps/ops/src/api/roster.js +++ b/apps/ops/src/api/roster.js @@ -112,6 +112,10 @@ export const reorderJobs = (updates) => jpost('/roster/reorder-jobs', { updates export const unassignJobRoster = (job) => jpost('/roster/unassign-job', { job }) // Situer manuellement un job « hors carte » : pose latitude/longitude (+ adresse) choisis sur la carte export const setJobLocation = (job, lat, lon, address) => jpost('/roster/job/set-location', { job, lat, lon, address }) +// ÉQUIPE d'un job (assistants en renfort) — le lead reste inchangé ; l'assistant épinglé voit un bloc hachuré dans son horaire. +export const getJobTeam = (job) => jget('/roster/job/team?job=' + encodeURIComponent(job)) +export const addAssistant = (job, a) => jpost('/roster/job/team', { job, add: a }) +export const removeAssistant = (job, techId) => jpost('/roster/job/team', { job, remove: techId }) // Réconciliation des techniciens (staff legacy ↔ Dispatch Technician ↔ groupe Authentik tech) : rapport + apply manuel // Lien app PWA du tech (à copier / SMS) + son téléphone export const techAppLink = (tech) => jget('/roster/tech-link?tech=' + encodeURIComponent(tech)) @@ -123,7 +127,15 @@ export const startJob = (job) => jpost('/roster/job/start', { job }) export const finishJob = (job) => jpost('/roster/job/finish', { job }) export const techSyncReport = () => jget('/dispatch/legacy-sync/tech-sync') export const techSyncApply = (ids) => jpost('/dispatch/legacy-sync/tech-sync/apply?ids=' + encodeURIComponent((ids || []).join(',')), {}) +// Jobs Legacy (osTicket) ouverts assignés à UN tech (lecture seule) — vue « jobs courants du tech » +export const legacyTechTickets = (tech) => jget('/dispatch/legacy-sync/tech-tickets?tech=' + encodeURIComponent(tech)) +// Charge Legacy DATÉE par tech×jour sur la fenêtre affichée → durées estimées appliquées aux timelines +export const legacyWindowLoad = (start, days) => jget('/dispatch/legacy-sync/window-load?start=' + encodeURIComponent(start) + '&days=' + encodeURIComponent(days)) // Retour au POOL legacy (action EXPLICITE) : désassigne (ERPNext) + réécrit le ticket à Tech Targo (3301) dans osTicket export const returnJobToPool = (job) => jpost('/dispatch/legacy-sync/return-to-pool?job=' + encodeURIComponent(job), {}) // Aviser le client d'un report : désassigne + SMS lien /book — { job, phone?, message? } export const notifyReschedule = (body) => jpost('/roster/job/notify-reschedule', body) +// URL d'export GPX du parcours GPS d'un tech (jour courant) via Traccar — { tech, from, to } ISO 8601. window.open() = téléchargement. +export const gpxUrl = (tech, from, to) => HUB + '/traccar/gpx?tech=' + encodeURIComponent(tech) + '&from=' + encodeURIComponent(from) + '&to=' + encodeURIComponent(to) +// Tracé GPS (breadcrumb) d'un tech sur UNE journée → { coords:[[lon,lat]…], count }. Plage exacte (1 jour) obligatoire (Traccar lourd sinon). +export const traccarTrack = (tech, from, to) => jget('/traccar/track?tech=' + encodeURIComponent(tech) + '&from=' + encodeURIComponent(from) + '&to=' + encodeURIComponent(to)) diff --git a/apps/ops/src/components/customer/PhoneModal.vue b/apps/ops/src/components/customer/PhoneModal.vue index 73e6715..9a64193 100644 --- a/apps/ops/src/components/customer/PhoneModal.vue +++ b/apps/ops/src/components/customer/PhoneModal.vue @@ -104,7 +104,7 @@ import { createDoc } from 'src/api/erp' import { Device } from '@twilio/voice-sdk' import { UserAgent, Registerer, Inviter, SessionState } from 'sip.js' -const HUB_URL = window.location.hostname === 'localhost' ? 'http://localhost:3300' : 'https://msg.gigafibre.ca' +const HUB_URL = window.location.hostname === 'localhost' ? 'http://localhost:3300' : (import.meta.env.BASE_URL||'/').replace(/\/$/,'')+'/hub' const SIP_WSS_URL = 'wss://voice.gigafibre.ca/ws' const props = defineProps({ diff --git a/apps/ops/src/components/dispatch/NlpInput.vue b/apps/ops/src/components/dispatch/NlpInput.vue index 6ed3a2f..b7c80be 100644 --- a/apps/ops/src/components/dispatch/NlpInput.vue +++ b/apps/ops/src/components/dispatch/NlpInput.vue @@ -64,7 +64,7 @@ const props = defineProps({ const emit = defineEmits(['action', 'applied']) const $q = useQuasar() -const HUB_URL = window.location.hostname === 'localhost' ? 'http://localhost:3300' : 'https://msg.gigafibre.ca' +const HUB_URL = window.location.hostname === 'localhost' ? 'http://localhost:3300' : (import.meta.env.BASE_URL||'/').replace(/\/$/,'')+'/hub' const inputRef = ref(null) const text = ref('') diff --git a/apps/ops/src/components/flow-editor/kind-catalogs.js b/apps/ops/src/components/flow-editor/kind-catalogs.js index 6f2331c..68850bb 100644 --- a/apps/ops/src/components/flow-editor/kind-catalogs.js +++ b/apps/ops/src/components/flow-editor/kind-catalogs.js @@ -88,6 +88,7 @@ export const PROJECT_KINDS = { label: 'Ticket', icon: 'confirmation_number', color: '#f59e0b', + help: 'Crée un ticket (ERPNext Issue) quand le flow atteint cette étape — c\'est l\'action « créer un ticket ».', fields: [ { name: 'subject', type: 'text', label: 'Sujet', required: true }, { name: 'description', type: 'textarea', label: 'Description' }, diff --git a/apps/ops/src/components/settings/AiRoutingCard.vue b/apps/ops/src/components/settings/AiRoutingCard.vue new file mode 100644 index 0000000..d819431 --- /dev/null +++ b/apps/ops/src/components/settings/AiRoutingCard.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/apps/ops/src/components/settings/CannedResponsesCard.vue b/apps/ops/src/components/settings/CannedResponsesCard.vue new file mode 100644 index 0000000..2d9c106 --- /dev/null +++ b/apps/ops/src/components/settings/CannedResponsesCard.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/apps/ops/src/components/settings/QueueTeamsCard.vue b/apps/ops/src/components/settings/QueueTeamsCard.vue new file mode 100644 index 0000000..9b8eb7b --- /dev/null +++ b/apps/ops/src/components/settings/QueueTeamsCard.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/apps/ops/src/components/shared/DepartmentBoard.vue b/apps/ops/src/components/shared/DepartmentBoard.vue new file mode 100644 index 0000000..3f3b6ca --- /dev/null +++ b/apps/ops/src/components/shared/DepartmentBoard.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/apps/ops/src/components/shared/InlineField.vue b/apps/ops/src/components/shared/InlineField.vue index 366ecd2..79c4562 100644 --- a/apps/ops/src/components/shared/InlineField.vue +++ b/apps/ops/src/components/shared/InlineField.vue @@ -11,7 +11,7 @@