Implement main app navigation
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
---
|
||||
phase: 02.1
|
||||
plan: 07
|
||||
subsystem: ui-shell
|
||||
tags: [kotlin, compose-multiplatform, empty-state, viewmodel, theme-tokens, accessibility, i18n, polish-copy]
|
||||
requires: [02.1-02, 02.1-04]
|
||||
provides: [EmptyState, PlannerScreen, RecipesScreen, PantryScreen, ShoppingScreen, PlannerViewModel, RecipesViewModel, PantryViewModel, ShoppingViewModel]
|
||||
affects: []
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [statelfow-method-per-action, mergeDescendants-a11y, RecipeTheme-tokens]
|
||||
key-files:
|
||||
created:
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/components/empty/EmptyState.kt
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/planner/PlannerScreen.kt
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/planner/PlannerViewModel.kt
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesScreen.kt
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesViewModel.kt
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/pantry/PantryScreen.kt
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/pantry/PantryViewModel.kt
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/shopping/ShoppingScreen.kt
|
||||
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/shopping/ShoppingViewModel.kt
|
||||
modified:
|
||||
- composeApp/src/commonMain/composeResources/values/strings.xml
|
||||
decisions:
|
||||
- "Used `BasicText` from compose-foundation rather than Material 3 `Text` to keep shell components Material-3-free per UI-SPEC line 31"
|
||||
- "Tab screens render inline title + centered EmptyState; chrome bottom inset is owned by AppShell, not screens"
|
||||
- "All 4 tab VMs ship a marker `isEmpty` field for forward-compatible expansion in feature phases (5/6/8/9)"
|
||||
metrics:
|
||||
duration: ~10m
|
||||
completed: 2026-05-08
|
||||
requirements: [UI-09]
|
||||
---
|
||||
|
||||
# Phase 02.1 Plan 07: Tab Empty States Summary
|
||||
|
||||
UI-09 anticipatory empty states: a reusable `EmptyState(icon, title, subtitle, modifier, action?)` composable plus four tab screens (Planner / Recipes / Pantry / Shopping) each rendering an inline title and a centered EmptyState with calm Polish copy from UI-SPEC § Copywriting Contract.
|
||||
|
||||
## What was built
|
||||
|
||||
- `EmptyState.kt` — reusable centered Column with 48dp muted icon, display headline, body subline, optional action slot, wrapped in `Modifier.semantics(mergeDescendants = true) {}` so VoiceOver reads the empty state as a single announcement (UI-SPEC line 226).
|
||||
- 4 tab `*Screen.kt` files — each `Box(background = RecipeTheme.colors.background)` containing a `Column` with status-bar inset + `xl` top padding, inline tab title in `RecipeTheme.typography.title`, and a centered `EmptyState` reading the tab-specific icon (from `BottomBarDestination.<Tab>.icon`) and resource strings.
|
||||
- 4 tab `*ViewModel.kt` files — each `ViewModel` exposes a `state: StateFlow<*State>` with a marker `isEmpty: Boolean = true` field; no actions in this phase.
|
||||
- `strings.xml` extended with 8 empty-state keys (Polish copy verbatim from UI-SPEC § Copywriting Contract).
|
||||
|
||||
## Tasks & commits
|
||||
|
||||
| Task | Commit | Description |
|
||||
|------|---------|-------------|
|
||||
| 1 | 1cc4d9d | Add 8 empty-state strings (Polish copy) |
|
||||
| 2 | 98baed9 | Add reusable EmptyState composable |
|
||||
| 3 | fda8d2a | Add 4 tab ViewModels (StateFlow, no actions) |
|
||||
| 4 | c0ca16c | Add 4 tab screens with inline title + EmptyState |
|
||||
|
||||
## Verification
|
||||
|
||||
- `./gradlew :composeApp:compileKotlinIosSimulatorArm64 -q` — exit 0 after each task
|
||||
- `./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64 -q` — exit 0 (only the pre-existing `bundleId` warning)
|
||||
- `./gradlew :composeApp:generateComposeResClass -q` — exit 0; new `Res.string.empty_*` accessors generated
|
||||
- Material 3 boundary preserved: `grep -rc 'androidx.compose.material3' [9 new files]` returns 0
|
||||
- Zero hardcoded Polish literals in any *.kt — every string flows through `stringResource(Res.string.*)`
|
||||
|
||||
## Spacing accessor names verified
|
||||
|
||||
`RecipeSpacing` exposes: `xs (4dp)`, `sm (8dp)`, `lg (16dp)`, `xl (24dp)`, `xxl (32dp)`, `xxxl (48dp)`. Per `RecipeSpacing.kt` comment: UI-SPEC's `2xl` / `3xl` are remapped to `xxl` / `xxxl` because Kotlin identifiers cannot start with a digit. This plan uses only `sm`, `lg`, `xl` — all plain identifiers, no backticks needed.
|
||||
|
||||
## strings.xml state after this plan
|
||||
|
||||
- Total keys: **24**
|
||||
- Auth (pre-existing): 7 (`auth_*`)
|
||||
- Shell tabs (plan 02.1-04): 4 (`shell_tab_*`)
|
||||
- Search placeholders (plan 02.1-04): 2 (`search_placeholder_*`)
|
||||
- Search a11y (plan 02.1-04): 3 (`search_open_a11y`, `search_close_a11y`, `search_clear_a11y`) — verified each present exactly once
|
||||
- Empty-state (this plan): 8 (`empty_*_title` × 4 + `empty_*_subtitle` × 4)
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- All 9 created files exist (verified via Write tool success)
|
||||
- All 4 task commits present in git log (1cc4d9d, 98baed9, fda8d2a, c0ca16c)
|
||||
- Strings file modified with 8 new keys; total count 24
|
||||
- iOS K/N compile + link green
|
||||
Reference in New Issue
Block a user