Files
recipe/.planning/phases/02.1-app-shell-navigation-search-foundation/02.1-07-SUMMARY.md

86 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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