67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
import { useNavigate } from '@tanstack/react-router'
|
|
import type { HomeSummaryDto } from '@gruperly/shared'
|
|
import { UserCheck, Users, Wallet, type LucideIcon } from 'lucide-react'
|
|
import { cn } from '../../lib/utils'
|
|
import { formatPrice } from '../../lib/format'
|
|
|
|
type StatTarget = '/groups' | '/payments'
|
|
|
|
export function SummaryStats({ summary }: { summary: HomeSummaryDto }) {
|
|
const navigate = useNavigate()
|
|
const stats: {
|
|
label: string
|
|
value: string
|
|
sub?: string
|
|
icon: LucideIcon
|
|
to: StatTarget
|
|
}[] = [
|
|
{
|
|
label: 'Grupos activos',
|
|
value: String(summary.stats.groups),
|
|
icon: Users,
|
|
to: '/groups',
|
|
},
|
|
{
|
|
label: 'Cobros pendientes',
|
|
value: summary.stats.pendingAmount > 0 ? formatPrice(summary.stats.pendingAmount) : '0',
|
|
sub: `${summary.stats.pendingPayments} por cobrar`,
|
|
icon: Wallet,
|
|
to: '/payments',
|
|
},
|
|
{
|
|
label: 'Asistentes',
|
|
value: String(summary.stats.attendees),
|
|
icon: UserCheck,
|
|
to: '/groups',
|
|
},
|
|
]
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
|
{stats.map((stat) => {
|
|
const Icon = stat.icon
|
|
return (
|
|
<button
|
|
key={stat.label}
|
|
type="button"
|
|
onClick={() => void navigate({ to: stat.to })}
|
|
className={cn(
|
|
'flex items-center gap-3 rounded-xl border border-border bg-surface p-4 text-left transition-all',
|
|
'hover:border-accent/40',
|
|
)}
|
|
>
|
|
<span className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-accent-soft text-accent">
|
|
<Icon className="size-5" />
|
|
</span>
|
|
<span className="min-w-0">
|
|
<span className="block text-xl font-bold leading-tight text-primary">{stat.value}</span>
|
|
<span className="block truncate text-xs text-foreground/60">
|
|
{stat.sub ?? stat.label}
|
|
</span>
|
|
</span>
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
} |