AV launch: global color scheme to match logo — gold #ffb800 primary, red #d60701 secondary, warm dark bars (1a1208 / 161310) replacing the inherited BB blue palette. Swapped 21 root tokens + 112 component files.
This commit is contained in:
@@ -91,7 +91,7 @@ export default function ClientsPage() {
|
||||
setForm({ company_name: '', contact_name: '', contact_email: '', contact_phone: '', billing_address: '', notes: '' });
|
||||
}
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -102,7 +102,7 @@ export default function ClientsPage() {
|
||||
<h1 className="text-xl font-bold">Clients</h1>
|
||||
<Link href="/admin/accounting" className="text-xs text-[#555] hover:text-[#888]">← Accounting</Link>
|
||||
</div>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500 transition-colors">+ New Client</button>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500 transition-colors">+ New Client</button>
|
||||
</div>
|
||||
|
||||
{/* Success Toast */}
|
||||
@@ -116,9 +116,9 @@ export default function ClientsPage() {
|
||||
{/* Filters */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{['active','inactive','all'].map(s => (
|
||||
<button key={s} onClick={() => setFilter(s)} className={`px-3 py-1 rounded text-xs capitalize ${filter === s ? 'bg-[#3b82f6] text-white' : 'bg-[#111] border border-[#252525] text-[#888] hover:text-white'}`}>{s}</button>
|
||||
<button key={s} onClick={() => setFilter(s)} className={`px-3 py-1 rounded text-xs capitalize ${filter === s ? 'bg-[#ffb800] text-white' : 'bg-[#111] border border-[#252525] text-[#888] hover:text-white'}`}>{s}</button>
|
||||
))}
|
||||
<input value={search} onChange={e => setSearch(e.target.value)} placeholder="Search clients…" className="ml-auto px-3 py-1 bg-[#111] border border-[#252525] rounded text-xs text-white placeholder-[#555] focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input value={search} onChange={e => setSearch(e.target.value)} placeholder="Search clients…" className="ml-auto px-3 py-1 bg-[#111] border border-[#252525] rounded text-xs text-white placeholder-[#555] focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
|
||||
{/* New Client Form */}
|
||||
@@ -148,7 +148,7 @@ export default function ClientsPage() {
|
||||
type={f.key === 'contact_email' ? 'email' : 'text'}
|
||||
value={(form as any)[f.key]}
|
||||
onChange={e => handleChange(f.key, e.target.value)}
|
||||
className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#3b82f6] ${errors[f.key] ? 'border-red-500/60' : 'border-[#252525]'}`}
|
||||
className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#ffb800] ${errors[f.key] ? 'border-red-500/60' : 'border-[#252525]'}`}
|
||||
/>
|
||||
{errors[f.key] && <p className="mt-1 text-xs text-red-400">{errors[f.key]}</p>}
|
||||
</div>
|
||||
@@ -156,16 +156,16 @@ export default function ClientsPage() {
|
||||
<div className="col-span-2">
|
||||
<label className="text-xs text-[#888] block mb-1">Billing Address</label>
|
||||
<input value={form.billing_address} onChange={e => handleChange('billing_address', e.target.value)}
|
||||
className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<label className="text-xs text-[#888] block mb-1">Notes</label>
|
||||
<textarea value={form.notes} onChange={e => handleChange('notes', e.target.value)} rows={2}
|
||||
className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Create Client'}</button>
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Create Client'}</button>
|
||||
<button type="button" onClick={handleCancel} className="px-3 py-1.5 bg-[#1a1a1a] border border-[#252525] rounded text-xs text-[#888] hover:text-white">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -197,7 +197,7 @@ export default function ClientsPage() {
|
||||
<span className={`px-2 py-0.5 rounded-full text-xs ${c.status === 'active' ? 'bg-green-400/10 text-green-400' : 'bg-[#1a1a1a] text-[#555]'}`}>{c.status}</span>
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
<Link href={`/admin/accounting/clients/${c.id}`} className="text-xs text-[#3b82f6] hover:underline">View</Link>
|
||||
<Link href={`/admin/accounting/clients/${c.id}`} className="text-xs text-[#ffb800] hover:underline">View</Link>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function CommissionsPage() {
|
||||
load();
|
||||
}
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -84,7 +84,7 @@ export default function CommissionsPage() {
|
||||
<span>Tier progress</span><span>{pct}%</span>
|
||||
</div>
|
||||
<div className="h-1.5 bg-[#1a1a1a] rounded-full overflow-hidden">
|
||||
<div className="h-full bg-[#3b82f6] rounded-full transition-all" style={{ width: `${pct}%` }} />
|
||||
<div className="h-full bg-[#ffb800] rounded-full transition-all" style={{ width: `${pct}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -111,7 +111,7 @@ export default function CommissionsPage() {
|
||||
) : commissions.map((c: any) => (
|
||||
<tr key={c.id} className="border-b border-[#1a1a1a] hover:bg-[#161616]">
|
||||
<td className="px-3 py-2 text-white text-xs">{c.rmp_sales_staff?.full_name ?? '—'}</td>
|
||||
<td className="px-3 py-2 text-[#3b82f6] text-xs font-mono">{c.rmp_invoices?.invoice_number ?? '—'}</td>
|
||||
<td className="px-3 py-2 text-[#ffb800] text-xs font-mono">{c.rmp_invoices?.invoice_number ?? '—'}</td>
|
||||
<td className="px-3 py-2 text-[#ccc] text-xs">{fmt(c.sale_amount_cents ?? 0)}</td>
|
||||
<td className="px-3 py-2 text-[#888] text-xs">{((c.commission_rate ?? 0) * 100).toFixed(1)}%</td>
|
||||
<td className="px-3 py-2 text-green-400 text-xs">{fmt(c.commission_amount_cents ?? 0)}</td>
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function DocumentsPage() {
|
||||
|
||||
const years = Array.from({ length: 5 }, (_, i) => new Date().getFullYear() - i);
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -54,14 +54,14 @@ export default function DocumentsPage() {
|
||||
<h1 className="text-xl font-bold">Document Vault</h1>
|
||||
<Link href="/admin/accounting" className="text-xs text-[#555] hover:text-[#888]">← Accounting</Link>
|
||||
</div>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500">+ Upload Document</button>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500">+ Upload Document</button>
|
||||
</div>
|
||||
|
||||
{/* Year tabs */}
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
<button onClick={() => setFilterYear('')} className={`px-3 py-1 rounded text-xs ${!filterYear ? 'bg-[#3b82f6] text-white' : 'bg-[#111] border border-[#252525] text-[#888]'}`}>All Years</button>
|
||||
<button onClick={() => setFilterYear('')} className={`px-3 py-1 rounded text-xs ${!filterYear ? 'bg-[#ffb800] text-white' : 'bg-[#111] border border-[#252525] text-[#888]'}`}>All Years</button>
|
||||
{years.map(y => (
|
||||
<button key={y} onClick={() => setFilterYear(String(y))} className={`px-3 py-1 rounded text-xs ${filterYear === String(y) ? 'bg-[#3b82f6] text-white' : 'bg-[#111] border border-[#252525] text-[#888]'}`}>{y}</button>
|
||||
<button key={y} onClick={() => setFilterYear(String(y))} className={`px-3 py-1 rounded text-xs ${filterYear === String(y) ? 'bg-[#ffb800] text-white' : 'bg-[#111] border border-[#252525] text-[#888]'}`}>{y}</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -84,31 +84,31 @@ export default function DocumentsPage() {
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Title *</label>
|
||||
<input required value={form.title} onChange={e => setForm(p => ({ ...p, title: e.target.value }))} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input required value={form.title} onChange={e => setForm(p => ({ ...p, title: e.target.value }))} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
{form.document_type === 'sales_agreement' && (
|
||||
<>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Person *</label>
|
||||
<input required value={form.person} onChange={e => setForm(p => ({ ...p, person: e.target.value }))} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input required value={form.person} onChange={e => setForm(p => ({ ...p, person: e.target.value }))} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Year *</label>
|
||||
<input type="number" required value={form.year} onChange={e => setForm(p => ({ ...p, year: e.target.value }))} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input type="number" required value={form.year} onChange={e => setForm(p => ({ ...p, year: e.target.value }))} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="col-span-2">
|
||||
<label className="text-xs text-[#888] block mb-1">File URL</label>
|
||||
<input value={form.file_url} onChange={e => setForm(p => ({ ...p, file_url: e.target.value }))} placeholder="https://…" className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input value={form.file_url} onChange={e => setForm(p => ({ ...p, file_url: e.target.value }))} placeholder="https://…" className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<label className="text-xs text-[#888] block mb-1">Notes</label>
|
||||
<textarea value={form.notes} onChange={e => setForm(p => ({ ...p, notes: e.target.value }))} rows={2} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<textarea value={form.notes} onChange={e => setForm(p => ({ ...p, notes: e.target.value }))} rows={2} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Save Document'}</button>
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Save Document'}</button>
|
||||
<button type="button" onClick={() => setShowForm(false)} className="px-3 py-1.5 bg-[#1a1a1a] border border-[#252525] rounded text-xs text-[#888] hover:text-white">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -136,7 +136,7 @@ export default function DocumentsPage() {
|
||||
<td className="px-4 py-2 text-[#888] text-xs">{d.year ?? '—'}</td>
|
||||
<td className="px-4 py-2 text-[#555] text-xs">{d.uploaded_at ? new Date(d.uploaded_at).toLocaleDateString() : '—'}</td>
|
||||
<td className="px-4 py-2 flex gap-2">
|
||||
{d.file_url && <a href={d.file_url} target="_blank" rel="noopener noreferrer" className="text-xs text-[#3b82f6] hover:underline">Download</a>}
|
||||
{d.file_url && <a href={d.file_url} target="_blank" rel="noopener noreferrer" className="text-xs text-[#ffb800] hover:underline">Download</a>}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -84,7 +84,7 @@ export default function ExpensesPage() {
|
||||
setForm({ vendor: '', amount: '', category: 'other', description: '', expense_date: new Date().toISOString().split('T')[0], receipt_source: 'manual' });
|
||||
}
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -95,7 +95,7 @@ export default function ExpensesPage() {
|
||||
<h1 className="text-xl font-bold">Expenses</h1>
|
||||
<Link href="/admin/accounting" className="text-xs text-[#555] hover:text-[#888]">← Accounting</Link>
|
||||
</div>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500">+ Log Expense</button>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500">+ Log Expense</button>
|
||||
</div>
|
||||
|
||||
{/* Success Toast */}
|
||||
@@ -132,16 +132,16 @@ export default function ExpensesPage() {
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Vendor</label>
|
||||
<input type="text" value={form.vendor} onChange={e => handleChange('vendor', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input type="text" value={form.vendor} onChange={e => handleChange('vendor', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Amount ($) <span className="text-red-400">*</span></label>
|
||||
<input type="number" step="0.01" value={form.amount} onChange={e => handleChange('amount', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#3b82f6] ${errors.amount ? 'border-red-500/60' : 'border-[#252525]'}`} />
|
||||
<input type="number" step="0.01" value={form.amount} onChange={e => handleChange('amount', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#ffb800] ${errors.amount ? 'border-red-500/60' : 'border-[#252525]'}`} />
|
||||
{errors.amount && <p className="mt-1 text-xs text-red-400">{errors.amount}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Date <span className="text-red-400">*</span></label>
|
||||
<input type="date" value={form.expense_date} onChange={e => handleChange('expense_date', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#3b82f6] ${errors.expense_date ? 'border-red-500/60' : 'border-[#252525]'}`} />
|
||||
<input type="date" value={form.expense_date} onChange={e => handleChange('expense_date', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#ffb800] ${errors.expense_date ? 'border-red-500/60' : 'border-[#252525]'}`} />
|
||||
{errors.expense_date && <p className="mt-1 text-xs text-red-400">{errors.expense_date}</p>}
|
||||
</div>
|
||||
<div>
|
||||
@@ -152,11 +152,11 @@ export default function ExpensesPage() {
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<label className="text-xs text-[#888] block mb-1">Description</label>
|
||||
<input value={form.description} onChange={e => handleChange('description', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input value={form.description} onChange={e => handleChange('description', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Log Expense'}</button>
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Log Expense'}</button>
|
||||
<button type="button" onClick={handleCancel} className="px-3 py-1.5 bg-[#1a1a1a] border border-[#252525] rounded text-xs text-[#888] hover:text-white">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -83,7 +83,7 @@ function InvoicesContent() {
|
||||
setPayForm({ method: 'check', paid_date: new Date().toISOString().split('T')[0], wire_reference: '' });
|
||||
}
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -94,7 +94,7 @@ function InvoicesContent() {
|
||||
<h1 className="text-xl font-bold">Invoices</h1>
|
||||
<Link href="/admin/accounting" className="text-xs text-[#555] hover:text-[#888]">← Accounting</Link>
|
||||
</div>
|
||||
<Link href="/admin/accounting/orders" className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500">+ New Invoice (via Order)</Link>
|
||||
<Link href="/admin/accounting/orders" className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500">+ New Invoice (via Order)</Link>
|
||||
</div>
|
||||
|
||||
{/* Success Toast */}
|
||||
@@ -108,7 +108,7 @@ function InvoicesContent() {
|
||||
{/* Filters */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{['','pending','paid','overdue','cancelled'].map(s => (
|
||||
<button key={s} onClick={() => setFilterStatus(s)} className={`px-3 py-1 rounded text-xs capitalize ${filterStatus === s ? 'bg-[#3b82f6] text-white' : 'bg-[#111] border border-[#252525] text-[#888] hover:text-white'}`}>{s || 'All'}</button>
|
||||
<button key={s} onClick={() => setFilterStatus(s)} className={`px-3 py-1 rounded text-xs capitalize ${filterStatus === s ? 'bg-[#ffb800] text-white' : 'bg-[#111] border border-[#252525] text-[#888] hover:text-white'}`}>{s || 'All'}</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -168,7 +168,7 @@ function InvoicesContent() {
|
||||
<tr><td colSpan={9} className="px-4 py-8 text-center text-[#555] text-xs">No invoices found</td></tr>
|
||||
) : invoices.map((inv: any) => (
|
||||
<tr key={inv.id} className="border-b border-[#1a1a1a] hover:bg-[#161616]">
|
||||
<td className="px-3 py-2 text-[#3b82f6] font-mono text-xs">{inv.invoice_number}</td>
|
||||
<td className="px-3 py-2 text-[#ffb800] font-mono text-xs">{inv.invoice_number}</td>
|
||||
<td className="px-3 py-2 text-white text-xs">{inv.rmp_clients?.company_name ?? '—'}</td>
|
||||
<td className="px-3 py-2 text-[#888] text-xs capitalize">{inv.site}</td>
|
||||
<td className="px-3 py-2 text-[#888] text-xs font-mono">{inv.rmp_orders?.internal_order_number ?? '—'}</td>
|
||||
@@ -195,7 +195,7 @@ function InvoicesContent() {
|
||||
|
||||
export default function InvoicesPage() {
|
||||
return (
|
||||
<Suspense fallback={<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>}>
|
||||
<Suspense fallback={<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>}>
|
||||
<InvoicesContent />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
@@ -104,7 +104,7 @@ function OrdersContent() {
|
||||
setForm({ client_id: '', site: '', product_id: '', ad_unit: '', description: '', start_date: '', end_date: '', total_amount_cents: '', currency: 'USD', invoicing_schedule: 'single', next_invoice_date: '', staff_id: '', po_number: '', notes: '' });
|
||||
}
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -115,7 +115,7 @@ function OrdersContent() {
|
||||
<h1 className="text-xl font-bold">Orders</h1>
|
||||
<Link href="/admin/accounting" className="text-xs text-[#555] hover:text-[#888]">← Accounting</Link>
|
||||
</div>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500">+ New Order</button>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500">+ New Order</button>
|
||||
</div>
|
||||
|
||||
{/* Success Toast */}
|
||||
@@ -136,7 +136,7 @@ function OrdersContent() {
|
||||
<option value="">All Statuses</option>
|
||||
{STATUSES.map(s => <option key={s} value={s}>{s}</option>)}
|
||||
</select>
|
||||
<input value={filterClient} onChange={e => setFilterClient(e.target.value)} placeholder="Filter by client…" className="px-2 py-1 bg-[#111] border border-[#252525] rounded text-xs text-white placeholder-[#555] focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input value={filterClient} onChange={e => setFilterClient(e.target.value)} placeholder="Filter by client…" className="px-2 py-1 bg-[#111] border border-[#252525] rounded text-xs text-white placeholder-[#555] focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
|
||||
{/* New Order Form */}
|
||||
@@ -154,7 +154,7 @@ function OrdersContent() {
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Client <span className="text-red-400">*</span></label>
|
||||
<select value={form.client_id} onChange={e => handleChange('client_id', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#3b82f6] ${errors.client_id ? 'border-red-500/60' : 'border-[#252525]'}`}>
|
||||
<select value={form.client_id} onChange={e => handleChange('client_id', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#ffb800] ${errors.client_id ? 'border-red-500/60' : 'border-[#252525]'}`}>
|
||||
<option value="">Select client…</option>
|
||||
{clients.map((c: any) => <option key={c.id} value={c.id}>{c.company_name}</option>)}
|
||||
</select>
|
||||
@@ -162,7 +162,7 @@ function OrdersContent() {
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Site <span className="text-red-400">*</span></label>
|
||||
<select value={form.site} onChange={e => handleChange('site', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#3b82f6] ${errors.site ? 'border-red-500/60' : 'border-[#252525]'}`}>
|
||||
<select value={form.site} onChange={e => handleChange('site', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#ffb800] ${errors.site ? 'border-red-500/60' : 'border-[#252525]'}`}>
|
||||
<option value="">Select site…</option>
|
||||
{SITES.map(s => <option key={s} value={s}>{s}</option>)}
|
||||
</select>
|
||||
@@ -170,57 +170,57 @@ function OrdersContent() {
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Product</label>
|
||||
<select value={form.product_id} onChange={e => handleChange('product_id', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]">
|
||||
<select value={form.product_id} onChange={e => handleChange('product_id', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]">
|
||||
<option value="">Select product…</option>
|
||||
{products.map((p: any) => <option key={p.id} value={p.id}>{p.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Ad Unit</label>
|
||||
<input value={form.ad_unit} onChange={e => handleChange('ad_unit', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input value={form.ad_unit} onChange={e => handleChange('ad_unit', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Start Date</label>
|
||||
<input type="date" value={form.start_date} onChange={e => handleChange('start_date', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input type="date" value={form.start_date} onChange={e => handleChange('start_date', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">End Date</label>
|
||||
<input type="date" value={form.end_date} onChange={e => handleChange('end_date', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#3b82f6] ${errors.end_date ? 'border-red-500/60' : 'border-[#252525]'}`} />
|
||||
<input type="date" value={form.end_date} onChange={e => handleChange('end_date', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#ffb800] ${errors.end_date ? 'border-red-500/60' : 'border-[#252525]'}`} />
|
||||
{errors.end_date && <p className="mt-1 text-xs text-red-400">{errors.end_date}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Total Amount ($)</label>
|
||||
<input type="number" step="0.01" value={form.total_amount_cents} onChange={e => handleChange('total_amount_cents', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#3b82f6] ${errors.total_amount_cents ? 'border-red-500/60' : 'border-[#252525]'}`} />
|
||||
<input type="number" step="0.01" value={form.total_amount_cents} onChange={e => handleChange('total_amount_cents', e.target.value)} className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#ffb800] ${errors.total_amount_cents ? 'border-red-500/60' : 'border-[#252525]'}`} />
|
||||
{errors.total_amount_cents && <p className="mt-1 text-xs text-red-400">{errors.total_amount_cents}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Invoicing Schedule</label>
|
||||
<select value={form.invoicing_schedule} onChange={e => handleChange('invoicing_schedule', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]">
|
||||
<select value={form.invoicing_schedule} onChange={e => handleChange('invoicing_schedule', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]">
|
||||
{SCHEDULES.map(s => <option key={s} value={s}>{s}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Next Invoice Date</label>
|
||||
<input type="date" value={form.next_invoice_date} onChange={e => handleChange('next_invoice_date', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input type="date" value={form.next_invoice_date} onChange={e => handleChange('next_invoice_date', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">Salesperson</label>
|
||||
<select value={form.staff_id} onChange={e => handleChange('staff_id', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]">
|
||||
<select value={form.staff_id} onChange={e => handleChange('staff_id', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]">
|
||||
<option value="">None</option>
|
||||
{staff.map((s: any) => <option key={s.id} value={s.id}>{s.full_name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-[#888] block mb-1">PO Number</label>
|
||||
<input value={form.po_number} onChange={e => handleChange('po_number', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<input value={form.po_number} onChange={e => handleChange('po_number', e.target.value)} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
<div className="col-span-2 md:col-span-3">
|
||||
<label className="text-xs text-[#888] block mb-1">Notes</label>
|
||||
<textarea value={form.notes} onChange={e => handleChange('notes', e.target.value)} rows={2} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#3b82f6]" />
|
||||
<textarea value={form.notes} onChange={e => handleChange('notes', e.target.value)} rows={2} className="w-full px-2 py-1.5 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none focus:border-[#ffb800]" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Create Order'}</button>
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Create Order'}</button>
|
||||
<button type="button" onClick={handleCancel} className="px-3 py-1.5 bg-[#1a1a1a] border border-[#252525] rounded text-xs text-[#888] hover:text-white">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -245,7 +245,7 @@ function OrdersContent() {
|
||||
<tr key={o.id} className="border-b border-[#1a1a1a] hover:bg-[#161616]">
|
||||
<td className="px-3 py-2 text-white text-xs">{o.rmp_clients?.company_name ?? '—'}</td>
|
||||
<td className="px-3 py-2 text-[#888] text-xs">{o.po_number ?? '—'}</td>
|
||||
<td className="px-3 py-2 text-[#3b82f6] text-xs font-mono">{o.internal_order_number}</td>
|
||||
<td className="px-3 py-2 text-[#ffb800] text-xs font-mono">{o.internal_order_number}</td>
|
||||
<td className="px-3 py-2 text-[#888] text-xs capitalize">{o.site}</td>
|
||||
<td className="px-3 py-2 text-[#ccc] text-xs">{o.rmp_products?.name ?? o.ad_unit ?? '—'}</td>
|
||||
<td className="px-3 py-2 text-[#888] text-xs">{o.ad_unit ?? '—'}</td>
|
||||
@@ -255,7 +255,7 @@ function OrdersContent() {
|
||||
<span className={`px-2 py-0.5 rounded-full text-xs ${o.status === 'active' ? 'bg-green-400/10 text-green-400' : o.status === 'cancelled' ? 'bg-red-400/10 text-red-400' : 'bg-[#1a1a1a] text-[#555]'}`}>{o.status}</span>
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<Link href={`/admin/accounting/orders/${o.id}`} className="text-xs text-[#3b82f6] hover:underline">View</Link>
|
||||
<Link href={`/admin/accounting/orders/${o.id}`} className="text-xs text-[#ffb800] hover:underline">View</Link>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@@ -269,7 +269,7 @@ function OrdersContent() {
|
||||
|
||||
export default function OrdersPage() {
|
||||
return (
|
||||
<Suspense fallback={<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>}>
|
||||
<Suspense fallback={<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>}>
|
||||
<OrdersContent />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function AccountingDashboard() {
|
||||
|
||||
if (loading || loadingData) return (
|
||||
<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center">
|
||||
<div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" />
|
||||
<div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
);
|
||||
if (!user) return null;
|
||||
@@ -59,11 +59,11 @@ export default function AccountingDashboard() {
|
||||
<p className="text-[#888] text-sm mt-1">Relevant Media Properties, LLC</p>
|
||||
</div>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Link href="/admin/accounting/clients" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#3b82f6] hover:bg-[#0d1a2d] transition-colors">+ New Client</Link>
|
||||
<Link href="/admin/accounting/orders" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#3b82f6] hover:bg-[#0d1a2d] transition-colors">+ New Order</Link>
|
||||
<Link href="/admin/accounting/invoices" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#3b82f6] hover:bg-[#0d1a2d] transition-colors">+ New Invoice</Link>
|
||||
<Link href="/admin/accounting/expenses" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#3b82f6] hover:bg-[#0d1a2d] transition-colors">+ Log Expense</Link>
|
||||
<Link href="/admin/accounting/reconciliation" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#3b82f6] hover:bg-[#0d1a2d] transition-colors">Upload Bank CSV</Link>
|
||||
<Link href="/admin/accounting/clients" className="px-3 py-1.5 bg-[#1a1208] border border-[#2a3a50] rounded text-xs text-[#ffb800] hover:bg-[#0d1a2d] transition-colors">+ New Client</Link>
|
||||
<Link href="/admin/accounting/orders" className="px-3 py-1.5 bg-[#1a1208] border border-[#2a3a50] rounded text-xs text-[#ffb800] hover:bg-[#0d1a2d] transition-colors">+ New Order</Link>
|
||||
<Link href="/admin/accounting/invoices" className="px-3 py-1.5 bg-[#1a1208] border border-[#2a3a50] rounded text-xs text-[#ffb800] hover:bg-[#0d1a2d] transition-colors">+ New Invoice</Link>
|
||||
<Link href="/admin/accounting/expenses" className="px-3 py-1.5 bg-[#1a1208] border border-[#2a3a50] rounded text-xs text-[#ffb800] hover:bg-[#0d1a2d] transition-colors">+ Log Expense</Link>
|
||||
<Link href="/admin/accounting/reconciliation" className="px-3 py-1.5 bg-[#1a1208] border border-[#2a3a50] rounded text-xs text-[#ffb800] hover:bg-[#0d1a2d] transition-colors">Upload Bank CSV</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -146,7 +146,7 @@ export default function AccountingDashboard() {
|
||||
{ label: 'Reports', href: '/admin/accounting/reports' },
|
||||
{ label: 'Documents', href: '/admin/accounting/documents' },
|
||||
].map(l => (
|
||||
<Link key={l.href} href={l.href} className="bg-[#111] border border-[#252525] rounded-lg p-3 text-center text-sm text-[#888] hover:text-white hover:border-[#3b82f6] transition-colors">
|
||||
<Link key={l.href} href={l.href} className="bg-[#111] border border-[#252525] rounded-lg p-3 text-center text-sm text-[#888] hover:text-white hover:border-[#ffb800] transition-colors">
|
||||
{l.label}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function ReconciliationPage() {
|
||||
load();
|
||||
}
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -59,7 +59,7 @@ export default function ReconciliationPage() {
|
||||
<h1 className="text-xl font-bold">Bank Reconciliation</h1>
|
||||
<Link href="/admin/accounting" className="text-xs text-[#555] hover:text-[#888]">← Accounting</Link>
|
||||
</div>
|
||||
<label className={`px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500 cursor-pointer ${uploading ? 'opacity-50' : ''}`}>
|
||||
<label className={`px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500 cursor-pointer ${uploading ? 'opacity-50' : ''}`}>
|
||||
{uploading ? 'Uploading…' : 'Upload Bank CSV'}
|
||||
<input type="file" accept=".csv" className="hidden" onChange={handleUpload} disabled={uploading} />
|
||||
</label>
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function ReportsPage() {
|
||||
|
||||
useEffect(() => { if (user) loadReport(); }, [user]);
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -43,12 +43,12 @@ export default function ReportsPage() {
|
||||
<input type="date" value={startDate} onChange={e => setStartDate(e.target.value)} className="px-2 py-1 bg-[#111] border border-[#252525] rounded text-xs text-white focus:outline-none" />
|
||||
<span className="text-[#555] text-xs">to</span>
|
||||
<input type="date" value={endDate} onChange={e => setEndDate(e.target.value)} className="px-2 py-1 bg-[#111] border border-[#252525] rounded text-xs text-white focus:outline-none" />
|
||||
<button onClick={loadReport} disabled={loadingData} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">Run Report</button>
|
||||
<button onClick={loadReport} disabled={loadingData} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">Run Report</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{loadingData ? (
|
||||
<div className="flex items-center justify-center py-20"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>
|
||||
<div className="flex items-center justify-center py-20"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>
|
||||
) : report ? (
|
||||
<div className="space-y-4">
|
||||
{/* P&L Summary */}
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function StaffPage() {
|
||||
if (errors[errKey]) setErrors(p => { const n = { ...p }; delete n[errKey]; return n; });
|
||||
}
|
||||
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (loading) return <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center"><div className="w-8 h-8 border-2 border-[#ffb800] border-t-transparent rounded-full animate-spin" /></div>;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
@@ -108,7 +108,7 @@ export default function StaffPage() {
|
||||
<h1 className="text-xl font-bold">Sales Staff</h1>
|
||||
<Link href="/admin/accounting" className="text-xs text-[#555] hover:text-[#888]">← Accounting</Link>
|
||||
</div>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500">+ Add Salesperson</button>
|
||||
<button onClick={() => setShowForm(true)} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500">+ Add Salesperson</button>
|
||||
</div>
|
||||
|
||||
{/* Success Toast */}
|
||||
@@ -140,7 +140,7 @@ export default function StaffPage() {
|
||||
type={f.key === 'email' ? 'email' : 'text'}
|
||||
value={(form as any)[f.key]}
|
||||
onChange={e => handleChange(f.key, e.target.value)}
|
||||
className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#3b82f6] ${errors[f.key] ? 'border-red-500/60' : 'border-[#252525]'}`}
|
||||
className={`w-full px-2 py-1.5 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none focus:border-[#ffb800] ${errors[f.key] ? 'border-red-500/60' : 'border-[#252525]'}`}
|
||||
/>
|
||||
{errors[f.key] && <p className="mt-1 text-xs text-red-400">{errors[f.key]}</p>}
|
||||
</div>
|
||||
@@ -149,7 +149,7 @@ export default function StaffPage() {
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<label className="text-xs text-[#888]">Commission Tiers ({new Date().getFullYear()})</label>
|
||||
<button type="button" onClick={() => setTiers(t => [...t, { tier_order: t.length + 1, threshold_cents: '', rate: '' }])} className="text-xs text-[#3b82f6] hover:underline">+ Add Tier</button>
|
||||
<button type="button" onClick={() => setTiers(t => [...t, { tier_order: t.length + 1, threshold_cents: '', rate: '' }])} className="text-xs text-[#ffb800] hover:underline">+ Add Tier</button>
|
||||
</div>
|
||||
{tiers.map((tier, i) => (
|
||||
<div key={i} className="flex gap-2 mb-2 items-start">
|
||||
@@ -167,7 +167,7 @@ export default function StaffPage() {
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#3b82f6] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Create'}</button>
|
||||
<button type="submit" disabled={saving} className="px-3 py-1.5 bg-[#ffb800] rounded text-xs font-medium hover:bg-blue-500 disabled:opacity-50">{saving ? 'Saving…' : 'Create'}</button>
|
||||
<button type="button" onClick={handleCancel} className="px-3 py-1.5 bg-[#1a1a1a] border border-[#252525] rounded text-xs text-[#888] hover:text-white">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -197,7 +197,7 @@ export default function StaffPage() {
|
||||
<td className="px-4 py-2 text-green-400 text-xs">{fmt(s.commission_ytd ?? 0)}</td>
|
||||
<td className="px-4 py-2 text-yellow-400 text-xs">{fmt(s.commission_outstanding ?? 0)}</td>
|
||||
<td className="px-4 py-2">
|
||||
<Link href={`/admin/accounting/staff/${s.id}`} className="text-xs text-[#3b82f6] hover:underline">View</Link>
|
||||
<Link href={`/admin/accounting/staff/${s.id}`} className="text-xs text-[#ffb800] hover:underline">View</Link>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user