import { api } from '../../lib/api.mjs'; const CODE_TYPES = [ { key: 'gl_account', label: 'GL Account Code', placeholder: 'e.g. 6100-Operations' }, { key: 'cost_center', label: 'Cost Center / Department', placeholder: 'e.g. CC-4200' }, { key: 'wbs', label: 'Project WBS / Phase', placeholder: 'e.g. P-1234-A' }, { key: 'billing_ref', label: 'Billing Reference', placeholder: 'e.g. INV-2024-001' }, ]; class WoAccounting extends HTMLElement { #woId = null; #codes = {}; #saving = false; #loading = true; static get observedAttributes() { return ['wo-id']; } connectedCallback() { if (!this.shadowRoot) this.attachShadow({ mode: 'open' }); if (this.#woId) this.#load(); } attributeChangedCallback(_, __, val) { this.#woId = val ? +val : null; if (this.shadowRoot && this.#woId) this.#load(); } async #load() { this.#loading = true; this.#render(); try { const rows = await api.get(`/work-orders/${this.#woId}/accounting`) || []; this.#codes = {}; rows.forEach(r => { this.#codes[r.code_type] = { value: r.code_value, description: r.description }; }); } catch { this.#codes = {}; } this.#loading = false; this.#render(); } async #save() { if (this.#saving) return; this.#saving = true; this.#updateSaveIndicator('saving'); try { const s = this.shadowRoot; const payload = CODE_TYPES .filter(ct => { const v = s.querySelector(`[data-key="${ct.key}"] .code-value`)?.value?.trim(); return v; }) .map(ct => ({ code_type: ct.key, code_value: s.querySelector(`[data-key="${ct.key}"] .code-value`).value.trim(), description: s.querySelector(`[data-key="${ct.key}"] .code-desc`).value.trim(), })); await api.put(`/work-orders/${this.#woId}/accounting`, payload); this.#updateSaveIndicator('saved'); } catch (err) { this.#updateSaveIndicator('error'); window.dispatchEvent(new CustomEvent('wo:toast', { detail: { message: err.message, type: 'error' } })); } finally { this.#saving = false; } } #updateSaveIndicator(state) { const el = this.shadowRoot?.querySelector('#save-indicator'); if (!el) return; const msgs = { saving: '⋯ Saving…', saved: '✓ Saved', error: '✗ Error' }; const colors = { saving: 'var(--text-muted)', saved: 'var(--success)', error: 'var(--danger)' }; el.textContent = msgs[state]; el.style.color = colors[state]; if (state !== 'saving') setTimeout(() => { if (el) el.textContent = ''; }, 2500); } #render() { const s = this.shadowRoot; if (this.#loading) { s.innerHTML = ``; return; } s.innerHTML = `
Accounting Codes
${CODE_TYPES.map((ct, i) => ` ${i > 0 ? '
' : ''}
${ct.label}
`).join('')}
`; s.querySelectorAll('.code-value, .code-desc').forEach(input => { input.addEventListener('blur', () => this.#save()); input.addEventListener('keydown', e => { if (e.key === 'Enter') input.blur(); }); }); } #esc(s) { return (s || '').replace(/&/g,'&').replace(//g,'>'); } } customElements.define('wo-accounting', WoAccounting);