|
|
import { Link } from 'react-router-dom'
|
|
|
import {
|
|
|
BellRing, Boxes, CalendarClock, Files, Filter, LayoutDashboard, MessageSquare, Users, Wrench,
|
|
|
type LucideIcon,
|
|
|
} from 'lucide-react'
|
|
|
import { displayName, getDashboard, getPipeline, getSmsBalances, getTickets } from '../api'
|
|
|
import { useData } from './Clients'
|
|
|
|
|
|
type Tone = 'warn' | 'err'
|
|
|
interface Stat { text: string; tone?: Tone }
|
|
|
interface HubCard { to: string; label: string; desc: string; icon: LucideIcon; stat?: Stat }
|
|
|
|
|
|
/**
|
|
|
* Home hub (D30) — a quick-access launcher for the sections used day to day. Not
|
|
|
* every screen (the sidebar has those); just the main ones, each a big target with
|
|
|
* a live count so the landing doubles as a status board.
|
|
|
*/
|
|
|
export function Home() {
|
|
|
const name = displayName()
|
|
|
const dash = useData(() => getDashboard(false), [])
|
|
|
const sms = useData(getSmsBalances, [])
|
|
|
const tickets = useData(() => getTickets({ status: 'open' }), [])
|
|
|
const pipeline = useData(() => getPipeline({ filter: 'all' }), [])
|
|
|
|
|
|
const d = dash.data
|
|
|
const remStat: Stat | undefined = d === undefined ? undefined
|
|
|
: d.totals.failed > 0 ? { text: `${d.totals.failed} failed · ${d.totals.queued} queued`, tone: 'err' }
|
|
|
: { text: `${d.totals.queued} queued` }
|
|
|
const renewStat: Stat | undefined = d !== undefined && d.renewalsThisMonth.length > 0
|
|
|
? { text: `${d.renewalsThisMonth.length} this month`, tone: 'warn' } : undefined
|
|
|
const smsStat: Stat | undefined = sms.data !== undefined && sms.data.lowCount > 0
|
|
|
? { text: `${sms.data.lowCount} low`, tone: 'warn' } : undefined
|
|
|
const ticketStat: Stat | undefined = tickets.data !== undefined && tickets.data.total > 0
|
|
|
? { text: `${tickets.data.total} open` } : undefined
|
|
|
const pipeStat: Stat | undefined = pipeline.data !== undefined && pipeline.data.total > 0
|
|
|
? { text: `${pipeline.data.total} open` } : undefined
|
|
|
|
|
|
// Main, look-at-it-all-the-time sections only — the rest live in the sidebar.
|
|
|
const cards: HubCard[] = [
|
|
|
{ to: '/dashboard', label: 'Dashboard', desc: 'Today’s money — dues, renewals, follow-ups, the send queue.', icon: LayoutDashboard, stat: { text: 'My Day' } },
|
|
|
{ to: '/clients', label: 'Clients', desc: 'The client book — profiles, modules, ledger, support.', icon: Users },
|
|
|
{ to: '/documents', label: 'Documents', desc: 'Quotations, proformas, invoices, credit notes.', icon: Files },
|
|
|
{ to: '/reminders', label: 'Reminders', desc: 'The email send queue — preview, send, dismiss.', icon: BellRing, ...(remStat !== undefined ? { stat: remStat } : {}) },
|
|
|
{ to: '/renewals', label: 'Renewals', desc: 'AMC and module renewals — one-click renewal quotes.', icon: CalendarClock, ...(renewStat !== undefined ? { stat: renewStat } : {}) },
|
|
|
{ to: '/pipeline', label: 'Pipeline', desc: 'Leads and quotes in flight, with next-action nudges.', icon: Filter, ...(pipeStat !== undefined ? { stat: pipeStat } : {}) },
|
|
|
{ to: '/tickets', label: 'Tickets', desc: 'Support desk with SLA aging and overdue flags.', icon: Wrench, ...(ticketStat !== undefined ? { stat: ticketStat } : {}) },
|
|
|
{ to: '/sms-balances', label: 'SMS credits', desc: 'Client SMS balances and top-ups.', icon: MessageSquare, ...(smsStat !== undefined ? { stat: smsStat } : {}) },
|
|
|
{ to: '/modules', label: 'Modules', desc: 'Sellable modules with the dated price book.', icon: Boxes },
|
|
|
]
|
|
|
|
|
|
return (
|
|
|
<div className="wf-page">
|
|
|
<div className="hub-head">
|
|
|
<h1>Welcome back{name !== '' ? `, ${name}` : ''}</h1>
|
|
|
<p>Your most-used areas — everything else is in the sidebar.</p>
|
|
|
</div>
|
|
|
<div className="hub-cards">
|
|
|
{cards.map((c) => (
|
|
|
<Link key={c.to} to={c.to} className="hub-card">
|
|
|
<span className="hub-ic"><c.icon size={18} strokeWidth={1.9} aria-hidden /></span>
|
|
|
<span className="hub-body">
|
|
|
<span className="hub-t">{c.label}</span>
|
|
|
<span className="hub-d">{c.desc}</span>
|
|
|
{c.stat !== undefined && (
|
|
|
<span className={`hub-stat${c.stat.tone !== undefined ? ` ${c.stat.tone}` : ''}`}>{c.stat.text}</span>
|
|
|
)}
|
|
|
</span>
|
|
|
<span className="hub-arrow" aria-hidden>→</span>
|
|
|
</Link>
|
|
|
))}
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
|
}
|