Skip to content

Commit d01ca22

Browse files
committed
update: better mobile UX
1 parent bc6502f commit d01ca22

File tree

5 files changed

+151
-30
lines changed

5 files changed

+151
-30
lines changed

src/components/Header.tsx

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
Home,
1212
RefreshCw,
1313
UserPlus,
14-
Activity
14+
Activity,
15+
Menu
1516
} from "lucide-react";
1617
import { useAuth, hasRequiredApiKeys } from "@/lib/auth";
1718
import { useRBAC } from "@/hooks/useRBAC";
@@ -141,8 +142,9 @@ export default function Header() {
141142
<div className="flex items-center gap-2 sm:gap-3">
142143
{isAuthenticated ? (
143144
<>
145+
{/* Desktop Profile Dropdown */}
144146
<DropdownMenu>
145-
<DropdownMenuTrigger asChild>
147+
<DropdownMenuTrigger asChild className="hidden md:flex">
146148
<Button variant="outline" size="sm" className="max-w-[150px] sm:max-w-none">
147149
<UserIcon className="h-4 w-4 mr-2" />
148150
<span className="truncate">{profile?.name || profile?.full_name || user?.email || 'Profile'}</span>
@@ -167,30 +169,90 @@ export default function Header() {
167169
Settings
168170
</Link>
169171
</DropdownMenuItem>
170-
<DropdownMenuItem asChild className="md:hidden">
172+
<RoleGate permissions={['admin.access']}>
173+
<DropdownMenuSeparator />
174+
<DropdownMenuLabel className="text-xs text-muted-foreground">Admin</DropdownMenuLabel>
175+
<DropdownMenuItem asChild>
176+
<Link to="/admin/invitations" className="flex items-center">
177+
<UserPlus className="h-4 w-4 mr-2" />
178+
Invitations
179+
</Link>
180+
</DropdownMenuItem>
181+
<DropdownMenuItem asChild>
182+
<Link to="/admin/users" className="flex items-center">
183+
<UserIcon className="h-4 w-4 mr-2" />
184+
User Management
185+
</Link>
186+
</DropdownMenuItem>
187+
<DropdownMenuItem asChild>
188+
<Link to="/admin/roles" className="flex items-center">
189+
<Settings className="h-4 w-4 mr-2" />
190+
Role Management
191+
</Link>
192+
</DropdownMenuItem>
193+
</RoleGate>
194+
<DropdownMenuSeparator />
195+
<DropdownMenuItem onSelect={logout} className="text-red-600">
196+
<LogOut className="h-4 w-4 mr-2" />
197+
Logout
198+
</DropdownMenuItem>
199+
</DropdownMenuContent>
200+
</DropdownMenu>
201+
202+
{/* Mobile Hamburger Menu */}
203+
<DropdownMenu>
204+
<DropdownMenuTrigger asChild className="md:hidden">
205+
<Button variant="outline" size="icon">
206+
<Menu className="h-5 w-5" />
207+
</Button>
208+
</DropdownMenuTrigger>
209+
<DropdownMenuContent align="end" className="w-56">
210+
<DropdownMenuLabel>
211+
<div className="flex flex-col gap-1">
212+
<span className="text-sm font-normal truncate">{profile?.name || profile?.full_name || user?.email}</span>
213+
<RoleBadge className="mt-1" />
214+
</div>
215+
</DropdownMenuLabel>
216+
<DropdownMenuSeparator />
217+
<DropdownMenuLabel className="text-xs text-muted-foreground">Navigation</DropdownMenuLabel>
218+
<DropdownMenuItem asChild>
171219
<Link to="/dashboard" className="flex items-center">
172220
<Home className="h-4 w-4 mr-2" />
173221
Dashboard
174222
</Link>
175223
</DropdownMenuItem>
176-
<DropdownMenuItem asChild className="md:hidden">
224+
<DropdownMenuItem asChild>
177225
<Link to="/analysis-records" className="flex items-center">
178226
<FileText className="h-4 w-4 mr-2" />
179227
Analysis Records
180228
</Link>
181229
</DropdownMenuItem>
182-
<DropdownMenuItem asChild className="md:hidden">
230+
<DropdownMenuItem asChild>
183231
<Link to="/rebalance-records" className="flex items-center">
184232
<RefreshCw className="h-4 w-4 mr-2" />
185233
Rebalance Records
186234
</Link>
187235
</DropdownMenuItem>
188-
<DropdownMenuItem asChild className="md:hidden">
236+
<DropdownMenuItem asChild>
189237
<Link to="/trade-history" className="flex items-center">
190238
<TrendingUp className="h-4 w-4 mr-2" />
191239
Trade History
192240
</Link>
193241
</DropdownMenuItem>
242+
<DropdownMenuSeparator />
243+
<DropdownMenuLabel className="text-xs text-muted-foreground">Account</DropdownMenuLabel>
244+
<DropdownMenuItem asChild>
245+
<Link to="/profile" className="flex items-center">
246+
<UserIcon className="h-4 w-4 mr-2" />
247+
Profile
248+
</Link>
249+
</DropdownMenuItem>
250+
<DropdownMenuItem asChild>
251+
<Link to="/settings" className="flex items-center">
252+
<Settings className="h-4 w-4 mr-2" />
253+
Settings
254+
</Link>
255+
</DropdownMenuItem>
194256
<RoleGate permissions={['admin.access']}>
195257
<DropdownMenuSeparator />
196258
<DropdownMenuLabel className="text-xs text-muted-foreground">Admin</DropdownMenuLabel>

src/components/RebalanceHistoryTable.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,18 @@ export default function RebalanceHistoryTable() {
519519
<CardContent className="pt-6">
520520
<Tabs defaultValue="all" className="space-y-4">
521521
<TabsList className="grid w-full grid-cols-4">
522-
<TabsTrigger value="all">All ({totalCount})</TabsTrigger>
523-
<TabsTrigger value="running">Running ({runningRebalances.length})</TabsTrigger>
524-
<TabsTrigger value="completed">Completed ({completedRebalances.length})</TabsTrigger>
525-
<TabsTrigger value="cancelled">Cancelled ({cancelledRebalances.length})</TabsTrigger>
522+
<TabsTrigger value="all">
523+
All <span className="hidden sm:inline">({totalCount})</span>
524+
</TabsTrigger>
525+
<TabsTrigger value="running">
526+
Running <span className="hidden sm:inline">({runningRebalances.length})</span>
527+
</TabsTrigger>
528+
<TabsTrigger value="completed">
529+
Completed <span className="hidden sm:inline">({completedRebalances.length})</span>
530+
</TabsTrigger>
531+
<TabsTrigger value="cancelled">
532+
Cancelled <span className="hidden sm:inline">({cancelledRebalances.length})</span>
533+
</TabsTrigger>
526534
</TabsList>
527535

528536
<TabsContent value="all" className="space-y-4">

src/components/StandaloneWatchlist.tsx

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ export default function StandaloneWatchlist({ onSelectStock, selectedStock }: St
611611
{watchlist.map((item) => (
612612
<div
613613
key={item.ticker}
614-
className={`flex items-center justify-between p-4 rounded-lg border transition-colors cursor-pointer ${selectedStock === item.ticker
614+
className={`relative flex flex-col sm:flex-row sm:items-center sm:justify-between sm:p-4 rounded-lg border transition-colors cursor-pointer ${selectedStock === item.ticker
615615
? 'border-primary bg-primary/10'
616616
: 'border-border hover:bg-muted/50'
617617
}`}
@@ -621,16 +621,29 @@ export default function StandaloneWatchlist({ onSelectStock, selectedStock }: St
621621
onSelectStock?.(item.ticker);
622622
}}
623623
>
624-
<div className="flex-1">
625-
<div className="flex items-center gap-2">
624+
{/* Mobile: X button in top-right corner */}
625+
<Button
626+
size="sm"
627+
variant="ghost"
628+
className="absolute top-2 right-2 sm:hidden h-8 w-8 p-0"
629+
onClick={(e) => {
630+
e.stopPropagation();
631+
removeFromWatchlist(item.ticker);
632+
}}
633+
>
634+
<X className="h-4 w-4" />
635+
</Button>
636+
637+
<div className="flex-1 p-4 pb-2 sm:p-0 pr-10 sm:pr-0">
638+
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
626639
<span className="font-semibold">{item.ticker}</span>
627640
{item.description && (
628-
<span className="text-sm text-muted-foreground">
641+
<span className="text-sm text-muted-foreground line-clamp-1">
629642
{item.description}
630643
</span>
631644
)}
632645
</div>
633-
<div className="flex items-center gap-4 mt-1 text-sm">
646+
<div className="flex flex-wrap items-center gap-2 sm:gap-4 mt-1 text-sm">
634647
{item.currentPrice > 0 && (
635648
<span className="font-medium">
636649
${item.currentPrice.toFixed(2)}
@@ -647,15 +660,40 @@ export default function StandaloneWatchlist({ onSelectStock, selectedStock }: St
647660
<span>{Math.abs(item.priceChangePercent).toFixed(2)}%</span>
648661
</div>
649662
)}
650-
{(item.lastAnalysis || item.currentPrice > 0) && (
651-
<span className="text-muted-foreground">
652-
Last: {item.lastAnalysis || new Date().toISOString().split('T')[0]}
653-
</span>
654-
)}
663+
<span className="text-muted-foreground hidden sm:inline">
664+
Last: {item.lastAnalysis || new Date().toISOString().split('T')[0]}
665+
</span>
655666
{getDecisionBadge(item.lastDecision)}
656667
</div>
657668
</div>
658-
<div className="flex items-center gap-2">
669+
670+
{/* Mobile: Show analyze button below at full width */}
671+
<div className="sm:hidden border-t border-border/50 px-4 py-2 bg-muted/30">
672+
<Button
673+
size="sm"
674+
variant="outline"
675+
className="w-full border border-slate-700"
676+
onClick={(e) => {
677+
e.stopPropagation();
678+
openAnalysis(item.ticker);
679+
}}
680+
>
681+
{runningAnalyses.has(item.ticker) ? (
682+
<>
683+
<Eye className="h-4 w-4 mr-1" />
684+
View Progress
685+
</>
686+
) : (
687+
<>
688+
<Play className="h-4 w-4 mr-1" />
689+
Analyze
690+
</>
691+
)}
692+
</Button>
693+
</div>
694+
695+
{/* Desktop: Show buttons on the right */}
696+
<div className="hidden sm:flex items-center gap-2">
659697
<Button
660698
size="sm"
661699
variant="outline"

src/components/TradeHistoryTable.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -700,19 +700,24 @@ export default function TradeHistoryTable() {
700700
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
701701
<TabsList className="grid w-full grid-cols-5">
702702
<TabsTrigger value="all">
703-
All ({allTrades.length})
703+
<span className="sm:hidden">All</span>
704+
<span className="hidden sm:inline">All ({allTrades.length})</span>
704705
</TabsTrigger>
705706
<TabsTrigger value="pending">
706-
Pending ({getFilteredTrades('pending').length})
707+
<span className="sm:hidden">Pending</span>
708+
<span className="hidden sm:inline">Pending ({getFilteredTrades('pending').length})</span>
707709
</TabsTrigger>
708710
<TabsTrigger value="approved">
709-
Approved ({getFilteredTrades('approved').length})
711+
<span className="sm:hidden">Approved</span>
712+
<span className="hidden sm:inline">Approved ({getFilteredTrades('approved').length})</span>
710713
</TabsTrigger>
711714
<TabsTrigger value="executed">
712-
Executed ({getFilteredTrades('executed').length})
715+
<span className="sm:hidden">Executed</span>
716+
<span className="hidden sm:inline">Executed ({getFilteredTrades('executed').length})</span>
713717
</TabsTrigger>
714718
<TabsTrigger value="rejected">
715-
Rejected ({getFilteredTrades('rejected').length})
719+
<span className="sm:hidden">Rejected</span>
720+
<span className="hidden sm:inline">Rejected ({getFilteredTrades('rejected').length})</span>
716721
</TabsTrigger>
717722
</TabsList>
718723

src/components/UnifiedAnalysisHistory.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -537,10 +537,18 @@ export default function UnifiedAnalysisHistory() {
537537
<CardContent className="pt-6">
538538
<Tabs defaultValue="all" className="space-y-4">
539539
<TabsList className="grid w-full grid-cols-4">
540-
<TabsTrigger value="all">All ({history.length + runningAnalyses.length + canceledAnalyses.length})</TabsTrigger>
541-
<TabsTrigger value="running">Running ({runningAnalyses.length})</TabsTrigger>
542-
<TabsTrigger value="completed">Completed ({history.length})</TabsTrigger>
543-
<TabsTrigger value="canceled">Canceled ({canceledAnalyses.length})</TabsTrigger>
540+
<TabsTrigger value="all">
541+
All <span className="hidden sm:inline">({history.length + runningAnalyses.length + canceledAnalyses.length})</span>
542+
</TabsTrigger>
543+
<TabsTrigger value="running">
544+
Running <span className="hidden sm:inline">({runningAnalyses.length})</span>
545+
</TabsTrigger>
546+
<TabsTrigger value="completed">
547+
Completed <span className="hidden sm:inline">({history.length})</span>
548+
</TabsTrigger>
549+
<TabsTrigger value="canceled">
550+
Canceled <span className="hidden sm:inline">({canceledAnalyses.length})</span>
551+
</TabsTrigger>
544552
</TabsList>
545553

546554
<TabsContent value="all" className="space-y-4">

0 commit comments

Comments
 (0)