Skip to content

Commit 4cfa78a

Browse files
committed
notif fix
1 parent 328d996 commit 4cfa78a

File tree

2 files changed

+78
-19
lines changed

2 files changed

+78
-19
lines changed

src/components/AppSidebar.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,25 @@ export function AppSidebar({ activeTab, setActiveTab, hasAccess = false, ...prop
6666
const { state, isMobile, setOpenMobile } = useSidebar();
6767
const { isDark, toggleTheme } = useTheme();
6868
const menuButtonClass = cn(
69-
"transition-colors",
69+
"relative isolate transition-colors",
70+
"data-[active=true]:shadow-[inset_0_1px_0_rgba(255,255,255,0.06),0_10px_20px_rgba(0,0,0,0.35)]",
71+
"data-[active=true]:ring-1 data-[active=true]:ring-[#FF8A5B]/25",
72+
"data-[active=true]:before:content-[''] data-[active=true]:before:absolute data-[active=true]:before:inset-0 data-[active=true]:before:pointer-events-none data-[active=true]:before:z-0",
73+
"data-[active=true]:after:content-[''] data-[active=true]:after:absolute data-[active=true]:after:inset-0 data-[active=true]:after:pointer-events-none data-[active=true]:after:z-0",
74+
"data-[active=true]:[&>*]:relative data-[active=true]:[&>*]:z-10",
7075
isDark
71-
? "text-white/70 hover:text-white hover:bg-white/5 data-[active=true]:bg-[#FF8A5B]/12 data-[active=true]:text-white data-[active=true]:ring-1 data-[active=true]:ring-[#FF8A5B]/25"
72-
: "text-gray-600 hover:text-gray-900 hover:bg-black/5 data-[active=true]:bg-[#FF8A5B]/12 data-[active=true]:text-[#7A341C] data-[active=true]:ring-1 data-[active=true]:ring-[#FF8A5B]/20"
76+
? [
77+
"text-white/70 hover:text-white hover:bg-white/5",
78+
"data-[active=true]:text-white data-[active=true]:bg-[linear-gradient(145deg,#151515_0%,#0B0B0B_100%)]",
79+
"data-[active=true]:before:bg-[radial-gradient(circle_at_20%_15%,rgba(255,138,91,0.18),transparent_55%)]",
80+
"data-[active=true]:after:bg-[url('/noise.webp')] data-[active=true]:after:bg-[length:30%] data-[active=true]:after:opacity-[0.12]",
81+
].join(" ")
82+
: [
83+
"text-gray-600 hover:text-gray-900 hover:bg-black/5",
84+
"data-[active=true]:text-[#5B2A16] data-[active=true]:bg-[linear-gradient(145deg,#FFF6F0_0%,#FFFFFF_100%)]",
85+
"data-[active=true]:before:bg-[radial-gradient(circle_at_20%_15%,rgba(255,138,91,0.14),transparent_55%)]",
86+
"data-[active=true]:after:bg-[url('/noise.webp')] data-[active=true]:after:bg-[length:30%] data-[active=true]:after:opacity-[0.08]",
87+
].join(" ")
7388
);
7489

7590

src/components/DashboardViews/NotificationCenter.tsx

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { useState, useEffect, useCallback, useRef } from 'react';
1+
import { useState, useEffect, useCallback, useRef, useLayoutEffect } from 'react';
2+
import { createPortal } from 'react-dom';
23
import {
34
Bell, Check, CheckCheck, Trash2, Loader2, X,
45
CreditCard, Users, Shield, Phone, Info, AlertTriangle, CheckCircle2,
@@ -30,10 +31,10 @@ const TYPE_ICONS: Record<string, React.ReactNode> = {
3031
};
3132

3233
const TYPE_COLORS: Record<string, string> = {
33-
info: 'text-blue-400 bg-blue-500/10',
34-
warning: 'text-orange-400 bg-orange-500/10',
35-
success: 'text-emerald-400 bg-emerald-500/10',
36-
error: 'text-red-400 bg-red-500/10',
34+
info: 'text-[#FFB286] bg-[#FF8A5B]/12',
35+
warning: 'text-[#FF9E6C] bg-[#FF8A5B]/14',
36+
success: 'text-[#FFD1B8] bg-[#FF8A5B]/10',
37+
error: 'text-red-400 bg-red-500/12',
3738
};
3839

3940
const CATEGORY_ICONS: Record<string, React.ReactNode> = {
@@ -60,7 +61,9 @@ export default function NotificationCenter({ isDark, className }: NotificationCe
6061
const [loading, setLoading] = useState(false);
6162
const [notifications, setNotifications] = useState<NotificationEntry[]>([]);
6263
const [unreadCount, setUnreadCount] = useState(0);
64+
const buttonRef = useRef<HTMLButtonElement>(null);
6365
const dropdownRef = useRef<HTMLDivElement>(null);
66+
const [dropdownStyle, setDropdownStyle] = useState<React.CSSProperties>({});
6467

6568
const apiCall = useCallback(async (path: string, options?: RequestInit) => {
6669
try {
@@ -113,14 +116,47 @@ export default function NotificationCenter({ isDark, className }: NotificationCe
113116
// Close on outside click
114117
useEffect(() => {
115118
const handler = (e: MouseEvent) => {
116-
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
117-
setOpen(false);
118-
}
119+
const target = e.target as Node;
120+
if (dropdownRef.current?.contains(target) || buttonRef.current?.contains(target)) return;
121+
setOpen(false);
119122
};
120123
if (open) document.addEventListener('mousedown', handler);
121124
return () => document.removeEventListener('mousedown', handler);
122125
}, [open]);
123126

127+
const updatePosition = useCallback(() => {
128+
const btn = buttonRef.current;
129+
if (!btn) return;
130+
const rect = btn.getBoundingClientRect();
131+
const isMobileViewport = window.innerWidth < 640;
132+
const width = Math.min(380, Math.floor(window.innerWidth * 0.92));
133+
const left = isMobileViewport
134+
? (window.innerWidth - width) / 2
135+
: Math.min(Math.max(rect.right - width, 8), window.innerWidth - width - 8);
136+
const top = Math.min(rect.bottom + 12, window.innerHeight - 16);
137+
const maxHeight = Math.max(240, window.innerHeight - top - 16);
138+
setDropdownStyle({
139+
position: 'fixed',
140+
top,
141+
left,
142+
width,
143+
maxHeight,
144+
});
145+
}, []);
146+
147+
useLayoutEffect(() => {
148+
if (!open) return;
149+
updatePosition();
150+
const onResize = () => updatePosition();
151+
const onScroll = () => updatePosition();
152+
window.addEventListener('resize', onResize);
153+
window.addEventListener('scroll', onScroll, true);
154+
return () => {
155+
window.removeEventListener('resize', onResize);
156+
window.removeEventListener('scroll', onScroll, true);
157+
};
158+
}, [open, updatePosition]);
159+
124160
const markAllRead = async () => {
125161
try {
126162
await apiCall('mark-read', { method: 'POST', body: JSON.stringify({ ids: [] }) });
@@ -145,9 +181,10 @@ export default function NotificationCenter({ isDark, className }: NotificationCe
145181
};
146182

147183
return (
148-
<div className={cn("relative", className)} ref={dropdownRef}>
184+
<div className={cn("relative", className)}>
149185
{/* Bell Button */}
150186
<button
187+
ref={buttonRef}
151188
onClick={() => setOpen(!open)}
152189
className={cn(
153190
"relative inline-flex h-8 w-8 items-center justify-center rounded-full border transition-colors",
@@ -170,12 +207,18 @@ export default function NotificationCenter({ isDark, className }: NotificationCe
170207
</button>
171208

172209
{/* Dropdown */}
173-
{open && (
174-
<div className={cn(
175-
"absolute top-full mt-3 w-[min(92vw,380px)] max-h-[480px] rounded-2xl border shadow-2xl z-[100] flex flex-col overflow-hidden backdrop-blur-xl",
176-
"left-1/2 -translate-x-1/2 sm:left-auto sm:right-0 sm:translate-x-0",
177-
isDark ? "bg-[#0F0F12]/95 border-white/10 shadow-black/60" : "bg-white/95 border-gray-200 shadow-lg shadow-gray-200/50"
178-
)}>
210+
{open && createPortal(
211+
<div
212+
ref={dropdownRef}
213+
style={dropdownStyle}
214+
className={cn(
215+
"rounded-2xl border shadow-2xl z-[500] flex flex-col overflow-hidden backdrop-blur-2xl",
216+
"relative [&>*]:relative [&>*]:z-10",
217+
"before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:bg-[radial-gradient(circle_at_20%_0%,rgba(255,138,91,0.16),transparent_60%)]",
218+
"after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[url('/noise.webp')] after:bg-[length:30%] after:opacity-[0.08]",
219+
isDark ? "bg-[#0F0F12]/98 border-white/10 shadow-black/60" : "bg-white/98 border-gray-200 shadow-lg shadow-gray-200/50"
220+
)}
221+
>
179222
{/* Header */}
180223
<div className={cn("px-4 py-3 border-b flex items-center justify-between shrink-0", isDark ? "border-white/10" : "border-gray-100")}>
181224
<h3 className={cn("text-sm font-semibold", isDark ? "text-white" : "text-gray-900")}>Notifications</h3>
@@ -246,7 +289,8 @@ export default function NotificationCenter({ isDark, className }: NotificationCe
246289
</div>
247290
)}
248291
</div>
249-
</div>
292+
</div>,
293+
document.body
250294
)}
251295
</div>
252296
);

0 commit comments

Comments
 (0)