// Approvals — unified approval workflow for design, content, invoice, leave, etc. (() => { const { Card, Badge } = window.TechyFuelOSDesignSystem_be0222; const TYPES = [['design','Design'],['content','Content'],['invoice','Invoice'],['leave','Leave'],['other','Other']]; const TYPE_LABEL = Object.fromEntries(TYPES); const EMPTY = { type: 'design', title: '', detail: '', link_url: '' }; function fmtWhen(ts) { if (!ts) return ''; const d = new Date(String(ts).replace(' ', 'T') + (String(ts).endsWith('Z') ? '' : 'Z')); return isNaN(d) ? '' : d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short' }); } function Approvals() { useLucide(); const [rows, setRows] = React.useState([]); const [loading, setLoading] = React.useState(true); const [filter, setFilter] = React.useState('pending'); const [modalOpen, setModalOpen] = React.useState(false); const [form, setForm] = React.useState(EMPTY); const [saving, setSaving] = React.useState(false); const canReview = ['owner','admin','manager'].includes(window.TFMyRole); const load = React.useCallback(() => { if (!window.API || !window.API.getApprovals) { setLoading(false); return; } setLoading(true); window.API.getApprovals().then(r => { if (r.data) setRows(r.data); }).catch(()=>{}).finally(()=>setLoading(false)); }, []); React.useEffect(() => { load(); }, [load]); function set(k,v){ setForm(f=>({ ...f, [k]:v })); } async function submit() { if (!form.title.trim() || !window.API) return; setSaving(true); try { const { data } = await window.API.createApproval(form); if (data) setRows(prev => [data, ...prev]); setModalOpen(false); setForm(EMPTY); } catch { alert('Could not submit approval.'); } setSaving(false); } async function review(a, status) { setRows(prev => prev.map(x => x.id===a.id?{...x,status}:x)); try { await window.API.reviewApproval(a.id, { status }); } catch { load(); } } async function remove(a) { if (!window.confirm('Delete this approval?')) return; setRows(prev => prev.filter(x => x.id !== a.id)); try { await window.API.deleteApproval(a.id); } catch {} } const filtered = filter === 'all' ? rows : rows.filter(r => r.status === filter); const pendingCount = rows.filter(r => r.status === 'pending').length; return (

Approvals

Design, content, invoices & leave — request and approve in one place.

{[['pending',`Pending${pendingCount?` (${pendingCount})`:''}`],['approved','Approved'],['rejected','Rejected'],['all','All']].map(([v,l]) => ( ))}
{['Title','Type','Requested by','When','Status',''].map((h,i)=>())} {loading && } {!loading && filtered.length===0 && } {!loading && filtered.map(a => ( ))}
{h}
Loading…
No {filter==='all'?'':filter} approvals.
{a.link_url ? {a.title} ↗ : a.title} {a.detail &&
{a.detail.slice(0,80)}
}
{TYPE_LABEL[a.type]||a.type} {a.requested_by_name||'—'} {fmtWhen(a.created_at)} {a.status} {canReview && a.status==='pending' && (
)} {canReview && a.status!=='pending' && }
setModalOpen(false)} title="Request approval" onSubmit={submit} loading={saving} submitLabel="Submit"> set('title', e.target.value)} placeholder="e.g. Homepage banner design" />