gigafibre-fsm/apps/ops/src/composables/useContextMenus.js
louispaulb 7d7b4fdb06 feat: nested tasks, project wizard, n8n webhooks, inline task editing
Major dispatch/task system overhaul:
- Project templates with 3-step wizard (choose template → edit steps → publish)
- 4 built-in templates: phone service, fiber install, move, repair
- Nested task tree with recursive TaskNode component (parent_job hierarchy)
- n8n webhook integration (on_open_webhook, on_close_webhook per task)
- Inline task editing: status, priority, type, tech assignment, tags, delete
- Tech assignment + tags from ticket modal → jobs appear on dispatch timeline
- ERPNext custom fields: parent_job, on_open_webhook, on_close_webhook, step_order
- Refactored ClientDetailPage, ChatterPanel, DetailModal, dispatch store
- CSS consolidation, dead code cleanup, composable extraction
- Dashboard KPIs with dispatch integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 13:01:20 -04:00

72 lines
2.9 KiB
JavaScript

import { ref } from 'vue'
import { updateJob } from 'src/api/dispatch'
import { serializeAssistants } from 'src/composables/useHelpers'
export function useContextMenus ({ store, rightPanel, invalidateRoutes, fullUnassign, openMoveModal, openEditModal }) {
const ctxMenu = ref(null)
const techCtx = ref(null)
const assistCtx = ref(null)
const assistNoteModal = ref(null)
function openCtxMenu (e, job, techId) {
e.preventDefault(); e.stopPropagation()
ctxMenu.value = { x: Math.min(e.clientX, window.innerWidth-180), y: Math.min(e.clientY, window.innerHeight-200), job, techId }
}
function closeCtxMenu () { ctxMenu.value = null }
function openTechCtx (e, tech) {
e.preventDefault(); e.stopPropagation()
techCtx.value = { x: Math.min(e.clientX, window.innerWidth-200), y: Math.min(e.clientY, window.innerHeight-200), tech }
}
function openAssistCtx (e, job, techId) {
e.preventDefault(); e.stopPropagation()
assistCtx.value = { x: Math.min(e.clientX, window.innerWidth-200), y: Math.min(e.clientY, window.innerHeight-150), job, techId }
}
function assistCtxTogglePin () {
if (!assistCtx.value) return
const { job, techId } = assistCtx.value
const assist = job.assistants.find(a => a.techId === techId)
if (assist) {
assist.pinned = !assist.pinned
updateJob(job.name || job.id, { assistants: serializeAssistants(job.assistants) }).catch(() => {})
invalidateRoutes()
}
assistCtx.value = null
}
function assistCtxRemove () {
if (!assistCtx.value) return
store.removeAssistant(assistCtx.value.job.id, assistCtx.value.techId)
invalidateRoutes(); assistCtx.value = null
}
function assistCtxNote () {
if (!assistCtx.value) return
const { job, techId } = assistCtx.value
const assist = job.assistants.find(a => a.techId === techId)
assistNoteModal.value = { job, techId, note: assist?.note || '' }
assistCtx.value = null
}
function confirmAssistNote () {
if (!assistNoteModal.value) return
const { job, techId, note } = assistNoteModal.value
const assist = job.assistants.find(a => a.techId === techId)
if (assist) {
assist.note = note
updateJob(job.name || job.id, { assistants: serializeAssistants(job.assistants) }).catch(() => {})
}
assistNoteModal.value = null
}
function ctxDetails () {
const { job, techId } = ctxMenu.value
rightPanel.value = { mode: 'details', data: { job, tech: store.technicians.find(t => t.id === techId) } }; closeCtxMenu()
}
function ctxMove () { const { job, techId } = ctxMenu.value; openMoveModal(job, techId); closeCtxMenu() }
function ctxUnschedule () { fullUnassign(ctxMenu.value.job); closeCtxMenu() }
return {
ctxMenu, techCtx, assistCtx, assistNoteModal,
openCtxMenu, closeCtxMenu, openTechCtx, openAssistCtx,
assistCtxTogglePin, assistCtxRemove, assistCtxNote, confirmAssistNote,
ctxDetails, ctxMove, ctxUnschedule,
}
}