Skip to content

Commit cba6cf3

Browse files
authored
fix(frontend): optimize mobile toolbar layout for long team/model names (#224)
- Add compact icon mode to QuotaUsage component that shows only a coin icon with tooltip when space is constrained - Calculate combined team + model name length threshold to auto-switch quota display to compact mode on mobile (<640px) - Set minimum width (60px/50px) for TeamSelector and ModelSelector on mobile to ensure they remain clickable even with long names - Add flex-shrink to selectors to allow graceful text truncation while maintaining clickable touch targets Co-authored-by: qdaxb <4157870+qdaxb@users.noreply.github.com>
1 parent 327839a commit cba6cf3

File tree

4 files changed

+49
-7
lines changed

4 files changed

+49
-7
lines changed

frontend/src/features/tasks/components/ChatArea.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import { useToast } from '@/hooks/use-toast';
2626
import { taskApis } from '@/apis/tasks';
2727

2828
const SHOULD_HIDE_QUOTA_NAME_LIMIT = 18;
29+
// Threshold for combined team name + model name length to trigger compact quota mode
30+
const COMPACT_QUOTA_NAME_THRESHOLD = 22;
2931

3032
interface ChatAreaProps {
3133
teams: Team[];
@@ -161,6 +163,15 @@ export default function ChatArea({
161163
return selectedTeam.name.trim().length > SHOULD_HIDE_QUOTA_NAME_LIMIT;
162164
}, [selectedTeam, isMobile]);
163165

166+
// Determine if compact quota mode should be used (icon only)
167+
// On mobile, when combined team + model name exceeds threshold, use compact mode
168+
const shouldUseCompactQuota = React.useMemo(() => {
169+
if (!isMobile) return false;
170+
const teamNameLength = selectedTeam?.name?.trim().length || 0;
171+
const modelNameLength = selectedModel?.name?.trim().length || 0;
172+
return teamNameLength + modelNameLength > COMPACT_QUOTA_NAME_THRESHOLD;
173+
}, [isMobile, selectedTeam?.name, selectedModel?.name]);
174+
164175
const handleTeamChange = (team: Team | null) => {
165176
console.log('[ChatArea] handleTeamChange called:', team?.name || 'null', team?.id || 'null');
166177
setSelectedTeam(team);
@@ -529,7 +540,9 @@ export default function ChatArea({
529540
)}
530541
</div>
531542
<div className="ml-auto flex items-center gap-2 flex-shrink-0">
532-
{!shouldHideQuotaUsage && <QuotaUsage className="flex-shrink-0" />}
543+
{!shouldHideQuotaUsage && (
544+
<QuotaUsage className="flex-shrink-0" compact={shouldUseCompactQuota} />
545+
)}
533546
{selectedTaskDetail?.status === 'PENDING' ? (
534547
<Button
535548
variant="ghost"
@@ -669,7 +682,9 @@ export default function ChatArea({
669682
)}
670683
</div>
671684
<div className="ml-auto flex items-center gap-2 flex-shrink-0">
672-
{!shouldHideQuotaUsage && <QuotaUsage className="flex-shrink-0" />}
685+
{!shouldHideQuotaUsage && (
686+
<QuotaUsage className="flex-shrink-0" compact={shouldUseCompactQuota} />
687+
)}
673688
{selectedTaskDetail?.status === 'PENDING' ? (
674689
<Button
675690
variant="ghost"

frontend/src/features/tasks/components/ModelSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ export default function ModelSelector({
235235
<div className="flex items-center gap-0">
236236
{/* Model selector with integrated checkbox in dropdown */}
237237
<div
238-
className="flex items-center space-x-2 min-w-0"
239-
style={{ maxWidth: isMobile ? 140 : 180 }}
238+
className="flex items-center space-x-2 min-w-0 flex-shrink"
239+
style={{ maxWidth: isMobile ? 140 : 180, minWidth: isMobile ? 50 : 70 }}
240240
>
241241
<CpuChipIcon
242242
className={`w-3 h-3 text-text-muted flex-shrink-0 ml-1 ${isLoading || externalLoading ? 'animate-pulse' : ''}`}

frontend/src/features/tasks/components/QuotaUsage.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useEffect, useState } from 'react';
2+
import { Coins } from 'lucide-react';
23
import { Button } from '@/components/ui/button';
34
import { Spinner } from '@/components/ui/spinner';
45
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
@@ -9,9 +10,11 @@ import { useToast } from '@/hooks/use-toast';
910

1011
type QuotaUsageProps = {
1112
className?: string;
13+
// When true, display only an icon instead of full text (for mobile space constraints)
14+
compact?: boolean;
1215
};
1316

14-
export default function QuotaUsage({ className }: QuotaUsageProps) {
17+
export default function QuotaUsage({ className, compact = false }: QuotaUsageProps) {
1518
const { t } = useTranslation('common');
1619
const { toast } = useToast();
1720
const [quota, setQuota] = useState<QuotaData | null>(null);
@@ -109,6 +112,30 @@ export default function QuotaUsage({ className }: QuotaUsageProps) {
109112
</div>
110113
);
111114

115+
// Compact mode: show only icon with tooltip
116+
if (compact) {
117+
return (
118+
<Tooltip>
119+
<TooltipTrigger asChild>
120+
<Button
121+
variant="ghost"
122+
size="icon"
123+
className={`h-6 w-6 flex-shrink-0 ${className ?? ''}`}
124+
style={{
125+
padding: 0,
126+
}}
127+
>
128+
<Coins className="w-4 h-4 text-text-muted hover:text-text-primary" />
129+
</Button>
130+
</TooltipTrigger>
131+
<TooltipContent side="bottom">
132+
<div className="text-xs mb-1">{brief}</div>
133+
{detail}
134+
</TooltipContent>
135+
</Tooltip>
136+
);
137+
}
138+
112139
return (
113140
<Tooltip>
114141
<TooltipTrigger asChild>

frontend/src/features/tasks/components/TeamSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ export default function TeamSelector({
116116

117117
return (
118118
<div
119-
className="flex items-center space-x-2 min-w-0"
119+
className="flex items-center space-x-2 min-w-0 flex-shrink"
120120
data-tour="team-selector"
121-
style={{ maxWidth: isMobile ? 200 : 260 }}
121+
style={{ maxWidth: isMobile ? 200 : 260, minWidth: isMobile ? 60 : 80 }}
122122
>
123123
<FaUsers
124124
className={`w-3 h-3 text-text-muted flex-shrink-0 ml-1 ${isLoading ? 'animate-pulse' : ''}`}

0 commit comments

Comments
 (0)