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>
72 lines
2.9 KiB
JavaScript
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,
|
|
}
|
|
}
|