Integrates the Dispatch PWA (Vue/Quasar) into the gigafibre-fsm monorepo. Full git history accessible via `git log -- apps/dispatch/`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
3.0 KiB
JavaScript
79 lines
3.0 KiB
JavaScript
// ── Undo stack composable ────────────────────────────────────────────────────
|
|
import { ref, nextTick } from 'vue'
|
|
import { updateJob } from 'src/api/dispatch'
|
|
import { serializeAssistants } from './useHelpers'
|
|
|
|
export function useUndo (store, invalidateRoutes) {
|
|
const undoStack = ref([])
|
|
|
|
function pushUndo (action) {
|
|
undoStack.value.push(action)
|
|
if (undoStack.value.length > 30) undoStack.value.shift()
|
|
}
|
|
|
|
// Restore a single job to its previous state (unassign from current tech, re-assign if it had one)
|
|
function _restoreJob (prev) {
|
|
const job = store.jobs.find(j => j.id === prev.jobId)
|
|
if (!job) return
|
|
// Remove from all tech queues first
|
|
store.technicians.forEach(t => { t.queue = t.queue.filter(q => q.id !== prev.jobId) })
|
|
if (prev.techId) {
|
|
// Was assigned before — re-assign
|
|
store.assignJobToTech(prev.jobId, prev.techId, prev.routeOrder, prev.scheduledDate)
|
|
} else {
|
|
// Was unassigned before — just mark as open
|
|
job.assignedTech = null
|
|
job.status = 'open'
|
|
job.scheduledDate = prev.scheduledDate || null
|
|
updateJob(job.name || job.id, { assigned_tech: null, status: 'open', scheduled_date: prev.scheduledDate || '' }).catch(() => {})
|
|
}
|
|
if (prev.assistants?.length) {
|
|
job.assistants = prev.assistants
|
|
updateJob(job.name || job.id, { assistants: serializeAssistants(job.assistants) }).catch(() => {})
|
|
}
|
|
}
|
|
|
|
function performUndo () {
|
|
const action = undoStack.value.pop()
|
|
if (!action) return
|
|
|
|
if (action.type === 'removeAssistant') {
|
|
store.addAssistant(action.jobId, action.techId)
|
|
nextTick(() => {
|
|
const job = store.jobs.find(j => j.id === action.jobId)
|
|
const a = job?.assistants.find(x => x.techId === action.techId)
|
|
if (a) { a.duration = action.duration; a.note = action.note }
|
|
updateJob(job.name || job.id, { assistants: serializeAssistants(job.assistants) }).catch(() => {})
|
|
})
|
|
|
|
} else if (action.type === 'optimizeRoute') {
|
|
const tech = store.technicians.find(t => t.id === action.techId)
|
|
if (tech) {
|
|
tech.queue = action.prevQueue
|
|
action.prevQueue.forEach((j, i) => { j.routeOrder = i })
|
|
}
|
|
|
|
} else if (action.type === 'autoDistribute') {
|
|
action.assignments.forEach(a => _restoreJob(a))
|
|
if (action.prevQueues) {
|
|
store.technicians.forEach(t => {
|
|
if (action.prevQueues[t.id]) t.queue = action.prevQueues[t.id]
|
|
})
|
|
}
|
|
|
|
} else if (action.type === 'batchAssign') {
|
|
// Undo a multi-select drag — restore each job to previous state
|
|
action.assignments.forEach(a => _restoreJob(a))
|
|
|
|
} else if (action.type === 'unassignJob') {
|
|
_restoreJob(action)
|
|
}
|
|
|
|
// Rebuild assistJobs on all techs
|
|
store.technicians.forEach(t => { t.assistJobs = store.jobs.filter(j => j.assistants.some(a => a.techId === t.id)) })
|
|
invalidateRoutes()
|
|
}
|
|
|
|
return { undoStack, pushUndo, performUndo }
|
|
}
|