'use client'; import React, { useState, useEffect, useCallback } from 'react'; import Link from 'next/link'; import { useAuth } from '@/contexts/AuthContext'; import { useRouter } from 'next/navigation'; function fmt(cents: number) { return '$' + (cents / 100).toLocaleString('en-US', { minimumFractionDigits: 2 }); } function validateEmail(email: string) { return !email || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); } export default function StaffPage() { const { user, loading } = useAuth(); const router = useRouter(); const [staff, setStaff] = useState([]); const [loadingData, setLoadingData] = useState(true); const [showForm, setShowForm] = useState(false); const [saving, setSaving] = useState(false); const [form, setForm] = useState({ full_name: '', email: '', phone: '', notes: '' }); const [tiers, setTiers] = useState([{ tier_order: 1, threshold_cents: '', rate: '' }]); const [errors, setErrors] = useState>({}); const [successMsg, setSuccessMsg] = useState(''); useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]); const load = useCallback(async () => { setLoadingData(true); const r = await fetch('/api/accounting/staff'); const d = await r.json(); setStaff(d.staff ?? []); setLoadingData(false); }, []); useEffect(() => { if (user) load(); }, [user, load]); function handleChange(key: string, value: string) { setForm(p => ({ ...p, [key]: value })); if (errors[key]) setErrors(p => { const n = { ...p }; delete n[key]; return n; }); } function validate() { const errs: Record = {}; if (!form.full_name.trim()) errs.full_name = 'Full name is required.'; if (!validateEmail(form.email)) errs.email = 'Enter a valid email address.'; tiers.forEach((t, i) => { if (t.rate === '' || t.rate === undefined) { errs[`tier_rate_${i}`] = 'Commission rate is required.'; } else if (isNaN(parseFloat(t.rate)) || parseFloat(t.rate) < 0 || parseFloat(t.rate) > 100) { errs[`tier_rate_${i}`] = 'Rate must be between 0 and 100.'; } }); return errs; } async function handleCreate(e: React.FormEvent) { e.preventDefault(); const errs = validate(); if (Object.keys(errs).length > 0) { setErrors(errs); return; } setSaving(true); try { const body = { ...form, tiers: tiers.map(t => ({ tier_order: t.tier_order, threshold_cents: t.threshold_cents ? Math.round(parseFloat(t.threshold_cents) * 100) : null, rate: t.rate ? parseFloat(t.rate) / 100 : 0, effective_year: new Date().getFullYear(), })), }; const res = await fetch('/api/accounting/staff', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (!res.ok) throw new Error('Failed to create staff'); setShowForm(false); setErrors({}); setForm({ full_name: '', email: '', phone: '', notes: '' }); setTiers([{ tier_order: 1, threshold_cents: '', rate: '' }]); setSuccessMsg(`Salesperson "${form.full_name}" added successfully.`); setTimeout(() => setSuccessMsg(''), 4000); load(); } catch { setErrors({ _form: 'Something went wrong. Please try again.' }); } finally { setSaving(false); } } function handleCancel() { setShowForm(false); setErrors({}); setForm({ full_name: '', email: '', phone: '', notes: '' }); setTiers([{ tier_order: 1, threshold_cents: '', rate: '' }]); } function handleTierChange(i: number, key: string, value: string) { setTiers(t => t.map((x, j) => j === i ? { ...x, [key]: value } : x)); const errKey = `tier_rate_${i}`; if (errors[errKey]) setErrors(p => { const n = { ...p }; delete n[errKey]; return n; }); } if (loading) return
; if (!user) return null; return (

Sales Staff

← Accounting
{/* Success Toast */} {successMsg && (
{successMsg}
)} {showForm && (

New Salesperson

{errors._form && (
{errors._form}
)}
{[{ key: 'full_name', label: 'Full Name', required: true }, { key: 'email', label: 'Email' }, { key: 'phone', label: 'Phone' }].map(f => (
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]'}`} /> {errors[f.key] &&

{errors[f.key]}

}
))}
{tiers.map((tier, i) => (
Tier {tier.tier_order}
handleTierChange(i, 'threshold_cents', e.target.value)} placeholder="100000" className="w-full px-2 py-1 bg-[#0a0a0a] border border-[#252525] rounded text-xs text-white focus:outline-none" />
handleTierChange(i, 'rate', e.target.value)} placeholder="25" className={`w-full px-2 py-1 bg-[#0a0a0a] border rounded text-xs text-white focus:outline-none ${errors[`tier_rate_${i}`] ? 'border-red-500/60' : 'border-[#252525]'}`} /> {errors[`tier_rate_${i}`] &&

{errors[`tier_rate_${i}`]}

}
))}
)}
{['Name','Email','Status','Commission YTD','Outstanding',''].map(h => ( ))} {loadingData ? ( ) : staff.length === 0 ? ( ) : staff.map((s: any) => ( ))}
{h}
Loading…
No staff found
{s.full_name} {s.email} {s.status} {fmt(s.commission_ytd ?? 0)} {fmt(s.commission_outstanding ?? 0)} View
); }