mirror of
https://github.com/tiennm99/loto.git
synced 2026-09-02 04:20:39 +00:00
refactor: move shared modules out of app/
`app/` should hold route segments only. Game logic and the player-card component are imported by both routes, so they belong outside: app/loto-game-logic.ts -> lib/game-logic.ts app/loto-player-board.tsx -> components/player-board.tsx Imports use the existing @/* alias. Drops the redundant `loto-` prefix. Also fixes the package name from the scaffold default.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import PlayerBoard from "../loto-player-board";
|
||||
import PlayerBoard from "@/components/player-board";
|
||||
|
||||
const STORAGE_KEY = "loto_master";
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import PlayerBoard from "./loto-player-board";
|
||||
import PlayerBoard from "@/components/player-board";
|
||||
|
||||
export default function Home() {
|
||||
const [showInstructions, setShowInstructions] = useState(false);
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
loadGrid,
|
||||
saveCrossedState,
|
||||
saveGrid,
|
||||
} from "./loto-game-logic";
|
||||
} from "@/lib/game-logic";
|
||||
|
||||
interface PlayerBoardProps {
|
||||
/** localStorage key prefix; allows multiple independent boards (e.g. user vs master) */
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## File Naming & Structure
|
||||
|
||||
- **Kebab-case** for all files: `loto-player-board.tsx`, `loto-game-logic.ts`.
|
||||
- **Kebab-case** for all files: `player-board.tsx`, `game-logic.ts`.
|
||||
- **Descriptive names**: Long names are preferred for self-documentation. Avoid ambiguity.
|
||||
- **Single responsibility**: Each file has one primary export (component or utilities).
|
||||
- **Max 200 lines per file**: Split larger components into smaller focused ones.
|
||||
@@ -81,13 +81,13 @@ function loadGrid(prefix = "loto"): number[][] | null {
|
||||
| camelCase | `handleCellClick`, `storagePrefix` | variables, functions, props |
|
||||
| PascalCase | `PlayerBoard`, `MasterPage` | components, types |
|
||||
| UPPER_SNAKE | `STORAGE_KEY`, `NUM_ROWS` | constants |
|
||||
| kebab-case | `loto-player-board.tsx` | file names |
|
||||
| kebab-case | `player-board.tsx` | file names |
|
||||
|
||||
## Comment Style
|
||||
|
||||
- Document **why**, not **what**. The code shows what it does.
|
||||
- Use `/** JSDoc */` for exported functions.
|
||||
- Inline comments for complex logic (e.g., weighted random selection in `loto-game-logic.ts:19–28`).
|
||||
- Inline comments for complex logic (e.g., weighted random selection in `lib/game-logic.ts:19–28`).
|
||||
|
||||
### Example
|
||||
```typescript
|
||||
@@ -111,8 +111,8 @@ function randomANumberInRow(weights: number[]): number {
|
||||
```typescript
|
||||
import { useCallback, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import PlayerBoard from "./loto-player-board";
|
||||
import { generateGrid } from "./loto-game-logic";
|
||||
import PlayerBoard from "@/components/player-board";
|
||||
import { generateGrid } from "@/lib/game-logic";
|
||||
```
|
||||
|
||||
## Testing (Not Currently Implemented)
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
### Shared Components
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `app/loto-player-board.tsx` | Reusable player card (9×9 grid). Handles crossed state, bingo popup, "Chờ X" toast. Accepts `storagePrefix` prop for multi-card isolation. |
|
||||
| `components/player-board.tsx` | Reusable player card (9×9 grid). Handles crossed state, bingo popup, "Chờ X" toast. Accepts `storagePrefix` prop for multi-card isolation. |
|
||||
|
||||
### Game Logic
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `app/loto-game-logic.ts` | Stateless utilities: generateGrid (weighted column selection), saveGrid, loadGrid, saveCrossedState, loadCrossedState, isRowComplete, getWaitingNumber. |
|
||||
| `lib/game-logic.ts` | Stateless utilities: generateGrid (weighted column selection), saveGrid, loadGrid, saveCrossedState, loadCrossedState, isRowComplete, getWaitingNumber. |
|
||||
|
||||
### Styling
|
||||
| File | Purpose |
|
||||
@@ -66,10 +66,10 @@ RootLayout
|
||||
|
||||
| Function | Location | Effect |
|
||||
|----------|----------|--------|
|
||||
| `generateGrid()` | loto-game-logic.ts:52 | Creates 9×9 with weighted column selection (5 nums/row). |
|
||||
| `isRowComplete()` | loto-game-logic.ts:108 | Boolean: all non-zero cells in row crossed? |
|
||||
| `getWaitingNumber()` | loto-game-logic.ts:120 | Returns the single uncrossed number in row, or null. |
|
||||
| `handleCellClick()` | loto-player-board.tsx:112 | Toggle crossed[row][col]. |
|
||||
| `generateGrid()` | game-logic.ts:52 | Creates 9×9 with weighted column selection (5 nums/row). |
|
||||
| `isRowComplete()` | game-logic.ts:108 | Boolean: all non-zero cells in row crossed? |
|
||||
| `getWaitingNumber()` | game-logic.ts:120 | Returns the single uncrossed number in row, or null. |
|
||||
| `handleCellClick()` | player-board.tsx:112 | Toggle crossed[row][col]. |
|
||||
| `handleDrawNext()` | master/page.tsx:88 | Pop first number from remaining, add to called. |
|
||||
|
||||
Last reviewed: 2026-04-26
|
||||
|
||||
@@ -88,7 +88,7 @@ All pages use `"use client"` because:
|
||||
Files with `"use client"`:
|
||||
- `app/page.tsx`
|
||||
- `app/master/page.tsx`
|
||||
- `app/loto-player-board.tsx`
|
||||
- `components/player-board.tsx`
|
||||
|
||||
## Data Flow: Mark a Cell
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "nextjs-temp",
|
||||
"name": "loto",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user