1- import { Label , Dropdown , Separator } from '@heroui/react' ;
21import { today , getLocalTimeZone } from '@internationalized/date' ;
32import {
43 ListIcon ,
5- NoteIcon ,
6- UserIcon ,
7- RepeatIcon ,
8- MapPinIcon ,
9- SignOutIcon ,
10- GithubLogoIcon ,
114 NotePencilIcon ,
12- RoadHorizonIcon ,
13- CalendarDotsIcon ,
145 CalendarCheckIcon ,
15- ArrowSquareOutIcon ,
166} from '@phosphor-icons/react' ;
17- import { useLocation , useNavigate } from 'react-router' ;
187
19- import { CustomKbd , CustomButton } from '@components' ;
8+ import { UserMenu , CustomKbd , CustomButton } from '@components' ;
209import { useScreenWidth } from '@hooks' ;
21- import { signOut } from '@services' ;
2210import {
2311 useUser ,
2412 useNoteDrawerActions ,
13+ useMobileSidebarActions ,
2514 useOccurrenceDrawerActions ,
2615} from '@stores' ;
2716
2817import ThemeToggle from './ThemeToggle' ;
2918
30- const ROADMAP_URL = 'https://habitrack.featurebase.app/roadmap' ;
31- const GITHUB_URL = 'https://github.com/domhhv/habitrack' ;
32-
3319const Header = ( ) => {
3420 const user = useUser ( ) ;
35- const navigate = useNavigate ( ) ;
3621 const { isMobile } = useScreenWidth ( ) ;
37- const { pathname } = useLocation ( ) ;
22+ const { openMobileSidebar } = useMobileSidebarActions ( ) ;
3823 const { openNoteDrawer } = useNoteDrawerActions ( ) ;
3924 const { openOccurrenceDrawer } = useOccurrenceDrawerActions ( ) ;
4025
@@ -48,178 +33,23 @@ const Header = () => {
4833 } ) ;
4934 } ;
5035
51- const navItems = [
52- {
53- href : '/calendar/month' ,
54- icon : CalendarDotsIcon ,
55- id : 'calendar' ,
56- isActive : pathname . startsWith ( '/calendar' ) ,
57- label : 'Calendar' ,
58- } ,
59- ...( user
60- ? ( [
61- {
62- href : '/habits' ,
63- icon : RepeatIcon ,
64- id : 'habits' ,
65- isActive : pathname . startsWith ( '/habits' ) ,
66- label : 'Habits' ,
67- } ,
68- {
69- href : '/notes' ,
70- icon : NoteIcon ,
71- id : 'notes' ,
72- isActive : pathname === '/notes' ,
73- label : 'Notes' ,
74- } ,
75- {
76- href : '/account' ,
77- icon : UserIcon ,
78- id : 'account' ,
79- isActive : pathname === '/account' ,
80- label : 'Account' ,
81- } ,
82- ] as const )
83- : [ ] ) ,
84- ] as const ;
85-
86- const activeLabel =
87- navItems . find ( ( item ) => {
88- return item . isActive ;
89- } ) ?. label ?? 'Menu' ;
90-
9136 return (
92- < nav className = "bg-background border-border sticky top-0 z-50 h-16 w-full border-b" >
93- < header className = "flex h-full w-full items-center justify-between px-8 lg:px-8" >
94- < div className = "flex items-center gap-2" >
95- < Dropdown >
96- < CustomButton
97- size = "sm"
98- variant = "outline"
99- aria-label = "Open navigation menu"
100- >
101- < ListIcon size = { isMobile ? 16 : 18 } />
102- < span > { activeLabel } </ span >
103- </ CustomButton >
104- < Dropdown . Popover className = "min-w-[250px]" >
105- < Dropdown . Menu aria-label = "Navigation" >
106- < Dropdown . Section >
107- { navItems . map ( ( { href, icon : Icon , id, isActive, label } ) => {
108- return (
109- < Dropdown . Item
110- id = { id }
111- key = { id }
112- href = { href }
113- textValue = { label }
114- className = "justify-between"
115- >
116- < div className = "flex items-center gap-2" >
117- < Icon className = "size-4 shrink-0" />
118- < Label > { label } </ Label >
119- </ div >
120- { isActive && user && (
121- < div className = "text-muted flex items-center gap-1 text-xs" >
122- < MapPinIcon />
123- < span className = "ms-auto" > You're here</ span >
124- </ div >
125- ) }
126- </ Dropdown . Item >
127- ) ;
128- } ) }
129- </ Dropdown . Section >
130- < Separator className = "bg-muted/25" />
131- < Dropdown . Section >
132- < Dropdown . Item
133- id = "roadmap"
134- target = "_blank"
135- href = { ROADMAP_URL }
136- textValue = "Roadmap"
137- rel = "noopener noreferrer"
138- className = "justify-between"
139- >
140- < div className = "flex items-center gap-2" >
141- < RoadHorizonIcon className = "size-4 shrink-0" />
142- < Label > Roadmap</ Label >
143- </ div >
144- < ArrowSquareOutIcon className = "text-muted/50 size-4 shrink-0" />
145- </ Dropdown . Item >
146- < Dropdown . Item
147- target = "_blank"
148- id = "source-code"
149- href = { GITHUB_URL }
150- rel = "noopener noreferrer"
151- className = "justify-between"
152- textValue = "Source Code on GitHub"
153- >
154- < div className = "flex items-center gap-2" >
155- < GithubLogoIcon className = "size-4 shrink-0" />
156- < Label > Source Code on GitHub</ Label >
157- </ div >
158- < ArrowSquareOutIcon className = "text-muted/50 size-4 shrink-0 justify-self-end" />
159- </ Dropdown . Item >
160- </ Dropdown . Section >
161- { ! ! user && (
162- < >
163- < Separator className = "bg-muted/25" />
164- < Dropdown . Section >
165- < Dropdown . Item
166- id = "logout"
167- variant = "danger"
168- onAction = { signOut }
169- textValue = "Log Out"
170- >
171- < SignOutIcon className = "text-danger size-4 shrink-0" />
172- < Label > Log Out</ Label >
173- </ Dropdown . Item >
174- </ Dropdown . Section >
175- </ >
176- ) }
177- </ Dropdown . Menu >
178- </ Dropdown . Popover >
179- </ Dropdown >
180- </ div >
37+ < nav className = "bg-background border-border sticky top-0 z-50 h-16 w-full border-b lg:hidden" >
38+ < header className = "flex h-full w-full items-center justify-between px-4 md:px-8" >
39+ < CustomButton
40+ size = "sm"
41+ isIconOnly
42+ variant = "outline"
43+ onPress = { openMobileSidebar }
44+ aria-label = "Open navigation menu"
45+ >
46+ < ListIcon size = { isMobile ? 16 : 18 } />
47+ </ CustomButton >
18148 < div className = "flex items-center gap-2" >
18249 < ThemeToggle />
183- { ! ! user && (
184- < div className = "bg-background-secondary border-border dark:border-border fixed right-0 bottom-0 left-0 z-50 flex w-full gap-2 border-t p-2 px-4 md:right-4 md:bottom-4 md:left-auto md:w-auto md:border-0 md:bg-transparent md:p-0 xl:static xl:right-auto xl:bottom-auto xl:border-0 xl:bg-transparent xl:p-0 dark:md:bg-transparent dark:xl:bg-transparent" >
185- < CustomButton
186- size = "sm"
187- variant = "bordered"
188- fullWidth = { isMobile }
189- onPress = { dispatchNoteDrawerOpen }
190- className = "flex-1 max-md:h-8 max-md:gap-1 max-md:rounded-full md:flex-none"
191- >
192- < NotePencilIcon size = { isMobile ? 12 : 16 } />
193- Note
194- < CustomKbd
195- isSolid
196- size = "sm"
197- variant = "light"
198- shortcutParams = { [ 'n' , dispatchNoteDrawerOpen ] }
199- >
200- N
201- </ CustomKbd >
202- </ CustomButton >
203- < CustomButton
204- size = "sm"
205- variant = "primary"
206- fullWidth = { isMobile }
207- onPress = { dispatchOccurrenceDrawerOpen }
208- className = "flex-1 max-md:h-8 max-md:gap-1 max-md:rounded-full md:flex-none"
209- >
210- < CalendarCheckIcon size = { isMobile ? 12 : 16 } />
211- Log
212- < CustomKbd
213- size = "sm"
214- variant = "default"
215- shortcutParams = { [ 'l' , dispatchOccurrenceDrawerOpen ] }
216- >
217- L
218- </ CustomKbd >
219- </ CustomButton >
220- </ div >
221- ) }
222- { ! user && (
50+ { user ? (
51+ < UserMenu />
52+ ) : (
22353 < >
22454 < CustomButton
22555 size = "sm"
@@ -228,16 +58,7 @@ const Header = () => {
22858 data-testid = "login-button"
22959 >
23060 Log In
231- < CustomKbd
232- size = "sm"
233- variant = "default"
234- shortcutParams = { [
235- 'i' ,
236- ( ) => {
237- navigate ( '/login' ) ;
238- } ,
239- ] }
240- >
61+ < CustomKbd size = "sm" variant = "default" >
24162 I
24263 </ CustomKbd >
24364 </ CustomButton >
@@ -248,23 +69,44 @@ const Header = () => {
24869 data-testid = "register-button"
24970 >
25071 Join
251- < CustomKbd
252- size = "sm"
253- variant = "default"
254- shortcutParams = { [
255- 'j' ,
256- ( ) => {
257- navigate ( '/register' ) ;
258- } ,
259- ] }
260- >
72+ < CustomKbd size = "sm" variant = "default" >
26173 J
26274 </ CustomKbd >
26375 </ CustomButton >
26476 </ >
26577 ) }
26678 </ div >
26779 </ header >
80+ { ! ! user && (
81+ < div className = "bg-background-secondary border-border dark:border-border fixed right-0 bottom-0 left-0 z-50 flex w-full gap-2 border-t p-2 px-4 md:right-4 md:bottom-4 md:left-auto md:w-auto md:border-0 md:bg-transparent md:p-0 dark:md:bg-transparent" >
82+ < CustomButton
83+ size = "sm"
84+ variant = "bordered"
85+ fullWidth = { isMobile }
86+ onPress = { dispatchNoteDrawerOpen }
87+ className = "flex-1 max-md:h-8 max-md:gap-1 max-md:rounded-full md:flex-none"
88+ >
89+ < NotePencilIcon size = { isMobile ? 12 : 16 } />
90+ Note
91+ < CustomKbd isSolid size = "sm" variant = "light" >
92+ N
93+ </ CustomKbd >
94+ </ CustomButton >
95+ < CustomButton
96+ size = "sm"
97+ variant = "primary"
98+ fullWidth = { isMobile }
99+ onPress = { dispatchOccurrenceDrawerOpen }
100+ className = "flex-1 max-md:h-8 max-md:gap-1 max-md:rounded-full md:flex-none"
101+ >
102+ < CalendarCheckIcon size = { isMobile ? 12 : 16 } />
103+ Log
104+ < CustomKbd size = "sm" variant = "default" >
105+ L
106+ </ CustomKbd >
107+ </ CustomButton >
108+ </ div >
109+ ) }
268110 </ nav >
269111 ) ;
270112} ;
0 commit comments