event-chips.tsx44 lines · main
1'use client';
2
3import { motion } from 'motion/react';
4
5import { CountUp } from '../_components/stat-card';
6
7/**
8 * Animated event-type count chips for the email-events page. Each chip
9 * fades/slides in with a small stagger and its count counts up. Counts
10 * are computed server-side from the REAL fetched events — this component
11 * only animates what it's given (honest-data rule).
12 */
13
14export interface EventChip {
15 type: string;
16 count: number;
17 severity: 'ok' | 'warn' | 'fail';
18}
19
20const SEVERITY_CLASS: Record<EventChip['severity'], string> = {
21 ok: 'border-[var(--color-border-primary)] bg-[var(--color-primary-subtle)] text-[var(--color-primary)]',
22 fail: 'border-red-500/40 bg-red-500/10 text-red-400',
23 warn: 'border-yellow-500/40 bg-yellow-500/10 text-yellow-300',
24};
25
26export function EventChips({ chips }: { chips: readonly EventChip[] }) {
27 if (chips.length === 0) return null;
28 return (
29 <div className="flex flex-wrap gap-3">
30 {chips.map((c, i) => (
31 <motion.span
32 key={c.type}
33 initial={{ opacity: 0, y: 8 }}
34 animate={{ opacity: 1, y: 0 }}
35 transition={{ duration: 0.35, delay: i * 0.05, ease: 'easeOut' }}
36 className={`inline-flex items-baseline gap-2 rounded-full border px-3 py-1.5 font-mono text-xs ${SEVERITY_CLASS[c.severity]}`}
37 >
38 {c.type}
39 <CountUp value={c.count} className="font-mono text-xs" />
40 </motion.span>
41 ))}
42 </div>
43 );
44}