gigafibre-fsm/apps/dispatch/src/composables/useUndo.js
louispaulb 7da22ff132 merge: import dispatch-app into apps/dispatch/ (17 commits preserved)
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>
2026-03-28 08:08:51 -04:00

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 }
}