1 import { useState } from "react";
2 import { useLocation } from "wouter";
3 import { Button } from "@/components/ui/button";
4 import {
5 Sheet,
6 SheetContent,
7 SheetHeader,
8 SheetTitle,
9 SheetTrigger,
10 } from "@/components/ui/sheet";
11 import { Separator } from "@/components/ui/separator";
12 import { useAuth } from "@/hooks/useAuth";
13 import type { User } from "@shared/schema";
14 import {
15 Menu,
16 Wrench,
17 BookOpen,
18 Bookmark,
19 Sparkles,
20 Laptop,
21 Binary,
22 PlusCircle,
23 Download,
24 CloudOff,
25 LogIn,
26 LogOut,
27 ShieldCheck,
28 Palette
29 } from "lucide-react";
30 import { cn } from "@/lib/utils";
31 import ThemeCustomizerModal from "@/components/ui/theme-customizer-modal";
32
33 export function MobileMenu() {
34 const [open, setOpen] = useState(false);
35 const [themeModalOpen, setThemeModalOpen] = useState(false);
36 const [location, setLocation] = useLocation();
37 const { user, isAuthenticated, logoutMutation } = useAuth() as any;
38
39 const handleNavigation = (path: string) => {
40 setLocation(path);
41 setOpen(false);
42 };
43
44 const navItems = [
45 { label: "Repair Guides", path: "/", icon: BookOpen },
46 { label: "Device Finder", path: "/device-finder", icon: Laptop },
47 { label: "AI Diagnostic Assistant", path: "/troubleshooting", icon: Sparkles, badge: "AI" },
48 { label: "Software Hub", path: "/software-tools", icon: Binary },
49 { label: "Saved Guides", path: "/favorites", icon: Bookmark, authRequired: true },
50 ];
51
52 const toolItems = [
53 { label: "Create Repair Guide", path: "/create-guide", icon: PlusCircle },
54 { label: "Offline Manuals & Downloads", path: "/downloads", icon: Download },
55 { label: "Offline Sync Engine", path: "/offline-settings", icon: CloudOff },
56 { label: "Admin Workbench", path: "/admin", icon: ShieldCheck },
57 ];
58
59 return (
60 <>
61 <Sheet open={open} onOpenChange={setOpen}>
62 <SheetTrigger asChild>
63 <Button variant="ghost" size="icon" className="lg:hidden rounded-xl h-9 w-9 text-muted-foreground hover:text-foreground">
64 <Menu className="h-5 w-5" />
65 <span className="sr-only">Toggle navigation menu</span>
66 </Button>
67 </SheetTrigger>
68 <SheetContent side="left" className="w-80 p-0 flex flex-col bg-background/95 backdrop-blur-2xl border-r border-border/60">
69
70 {/* Header Brand */}
71 <div className="p-5 border-b border-border/40 bg-muted/20">
72 <SheetHeader>
73 <SheetTitle className="flex items-center gap-3 text-left">
74 <div className="flex items-center justify-center h-9 w-9 rounded-xl bg-primary text-primary-foreground shadow-sm">
75 <Wrench className="h-4 w-4" />
76 </div>
77 <div>
78 <div className="flex items-center gap-1.5">
79 <span className="text-base font-extrabold tracking-tight">JCR GUIDE</span>
80 <span className="text-[9px] font-extrabold uppercase px-1.5 py-0.5 rounded bg-primary/10 text-primary border border-primary/20">
81 PRO
82 </span>
83 </div>
84 <p className="text-[10px] text-muted-foreground font-normal">Technician Field Guide</p>
85 </div>
86 </SheetTitle>
87 </SheetHeader>
88 </div>
89
90 {/* Navigation Links Scrollable */}
91 <div className="flex-1 overflow-y-auto p-4 space-y-6">
92
93 {/* Primary Nav */}
94 <div className="space-y-1.5">
95 <span className="text-[10px] font-bold text-muted-foreground uppercase tracking-wider px-3">
96 Navigation
97 </span>
98 <div className="space-y-1">
99 {navItems.map((item) => {
100 if (item.authRequired && !isAuthenticated) return null;
101 const isActive = location === item.path;
102 const Icon = item.icon;
103
104 return (
105 <Button
106 key={item.path}
107 variant="ghost"
108 className={cn(
109 "w-full justify-between rounded-xl h-10 px-3 text-xs font-semibold transition-colors",
110 isActive ? "bg-primary/10 text-primary font-bold" : "text-foreground/80 hover:bg-muted"
111 )}
112 onClick={() => handleNavigation(item.path)}
113 >
114 <div className="flex items-center gap-3">
115 <Icon className={cn("h-4 w-4", isActive ? "text-primary" : "text-muted-foreground")} />
116 <span>{item.label}</span>
117 </div>
118 {item.badge && (
119 <span className="text-[9px] font-bold px-1.5 py-0.5 rounded bg-primary/20 text-primary">
120 {item.badge}
121 </span>
122 )}
123 </Button>
124 );
125 })}
126 </div>
127 </div>
128
129 <Separator className="opacity-50" />
130
131 {/* Tools & Content */}
132 <div className="space-y-1.5">
133 <span className="text-[10px] font-bold text-muted-foreground uppercase tracking-wider px-3">
134 Technician Utilities
135 </span>
136 <div className="space-y-1">
137 {toolItems.map((item) => {
138 const isActive = location === item.path;
139 const Icon = item.icon;
140
141 return (
142 <Button
143 key={item.path}
144 variant="ghost"
145 className={cn(
146 "w-full justify-start rounded-xl h-10 px-3 text-xs font-semibold transition-colors gap-3",
147 isActive ? "bg-primary/10 text-primary font-bold" : "text-foreground/80 hover:bg-muted"
148 )}
149 onClick={() => handleNavigation(item.path)}
150 >
151 <Icon className={cn("h-4 w-4", isActive ? "text-primary" : "text-muted-foreground")} />
152 <span>{item.label}</span>
153 </Button>
154 );
155 })}
156
157 <Button
158 variant="ghost"
159 className="w-full justify-start rounded-xl h-10 px-3 text-xs font-semibold transition-colors gap-3 text-primary hover:bg-primary/10"
160 onClick={() => {
161 setOpen(false);
162 setThemeModalOpen(true);
163 }}
164 >
165 <Palette className="h-4 w-4 text-primary" />
166 <span>Customize Theme Colors</span>
167 </Button>
168 </div>
169 </div>
170
171 </div>
172
173 {/* Footer Auth area */}
174 <div className="p-4 border-t border-border/40 bg-muted/20">
175 {!isAuthenticated ? (
176 <Button
177 onClick={() => handleNavigation("/auth")}
178 className="w-full rounded-xl h-10 text-xs font-bold gap-2 shadow-xs"
179 >
180 <LogIn className="h-4 w-4" />
181 Sign In to Workbench
182 </Button>
183 ) : (
184 <div className="space-y-3">
185 <div className="flex items-center gap-3 p-2 rounded-xl bg-background border border-border/50">
186 <div className="h-8 w-8 rounded-lg bg-primary/10 text-primary flex items-center justify-center font-bold text-xs">
187 {user?.firstName ? user.firstName[0].toUpperCase() : "U"}
188 </div>
189 <div className="flex-1 min-w-0">
190 <p className="text-xs font-bold truncate">
191 {user?.firstName ? `${user.firstName} ${user.lastName || ""}`.trim() : "Technician"}
192 </p>
193 <p className="text-[10px] text-muted-foreground truncate">{user?.email}</p>
194 </div>
195 </div>
196
197 <Button
198 variant="outline"
199 size="sm"
200 onClick={() => {
201 logoutMutation.mutate();
202 setOpen(false);
203 }}
204 className="w-full rounded-xl h-9 text-xs font-semibold gap-2 border-border/60 text-destructive hover:bg-destructive/10 hover:text-destructive"
205 >
206 <LogOut className="h-3.5 w-3.5" />
207 Sign Out
208 </Button>
209 </div>
210 )}
211 </div>
212
213 </SheetContent>
214 </Sheet>
215
216 <ThemeCustomizerModal
217 open={themeModalOpen}
218 onOpenChange={setThemeModalOpen}
219 />
220 </>
221 );
222 }