# Haushalt Basis-Software mit 3 geplanten Apps: Task Manager, Shopping List, Meal Planner. Aktueller Stand: **Task module** implementiert (siehe `module.md`) — Backend + Vue + Kotlin App jeweils end-to-end mit Task-CRUD, Status-Handling und Datum. ## Tech-Stack | Schicht | Technologie | |---------|------------| | Backend | Symfony 7.4, PHP 8.3, Doctrine ORM | | Frontend Web | Vue 3 (Composition API), Vite, Pinia, Vue Router 4 | | Frontend Mobile | Kotlin + Jetpack Compose (Material 3), Retrofit + kotlinx.serialization | | Datenbank | MariaDB 10.11 (utf8mb4) | | CORS | Nelmio CORS Bundle | | Umgebung | DDEV | ## Struktur ``` backend/ src/ Collection/TaskCollection.php — IteratorAggregate, filterInactive/sortByDueDate* Controller/Api/TaskController.php — REST-Routen unter /api/tasks DTO/TaskRequest.php — readonly promoted constructor, Validator + Context(!Y-m-d) Entity/Task.php — Doctrine entity, getStatus() derived-past Enum/TaskStatus.php — active, done, inactive, past (past nicht user-selectable) Repository/TaskRepository.php — currentTasks(), allTasks() → TaskCollection Service/TaskManager.php — create/update/delete/toggle Business-Logik Kernel.php config/ — nelmio_cors, doctrine, framework, ... migrations/Version20260411141650.php — tasks table public/index.php frontend/ src/ components/Icon.vue — zentrales Icon-Component router/index.js — /, /tasks, /tasks/all, /tasks/create, /tasks/:id mit breadcrumb meta services/api.js — fetch wrapper, tasks() + statuses() Endpoints stores/tasks.js — Pinia store: fetchCurrent, fetchAll, get, create, update, remove, toggle, fetchStatuses views/ Startpage.vue — Nav-Kacheln Task.vue — /tasks (aktuelle Tasks), Card-pro-Datum mit Legend-Titel TaskAll.vue — /tasks/all (alle Tasks, flache Liste mit Edit/Delete) TaskCreate.vue — /tasks/create TaskEdit.vue — /tasks/:id App.vue — Breadcrumb-Layout + RouterView main.js — Vue-Init (Pinia + Router) app/app/src/main/java/de/haushalt/app/ MainActivity.kt — Compose entry MainScreen.kt — NavHost (start, tasks, tasks/all, tasks/create, tasks/{id}) StartScreen.kt — Nav-Kacheln data/ ApiClient.kt — Retrofit + kotlinx.serialization Setup TaskApi.kt — Retrofit interface (list, get, create, update, delete, toggle, statuses) Task.kt — Task + TaskStatus enum ui/task/ TaskScreen.kt — /tasks, gleiches Card-Gruppierungs-Muster wie Vue TaskAllScreen.kt — /tasks/all flache Liste, Edit/Delete TaskCreateScreen.kt / TaskCreateViewModel.kt TaskEditScreen.kt / TaskEditViewModel.kt TaskListViewModel.kt — visibleTasks, groupedTasks, showDone, refresh, toggle TaskAllViewModel.kt DatePickerField.kt — Material 3 DatePickerDialog Wrapper StatusDropdown.kt — ExposedDropdownMenuBox, Status-Labels vom Backend DateFormat.kt — formatDate() mit dd.MM.yyyy ``` ## Domänenmodell **Task** - `id`, `name`, `date` (nullable, ISO), `status` - Status-Enum (`TaskStatus`): `active`, `done`, `inactive`, `past` - `past` ist **derived** — wenn `date < today`, liefert `Task::getStatus()` automatisch `past`; der rohe gespeicherte Wert bleibt erhalten (`getRawStatus()`) - `past` ist **nicht user-selectable** (siehe `TaskStatus::userSelectableValues()`) - Filter `/api/tasks?filter=current` → Tasks ohne `inactive`, sortiert nach `date` aufsteigend (null-first) - Default (`/api/tasks`) → alle Tasks, sortiert nach `date` absteigend ## REST-API | Methode | Route | Zweck | |---|---|---| | GET | `/api/tasks?filter=current` | aktuelle Tasks (für `/tasks`) | | GET | `/api/tasks` | alle Tasks (für `/tasks/all`) | | GET | `/api/tasks/statuses` | user-selectable Statuswerte als `string[]` | | GET | `/api/tasks/{id}` | einzelner Task | | POST | `/api/tasks` | Task anlegen | | PUT | `/api/tasks/{id}` | Task aktualisieren | | DELETE | `/api/tasks/{id}` | Task löschen | | PATCH | `/api/tasks/{id}/toggle` | Status zwischen `active`/`done` togglen | Request-DTO: `TaskRequest` (name, date `!Y-m-d`, status). Deserialisiert + validiert via `#[MapRequestPayload]`. Response-Serialisierung: Symfony Serializer mit `groups: ['task:read']`, Datum als ISO `Y-m-d` String. ## UI-Muster - **`/tasks`** (Vue + Kotlin): Tasks nach Datum gruppiert in Cards. Datum sitzt als Pill auf dem Top-Border der Card (fieldset/legend-Look). No-Date-Tasks oben in titelloser Card. `showDone` default `false`. - **`/tasks/all`** (Vue + Kotlin): Flache Liste mit Edit- und Delete-Icons. `past`-Tasks mit Opacity 0.5, `inactive` kursiv, `done` durchgestrichen. - **Icon-Reihenfolge `/tasks` Header**: plus, list, eye, refresh. - Kotlin-Screens refreshen auf `Lifecycle.Event.ON_RESUME` via `DisposableEffect` (gegen stale Daten nach Navigation). - Status-Labels in Create/Edit kommen aus dem Backend (`/api/tasks/statuses`) — nicht clientseitig hardcoded. ## Dokumentation - **`base.md`** — Vision: was gebaut wird (3 Apps, Systeme, Datenbank-Skizze) - **`module.md`** — Implementierungs-Schritte als Feature-Module (Backend + Frontend end-to-end pro Modul) - **`CLAUDE.md`** (diese Datei) — Ist-Zustand des Codes ## Code-Konventionen - **Sprache Code**: Englisch (Klassen, Methoden, Variablen) - **Sprache UI**: Deutsch - **Enum-Werte**: Englisch in DB und Code - **Datum im Backend**: ISO `Y-m-d` (als DTO-Input und in Response-JSON) - **Datum im UI**: `dd.MM.yyyy` — Formatierung in Vue (`formatDate` in `Task.vue`) und Kotlin (`DateFormat.kt`) - **Frontend**: Vue 3 Composition API mit `