@@ -14,79 +14,98 @@ interface AnnouncementsCardProps {
1414
1515export function AnnouncementsCard ( { announcements } : AnnouncementsCardProps ) {
1616 if ( announcements . length === 0 ) {
17- return (
18- < motion . div
19- className = "relative overflow-hidden rounded-2xl border border-slate-700/50 bg-gradient-to-br from-slate-900 via-slate-800/80 to-slate-900 p-6 shadow-xl shadow-black/20"
20- initial = { { opacity : 0 , y : 20 } }
21- animate = { { opacity : 1 , y : 0 } }
22- transition = { { duration : 0.4 , ease : "easeOut" , delay : 0.1 } }
23- data-testid = "announcements-card-empty"
24- >
25- < div className = "flex items-center gap-3" >
26- < div className = "flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700/50 text-slate-500" >
27- < svg className = "h-5 w-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
28- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" />
29- </ svg >
30- </ div >
31- < div >
32- < h3 className = "text-lg font-semibold text-white" > Announcements</ h3 >
33- < p className = "text-sm text-slate-500" > No announcements at this time</ p >
34- </ div >
35- </ div >
36- </ motion . div >
37- )
17+ return null
3818 }
3919
4020 return (
4121 < motion . div
42- className = "relative overflow-hidden rounded-2xl border border-cyan-500/20 bg-gradient-to-br from-slate-900 via-cyan-950/20 to-slate-900 shadow- xl shadow-black/20 "
43- initial = { { opacity : 0 , y : 20 } }
22+ className = "relative overflow-hidden rounded-xl sm:rounded-2xl "
23+ initial = { { opacity : 0 , y : - 10 } }
4424 animate = { { opacity : 1 , y : 0 } }
45- transition = { { duration : 0.4 , ease : "easeOut" , delay : 0.1 } }
25+ transition = { { duration : 0.4 , ease : "easeOut" } }
4626 data-testid = "announcements-card"
4727 >
48- { /* Header */ }
49- < div className = "flex items-center gap-3 border-b border-white/5 px-6 py-4" >
50- < div className = "flex h-10 w-10 items-center justify-center rounded-lg bg-gradient-to-br from-cyan-500/20 to-cyan-600/10 text-cyan-400" >
51- < svg className = "h-5 w-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
52- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" />
53- </ svg >
28+ { /* Animated gradient border */ }
29+ < div
30+ className = "absolute inset-0 rounded-xl sm:rounded-2xl bg-gradient-to-r from-amber-500 via-orange-500 to-amber-500 opacity-80"
31+ style = { {
32+ backgroundSize : '200% 200%' ,
33+ animation : 'gradient-shift 3s ease infinite' ,
34+ } }
35+ />
36+
37+ { /* Inner content */ }
38+ < div className = "relative m-[1px] rounded-xl sm:rounded-2xl bg-gradient-to-br from-amber-950/90 via-slate-900 to-slate-900" >
39+ { /* Ambient glow */ }
40+ < div className = "pointer-events-none absolute -right-10 -top-10 h-32 w-32 rounded-full bg-amber-500/20 blur-3xl" />
41+ < div className = "pointer-events-none absolute -left-10 -bottom-10 h-24 w-24 rounded-full bg-orange-500/10 blur-2xl" />
42+
43+ { /* Header */ }
44+ < div className = "relative flex items-center gap-3 border-b border-amber-500/20 px-4 sm:px-5 py-3 sm:py-4" >
45+ { /* Animated bell icon */ }
46+ < motion . div
47+ className = "flex h-9 w-9 sm:h-10 sm:w-10 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-600 shadow-lg shadow-amber-500/30"
48+ animate = { { rotate : [ 0 , - 10 , 10 , - 10 , 0 ] } }
49+ transition = { { duration : 0.5 , repeat : Infinity , repeatDelay : 3 } }
50+ >
51+ < svg className = "h-5 w-5 sm:h-5 sm:w-5 text-white" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
52+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
53+ </ svg >
54+ </ motion . div >
55+
56+ < div className = "flex-1" >
57+ < div className = "flex items-center gap-2" >
58+ < h3 className = "text-base sm:text-lg font-bold text-white" > Announcements</ h3 >
59+ { /* Pulsing new badge */ }
60+ < span className = "relative flex" >
61+ < span className = "animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-400 opacity-50" />
62+ < span className = "relative inline-flex items-center rounded-full bg-gradient-to-r from-amber-500 to-orange-500 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white shadow-sm" >
63+ New
64+ </ span >
65+ </ span >
66+ </ div >
67+ < p className = "text-xs text-amber-200/70" > Important updates from the server</ p >
68+ </ div >
69+
70+ { announcements . length > 1 && (
71+ < span className = "rounded-full bg-amber-500/20 border border-amber-500/30 px-2.5 py-0.5 text-xs font-bold text-amber-300" >
72+ { announcements . length }
73+ </ span >
74+ ) }
5475 </ div >
55- < h3 className = "text-lg font-semibold text-white" > Announcements</ h3 >
56- { announcements . length > 1 && (
57- < span className = "ml-auto rounded-full bg-cyan-500/20 px-2.5 py-0.5 text-xs font-medium text-cyan-300" >
58- { announcements . length }
59- </ span >
60- ) }
61- </ div >
6276
63- { /* Announcements list */ }
64- < div className = "max-h-64 overflow-y-auto" >
65- < AnimatePresence >
66- { announcements . map ( ( announcement , index ) => (
67- < motion . article
68- key = { announcement . id }
69- className = "border-b border-white/5 px-6 py-4 last:border-b-0"
70- initial = { { opacity : 0 , x : - 10 } }
71- animate = { { opacity : 1 , x : 0 } }
72- transition = { { duration : 0.3 , delay : index * 0.05 } }
73- data-testid = { `announcement-${ announcement . id } ` }
74- >
75- < div className = "flex items-start gap-3" >
76- < div className = "mt-0.5 h-2 w-2 shrink-0 rounded-full bg-cyan-400" />
77- < div className = "flex-1 min-w-0" >
78- < h4 className = "font-medium text-white" > { announcement . title } </ h4 >
79- < div className = "mt-1 text-sm text-slate-300 prose prose-sm prose-invert max-w-none prose-p:my-1 prose-ul:my-1 prose-li:my-0.5 prose-a:text-cyan-400 prose-a:no-underline hover:prose-a:underline" >
80- < ReactMarkdown > { announcement . content } </ ReactMarkdown >
77+ { /* Announcements list */ }
78+ < div className = "relative max-h-48 sm:max-h-56 overflow-y-auto" >
79+ < AnimatePresence >
80+ { announcements . map ( ( announcement , index ) => (
81+ < motion . article
82+ key = { announcement . id }
83+ className = "border-b border-white/5 px-4 sm:px-5 py-3 sm:py-4 last:border-b-0 hover:bg-white/[0.02] transition-colors"
84+ initial = { { opacity : 0 , x : - 10 } }
85+ animate = { { opacity : 1 , x : 0 } }
86+ transition = { { duration : 0.3 , delay : index * 0.05 } }
87+ data-testid = { `announcement-${ announcement . id } ` }
88+ >
89+ < div className = "flex items-start gap-3" >
90+ { /* Glowing dot indicator */ }
91+ < div className = "relative mt-1.5 shrink-0" >
92+ < span className = "absolute inline-flex h-2 w-2 rounded-full bg-amber-400 opacity-50 animate-ping" />
93+ < span className = "relative inline-flex h-2 w-2 rounded-full bg-amber-400" />
94+ </ div >
95+ < div className = "flex-1 min-w-0" >
96+ < h4 className = "text-sm sm:text-base font-semibold text-white" > { announcement . title } </ h4 >
97+ < div className = "mt-1.5 text-xs sm:text-sm text-slate-300 prose prose-sm prose-invert max-w-none prose-p:my-1 prose-ul:my-1 prose-li:my-0.5 prose-a:text-amber-400 prose-a:no-underline hover:prose-a:underline" >
98+ < ReactMarkdown > { announcement . content } </ ReactMarkdown >
99+ </ div >
100+ < p className = "mt-2 text-xs text-amber-300/60 font-medium" >
101+ { formatRelativeDate ( announcement . createdAt ) }
102+ </ p >
81103 </ div >
82- < p className = "mt-2 text-xs text-slate-500" >
83- { formatRelativeDate ( announcement . createdAt ) }
84- </ p >
85104 </ div >
86- </ div >
87- </ motion . article >
88- ) ) }
89- </ AnimatePresence >
105+ </ motion . article >
106+ ) ) }
107+ </ AnimatePresence >
108+ </ div >
90109 </ div >
91110 </ motion . div >
92111 )
0 commit comments