Skip to content

Commit f91e0c5

Browse files
Copilotxenobiasoft
andauthored
Add spec for issue #214: New Landing/Home Page
Agent-Logs-Url: https://github.com/xenobiasoft/sudoku/sessions/f9f0b1d2-2fdc-4d3b-988f-0faf797154c6 Co-authored-by: xenobiasoft <6907805+xenobiasoft@users.noreply.github.com>
1 parent b54a0cd commit f91e0c5

1 file changed

Lines changed: 228 additions & 0 deletions

File tree

docs/specs/spec-home-page.md

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# New Landing / Home Page
2+
3+
**Covers:** Issue [#214](https://github.com/xenobiasoft/sudoku/issues/214)
4+
5+
---
6+
7+
## 1. 🧭 Overview
8+
9+
**Feature Name:** New Landing / Home Page
10+
11+
**Problem Statement:**
12+
The current landing page (`/`) uses an accordion-style menu to expose "Start New Game" and "Load Game" actions. This buries available features behind toggled sub-menus, offers no visual distinction between a new visitor and a returning player, and immediately redirects brand-new users to the profile-creation flow before they can see what the app offers. The result is a confusing first impression with no clear call-to-action hierarchy.
13+
14+
**Goals:**
15+
- Replace the accordion menu with a card-based home screen presenting three explicit navigation cards
16+
- Dynamically show "Create Profile" vs. "Manage Profile" based on whether the player has a saved local profile
17+
- Disable "Start New Game" and "Browse Game List" for players who have not yet created a profile, with visual affordance explaining why
18+
- Introduce a dedicated **Select Difficulty** page (replaces the inline difficulty accordion)
19+
- Introduce a dedicated **Game List** page (replaces the inline saved-game accordion)
20+
- Implement feature parity across both Blazor Server and React/Vite frontends
21+
22+
**Non-Goals:**
23+
- Redesigning the difficulty selection page beyond a simple three-option picker
24+
- Adding filtering, sorting, or pagination to the Game List page
25+
- Changing the profile creation or profile management flows
26+
- Adding an onboarding wizard, tutorial, or guided tour
27+
- Any backend, API, or domain changes
28+
29+
---
30+
31+
## 2. 📌 Functional Requirements
32+
33+
| ID | Requirement |
34+
|----|-------------|
35+
| FR-1 | The home page at `/` shall display three navigation cards: **Profile** (Create / Manage), **Start New Game**, and **Browse Game List**. |
36+
| FR-2 | The **Profile** card shall show the label "Create Profile" and navigate to `/create-profile` when no local profile exists, and the label "Manage Profile" navigating to `/profile` when a profile is stored. |
37+
| FR-3 | The **Start New Game** card shall be visually disabled (non-interactive) when no local player profile exists. |
38+
| FR-4 | The **Browse Game List** card shall be visually disabled (non-interactive) when no local player profile exists. |
39+
| FR-5 | Clicking **Start New Game** (when enabled) shall navigate to a new **Select Difficulty** page. |
40+
| FR-6 | The **Select Difficulty** page shall present Easy, Medium, and Hard options. Selecting one shall navigate to the existing game-creation route (`/new/{difficulty}`). |
41+
| FR-7 | Clicking **Browse Game List** (when enabled) shall navigate to a new **Game List** page. |
42+
| FR-8 | The **Game List** page shall display all in-progress saved games for the current player, with the ability to load (navigate to `/game/{id}`) or delete each game, and an empty-state message when no games exist. |
43+
| FR-9 | The home page shall **not** automatically redirect new players to `/create-profile`; the "Create Profile" card is the explicit call-to-action. |
44+
| FR-10 | The home page shall determine player state (new vs. returning) from local storage only, with no backend API call on initial render. |
45+
| FR-11 | Both Blazor Server and React/Vite frontends shall implement the above with feature parity. |
46+
47+
---
48+
49+
## 3. 🛡️ Non-Functional Requirements
50+
51+
- **Performance:** The home page must render without a full-page loading spinner for returning players. The player-state check must read local storage only and must not block the initial paint.
52+
- **Accessibility:** Navigation cards must be keyboard-accessible (focusable, activated by Enter/Space). Disabled cards must use `disabled` attribute or `aria-disabled="true"` with visible helper text explaining the requirement to create a profile.
53+
- **Reliability:** A local-storage read failure shall silently treat the user as a new player (all game-action cards disabled; no crash).
54+
- **Observability:** Blazor: log page initialization and navigation events using the existing `ILogger<T>` pattern. React: no change to current logging behaviour.
55+
- **Deployment:** Frontend-only change; no infrastructure, deployment pipeline, or backend configuration changes required.
56+
- **Scalability:** Not applicable (pure UI change).
57+
58+
---
59+
60+
## 4. 🏛️ Architecture Overview
61+
62+
**High-Level Description:**
63+
This is a pure UI-layer change across both frontends. No new backend endpoints, CQRS commands/queries, domain aggregates, or infrastructure components are required. The home page reads the locally-stored `ProfileInfo` object to decide card state; all other data fetching is deferred to the new dedicated pages.
64+
65+
**Affected Projects:**
66+
- `src/frontend/Sudoku.Blazor``Index.razor` / `Index.razor.cs` (replaced), new `SelectDifficulty.razor`, new `GameList.razor`, routing
67+
- `src/frontend/Sudoku.React``HomePage.tsx` (replaced), new `SelectDifficultyPage.tsx`, new `GameListPage.tsx`, `App.tsx` routing, `usePlayerService` hook
68+
69+
**Sequence Diagram:**
70+
71+
```mermaid
72+
sequenceDiagram
73+
participant User
74+
participant HomePage
75+
participant LocalStorage
76+
participant SelectDifficultyPage
77+
participant NewGamePage
78+
participant GameListPage
79+
80+
User->>HomePage: Navigate to /
81+
HomePage->>LocalStorage: Read ProfileInfo (sync / async)
82+
LocalStorage-->>HomePage: ProfileInfo | null
83+
84+
alt New Player (no profile)
85+
HomePage-->>User: Cards rendered — "Create Profile" enabled, others disabled
86+
User->>HomePage: Click "Create Profile"
87+
HomePage->>User: Navigate to /create-profile
88+
else Returning Player
89+
HomePage-->>User: All three cards enabled
90+
User->>HomePage: Click "Start New Game"
91+
HomePage->>SelectDifficultyPage: Navigate to /select-difficulty
92+
User->>SelectDifficultyPage: Choose difficulty
93+
SelectDifficultyPage->>NewGamePage: Navigate to /new/{difficulty}
94+
NewGamePage->>User: Create game → navigate to /game/{id}
95+
end
96+
97+
User->>HomePage: Click "Browse Game List"
98+
HomePage->>GameListPage: Navigate to /games
99+
GameListPage->>User: Fetch and display saved games
100+
```
101+
102+
---
103+
104+
## 5. 📦 Data Models & Contracts
105+
106+
**Domain Models:** No changes.
107+
108+
**DTOs / API Contracts:** No changes. The existing `GameModel` DTO (returned by `GET /api/games/{alias}`) is reused on the Game List page.
109+
110+
**Persistence Changes:** None. Local storage keys (`sudoku-profile`, `sudoku-alias`) and their shapes are unchanged.
111+
112+
---
113+
114+
## 6. 🔄 CQRS Components
115+
116+
No new CQRS components. The existing `GetPlayerGamesQuery` (used by `GameManager.LoadGamesAsync` in Blazor and `useGameService.loadGames` in React) is reused without modification on the Game List page.
117+
118+
---
119+
120+
## 7. 📣 Domain Events
121+
122+
Not applicable. No domain events are raised or consumed by these UI pages.
123+
124+
---
125+
126+
## 8. 🖥️ UI/UX Flow
127+
128+
**Frontend Target:** Both Blazor Server and React/Vite
129+
130+
### Home Page (`/`)
131+
132+
Three navigation cards arranged in a grid or column layout. Each card has an icon, a title, and an optional subtitle.
133+
134+
| Card | New Player | Returning Player | Destination |
135+
|------|-----------|-----------------|-------------|
136+
| Profile | "Create Profile" — **enabled** | "Manage Profile" — **enabled** | `/create-profile` or `/profile` |
137+
| Start New Game | **disabled** + helper text | **enabled** | `/select-difficulty` |
138+
| Browse Game List | **disabled** + helper text | **enabled** | `/games` |
139+
140+
- No loading spinner on initial page render for returning players.
141+
- Helper text on disabled cards (e.g., *"Create a profile to unlock this"*) guides new users toward the Profile card.
142+
143+
### Select Difficulty Page (`/select-difficulty`)
144+
145+
- Minimal page with three option buttons: Easy, Medium, Hard.
146+
- Selecting a difficulty navigates to `/new/{difficulty}` (the existing game-creation route).
147+
- Includes back navigation to `/`.
148+
- Requires an active player profile (redirect to `/` if no profile found).
149+
150+
### Game List Page (`/games`)
151+
152+
- Fetches and displays all in-progress saved games for the current player using the existing `GameThumbnail` / game-card components.
153+
- Load action navigates to `/game/{id}`.
154+
- Delete action removes the game and refreshes the list in place.
155+
- Empty state: displays a message such as *"No saved games yet. Start a new game to get going!"*
156+
- Error state: displays an error message if the API call fails.
157+
- Includes back navigation to `/`.
158+
- Requires an active player profile (redirect to `/` if no profile found).
159+
160+
**State Management:**
161+
- **Home page:** reads local storage on mount; no server calls; no loading state required.
162+
- **Select Difficulty page:** stateless; navigation only.
163+
- **Game List page:** fetches saved games via existing game service/hook on mount; manages loading and error states locally.
164+
165+
---
166+
167+
## 9. 🌐 API Endpoints
168+
169+
No new API endpoints. The existing `GET /api/games/{alias}` endpoint is used by the Game List page.
170+
171+
---
172+
173+
## 10. 🧪 Testing Strategy
174+
175+
**Unit Tests — React (Vitest + React Testing Library):**
176+
- `HomePage`: renders three cards; correct enabled/disabled state based on mocked player service; navigation targets; no API call on mount.
177+
- `SelectDifficultyPage`: renders three difficulty buttons; clicking each navigates to the correct `/new/{difficulty}` route.
178+
- `GameListPage`: renders game list from mocked service; load navigates to `/game/{id}`; delete calls service and removes item; empty state; error state.
179+
180+
**Unit Tests — Blazor (bunit):**
181+
- `Index` page: renders three cards for a new player with only "Create Profile" enabled; renders all cards enabled for a returning player; no backend call on `OnAfterRenderAsync`.
182+
- `SelectDifficulty` page: renders three difficulty buttons; clicking each triggers correct navigation.
183+
- `GameList` page: renders game thumbnails; load and delete actions behave correctly.
184+
185+
**Integration Tests:** No new integration tests required. Existing API-layer tests cover game retrieval and deletion.
186+
187+
**Test Data / Fixtures:**
188+
- React: reuse the existing `makeGame()` helper from `src/frontend/Sudoku.React/src/test/helpers`.
189+
- Blazor: use existing mock service patterns.
190+
191+
---
192+
193+
## 11. ⚠️ Risks & Considerations
194+
195+
- **Behaviour change — new-player onboarding:** The current flow auto-redirects unauthenticated users to `/create-profile`. The new flow presents a home screen instead. The "Create Profile" card must be visually prominent enough (e.g., featured/highlighted card) to drive new users to complete onboarding before trying to play.
196+
- **`usePlayerService` auto-navigation (React):** The hook currently calls `navigate('/create-profile')` whenever no profile is detected. This side effect must be removed or conditionally bypassed for the home page to render correctly for new players. A read-only local-storage utility or a new `isNewPlayer` flag exposed by the hook may be needed.
197+
- **Blazor `EnsureProfileInitializedAsync` backend call:** The current `Index.razor.cs` calls `EnsureProfileInitializedAsync()`, which hits the backend API. This call should be removed from the home-page initialization so the page renders without a network round-trip. Backend profile validation can be deferred to when the player attempts to start or load a game.
198+
- **Existing tests:** `HomePage.test.tsx` (React) and the Blazor `Index` page tests will need to be updated or replaced to reflect the new card-based UI. Difficulty and game-list tests will move to their respective new page test files.
199+
- **Route additions:** Two new routes (`/select-difficulty` and `/games`) must be registered in `App.tsx` (React) and the Blazor `Routes.razor` / `App.razor`.
200+
201+
---
202+
203+
## 12. 🔧 Implementation Plan
204+
205+
1. **React frontend**
206+
- Refactor `usePlayerService` to expose an `isNewPlayer` boolean derived from local storage only, removing the auto-navigate-to-create-profile side effect from the home page context.
207+
- Replace `HomePage.tsx` with a card-based layout; update `HomePage.module.css`.
208+
- Create `SelectDifficultyPage.tsx` at `/select-difficulty`.
209+
- Create `GameListPage.tsx` at `/games`, extracting the saved-game list logic previously inline in `HomePage.tsx`.
210+
- Register the two new routes in `App.tsx`.
211+
- Update `HomePage.test.tsx`; add `SelectDifficultyPage.test.tsx` and `GameListPage.test.tsx`.
212+
213+
2. **Blazor frontend**
214+
- Rewrite `Index.razor` and `Index.razor.cs` with card-based layout; remove `EnsureProfileInitializedAsync` call on initial render; use local-storage-only profile check.
215+
- Create `SelectDifficulty.razor` at `/select-difficulty`.
216+
- Create `GameList.razor` at `/games`, extracting game-list logic from the current `Index.razor.cs`.
217+
- Update bunit tests for all three pages.
218+
219+
---
220+
221+
## 13. ❓ Open Questions
222+
223+
1. **Route slugs:** Should the new pages live at `/select-difficulty` and `/games`, or are other slugs preferred (e.g., `/difficulty`, `/game-list`, `/my-games`)?
224+
2. **Card visual design:** Should the cards reuse the existing Font Awesome icons (`fa-play-circle`, `fa-folder-open`, `fa-user`) or adopt a new visual treatment?
225+
3. **Disabled-card helper text:** What exact copy should appear on disabled cards? Options include *"Create a profile to unlock this"*, *"Profile required"*, or simply leaving the card greyed out with no additional text.
226+
4. **Featured / highlighted card for new players:** Should the "Create Profile" card be visually differentiated (e.g., accent border, larger, or positioned first) when the player has no profile, to ensure discoverability?
227+
5. **Back navigation:** Should the Select Difficulty and Game List pages include an explicit "Back to Home" button, or rely on the browser back button / a shared nav component?
228+
6. **Profile validation deferral:** When a returning player clicks "Start New Game" or "Browse Game List", should the app perform a silent backend profile check at that point (to catch stale/orphaned profiles) before proceeding, or should it wait until the game action itself fails?

0 commit comments

Comments
 (0)