|
1 | 1 | import type { QueueStats } from '~/lib/types' |
2 | | -import { cn } from '~/lib/utils' |
| 2 | +import { StatCard } from '~/components/ui/stat-card' |
3 | 3 |
|
4 | 4 | interface StatsCardsProps { |
5 | 5 | stats: QueueStats |
6 | 6 | } |
7 | 7 |
|
8 | 8 | const statCards = [ |
9 | | - { |
10 | | - name: 'Queued Jobs', |
11 | | - key: 'totalQueued' as const, |
12 | | - description: 'Jobs waiting to be processed', |
13 | | - color: 'text-gray-600 dark:text-gray-400', |
14 | | - bgColor: 'bg-gray-50 dark:bg-gray-800', |
15 | | - }, |
16 | | - { |
17 | | - name: 'Active Jobs', |
18 | | - key: 'totalActive' as const, |
19 | | - description: 'Jobs currently processing', |
20 | | - color: 'text-gray-600 dark:text-gray-400', |
21 | | - bgColor: 'bg-gray-50 dark:bg-gray-800', |
22 | | - }, |
23 | | - { |
24 | | - name: 'Deferred Jobs', |
25 | | - key: 'totalDeferred' as const, |
26 | | - description: 'Jobs scheduled for later', |
27 | | - color: 'text-gray-600 dark:text-gray-400', |
28 | | - bgColor: 'bg-gray-50 dark:bg-gray-800', |
29 | | - }, |
30 | | - { |
31 | | - name: 'Total Jobs', |
32 | | - key: 'totalJobs' as const, |
33 | | - description: 'All jobs across queues', |
34 | | - color: 'text-gray-600 dark:text-gray-400', |
35 | | - bgColor: 'bg-gray-50 dark:bg-gray-800', |
36 | | - }, |
| 9 | + { name: 'Queued Jobs', key: 'totalQueued' as const, hint: 'waiting to process', accent: 'neutral' as const }, |
| 10 | + { name: 'Active', key: 'totalActive' as const, hint: 'processing now', accent: 'primary' as const }, |
| 11 | + { name: 'Deferred', key: 'totalDeferred' as const, hint: 'scheduled for later', accent: 'neutral' as const }, |
| 12 | + { name: 'Total Jobs', key: 'totalJobs' as const, hint: 'all-time across queues', accent: 'neutral' as const }, |
37 | 13 | ] |
38 | 14 |
|
39 | 15 | export function StatsCards ({ stats }: StatsCardsProps) { |
40 | 16 | return ( |
41 | 17 | <> |
42 | 18 | {statCards.map((stat) => ( |
43 | | - <div |
| 19 | + <StatCard |
44 | 20 | key={stat.key} |
45 | | - className={cn( |
46 | | - 'overflow-hidden rounded-xl border shadow-sm', |
47 | | - 'bg-white border-gray-200', |
48 | | - 'dark:bg-gray-900 dark:border-gray-800' |
49 | | - )} |
50 | | - > |
51 | | - <div className="p-5"> |
52 | | - <div className="flex items-center"> |
53 | | - <div className={cn('flex-shrink-0 rounded-lg p-3', stat.bgColor)}> |
54 | | - <StatIcon className={cn('h-6 w-6', stat.color)} /> |
55 | | - </div> |
56 | | - <div className="ml-4 flex-1"> |
57 | | - <p className="text-sm font-medium text-gray-500 dark:text-gray-400 truncate"> |
58 | | - {stat.name} |
59 | | - </p> |
60 | | - <p className={cn('text-2xl font-semibold', stat.color)}> |
61 | | - {stats[stat.key].toLocaleString()} |
62 | | - </p> |
63 | | - </div> |
64 | | - </div> |
65 | | - </div> |
66 | | - </div> |
| 21 | + label={stat.name} |
| 22 | + value={stats[stat.key].toLocaleString()} |
| 23 | + hint={stat.hint} |
| 24 | + accent={stat.accent} |
| 25 | + /> |
67 | 26 | ))} |
68 | 27 | </> |
69 | 28 | ) |
70 | 29 | } |
71 | | - |
72 | | -function StatIcon ({ className }: { className?: string }) { |
73 | | - return ( |
74 | | - <svg |
75 | | - className={className} |
76 | | - fill="none" |
77 | | - viewBox="0 0 24 24" |
78 | | - strokeWidth={1.5} |
79 | | - stroke="currentColor" |
80 | | - > |
81 | | - <path |
82 | | - strokeLinecap="round" |
83 | | - strokeLinejoin="round" |
84 | | - d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z" |
85 | | - /> |
86 | | - </svg> |
87 | | - ) |
88 | | -} |
0 commit comments