// ── Pure utility functions (no Vue dependencies) ─────────────────────────────
export function localDateStr (d) {
  if (!d || !(d instanceof Date) || isNaN(d.getTime())) return '—'
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`
}

// Safe date formatter — never throws RangeError
export function fmtDate (d, opts = { weekday:'short', day:'numeric', month:'short' }) {
  if (!d || !(d instanceof Date) || isNaN(d.getTime())) {
    console.warn('[fmtDate] invalid date:', d, typeof d)
    return '—'
  }
  try { return d.toLocaleDateString('fr-CA', opts) } catch (e) {
    console.warn('[fmtDate] toLocaleDateString failed:', d, e.message)
    return localDateStr(d)
  }
}

export function startOfWeek (d) {
  const r = new Date(d); r.setHours(0,0,0,0)
  const diff = r.getDay() === 0 ? -6 : 1 - r.getDay()
  r.setDate(r.getDate() + diff); return r
}

export function startOfMonth (d) { return new Date(d.getFullYear(), d.getMonth(), 1) }

export function timeToH (t) {
  const [h, m] = t.split(':').map(Number)
  return h + m / 60
}

// Occupation → couleur « chaleur » (vert = libre → rouge = saturé). SOURCE UNIQUE :
// bande mobile Planif (.pm-strip), <OccupancyStrip> du tableau de bord, en-tête desktop Planif.
export function heatColor (ratio) {
  const r = +ratio || 0
  if (r >= 1) return '#ef4444'    // saturé / dépassé
  if (r >= 0.75) return '#f59e0b' // chargé
  if (r >= 0.45) return '#84cc16' // moyen
  return '#22c55e'                // libre
}

export function hToTime (h) {
  const totalMin = Math.round(h * 60)
  const hh = Math.floor(totalMin / 60)
  const mm = totalMin % 60
  return `${String(hh).padStart(2,'0')}:${String(mm).padStart(2,'0')}`
}

export function fmtDur (h) {
  const totalMin = Math.round((parseFloat(h) || 0) * 60)
  const hh = Math.floor(totalMin / 60)
  const mm = totalMin % 60
  if (hh === 0) return `${mm}m`
  if (mm === 0) return `${hh}h`
  return `${hh}h${String(mm).padStart(2,'0')}`
}

export const SNAP_MIN = 5
export const SNAP = SNAP_MIN / 60
export function snapH (h) { return Math.round(h * 60 / SNAP_MIN) * SNAP_MIN / 60 }

export function dayLoadColor (ratio) {
  const r = Math.min(ratio, 1.2)
  if (r <= 0.5) return '#10b981'
  if (r <= 0.75) return '#f59e0b'
  if (r <= 1)    return '#f97316'
  return '#ef4444'
}

export function shortAddr (addr) {
  if (!addr) return ''
  const parts = addr.replace(/[A-Z]\d[A-Z]\s?\d[A-Z]\d/g, '').trim().split(/[\s,]+/)
  for (let i = parts.length - 1; i >= 0; i--) {
    if (parts[i].length > 2 && /^[A-ZÀ-Ú]/.test(parts[i])) return parts[i]
  }
  return parts.slice(-2).join(' ')
}

// Service colors & labels
export const SVC_COLORS = { 'Internet':'#3b82f6','Télévisión':'#a855f7','Téléphonie':'#10b981','Multi-service':'#f59e0b' }
export const SVC_ICONS  = { 'Internet':'🌐','Télévisión':'📺','Téléphonie':'📞','Multi-service':'🔧' }
const SVC_CODES  = { 'Internet':'WEB','Télévisión':'TV','Téléphonie':'TEL','Multi-service':'MX' }

export function jobSvcCode (job) {
  if (SVC_CODES[job.service_type]) return SVC_CODES[job.service_type]
  const s = (job.subject || '').toLowerCase()
  if (s.includes('internet')) return 'WEB'
  if (s.includes('tv') || s.includes('télév')) return 'TV'
  if (s.includes('téléph')) return 'TEL'
  if (s.includes('multi')) return 'MX'
  return 'WO'
}

// Couleur par TYPE de ticket, calquée sur le board legacy (osTicket). Téléphonie = vert
// PLUS PÂLE que l'installation. Ordre des tests important (téléph avant télé ; télé avant install).
export function legacyDeptColor (dept) {
  if (!dept) return null
  const d = String(dept).toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '')
  if (d.includes('teleph')) return '#8fce93'                          // Téléphonie → vert pâle
  if (d.includes('tele') || d.includes('televis')) return '#ec5fb0'   // Télé (install/réparation) → rose
  if (d.includes('desinstall') || d.includes('retrait')) return '#c0392b' // Désinstallation → rouge foncé
  if (d.includes('repar')) return '#f1c84b'                           // Réparation (Fibre) → jaune/or
  if (d.includes('installation') || d.includes('monteur') || d.includes('fusionneur')) return '#46992f' // Installation (Fibre) → vert
  return null
}

// Lien « Répondre au ticket » du serveur legacy (osTicket custom) — celui que les techs reçoivent
// pour écrire dans le ticket. Format observé : reply_ticket.php?ticket=<id>&staff=<staffId>.
// staff par défaut = 3301 (compte « Tech Targo » sous lequel le ticket est assigné dans legacy).
export const LEGACY_REPLY_BASE = 'https://store.targo.ca/targo/reply_ticket.php'
export function legacyReplyUrl (job, staffId) {
  const id = job && job.legacyTicketId
  if (!id) return null
  return `${LEGACY_REPLY_BASE}?ticket=${encodeURIComponent(id)}&staff=${staffId || 3301}`
}

export function jobColor (job, techColors, store) {
  // Tech en pause/absent (statut interne 'off') → ses jobs en ROUGE vif (à réassigner) — priorité opérationnelle
  if (job.assignedTech && store) {
    const at = store.technicians.find(x => x.id === job.assignedTech)
    if (at && at.status === 'off') return '#e53935'
  }
  // Type legacy (pont osTicket) → couleur « comme legacy »
  const ld = legacyDeptColor(job.legacyDept)
  if (ld) return ld
  if (SVC_COLORS[job.service_type]) return SVC_COLORS[job.service_type]
  const s = (job.subject||'').toLowerCase()
  if (s.includes('internet')) return '#3b82f6'
  if (s.includes('tv')||s.includes('télév')) return '#a855f7'
  if (s.includes('téléph')) return '#10b981'
  if (s.includes('multi')) return '#f59e0b'
  if (job.assignedTech && store) {
    const t = store.technicians.find(x=>x.id===job.assignedTech)
    if (t) return techColors[t.colorIdx]
  }
  return '#6b7280'
}

export function jobSpansDate (job, ds, tech) {
  const start = job.scheduledDate
  const end = job.endDate
  if (!start) return false
  if (!end) return start === ds
  if (ds < start || ds > end) return false
  // Multi-day jobs skip the tech's off-days (weekends, custom schedule)
  // unless the job is flagged as emergency/continuous
  if (tech && !job.continuous) {
    const sched = techDaySchedule(tech, ds)
    if (!sched) return false
  }
  return true
}

export function sortJobsByTime (jobs) {
  return jobs.slice().sort((a, b) => {
    const aH = a.startTime ? timeToH(a.startTime) : (a.startHour ?? 8)
    const bH = b.startTime ? timeToH(b.startTime) : (b.startHour ?? 8)
    return aH - bH
  })
}

// ── ERPNext ↔ internal status mapping ──
// ERPNext stores French labels; frontend uses internal codes
export const ERP_STATUS_TO_INTERNAL = {
  'Disponible': 'available',
  'En route':   'en-route',
  'En pause':   'off',
  'Hors ligne': 'offline',
  'Inactif':    'inactive',
}
export const INTERNAL_STATUS_TO_ERP = Object.fromEntries(
  Object.entries(ERP_STATUS_TO_INTERNAL).map(([k, v]) => [v, k])
)
export function normalizeStatus (erpStatus) {
  return ERP_STATUS_TO_INTERNAL[erpStatus] || erpStatus || 'available'
}
export function toErpStatus (internalStatus) {
  return INTERNAL_STATUS_TO_ERP[internalStatus] || internalStatus || 'Disponible'
}

// ── Weekly schedule helpers ──
const DAY_KEYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
export const DAY_LABELS = { mon: 'Lun', tue: 'Mar', wed: 'Mer', thu: 'Jeu', fri: 'Ven', sat: 'Sam', sun: 'Dim' }
export const WEEK_DAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
export const DEFAULT_WEEKLY_SCHEDULE = {
  mon: { start: '08:00', end: '16:00' }, tue: { start: '08:00', end: '16:00' },
  wed: { start: '08:00', end: '16:00' }, thu: { start: '08:00', end: '16:00' },
  fri: { start: '08:00', end: '16:00' }, sat: null, sun: null,
}
export const SCHEDULE_PRESETS = [
  { key: 'standard', label: '5×8h (lun-ven)', schedule: { ...DEFAULT_WEEKLY_SCHEDULE } },
  { key: '4x10', label: '4×10h (lun-jeu)', schedule: {
    mon: { start: '07:00', end: '17:00' }, tue: { start: '07:00', end: '17:00' },
    wed: { start: '07:00', end: '17:00' }, thu: { start: '07:00', end: '17:00' },
    fri: null, sat: null, sun: null,
  }},
  { key: '3x12', label: '3×12h (lun-mer)', schedule: {
    mon: { start: '06:00', end: '18:00' }, tue: { start: '06:00', end: '18:00' },
    wed: { start: '06:00', end: '18:00' }, thu: null, fri: null, sat: null, sun: null,
  }},
]
export function parseWeeklySchedule (jsonStr) {
  if (!jsonStr) return { ...DEFAULT_WEEKLY_SCHEDULE }
  try {
    const parsed = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr
    if (typeof parsed !== 'object' || parsed === null) return { ...DEFAULT_WEEKLY_SCHEDULE }
    return parsed
  } catch { return { ...DEFAULT_WEEKLY_SCHEDULE } }
}
export function techDaySchedule (tech, dateStr) {
  const d = new Date(dateStr + 'T12:00:00')
  const key = DAY_KEYS[d.getDay()]
  const sched = tech.weeklySchedule || DEFAULT_WEEKLY_SCHEDULE
  const day = sched[key]
  if (!day) return null
  const startH = timeToH(day.start || '08:00')
  const endH = timeToH(day.end || '16:00')
  return { start: day.start, end: day.end, startH, endH, hours: endH - startH }
}
export function techDayCapacityH (tech, dateStr) {
  const s = techDaySchedule(tech, dateStr)
  return s ? s.hours : 0
}

// Status helpers
export const STATUS_MAP = {
  'available':   { cls:'st-available', label:'Disponible' },
  'en-route':    { cls:'st-enroute',   label:'En route'   },
  'busy':        { cls:'st-busy',      label:'En cours'   },
  'in progress': { cls:'st-busy',      label:'En cours'   },
  'off':         { cls:'st-off',       label:'En pause'   },
  'offline':     { cls:'st-off',       label:'Hors ligne' },
  'inactive':    { cls:'st-inactive',  label:'Inactif'    },
}
export function stOf (t) { return STATUS_MAP[(t.status||'').toLowerCase()] || STATUS_MAP['available'] }

export function prioLabel (p) { return { high: 'Urgent', urgent: 'Urgent', medium: 'Moyenne', normal: 'Moyenne', low: 'Basse' }[p] || p || '—' }
export function prioClass (p) { return { high:'prio-high', medium:'prio-med', low:'prio-low' }[p] || '' }

// Lucide-style inline SVG icons (stroke-based)
const _s = (d, w=10) => `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${w}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">${d}</svg>`

export const ICON = {
  pin:      _s('<path d="M12 17v5"/><path d="M9 11V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v7"/><path d="M4 15h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z"/>'),
  mapPin:   _s('<path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/>'),
  wifi:     _s('<path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><circle cx="12" cy="20" r="1"/>'),
  tv:       _s('<rect x="2" y="7" width="20" height="15" rx="2" ry="2"/><path d="m17 2-5 5-5-5"/>'),
  phone:    _s('<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/>'),
  wrench:   _s('<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>'),
  cable:    _s('<path d="M4 9a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z"/><path d="M8 7V4"/><path d="M16 7V4"/><path d="M12 16v4"/>'),
  check:    _s('<path d="M20 6L9 17l-5-5"/>'),
  x:        _s('<path d="M18 6L6 18M6 6l12 12"/>'),
  clock:    _s('<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>'),
  loader:   _s('<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/>'),
  truck:    _s('<path d="M5 17h2l3-6h4l3 6h2M7 17a2 2 0 1 1-4 0M21 17a2 2 0 1 1-4 0"/>'),
  // ── Single-color toolbar set (used in dispatch top bar). All inherit
  // currentColor; CSS sizes them via .sb-icon-svg below. Strokes only,
  // 2px stroke for crisp rendering at 14-16px display size.
  user:     _s('<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>'),
  users:    _s('<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>'),
  package:  _s('<path d="m7.5 4.27 9 5.15"/><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/>'),
  sliders:  _s('<line x1="21" x2="14" y1="4" y2="4"/><line x1="10" x2="3" y1="4" y2="4"/><line x1="21" x2="12" y1="12" y2="12"/><line x1="8" x2="3" y1="12" y2="12"/><line x1="21" x2="16" y1="20" y2="20"/><line x1="12" x2="3" y1="20" y2="20"/><line x1="14" x2="14" y1="2" y2="6"/><line x1="8" x2="8" y1="10" y2="14"/><line x1="16" x2="16" y1="18" y2="22"/>'),
  chevDown: _s('<path d="m6 9 6 6 6-6"/>'),
  map:      _s('<path d="M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z"/><path d="M15 5.764v15"/><path d="M9 3.236v15"/>'),
  clipboard:_s('<rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>'),
  sparkles: _s('<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/><path d="M4 17v2"/><path d="M5 18H3"/>'),
  signal:   _s('<path d="M2 20h.01"/><path d="M7 20v-4"/><path d="M12 20v-8"/><path d="M17 20V8"/><path d="M22 4v16"/>'),
  rotateCw: _s('<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/>'),
  alertTri: _s('<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>'),
  moreH:    _s('<circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/>'),
  pause:    _s('<rect x="14" y="4" width="4" height="16" rx="1"/><rect x="6" y="4" width="4" height="16" rx="1"/>'),
  play:     _s('<polygon points="6 3 20 12 6 21 6 3"/>'),
  externalLink: _s('<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>'),
  target:   _s('<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>'),
  calendar: _s('<rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/>'),
}

// Job type icon based on service/subject
export function jobTypeIcon (job) {
  const s = (job.subject || '').toLowerCase()
  const svc = job.service_type || ''
  if (svc === 'Internet' || s.includes('internet') || s.includes('fibre') || s.includes('routeur') || s.includes('wifi')) return ICON.wifi
  if (svc === 'Télévisión' || s.includes('tv') || s.includes('télév')) return ICON.tv
  if (svc === 'Téléphonie' || s.includes('téléph') || s.includes('phone')) return ICON.phone
  if (s.includes('cable') || s.includes('câble') || s.includes('cablage')) return ICON.cable
  if (s.includes('camera') || s.includes('install')) return ICON.wrench
  return ICON.wrench
}

// ── RRULE expansion (pure JS, no deps) ───────────────────────────────────────
const _dayMap = { SU: 0, MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6 }
const _d = s => new Date(s + 'T12:00:00')
const _fmt = dt => `${dt.getFullYear()}-${String(dt.getMonth()+1).padStart(2,'0')}-${String(dt.getDate()).padStart(2,'0')}`

export function expandRRule (rrule, dtStart, rangeStart, rangeEnd, pausePeriods = []) {
  if (!rrule || !dtStart) return []
  const params = Object.fromEntries(rrule.split(';').map(p => p.split('=')))
  const freq = params.FREQ, interval = parseInt(params.INTERVAL || '1', 10)
  const byDay = params.BYDAY ? params.BYDAY.split(',') : null
  const byMonthDay = params.BYMONTHDAY ? parseInt(params.BYMONTHDAY, 10) : null
  const byMonth = params.BYMONTH ? parseInt(params.BYMONTH, 10) : null

  const end = _d(rangeEnd), rStart = _d(rangeStart), origin = _d(dtStart)
  const results = []
  const isPaused = dt => pausePeriods.some(p => dt >= _d(p.from) && dt <= _d(p.until))
  const push = dt => { if (dt >= rStart && dt <= end && !isPaused(dt)) results.push(_fmt(dt)) }

  if (freq === 'DAILY') {
    const c = new Date(origin); while (c <= end) { push(c); c.setDate(c.getDate() + interval) }
  } else if (freq === 'WEEKLY') {
    const daySet = byDay ? new Set(byDay.map(d => _dayMap[d])) : new Set([origin.getDay()])
    const ws = new Date(origin); ws.setDate(ws.getDate() - ws.getDay())
    while (ws <= end) {
      for (let dow = 0; dow < 7; dow++) {
        if (!daySet.has(dow)) continue
        const dt = new Date(ws); dt.setDate(dt.getDate() + dow)
        if (dt >= origin && dt <= end) push(dt)
      }
      ws.setDate(ws.getDate() + 7 * interval)
    }
  } else if (freq === 'MONTHLY') {
    const day = byMonthDay || origin.getDate()
    const c = new Date(origin); while (c <= end) {
      const dt = new Date(c.getFullYear(), c.getMonth(), day, 12)
      if (dt.getMonth() === c.getMonth()) push(dt)
      c.setMonth(c.getMonth() + interval)
    }
  } else if (freq === 'YEARLY') {
    const month = byMonth ? byMonth - 1 : origin.getMonth()
    const day = byMonthDay || origin.getDate()
    let year = origin.getFullYear(); while (year <= end.getFullYear()) {
      const dt = new Date(year, month, day, 12)
      if (dt.getMonth() === month) push(dt)
      year += interval
    }
  }
  return results
}

// Build RRULE string from UI fields
export function buildRRule ({ freq, interval, byDay, byMonthDay }) {
  let rule = `FREQ=${freq}`
  if (interval && interval > 1) rule += `;INTERVAL=${interval}`
  if (byDay?.length && freq === 'WEEKLY') rule += `;BYDAY=${byDay.join(',')}`
  if (byMonthDay && (freq === 'MONTHLY' || freq === 'YEARLY')) rule += `;BYMONTHDAY=${byMonthDay}`
  return rule
}

// Parse RRULE string to UI fields
export function parseRRule (rrule) {
  if (!rrule) return { freq: 'WEEKLY', interval: 1, byDay: ['MO'], byMonthDay: null }
  const params = Object.fromEntries(rrule.split(';').map(p => p.split('=')))
  return {
    freq: params.FREQ || 'WEEKLY',
    interval: parseInt(params.INTERVAL || '1', 10),
    byDay: params.BYDAY ? params.BYDAY.split(',') : [],
    byMonthDay: params.BYMONTHDAY ? parseInt(params.BYMONTHDAY, 10) : null,
  }
}

// Priority color
export function prioColor (p) {
  return { high: '#ef4444', medium: '#f59e0b', low: '#7b80a0' }[p] || '#7b80a0'
}

// Status icon (minimal, for timeline blocks)
// Serialize assistants array for ERPNext API calls (used in store + page)
export function serializeAssistants (assistants) {
  return (assistants || []).map(a => ({ tech_id: a.techId, tech_name: a.techName, duration_h: a.duration, note: a.note || '', pinned: a.pinned ? 1 : 0 }))
}

export function jobStatusIcon (job) {
  const st = (job.status || '').toLowerCase()
  if (st === 'completed') return { svg: ICON.check, cls: 'si-done' }
  if (st === 'cancelled') return { svg: ICON.x, cls: 'si-cancelled' }
  if (st === 'en-route') return { svg: ICON.truck, cls: 'si-enroute' }
  if (st === 'in progress') return { svg: ICON.loader, cls: 'si-progress' }
  return { svg: '', cls: '' } // no icon for open/assigned — the type icon is enough
}
