Skip to content

Commit 66bae69

Browse files
Merge pull request #263 from CSSLab/codex/practice-modal-queue-wip
Refine drill workflow and review UX
2 parents f83e9c0 + fbf3fa6 commit 66bae69

12 files changed

Lines changed: 630 additions & 512 deletions

File tree

next.config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ const withTM = require('next-transpile-modules')(['@react-chess/chessground'])
66
module.exports = withTM({
77
reactStrictMode: false,
88
output: 'standalone',
9+
async redirects() {
10+
return [
11+
{
12+
source: '/openings',
13+
destination: '/drills',
14+
permanent: true,
15+
},
16+
]
17+
},
918
async rewrites() {
1019
return [
1120
{

src/components/Common/Header.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,10 @@ export const Header: React.FC = () => {
209209
PUZZLES
210210
</Link>
211211
<Link
212-
href="/openings"
213-
className={`px-2 py-1 transition-all duration-200 hover:!text-primary ${router.pathname.startsWith('/openings') ? '!text-primary' : '!text-primary/80'}`}
212+
href="/drills"
213+
className={`px-2 py-1 transition-all duration-200 hover:!text-primary ${router.pathname.startsWith('/drills') ? '!text-primary' : '!text-primary/80'}`}
214214
>
215-
PRACTICE
215+
DRILLS
216216
</Link>
217217
<Link
218218
href="/turing"
@@ -391,8 +391,8 @@ export const Header: React.FC = () => {
391391
<Link href="/puzzles" className="uppercase">
392392
Puzzles
393393
</Link>
394-
<Link href="/openings" className="uppercase">
395-
Practice
394+
<Link href="/drills" className="uppercase">
395+
Drills
396396
</Link>
397397
<Link href="/turing" className="uppercase">
398398
Bot-or-not

src/components/Home/HomeHero.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,9 @@ export const HomeHero: React.FC<Props> = ({ scrollHandler }: Props) => {
229229
/>
230230
<FeatureCard
231231
icon="play_lesson"
232-
title="Practice"
232+
title="Drill"
233233
description="Learn and practice chess openings with Maia"
234-
href="/openings"
234+
href="/drills"
235235
index={4}
236236
featureKey="openings"
237237
/>

src/components/Home/Sections/AdditionalFeaturesSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ export const AdditionalFeaturesSection = ({
135135
const features: Feature[] = [
136136
{
137137
icon: <StarIcon />,
138-
title: 'Practice',
138+
title: 'Drill',
139139
description:
140140
"Drill chess openings against Maia models calibrated to specific rating levels, allowing you to practice against opponents similar to those you'll face.",
141-
action: { type: 'link', href: '/openings', label: 'Practice' },
141+
action: { type: 'link', href: '/drills', label: 'Drill' },
142142
iconBgColor: 'bg-human-3/10',
143143
iconTextColor: 'text-human-3',
144144
},

src/components/Openings/DrillPerformanceModal.tsx

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ interface Props {
3939
performanceData: DrillPerformanceData
4040
onContinueAnalyzing: () => void
4141
onNextDrill: () => void
42-
isLastDrill: boolean
42+
onReconfigureDrills: () => void
4343
}
4444

4545
// Helper function to extract move number from FEN string
@@ -769,7 +769,7 @@ const DesktopLayout: React.FC<{
769769
performanceData: DrillPerformanceData
770770
onContinueAnalyzing: () => void
771771
onNextDrill: () => void
772-
isLastDrill: boolean
772+
onReconfigureDrills: () => void
773773
gameTree: GameTree
774774
openingEndNode: GameNode
775775
playerMoveCount: number
@@ -788,7 +788,7 @@ const DesktopLayout: React.FC<{
788788
performanceData,
789789
onContinueAnalyzing,
790790
onNextDrill,
791-
isLastDrill,
791+
onReconfigureDrills,
792792
gameTree,
793793
openingEndNode,
794794
playerMoveCount,
@@ -812,9 +812,7 @@ const DesktopLayout: React.FC<{
812812
{/* Header */}
813813
<div className="flex items-center justify-between border-b border-glass-border p-4">
814814
<div>
815-
<h2 className="text-xl font-bold text-primary">
816-
Opening Analysis Complete
817-
</h2>
815+
<h2 className="text-xl font-bold text-primary">Drill Review</h2>
818816
<div className="mt-1">
819817
<p className="text-base font-medium text-secondary">
820818
{drill.selection.opening.name}
@@ -1064,23 +1062,29 @@ const DesktopLayout: React.FC<{
10641062
</div>
10651063

10661064
{/* Action Buttons */}
1067-
<div className="flex gap-3 border-t border-glass-border p-4">
1068-
<button
1069-
onClick={onContinueAnalyzing}
1070-
className={`${
1071-
isLastDrill ? 'w-full' : 'flex-1'
1072-
} rounded border border-glass-border bg-white/5 py-2 font-medium backdrop-blur-sm transition-colors hover:bg-white/10`}
1073-
>
1074-
Analyze
1075-
</button>
1076-
{!isLastDrill && (
1065+
<div className="border-t border-glass-border p-4">
1066+
<div className="flex gap-3">
1067+
<button
1068+
onClick={onContinueAnalyzing}
1069+
className="flex-1 rounded bg-human-4 py-2 font-medium text-white transition-colors hover:bg-human-4/80"
1070+
>
1071+
Analyze
1072+
</button>
10771073
<button
10781074
onClick={onNextDrill}
1079-
className="flex-1 rounded bg-human-4 py-2 font-medium transition-colors hover:bg-human-4/80"
1075+
className="flex-1 rounded bg-human-4 py-2 font-medium text-white transition-colors hover:bg-human-4/80"
10801076
>
10811077
Next Drill
10821078
</button>
1083-
)}
1079+
</div>
1080+
<div className="mt-3 flex justify-center">
1081+
<button
1082+
onClick={onReconfigureDrills}
1083+
className="text-sm font-medium text-white/55 transition-colors hover:text-white/80"
1084+
>
1085+
Reconfigure Drills
1086+
</button>
1087+
</div>
10841088
</div>
10851089
</div>
10861090
)
@@ -1093,7 +1097,7 @@ const MobileLayout: React.FC<{
10931097
performanceData: DrillPerformanceData
10941098
onContinueAnalyzing: () => void
10951099
onNextDrill: () => void
1096-
isLastDrill: boolean
1100+
onReconfigureDrills: () => void
10971101
activeTab: 'replay' | 'analysis' | 'insights'
10981102
setActiveTab: (tab: 'replay' | 'analysis' | 'insights') => void
10991103
gameTree: GameTree
@@ -1114,7 +1118,7 @@ const MobileLayout: React.FC<{
11141118
performanceData,
11151119
onContinueAnalyzing,
11161120
onNextDrill,
1117-
isLastDrill,
1121+
onReconfigureDrills,
11181122
activeTab,
11191123
setActiveTab,
11201124
gameTree,
@@ -1140,7 +1144,7 @@ const MobileLayout: React.FC<{
11401144
{/* Header */}
11411145
<div className="flex items-center justify-between border-b border-glass-border p-4">
11421146
<div className="min-w-0 flex-1">
1143-
<h2 className="text-lg font-bold text-primary">Analysis Complete</h2>
1147+
<h2 className="text-lg font-bold text-primary">Drill Review</h2>
11441148
<div className="mt-1">
11451149
<p className="text-sm font-medium text-secondary">
11461150
{drill.selection.opening.name}
@@ -1280,19 +1284,29 @@ const MobileLayout: React.FC<{
12801284
</div>
12811285

12821286
{/* Action Buttons */}
1283-
<div className="flex gap-3 border-t border-glass-border p-4">
1284-
<button
1285-
onClick={onContinueAnalyzing}
1286-
className="flex-1 rounded border border-glass-border bg-white/5 py-2 font-medium backdrop-blur-sm transition-colors hover:bg-white/10"
1287-
>
1288-
Analyze
1289-
</button>
1290-
<button
1291-
onClick={onNextDrill}
1292-
className="flex-1 rounded bg-human-4 py-2 font-medium transition-colors hover:bg-human-4/80"
1293-
>
1294-
{isLastDrill ? 'Summary' : 'Next'}
1295-
</button>
1287+
<div className="border-t border-glass-border p-4">
1288+
<div className="flex gap-3">
1289+
<button
1290+
onClick={onContinueAnalyzing}
1291+
className="flex-1 rounded bg-human-4 py-2 font-medium text-white transition-colors hover:bg-human-4/80"
1292+
>
1293+
Analyze
1294+
</button>
1295+
<button
1296+
onClick={onNextDrill}
1297+
className="flex-1 rounded bg-human-4 py-2 font-medium text-white transition-colors hover:bg-human-4/80"
1298+
>
1299+
Next Drill
1300+
</button>
1301+
</div>
1302+
<div className="mt-3 flex justify-center">
1303+
<button
1304+
onClick={onReconfigureDrills}
1305+
className="text-sm font-medium text-white/55 transition-colors hover:text-white/80"
1306+
>
1307+
Reconfigure Drills
1308+
</button>
1309+
</div>
12961310
</div>
12971311
</div>
12981312
)
@@ -1301,7 +1315,7 @@ export const DrillPerformanceModal: React.FC<Props> = ({
13011315
performanceData,
13021316
onContinueAnalyzing,
13031317
onNextDrill,
1304-
isLastDrill,
1318+
onReconfigureDrills,
13051319
}) => {
13061320
const { isMobile } = useContext(WindowSizeContext)
13071321
const [activeTab, setActiveTab] = useState<
@@ -1490,7 +1504,7 @@ export const DrillPerformanceModal: React.FC<Props> = ({
14901504
performanceData={performanceData}
14911505
onContinueAnalyzing={onContinueAnalyzing}
14921506
onNextDrill={onNextDrill}
1493-
isLastDrill={isLastDrill}
1507+
onReconfigureDrills={onReconfigureDrills}
14941508
activeTab={activeTab}
14951509
setActiveTab={setActiveTab}
14961510
gameTree={gameTree}
@@ -1510,7 +1524,7 @@ export const DrillPerformanceModal: React.FC<Props> = ({
15101524
performanceData={performanceData}
15111525
onContinueAnalyzing={onContinueAnalyzing}
15121526
onNextDrill={onNextDrill}
1513-
isLastDrill={isLastDrill}
1527+
onReconfigureDrills={onReconfigureDrills}
15141528
gameTree={gameTree}
15151529
openingEndNode={openingEndNode}
15161530
playerMoveCount={playerMoveCount}

src/components/Openings/OpeningDrillSidebar.tsx

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export const OpeningDrillSidebar: React.FC<Props> = ({
4848
const currentIsEndgame = currentDrill?.opening.categoryType === 'endgame'
4949
const currentTraitLabel = currentDrill?.endgameMeta?.traitLabel
5050
const currentGroupLabel = currentDrill?.endgameMeta?.groupLabel
51+
const currentPoolSelectionId = currentDrill
52+
? currentDrill.endgameMeta?.groupId ||
53+
currentDrill.id.replace(/__attempt_\d+$/, '')
54+
: null
5155

5256
const poolCategory =
5357
selectionPool[0]?.opening.categoryType ??
@@ -245,61 +249,91 @@ export const OpeningDrillSidebar: React.FC<Props> = ({
245249
</div>
246250
<div className="flex flex-col gap-1 border-t border-glass-border px-0 py-2">
247251
<h3 className="px-4 text-sm font-medium text-white/90">
248-
Active {poolLabel} Pool ({selectionPool.length})
252+
Active Drill Pool ({selectionPool.length})
249253
</h3>
250254
{selectionPool.length === 0 ? (
251255
<p className="mt-2 text-xs text-white/70">
252256
Add {poolLabelPlural.toLowerCase()} to begin drilling.
253257
</p>
254258
) : (
255259
<div className="flex w-full flex-col">
256-
{selectionPool.map((selection, index) => (
257-
<div
258-
key={`pool-${selection.id}-${index}`}
259-
className="flex w-full items-center gap-2 px-4 py-1"
260-
>
261-
{selection.opening.categoryType === 'endgame' ? (
262-
<span className="material-symbols-outlined text-sm text-human-3 md:text-base">
263-
trophy
264-
</span>
265-
) : (
266-
<div className="relative h-4 w-4 flex-shrink-0">
267-
<Image
268-
src={
269-
selection.playerColor === 'white'
270-
? '/assets/pieces/white king.svg'
271-
: '/assets/pieces/black king.svg'
272-
}
273-
fill={true}
274-
alt={`${selection.playerColor} king`}
275-
/>
276-
</div>
277-
)}
278-
<div className="min-w-0 flex-1">
279-
<div className="flex flex-wrap items-center gap-2">
280-
<p className="truncate text-xs font-medium text-white/90">
281-
{selection.opening.name}
282-
</p>
283-
{selection.opening.isCustom && (
284-
<span className="rounded border border-human-4/40 bg-human-4/10 px-2 py-0.5 text-xxs font-semibold uppercase tracking-wide text-human-2">
285-
Custom
286-
</span>
287-
)}
288-
</div>
289-
{selection.opening.categoryType === 'endgame'
290-
? selection.endgameMeta?.traitLabel && (
291-
<p className="text-xxs text-human-3">
292-
{selection.endgameMeta.traitLabel}
293-
</p>
294-
)
295-
: selection.variation && (
296-
<p className="truncate text-[11px] text-white/60">
297-
{selection.variation.name}
298-
</p>
260+
{selectionPool.map((selection, index) => {
261+
const isCurrentPoolSelection =
262+
currentPoolSelectionId === selection.id
263+
264+
return (
265+
<div
266+
key={`pool-${selection.id}-${index}`}
267+
className={`relative mx-2 flex w-auto items-center gap-2 rounded-md border px-3 py-2 transition-colors ${
268+
isCurrentPoolSelection
269+
? 'bg-human-4/32 border-human-4/50'
270+
: 'border-transparent bg-transparent'
271+
}`}
272+
>
273+
{isCurrentPoolSelection && (
274+
<span className="absolute inset-y-2 left-0 w-px rounded-full bg-human-4" />
275+
)}
276+
{selection.opening.categoryType === 'endgame' ? (
277+
<span className="material-symbols-outlined text-sm text-human-3 md:text-base">
278+
trophy
279+
</span>
280+
) : (
281+
<div className="relative h-4 w-4 flex-shrink-0">
282+
<Image
283+
src={
284+
selection.playerColor === 'white'
285+
? '/assets/pieces/white king.svg'
286+
: '/assets/pieces/black king.svg'
287+
}
288+
fill={true}
289+
alt={`${selection.playerColor} king`}
290+
/>
291+
</div>
292+
)}
293+
<div className="min-w-0 flex-1">
294+
<div className="flex flex-wrap items-center gap-2">
295+
<p
296+
className={`truncate text-xs font-medium ${
297+
isCurrentPoolSelection
298+
? 'text-white'
299+
: 'text-white/90'
300+
}`}
301+
>
302+
{selection.opening.name}
303+
</p>
304+
{selection.opening.isCustom && (
305+
<span className="rounded border border-human-4/40 bg-human-4/10 px-2 py-0.5 text-xxs font-semibold uppercase tracking-wide text-human-2">
306+
Custom
307+
</span>
299308
)}
309+
</div>
310+
{selection.opening.categoryType === 'endgame'
311+
? selection.endgameMeta?.traitLabel && (
312+
<p
313+
className={`text-xxs ${
314+
isCurrentPoolSelection
315+
? 'text-human-1'
316+
: 'text-human-3'
317+
}`}
318+
>
319+
{selection.endgameMeta.traitLabel}
320+
</p>
321+
)
322+
: selection.variation && (
323+
<p
324+
className={`truncate text-[11px] ${
325+
isCurrentPoolSelection
326+
? 'text-white/85'
327+
: 'text-white/60'
328+
}`}
329+
>
330+
{selection.variation.name}
331+
</p>
332+
)}
333+
</div>
300334
</div>
301-
</div>
302-
))}
335+
)
336+
})}
303337
</div>
304338
)}
305339
</div>

0 commit comments

Comments
 (0)