DispatchPage.vue: 1320→1217 lines - Extract SbModal.vue + SbContextMenu.vue reusable components - Extract useAbsenceResize composable - Extract dispatch constants to config/dispatch.js ProjectWizard.vue: 1185→673 lines (-43%) - Extract useWizardPublish composable (270-line publish function) - Extract useWizardCatalog composable - Extract wizard-constants.js (step labels, options, categories) SettingsPage.vue: 1172→850 lines (-27%) - Extract usePermissionMatrix composable - Extract useUserGroups composable - Extract useLegacySync composable ClientDetailPage.vue: 1169→864 lines (-26%) - Extract useClientData composable (loadCustomer broken into sub-functions) - Extract useEquipmentActions composable - Extract client-constants.js + erp-pdf.js utility checkout.js: 639→408 lines (-36%) - Extract address-search.js module - Extract otp.js module - Extract email-templates.js module - Extract project-templates.js module - Add erpQuery() helper to DRY repeated URL construction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
58 lines
2.0 KiB
JavaScript
58 lines
2.0 KiB
JavaScript
import { hToTime } from 'src/composables/useHelpers'
|
|
import { updateTech } from 'src/api/dispatch'
|
|
|
|
export function useAbsenceResize (pxPerHr, H_START) {
|
|
function startAbsenceResize (e, seg, tech, side) {
|
|
e.preventDefault()
|
|
const startX = e.clientX
|
|
const block = e.target.closest('.sb-block-absence')
|
|
const startW = block.offsetWidth
|
|
const startL = parseFloat(block.style.left)
|
|
const SNAP_PX = pxPerHr.value / 4
|
|
|
|
const snapPx = px => Math.round(px / SNAP_PX) * SNAP_PX
|
|
|
|
function onMove (ev) {
|
|
const dx = ev.clientX - startX
|
|
if (side === 'right') {
|
|
block.style.width = Math.max(SNAP_PX, snapPx(startW + dx)) + 'px'
|
|
} else {
|
|
const newL = snapPx(startL + dx)
|
|
const newW = startW + (startL - newL)
|
|
if (newW >= SNAP_PX && newL >= 0) {
|
|
block.style.left = newL + 'px'
|
|
block.style.width = newW + 'px'
|
|
}
|
|
}
|
|
const curL = parseFloat(block.style.left)
|
|
const curW = parseFloat(block.style.width)
|
|
const sH = H_START + curL / pxPerHr.value
|
|
const eH = sH + curW / pxPerHr.value
|
|
const lbl = block.querySelector('.sb-absence-label')
|
|
if (lbl) lbl.textContent = `${hToTime(sH)} → ${hToTime(eH)}`
|
|
}
|
|
|
|
function onUp () {
|
|
document.removeEventListener('mousemove', onMove)
|
|
document.removeEventListener('mouseup', onUp)
|
|
const curL = parseFloat(block.style.left)
|
|
const curW = parseFloat(block.style.width)
|
|
const newStartH = H_START + curL / pxPerHr.value
|
|
const newEndH = newStartH + curW / pxPerHr.value
|
|
const startTime = hToTime(newStartH)
|
|
const endTime = hToTime(newEndH)
|
|
tech.absenceStartTime = startTime
|
|
tech.absenceEndTime = endTime
|
|
updateTech(tech.name || tech.id, {
|
|
absence_start_time: startTime,
|
|
absence_end_time: endTime,
|
|
}).catch(() => {})
|
|
}
|
|
|
|
document.addEventListener('mousemove', onMove)
|
|
document.addEventListener('mouseup', onUp)
|
|
}
|
|
|
|
return { startAbsenceResize }
|
|
}
|