Skip to content

Commit 4235c7d

Browse files
committed
mobile friendly UI
1 parent 89c1af1 commit 4235c7d

7 files changed

Lines changed: 123 additions & 30 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
build/
44
/.env
55
/libs
6-
bin/
6+
bin/
7+
.kotlin/

src/dashboard/src/components/layout/Sidebar.tsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Settings2,
99
Sun,
1010
Users,
11+
X,
1112
} from 'lucide-react';
1213
import { useTranslation } from '@/lib/i18n';
1314
import { ThemeToggle } from '@/components/ui/ThemeToggle';
@@ -38,6 +39,10 @@ interface SidebarProps {
3839
currentState?: string;
3940
guildId?: string;
4041
isManualStep?: boolean;
42+
43+
// Mobile
44+
isOpen?: boolean;
45+
onClose?: () => void;
4146
}
4247

4348
export const Sidebar: React.FC<SidebarProps> = ({
@@ -59,6 +64,8 @@ export const Sidebar: React.FC<SidebarProps> = ({
5964
currentState,
6065
guildId,
6166
isManualStep = false,
67+
isOpen = false,
68+
onClose,
6269
}) => {
6370
const { t } = useTranslation();
6471
const { user } = useAuth();
@@ -86,15 +93,28 @@ export const Sidebar: React.FC<SidebarProps> = ({
8693
const isLobby = currentState === 'SETUP' || !currentState;
8794

8895
return (
89-
<aside className="w-full md:w-64 bg-slate-100 dark:bg-slate-950 border-r border-slate-300 dark:border-slate-800 flex flex-col shrink-0">
90-
<div className="p-6 flex items-center gap-3 border-b border-slate-300 dark:border-slate-800">
91-
<div className="w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center">
92-
<Moon className="w-5 h-5 text-white" />
96+
<aside
97+
className={`fixed inset-y-0 left-0 z-50 w-64 bg-slate-100 dark:bg-slate-950 border-r border-slate-300 dark:border-slate-800 flex flex-col shrink-0 transition-transform duration-300 ease-in-out md:relative md:translate-x-0 ${
98+
isOpen ? 'translate-x-0 shadow-2xl' : '-translate-x-full'
99+
}`}
100+
>
101+
<div className="p-6 flex items-center justify-between border-b border-slate-300 dark:border-slate-800">
102+
<div className="flex items-center gap-3">
103+
<div className="w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center">
104+
<Moon className="w-5 h-5 text-white" />
105+
</div>
106+
<span className="font-bold text-lg text-slate-900 dark:text-slate-100 tracking-tight">
107+
{t('app.title').split('助手')[0]}
108+
<span className="text-indigo-500 dark:text-indigo-400">助手</span>
109+
</span>
93110
</div>
94-
<span className="font-bold text-lg text-slate-900 dark:text-slate-100 tracking-tight">
95-
{t('app.title').split('助手')[0]}
96-
<span className="text-indigo-500 dark:text-indigo-400">助手</span>
97-
</span>
111+
{/* Mobile Close Button */}
112+
<button
113+
onClick={onClose}
114+
className="md:hidden p-2 text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
115+
>
116+
<X className="w-5 h-5" />
117+
</button>
98118
</div>
99119

100120
<div className="p-4 border-b border-slate-300 dark:border-slate-800 space-y-4">

src/dashboard/src/features/game/components/GameSettingsPage.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,8 @@ export const GameSettingsPage: React.FC = () => {
329329
<h2 className="text-xl">{t('settings.playerCount')}</h2>
330330
</div>
331331
<div className="bg-white dark:bg-slate-900/40 rounded-3xl border border-slate-200 dark:border-slate-800 p-6 shadow-sm">
332-
<div className="flex items-center justify-between gap-8">
333-
<div className="space-y-1.5 flex-1">
332+
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 md:gap-8">
333+
<div className="space-y-1.5 flex-1 w-full">
334334
<label className="text-lg font-bold text-slate-900 dark:text-slate-100 block">
335335
{t('settings.totalPlayers')}
336336
</label>
@@ -347,6 +347,7 @@ export const GameSettingsPage: React.FC = () => {
347347
loading={updatingPlayerCount}
348348
disabled={updatingPlayerCount}
349349
variant="card"
350+
className="w-fit"
350351
/>
351352
</div>
352353

src/dashboard/src/features/game/components/MainDashboard.tsx

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import {
77
Shuffle,
88
SkipForward,
99
Skull,
10+
Settings2,
1011
StepForward,
1112
Sun,
1213
Users,
14+
X,
1315
} from 'lucide-react';
1416
import { useTranslation } from '@/lib/i18n';
1517
import { useMutation } from '@tanstack/react-query';
@@ -45,6 +47,7 @@ export const MainDashboard = ({
4547
const [transitionClass, setTransitionClass] = useState('stage-enter-forward');
4648
const [isStageAnimating, setIsStageAnimating] = useState(false);
4749
const [lastWordsTimeLeft, setLastWordsTimeLeft] = useState(0);
50+
const [showMobileControls, setShowMobileControls] = useState(false);
4851

4952
// Mutations
5053
const setState = useMutation(setStateMutation());
@@ -445,7 +448,15 @@ export const MainDashboard = ({
445448
};
446449

447450
return (
448-
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6 w-full h-full">
451+
<div className="flex flex-col-reverse lg:grid lg:grid-cols-4 gap-6 w-full h-full p-4 lg:p-0">
452+
{/* Mobile Toggle Button */}
453+
<button
454+
onClick={() => setShowMobileControls((prev) => !prev)}
455+
className="lg:hidden fixed bottom-24 right-6 z-[100] p-4 rounded-full bg-slate-800 text-white shadow-xl shadow-slate-900/30 hover:bg-slate-700 transition-all active:scale-95"
456+
>
457+
{showMobileControls ? <X className="w-6 h-6" /> : <Settings2 className="w-6 h-6" />}
458+
</button>
459+
449460
{/* Left: Stage Content Area */}
450461
<div
451462
className={`lg:col-span-3 min-w-0 overflow-y-auto ${isStageAnimating ? 'scrollbar-hide' : ''}`}
@@ -458,8 +469,25 @@ export const MainDashboard = ({
458469
</div>
459470

460471
{/* Right: Stage Navigator */}
461-
<div className="space-y-4">
462-
{/* Navigation Controls */}
472+
<div
473+
className={`fixed inset-0 z-50 lg:static lg:z-auto bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm lg:bg-transparent lg:dark:bg-transparent lg:backdrop-blur-none transition-transform duration-300 ease-in-out ${
474+
showMobileControls ? 'translate-x-0' : 'translate-x-full lg:translate-x-0'
475+
}`}
476+
>
477+
<div className="h-full overflow-y-auto p-6 lg:p-0 lg:overflow-visible space-y-4 lg:sticky lg:top-0">
478+
<div className="flex items-center justify-between lg:hidden mb-4">
479+
<h3 className="text-lg font-bold text-slate-900 dark:text-white">
480+
{t('dashboard.gameControls')}
481+
</h3>
482+
<button
483+
onClick={() => setShowMobileControls(false)}
484+
className="p-2 text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
485+
>
486+
<X className="w-6 h-6" />
487+
</button>
488+
</div>
489+
490+
{/* Navigation Controls */}
463491
<div className="p-4 rounded-xl border border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-900">
464492
<div className="flex items-center gap-2 text-sm font-semibold text-slate-700 dark:text-slate-300 mb-3">
465493
<StepForward className="w-4 h-4" />
@@ -524,5 +552,6 @@ export const MainDashboard = ({
524552
</div>
525553
</div>
526554
</div>
555+
</div>
527556
);
528557
};

src/dashboard/src/features/game/components/PlayerManager.tsx

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useEffect, useRef, useState } from 'react';
22
import { useNavigate, useParams } from 'react-router-dom';
3-
import { MessageSquare, X } from 'lucide-react';
3+
import { Menu, MessageSquare, X } from 'lucide-react';
44
import { useTranslation } from '@/lib/i18n';
55
import { useAuth } from '@/features/auth/contexts/AuthContext';
66

@@ -37,6 +37,7 @@ export const PlayerManager = () => {
3737
const popupCloseTimeoutRef = useRef<number | null>(null);
3838
const [lastSeenLogCount, setLastSeenLogCount] = useState(0);
3939
const [isSpectatorSimulation, setIsSpectatorSimulation] = useState(false);
40+
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
4041

4142
// Business Logic Hooks
4243
const {
@@ -164,13 +165,43 @@ export const PlayerManager = () => {
164165

165166
return (
166167
<div className="min-h-screen bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100 font-sans flex flex-col md:flex-row overflow-hidden">
168+
{/* Mobile Header */}
169+
<div className="md:hidden flex items-center justify-between p-4 bg-white dark:bg-slate-900 border-b border-slate-200 dark:border-slate-800 shrink-0">
170+
<button
171+
onClick={() => setIsSidebarOpen(true)}
172+
className="p-2 -ml-2 text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg transition-colors"
173+
>
174+
<Menu className="w-6 h-6" />
175+
</button>
176+
<span className="font-bold text-lg text-slate-900 dark:text-slate-100">
177+
{t('app.title').split('助手')[0]}
178+
<span className="text-indigo-500 dark:text-indigo-400">助手</span>
179+
</span>
180+
<div className="w-10" /> {/* Spacer */}
181+
</div>
182+
167183
<Sidebar
168184
onLogout={logout}
169-
onSettingsClick={() => navigate(`/server/${guildId}/settings`)}
170-
onDashboardClick={() => navigate(`/server/${guildId}`)}
171-
onPlayersClick={() => navigate(`/server/${guildId}/players`)}
172-
onSpectatorClick={() => navigate(`/server/${guildId}/spectator`)}
173-
onSpeechClick={() => navigate(`/server/${guildId}/speech`)}
185+
onSettingsClick={() => {
186+
navigate(`/server/${guildId}/settings`);
187+
setIsSidebarOpen(false);
188+
}}
189+
onDashboardClick={() => {
190+
navigate(`/server/${guildId}`);
191+
setIsSidebarOpen(false);
192+
}}
193+
onPlayersClick={() => {
194+
navigate(`/server/${guildId}/players`);
195+
setIsSidebarOpen(false);
196+
}}
197+
onSpectatorClick={() => {
198+
navigate(`/server/${guildId}/spectator`);
199+
setIsSidebarOpen(false);
200+
}}
201+
onSpeechClick={() => {
202+
navigate(`/server/${guildId}/speech`);
203+
setIsSidebarOpen(false);
204+
}}
174205
onSwitchServer={() => navigate('/')}
175206
onToggleSpectatorMode={toggleSpectatorSimulation}
176207
isSpectatorMode={isSpectatorSimulation}
@@ -183,7 +214,17 @@ export const PlayerManager = () => {
183214
currentState={gameState.currentState}
184215
guildId={guildId}
185216
isManualStep={false}
217+
isOpen={isSidebarOpen}
218+
onClose={() => setIsSidebarOpen(false)}
186219
/>
220+
221+
{/* Mobile Sidebar Overlay */}
222+
{isSidebarOpen && (
223+
<div
224+
className="fixed inset-0 bg-black/50 z-40 md:hidden animate-in fade-in duration-200"
225+
onClick={() => setIsSidebarOpen(false)}
226+
/>
227+
)}
187228
<main className="flex-1 flex flex-col h-screen overflow-hidden relative">
188229
<div className="flex-1 overflow-hidden relative flex flex-col lg:flex-row bg-slate-50 dark:bg-slate-900/30">
189230
<div className="flex-1 overflow-y-auto scrollbar-hide p-4 md:p-8">
@@ -330,7 +371,7 @@ export const PlayerManager = () => {
330371

331372
if (playerSelectModal.type === 'ASSIGN_JUDGE') return !isJudge;
332373
if (playerSelectModal.type === 'DEMOTE_JUDGE') return isJudge;
333-
if (playerSelectModal.type === 'FORCE_POLICE') return !!p.alive;
374+
if (playerSelectModal.type === 'FORCE_POLICE') return p.alive;
334375
if (playerSelectModal.type === 'ADD_SPECTATOR')
335376
return !isPlayer && !isJudge && !isSpectator;
336377
if (playerSelectModal.type === 'REMOVE_SPECTATOR') return isSpectator;

src/dashboard/src/features/spectator/components/SpectatorView.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ export const SpectatorView: React.FC<SpectatorViewProps> = ({ players, doubleIde
8888
</div>
8989
</header>
9090

91-
<div className="grid grid-cols-12 gap-12">
91+
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 lg:gap-12 px-4 lg:px-0 pb-20">
9292
{/* Left Sidebar: Faction Statistics */}
93-
<aside className="col-span-3 space-y-6">
93+
<aside className="lg:col-span-3 space-y-6">
9494
<div className="flex items-center gap-2 text-slate-400 font-black text-sm uppercase tracking-[0.3em] mb-4">
9595
<Activity className="w-4 h-4" />
9696
<span>{t('spectator.factionStats')}</span>
@@ -154,12 +154,12 @@ export const SpectatorView: React.FC<SpectatorViewProps> = ({ players, doubleIde
154154
</aside>
155155

156156
{/* Main Content */}
157-
<main className="col-span-9 space-y-12">
157+
<main className="lg:col-span-9 space-y-8 lg:space-y-12">
158158
{/* Win Tracking Banner */}
159-
<section className="bg-slate-900/30 border border-slate-800/50 rounded-3xl p-10 relative overflow-hidden backdrop-blur-md">
160-
<div className="flex items-center justify-between gap-12">
159+
<section className="bg-slate-900/30 border border-slate-800/50 rounded-3xl p-6 md:p-10 relative overflow-hidden backdrop-blur-md">
160+
<div className="flex flex-col md:flex-row items-center justify-between gap-8 md:gap-12">
161161
{/* Good Faction Progress */}
162-
<div className="flex-1 space-y-5">
162+
<div className="flex-1 space-y-5 w-full">
163163
<div className="flex items-center justify-between text-xl font-black tracking-widest text-blue-400 uppercase">
164164
<span>
165165
{doubleIdentities && stats.jbbs > 0
@@ -183,7 +183,7 @@ export const SpectatorView: React.FC<SpectatorViewProps> = ({ players, doubleIde
183183
</div>
184184

185185
{/* Wolf Faction Progress */}
186-
<div className="flex-1 space-y-5">
186+
<div className="flex-1 space-y-5 w-full">
187187
<div className="flex items-center justify-between text-xl font-black tracking-widest text-red-400 uppercase">
188188
<span>{Math.round(winProgressWolves)}%</span>
189189
<span>
@@ -216,7 +216,7 @@ export const SpectatorView: React.FC<SpectatorViewProps> = ({ players, doubleIde
216216
</div>
217217
</div>
218218

219-
<div className="grid grid-cols-6 gap-6">
219+
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 md:gap-6">
220220
{players.map((player, idx) => (
221221
<SpectatorPlayerCard key={player.id || idx} player={player} />
222222
))}

src/dashboard/src/locales/zh-TW.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@
122122
"eliminated": "已死亡",
123123
"gameStageTitle": "遊戲階段",
124124
"nightPhase": "夜晚",
125-
"day": "白天"
125+
"day": "白天",
126+
"gameControls": "遊戲控制"
126127
},
127128
"steps": {
128129
"setup": "遊戲設置",

0 commit comments

Comments
 (0)