- Implemented argument parsing to allow for optional loading of example data with --seed-data flag. - Added functions to seed example data for customs brokers, clients and providers, packages, classes, parts, pedimentos, and invoices. - Enhanced user feedback during the seeding process with detailed output of the seeded data counts. - Updated usage instructions in the script header to reflect the new functionality.
81 lines
2.8 KiB
Svelte
81 lines
2.8 KiB
Svelte
<script lang="ts">
|
|
import * as Card from '$lib/components/ui/card';
|
|
import type { ChartDataPoint } from '$lib/api/dashboard/types';
|
|
|
|
interface Props {
|
|
title: string;
|
|
description?: string;
|
|
data: ChartDataPoint[];
|
|
type?: 'bar' | 'line' | 'pie';
|
|
class?: string;
|
|
}
|
|
|
|
let { title, description, data, type = 'bar', class: cls = '' }: Props = $props();
|
|
|
|
let maxValue = $derived(Math.max(...data.map((d) => d.value), 1));
|
|
|
|
const rankColors = [
|
|
'text-yellow-600 bg-yellow-50 dark:bg-yellow-950/40 border-yellow-200 dark:border-yellow-800',
|
|
'text-slate-500 bg-slate-50 dark:bg-slate-800 border-slate-200 dark:border-slate-700',
|
|
'text-orange-600 bg-orange-50 dark:bg-orange-950/40 border-orange-200 dark:border-orange-800',
|
|
'text-muted-foreground bg-muted border-border',
|
|
'text-muted-foreground bg-muted border-border'
|
|
];
|
|
|
|
const barGradients = [
|
|
'from-primary to-primary/60',
|
|
'from-primary/85 to-primary/50',
|
|
'from-primary/70 to-primary/40',
|
|
'from-primary/55 to-primary/30',
|
|
'from-primary/40 to-primary/20'
|
|
];
|
|
</script>
|
|
|
|
<Card.Root class={`overflow-hidden ${cls}`}>
|
|
<Card.Header class="pb-3">
|
|
<Card.Title class="text-base">{title}</Card.Title>
|
|
{#if description}
|
|
<Card.Description>{description}</Card.Description>
|
|
{/if}
|
|
</Card.Header>
|
|
<Card.Content>
|
|
{#if data.length === 0}
|
|
<div class="flex flex-col items-center justify-center py-8 gap-2 text-muted-foreground">
|
|
<p class="text-sm">No hay datos disponibles</p>
|
|
</div>
|
|
{:else if type === 'bar'}
|
|
<div class="space-y-3">
|
|
{#each data as item, i}
|
|
<div class="space-y-1.5">
|
|
<div class="flex items-center gap-2 text-sm">
|
|
<span
|
|
class={`inline-flex items-center justify-center w-5 h-5 rounded text-[10px] font-bold border shrink-0 ${rankColors[i] ?? rankColors[3]}`}
|
|
>{i + 1}</span>
|
|
<span class="font-medium truncate flex-1">{item.label}</span>
|
|
<span class="text-muted-foreground font-medium tabular-nums">{item.value.toLocaleString()}</span>
|
|
</div>
|
|
<div class="h-1.5 bg-muted rounded-full overflow-hidden">
|
|
<div
|
|
class={`h-full rounded-full bg-gradient-to-r transition-all duration-700 ${barGradients[i] ?? barGradients[4]}`}
|
|
style="width: {(item.value / maxValue) * 100}%"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{:else if type === 'pie'}
|
|
<div class="grid grid-cols-2 gap-3">
|
|
{#each data as item, i}
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-2.5 h-2.5 rounded-full bg-primary" style="opacity: {1 - i * 0.15}"></div>
|
|
<div class="flex-1 min-w-0">
|
|
<div class="text-sm font-medium truncate">{item.label}</div>
|
|
<div class="text-xs text-muted-foreground">{item.value.toLocaleString()}</div>
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</Card.Content>
|
|
</Card.Root>
|