gigafibre-fsm/apps/ops/src/composables/useEquipmentActions.js
louispaulb c6b2dd1491 refactor: extract composables from 5 largest files — net -1950 lines from main components
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>
2026-04-08 17:57:24 -04:00

138 lines
4.7 KiB
JavaScript

import { ref, watch } from 'vue'
import { Notify } from 'quasar'
import { listDocs, createDoc, updateDoc } from 'src/api/erp'
import { useScanner } from 'src/composables/useScanner'
import { equipScanTypeMap, defaultNewEquip } from 'src/data/client-constants'
export function useEquipmentActions (customer, equipment) {
const scannerState = useScanner()
const addEquipOpen = ref(false)
const addEquipLoc = ref(null)
const addingEquip = ref(false)
const equipLookupResult = ref(null)
const equipLookingUp = ref(false)
const newEquip = ref(defaultNewEquip())
let lookupTimer = null
watch(() => newEquip.value.serial_number, (sn) => {
clearTimeout(lookupTimer)
equipLookupResult.value = null
if (!sn || sn.length < 3) return
lookupTimer = setTimeout(() => lookupSerial(sn), 500)
})
async function lookupSerial (sn) {
equipLookingUp.value = true
try {
const results = await listDocs('Service Equipment', {
filters: { serial_number: sn },
fields: ['name', 'equipment_type', 'brand', 'model', 'serial_number', 'service_location', 'customer'],
limit: 1,
})
if (results.length) {
equipLookupResult.value = { found: true, equipment: results[0] }
}
} catch { /* ignore */ }
finally { equipLookingUp.value = false }
}
function openAddEquipment (loc) {
addEquipLoc.value = loc
newEquip.value = defaultNewEquip()
equipLookupResult.value = null
scannerState.clearBarcodes()
addEquipOpen.value = true
}
function closeAddEquipment () {
addEquipOpen.value = false
addEquipLoc.value = null
scannerState.clearBarcodes()
}
async function onScanPhoto (e) {
const file = e.target?.files?.[0]
if (!file) return
const result = await scannerState.scanEquipmentLabel(file)
if (result) {
if (result.serial_number && !newEquip.value.serial_number) newEquip.value.serial_number = result.serial_number
if (result.brand) newEquip.value.brand = result.brand
if (result.model) newEquip.value.model = result.model
if (result.mac_address) newEquip.value.mac_address = result.mac_address
if (result.ip_address) newEquip.value.ip_address = result.ip_address
if (result.equipment_type) {
const mapped = equipScanTypeMap[result.equipment_type.toLowerCase()]
if (mapped) newEquip.value.equipment_type = mapped
}
}
e.target.value = ''
}
function applyScannedCode (code) {
newEquip.value.serial_number = code
}
async function createEquipment () {
addingEquip.value = true
try {
const doc = await createDoc('Service Equipment', {
...newEquip.value,
customer: customer.value.name,
service_location: addEquipLoc.value.name,
})
equipment.value.push({
name: doc.name,
equipment_type: newEquip.value.equipment_type,
brand: newEquip.value.brand,
model: newEquip.value.model,
serial_number: newEquip.value.serial_number,
mac_address: newEquip.value.mac_address,
ip_address: newEquip.value.ip_address,
status: newEquip.value.status,
service_location: addEquipLoc.value.name,
})
Notify.create({ type: 'positive', message: `Equipement ${doc.name} cree`, timeout: 2000 })
closeAddEquipment()
} catch (e) {
Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
} finally {
addingEquip.value = false
}
}
async function linkExistingEquipment () {
if (!equipLookupResult.value?.found) return
addingEquip.value = true
try {
const existing = equipLookupResult.value.equipment
await updateDoc('Service Equipment', existing.name, {
customer: customer.value.name,
service_location: addEquipLoc.value.name,
})
const idx = equipment.value.findIndex(e => e.name === existing.name)
if (idx >= 0) {
equipment.value[idx].service_location = addEquipLoc.value.name
} else {
equipment.value.push({
...existing,
service_location: addEquipLoc.value.name,
customer: customer.value.name,
})
}
Notify.create({ type: 'positive', message: `Equipement ${existing.name} lie a ${addEquipLoc.value.address_line || addEquipLoc.value.location_name}`, timeout: 2000 })
closeAddEquipment()
} catch (e) {
Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
} finally {
addingEquip.value = false
}
}
return {
scannerState, addEquipOpen, addEquipLoc, addingEquip,
equipLookupResult, equipLookingUp, newEquip,
openAddEquipment, closeAddEquipment, onScanPhoto,
applyScannedCode, createEquipment, linkExistingEquipment,
}
}