- Implemented DTOs for KPIs, activity items, chart data points, and dashboard statistics. - Created API routes for fetching dashboard statistics, operations overview, and inventory metrics. - Developed a service layer to handle the logic for generating dashboard statistics, including invoice metrics, client/provider metrics, and recent activity. - Added frontend API client methods for fetching dashboard data. - Created TypeScript types for dashboard data structures. - Developed Svelte components for displaying recent activity, KPI cards, trend charts, and donut charts.
57 lines
1.6 KiB
Svelte
57 lines
1.6 KiB
Svelte
<script lang="ts">
|
|
import * as Card from '$lib/components/ui/card';
|
|
import { TrendingUp, TrendingDown, Minus } from 'lucide-svelte';
|
|
import type { KPIMetric } from '$lib/api/dashboard/types';
|
|
import { cn } from '$lib/utils';
|
|
|
|
interface Props {
|
|
metric: KPIMetric;
|
|
icon?: any;
|
|
iconColor?: string;
|
|
}
|
|
|
|
let { metric, icon: Icon, iconColor = 'text-primary' }: Props = $props();
|
|
|
|
const trendIcons = {
|
|
up: TrendingUp,
|
|
down: TrendingDown,
|
|
stable: Minus
|
|
};
|
|
|
|
const trendColors = {
|
|
up: 'text-green-600',
|
|
down: 'text-red-600',
|
|
stable: 'text-gray-600'
|
|
};
|
|
|
|
const TrendIcon = metric.trend ? trendIcons[metric.trend] : null;
|
|
</script>
|
|
|
|
<Card.Root class="overflow-hidden">
|
|
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<Card.Title class="text-sm font-medium text-muted-foreground">
|
|
{metric.label}
|
|
</Card.Title>
|
|
{#if Icon}
|
|
<Icon class={cn('h-4 w-4', iconColor)} />
|
|
{/if}
|
|
</Card.Header>
|
|
<Card.Content>
|
|
<div class="text-2xl font-bold">
|
|
{metric.value.toLocaleString()}
|
|
{#if metric.unit}
|
|
<span class="text-sm font-normal text-muted-foreground ml-1">{metric.unit}</span>
|
|
{/if}
|
|
</div>
|
|
{#if metric.percentage_change !== undefined && TrendIcon}
|
|
<div class="flex items-center text-xs mt-1">
|
|
<TrendIcon class={cn('h-3 w-3 mr-1', trendColors[metric.trend || 'stable'])} />
|
|
<span class={cn(trendColors[metric.trend || 'stable'])}>
|
|
{Math.abs(metric.percentage_change).toFixed(1)}%
|
|
</span>
|
|
<span class="text-muted-foreground ml-1">vs mes anterior</span>
|
|
</div>
|
|
{/if}
|
|
</Card.Content>
|
|
</Card.Root>
|