// Global "Quick Add" — press Ctrl/Cmd+J or the sidebar "+" button from any // screen to create a Task, Client, Project or Invoice without navigating // away from whatever you're doing. Kept intentionally minimal (only the // fields each table actually requires) — full editing still happens on the // dedicated screens. (() => { const TYPES = [ { id: 'task', label: 'Task', icon: 'circle-check-big' }, { id: 'client', label: 'Client', icon: 'contact' }, { id: 'project', label: 'Project', icon: 'folder-kanban' }, { id: 'invoice', label: 'Invoice', icon: 'wallet' }, ]; const EMPTY = { task: { title: '', client_id: '', project_id: '', assigned_to: '', due_date: '', priority: 'medium' }, client: { name: '', company: '', email: '', phone: '' }, project: { name: '', client_id: '', due_date: '', priority: 'medium' }, invoice: { client_id: '', amount: '', currency: 'PKR', due_date: '' }, }; function QuickAddModal({ open, onClose, onNavigate }) { const [type, setType] = React.useState('task'); const [form, setForm] = React.useState(EMPTY.task); const [clients, setClients] = React.useState([]); const [projects, setProjects] = React.useState([]); const [team, setTeam] = React.useState([]); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(''); React.useEffect(() => { if (!open || !window.API) return; setError(''); (async () => { try { const [c, p, t] = await Promise.all([ window.API.getClients(), window.API.getProjects(), window.API.getTeam(), ]); setClients(c.data || []); setProjects(p.data || []); setTeam(t.data || []); } catch {} })(); }, [open]); React.useEffect(() => { setForm(EMPTY[type]); setError(''); }, [type]); function set(k, v) { setForm(f => ({ ...f, [k]: v })); } async function handleSubmit() { if (!window.API) return; setError(''); if (type === 'task' && !form.title.trim()) return setError('Title is required.'); if (type === 'client' && !form.name.trim()) return setError('Name is required.'); if (type === 'project' && !form.name.trim()) return setError('Name is required.'); if (type === 'invoice' && (!form.client_id || !form.amount)) return setError('Client and amount are required.'); setLoading(true); try { let result; if (type === 'task') { result = await window.API.createTask({ title: form.title.trim(), client_id: form.client_id || null, project_id: form.project_id || null, assigned_to: form.assigned_to || null, due_date: form.due_date || null, priority: form.priority, created_by: window.TFMyMemberId || null, }); } else if (type === 'client') { result = await window.API.createClient({ name: form.name.trim(), company: form.company || null, email: form.email || null, phone: form.phone || null, }); } else if (type === 'project') { result = await window.API.createProject({ name: form.name.trim(), client_id: form.client_id || null, due_date: form.due_date || null, priority: form.priority, created_by: window.TFMyMemberId || null, }); } else if (type === 'invoice') { // invoice_no is NOT NULL in the DB, and Quick add has no field for it, // so auto-generate a unique one (INV-YYYYMMDD-HHMMSS). Users can rename // it later from the Finance screen's edit form. const now = new Date(); const pad = n => String(n).padStart(2, '0'); const autoNo = `INV-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`; result = await window.API.createInvoice({ invoice_no: autoNo, client_id: form.client_id, amount: Number(form.amount), currency: form.currency, due_date: form.due_date || null, status: 'draft', }); } if (result && result.error) throw new Error(result.error.message || 'Could not create it. Please try again.'); onClose(); if (onNavigate) onNavigate(type === 'task' ? 'tasks' : type === 'client' ? 'crm' : type === 'project' ? 'projects' : 'finance'); } catch (err) { setError(err.message || 'Something went wrong. Please try again.'); } finally { setLoading(false); } } return (
{TYPES.map(t => ( ))}
{error && (
{error}
)} {type === 'task' && ( <> set('title', e.target.value)} placeholder="e.g. Follow up with client" autoFocus />
set('due_date', e.target.value)} />
)} {type === 'client' && ( <> set('name', e.target.value)} placeholder="Client or company name" autoFocus />
set('company', e.target.value)} /> set('phone', e.target.value)} />
set('email', e.target.value)} /> )} {type === 'project' && ( <> set('name', e.target.value)} placeholder="Project name" autoFocus />
set('due_date', e.target.value)} />
)} {type === 'invoice' && ( <>
set('amount', e.target.value)} placeholder="0.00" />
set('due_date', e.target.value)} /> )}
); } Object.assign(window, { QuickAddModal }); })();