+76
-48
src/components/AppHeader.tsx
+76
-48
src/components/AppHeader.tsx
···
1
1
import { useState, useEffect, useRef } from "react";
2
+
import { createPortal } from "react-dom";
2
3
import { Heart, Home, LogOut, ChevronDown } from "lucide-react";
3
4
import ThemeControls from "./ThemeControls";
4
5
import FireflyLogo from "../assets/at-firefly-logo.svg?react";
···
33
34
onToggleMotion,
34
35
}: AppHeaderProps) {
35
36
const [showMenu, setShowMenu] = useState(false);
37
+
const [menuPosition, setMenuPosition] = useState({ top: 0, right: 0 });
36
38
const menuRef = useRef<HTMLDivElement>(null);
39
+
const buttonRef = useRef<HTMLButtonElement>(null);
37
40
38
41
useEffect(() => {
39
42
function handleClickOutside(event: MouseEvent) {
40
-
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
43
+
if (
44
+
menuRef.current &&
45
+
!menuRef.current.contains(event.target as Node) &&
46
+
buttonRef.current &&
47
+
!buttonRef.current.contains(event.target as Node)
48
+
) {
41
49
setShowMenu(false);
42
50
}
43
51
}
···
45
53
return () => document.removeEventListener("mousedown", handleClickOutside);
46
54
}, []);
47
55
56
+
useEffect(() => {
57
+
if (showMenu && buttonRef.current) {
58
+
const rect = buttonRef.current.getBoundingClientRect();
59
+
setMenuPosition({
60
+
top: rect.bottom + 8,
61
+
right: window.innerWidth - rect.right,
62
+
});
63
+
}
64
+
}, [showMenu]);
65
+
48
66
return (
49
-
<div className="bg-white dark:bg-slate-900 border-b-2 border-cyan-500/30 dark:border-purple-500/30 backdrop-blur-xl relative z-[100]">
67
+
<div className="bg-white dark:bg-slate-900 border-b-2 border-cyan-500/30 dark:border-purple-500/30 backdrop-blur-xl relative z-50">
50
68
<div className="max-w-6xl mx-auto px-4 py-1">
51
69
<div className="flex items-center justify-between">
52
70
<button
···
69
87
/>
70
88
)}
71
89
{session && (
72
-
<div className="relative z-[9999]" ref={menuRef}>
90
+
<>
73
91
<button
92
+
ref={buttonRef}
74
93
onClick={() => setShowMenu(!showMenu)}
75
94
className="flex items-center space-x-3 px-3 py-1 rounded-lg hover:bg-purple-50 dark:hover:bg-slate-800 transition-colors focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400"
76
95
>
···
95
114
/>
96
115
</button>
97
116
98
-
{showMenu && (
99
-
<div className="absolute right-0 mt-2 w-64 bg-white dark:bg-slate-900 rounded-lg shadow-lg border-2 border-cyan-500/30 dark:border-purple-500/30 py-2 z-[9999]">
100
-
<div className="px-4 py-3">
101
-
<div className="font-semibold text-purple-950 dark:text-cyan-50">
102
-
{session?.displayName || session.handle}
103
-
</div>
104
-
<div className="text-sm text-purple-750 dark:text-cyan-250">
105
-
@{session?.handle}
106
-
</div>
107
-
</div>
108
-
<button
109
-
onClick={() => {
110
-
setShowMenu(false);
111
-
onNavigate("home");
112
-
}}
113
-
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-purple-50 dark:hover:bg-slate-800 transition-colors text-left"
114
-
>
115
-
<Home className="w-4 h-4 text-purple-950 dark:text-cyan-50" />
116
-
<span className="text-purple-950 dark:text-cyan-50">
117
-
Dashboard
118
-
</span>
119
-
</button>
120
-
<button
121
-
onClick={() => {
122
-
setShowMenu(false);
123
-
onNavigate("login");
124
-
}}
125
-
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-purple-50 dark:hover:bg-slate-800 transition-colors text-left"
126
-
>
127
-
<Heart className="w-4 h-4 text-purple-950 dark:text-cyan-50" />
128
-
<span className="text-purple-950 dark:text-cyan-50">
129
-
Login screen
130
-
</span>
131
-
</button>
132
-
<button
133
-
onClick={() => {
134
-
setShowMenu(false);
135
-
onLogout();
117
+
{showMenu &&
118
+
createPortal(
119
+
<div
120
+
ref={menuRef}
121
+
className="fixed w-64 bg-white dark:bg-slate-900 rounded-lg shadow-2xl border-2 border-cyan-500/30 dark:border-purple-500/30 py-2 z-[9999]"
122
+
style={{
123
+
top: `${menuPosition.top}px`,
124
+
right: `${menuPosition.right}px`,
136
125
}}
137
-
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors text-left text-red-600 dark:text-red-400"
138
126
>
139
-
<LogOut className="w-4 h-4" />
140
-
<span>Log out</span>
141
-
</button>
142
-
</div>
143
-
)}
144
-
</div>
127
+
<div className="px-4 py-3">
128
+
<div className="font-semibold text-purple-950 dark:text-cyan-50">
129
+
{session?.displayName || session.handle}
130
+
</div>
131
+
<div className="text-sm text-purple-750 dark:text-cyan-250">
132
+
@{session?.handle}
133
+
</div>
134
+
</div>
135
+
<button
136
+
onClick={() => {
137
+
setShowMenu(false);
138
+
onNavigate("home");
139
+
}}
140
+
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-purple-50 dark:hover:bg-slate-800 transition-colors text-left"
141
+
>
142
+
<Home className="w-4 h-4 text-purple-950 dark:text-cyan-50" />
143
+
<span className="text-purple-950 dark:text-cyan-50">
144
+
Dashboard
145
+
</span>
146
+
</button>
147
+
<button
148
+
onClick={() => {
149
+
setShowMenu(false);
150
+
onNavigate("login");
151
+
}}
152
+
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-purple-50 dark:hover:bg-slate-800 transition-colors text-left"
153
+
>
154
+
<Heart className="w-4 h-4 text-purple-950 dark:text-cyan-50" />
155
+
<span className="text-purple-950 dark:text-cyan-50">
156
+
Login screen
157
+
</span>
158
+
</button>
159
+
<button
160
+
onClick={() => {
161
+
setShowMenu(false);
162
+
onLogout();
163
+
}}
164
+
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors text-left text-red-600 dark:text-red-400"
165
+
>
166
+
<LogOut className="w-4 h-4" />
167
+
<span>Log out</span>
168
+
</button>
169
+
</div>,
170
+
document.body,
171
+
)}
172
+
</>
145
173
)}
146
174
</div>
147
175
</div>
+3
-3
src/components/SetupWizard.tsx
+3
-3
src/components/SetupWizard.tsx
···
281
281
onChange={(e) =>
282
282
setAutomationFrequency(
283
283
e.target.value as
284
-
| "weekly"
285
-
| "monthly"
286
-
| "quarterly",
284
+
| "Weekly"
285
+
| "Monthly"
286
+
| "Quarterly",
287
287
)
288
288
}
289
289
className="mt-2 px-3 py-2 bg-white dark:bg-slate-800 border border-cyan-500/30 dark:border-purple-500/30 rounded-lg text-sm w-full text-purple-950 dark:text-cyan-50 hover:border-cyan-400 dark:hover:border-purple-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400 transition-colors"