|
| 1 | +'use client'; |
| 2 | + |
1 | 3 | import Link from 'next/link'; |
2 | 4 | import { ActivityDto } from '@fairshare/shared-types'; |
3 | | -import { Clock } from 'lucide-react'; |
4 | | -import { glassPanel } from '../layout/layoutStyles'; |
| 5 | +import { Clock, Receipt, Users, Plus, Trash2, UserPlus, Milestone, ArrowUpRight } from 'lucide-react'; |
| 6 | +import { motion } from 'framer-motion'; |
5 | 7 |
|
6 | | -function labelForType(type: ActivityDto['type']): string { |
| 8 | +function getIconForType(type: ActivityDto['type']) { |
7 | 9 | switch (type) { |
8 | | - case 'expense_created': |
9 | | - return 'Expense created'; |
10 | | - case 'expense_updated': |
11 | | - return 'Expense updated'; |
12 | | - case 'expense_deleted': |
13 | | - return 'Expense deleted'; |
14 | | - case 'settlement_created': |
15 | | - return 'Settlement created'; |
16 | | - case 'member_joined': |
17 | | - return 'Member joined'; |
18 | | - case 'member_invited': |
19 | | - return 'Member invited'; |
20 | | - default: |
21 | | - return 'Activity'; |
| 10 | + case 'expense_created': return { Icon: Plus, color: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20' }; |
| 11 | + case 'expense_updated': return { Icon: Receipt, color: 'bg-purple-500/10 text-purple-400 border-purple-500/20' }; |
| 12 | + case 'expense_deleted': return { Icon: Trash2, color: 'bg-rose-500/10 text-rose-400 border-rose-500/20' }; |
| 13 | + case 'settlement_created': return { Icon: Milestone, color: 'bg-indigo-500/10 text-indigo-400 border-indigo-500/20' }; |
| 14 | + case 'member_joined': return { Icon: UserPlus, color: 'bg-violet-500/10 text-violet-400 border-violet-500/20' }; |
| 15 | + default: return { Icon: ActivitySquare, color: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20' }; |
22 | 16 | } |
23 | 17 | } |
24 | 18 |
|
| 19 | +function labelForType(type: ActivityDto['type']): string { |
| 20 | + return type.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '); |
| 21 | +} |
| 22 | + |
| 23 | +import { ActivitySquare } from 'lucide-react'; |
| 24 | + |
25 | 25 | export function ActivityList({ items = [], groupId = '' }: { items?: ActivityDto[]; groupId?: string }) { |
26 | 26 | const safeItems = Array.isArray(items) ? items : []; |
27 | 27 |
|
28 | 28 | return ( |
29 | | - <div className={`${glassPanel} p-7`}> |
30 | | - <div className="flex items-center justify-between gap-3 mb-6"> |
31 | | - <h2 className="text-xl font-extrabold tracking-tight text-[var(--fs-text-primary)]">Recent Activity</h2> |
| 29 | + <div className="group relative overflow-hidden rounded-2xl border border-white/5 bg-white/[0.01] p-6 transition-all hover:bg-white/[0.02]"> |
| 30 | + <div className="flex items-center justify-between gap-3 mb-8"> |
| 31 | + <div> |
| 32 | + <h2 className="text-sm font-black italic tracking-tight text-white uppercase">Live Signals</h2> |
| 33 | + <p className="text-[10px] font-bold tracking-widest text-zinc-600 uppercase mt-0.5">Real-time ledger audit</p> |
| 34 | + </div> |
32 | 35 | <Link |
33 | 36 | href={`/dashboard/activity?groupId=${encodeURIComponent(groupId)}`} |
34 | | - className="text-[11px] font-bold uppercase tracking-[0.14em] text-[var(--fs-text-muted)] hover:text-[var(--fs-primary)] transition-colors" |
| 37 | + className="group/link flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-zinc-600 hover:text-white transition-colors" |
35 | 38 | > |
36 | | - View timeline |
| 39 | + Timeline <ArrowUpRight size={12} className="group-hover/link:translate-x-0.5 group-hover/link:-translate-y-0.5 transition-transform" /> |
37 | 40 | </Link> |
38 | 41 | </div> |
39 | 42 |
|
40 | | - <div className="space-y-3"> |
41 | | - {safeItems.map((item) => ( |
42 | | - <div |
43 | | - key={item.id} |
44 | | - className="flex items-center justify-between gap-3 rounded-xl border border-[var(--fs-border)] bg-[var(--fs-background)]/70 px-4 py-3 hover:border-[var(--fs-primary)] transition-colors" |
45 | | - > |
46 | | - <div> |
47 | | - <p className="text-sm font-semibold text-[var(--fs-text-primary)]">{labelForType(item.type)}</p> |
48 | | - <p className="text-[11px] font-medium text-[var(--fs-text-muted)] flex items-center gap-2"> |
49 | | - <Clock size={14} strokeWidth={2} /> |
50 | | - {new Date(item.createdAt).toLocaleString()} |
51 | | - </p> |
52 | | - </div> |
53 | | - <span className="text-[10px] font-bold uppercase tracking-[0.14em] text-[var(--fs-primary)] bg-[var(--fs-primary)]/10 px-3 py-1 rounded-lg"> |
54 | | - {item.type.replace('_', ' ')} |
55 | | - </span> |
56 | | - </div> |
57 | | - ))} |
| 43 | + <div className="space-y-4"> |
| 44 | + {safeItems.map((item, idx) => { |
| 45 | + const { Icon, color } = getIconForType(item.type); |
| 46 | + return ( |
| 47 | + <motion.div |
| 48 | + key={item.id} |
| 49 | + initial={{ opacity: 0, x: -10 }} |
| 50 | + animate={{ opacity: 1, x: 0 }} |
| 51 | + transition={{ delay: idx * 0.05 }} |
| 52 | + whileHover={{ x: 2 }} |
| 53 | + className="flex items-center gap-4 group/item cursor-pointer" |
| 54 | + > |
| 55 | + <div className={`h-10 w-10 shrink-0 flex items-center justify-center rounded-xl border ${color}`}> |
| 56 | + <Icon size={16} /> |
| 57 | + </div> |
| 58 | + |
| 59 | + <div className="flex-grow min-w-0"> |
| 60 | + <p className="text-xs font-black tracking-tight text-white group-hover:text-purple-400 transition-colors truncate"> |
| 61 | + {labelForType(item.type)} |
| 62 | + </p> |
| 63 | + <div className="flex items-center gap-2 mt-1"> |
| 64 | + <p className="text-[10px] font-bold uppercase tracking-widest text-zinc-600"> |
| 65 | + {new Date(item.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} |
| 66 | + </p> |
| 67 | + <div className="h-0.5 w-0.5 rounded-full bg-zinc-800" /> |
| 68 | + <p className="text-[10px] font-bold uppercase tracking-widest text-zinc-800"> |
| 69 | + Protocol Signal |
| 70 | + </p> |
| 71 | + </div> |
| 72 | + </div> |
58 | 73 |
|
59 | | - {safeItems.length === 0 ? ( |
60 | | - <div className="rounded-2xl border border-[var(--fs-border)] bg-[var(--fs-background)]/50 px-6 py-8 text-center"> |
61 | | - <p className="text-sm font-semibold text-[var(--fs-text-primary)] mb-1">No activity yet</p> |
62 | | - <p className="text-[12px] font-medium text-[var(--fs-text-muted)]"> |
63 | | - Refresh after new expenses or settlements to see the latest activity. |
64 | | - </p> |
| 74 | + <div className="hidden sm:block"> |
| 75 | + <div className="h-8 w-8 rounded-full border border-white/5 flex items-center justify-center text-[8px] font-black text-zinc-700 uppercase"> |
| 76 | + FS |
| 77 | + </div> |
| 78 | + </div> |
| 79 | + </motion.div> |
| 80 | + ); |
| 81 | + })} |
| 82 | + |
| 83 | + {safeItems.length === 0 && ( |
| 84 | + <div className="rounded-2xl border border-dashed border-white/5 p-10 text-center"> |
| 85 | + <div className="mx-auto w-10 h-10 rounded-full border border-white/5 flex items-center justify-center text-zinc-800 mb-4"> |
| 86 | + <Milestone size={18} /> |
| 87 | + </div> |
| 88 | + <p className="text-[10px] font-black tracking-widest text-zinc-700 uppercase">NO_SIGNAL_DETECTED</p> |
65 | 89 | </div> |
66 | | - ) : null} |
| 90 | + )} |
67 | 91 | </div> |
68 | 92 | </div> |
69 | 93 | ); |
|
0 commit comments