Files
clearity/src/routes/dashboard/metric-cards.svelte
T

78 lines
2.2 KiB
Svelte

<script lang="ts">
import BanknoteIcon from '@lucide/svelte/icons/banknote';
import BriefcaseBusinessIcon from '@lucide/svelte/icons/briefcase-business';
import Building2Icon from '@lucide/svelte/icons/building-2';
import CalendarClockIcon from '@lucide/svelte/icons/calendar-clock';
import FileWarningIcon from '@lucide/svelte/icons/file-warning';
import UsersIcon from '@lucide/svelte/icons/users';
import * as Card from '$lib/components/ui/card/index.js';
import type { PageData } from './$types';
let {
metrics,
monthLabel,
formatMoney
}: {
metrics: PageData['metrics'];
monthLabel: string;
formatMoney: (value: unknown) => string;
} = $props();
const cards = $derived([
{
label: 'Active clients',
value: metrics.activeClients,
detail: 'Unarchived client records',
icon: UsersIcon
},
{
label: 'Active contracts',
value: metrics.activeContracts,
detail: `${formatMoney(metrics.monthlyLicenseFeesGbp)} monthly license fees`,
icon: BriefcaseBusinessIcon
},
{
label: 'Upcoming bookings',
value: metrics.upcomingBookings,
detail: 'Future bookings excluding cancelled',
icon: CalendarClockIcon
},
{
label: 'Past-due invoices',
value: metrics.overdueInvoices,
detail: formatMoney(metrics.overdueInvoicesGbp),
icon: FileWarningIcon
},
{
label: `${monthLabel} billing`,
value: formatMoney(metrics.monthlyBillingGbp),
detail: 'Issued invoice total',
icon: BanknoteIcon
},
{
label: 'Room occupancy',
value: `${metrics.occupancyPercent}%`,
detail: `${metrics.allocatedRooms} of ${metrics.totalRooms} rooms allocated`,
icon: Building2Icon
}
]);
</script>
<section class="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
{#each cards as card (card.label)}
{@const Icon = card.icon}
<Card.Root size="sm">
<Card.Header>
<Card.Title class="flex items-center gap-2 text-sm text-muted-foreground">
<Icon class="size-4 text-foreground" />
{card.label}
</Card.Title>
</Card.Header>
<Card.Content>
<div class="text-2xl font-semibold tracking-tight">{card.value}</div>
<p class="mt-1 text-xs text-muted-foreground">{card.detail}</p>
</Card.Content>
</Card.Root>
{/each}
</section>