mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-app.git
synced 2026-06-17 16:40:31 +00:00
🖼️ Implement Logo/Avatar Uploads & Recurrence UI in Mobile App
- **A1**: Added image uploads (Meetup logo, Lecturer avatar, Course logo) via `HandlesImageUpload` with reusable `<x-image-picker>` component and Saloon multipart requests. - **A2**: Introduced Recurrence UI in Event Editor with support for `weekly`, `monthly`, and `custom` recurrence types, aligning with portal capabilities. - Fixed `myCourseEvents` API response handling (`data` wrapper) for consistency.
This commit is contained in:
@@ -114,9 +114,11 @@
|
||||
|
||||
> Folge-Arbeit, sobald die Portal-Phasen stehen. Entsperrt die in `VERSION_1_2_0.md` als „portalseitig blockiert" geparkten Punkte. Detailplanung dort.
|
||||
|
||||
- [ ] **A1** **Logo/Avatar-Upload in den App-Editoren** (NativePHP Camera/Photo-Picker → multipart an die neuen Endpunkte aus P1). Entsperrt `VERSION_1_2_0.md` **4.6** (Meetup-Logo) + **7.1** (Referenten-Avatar). Felder in `meetup-editor`/`lecturer-editor`/`course-editor` ergänzen, `PortalWriter` um Upload-Methoden erweitern.
|
||||
- [ ] **A2** **Recurrence-UI im `event-editor`** (Wiederhol-Typ/Tag/Position/Ende) → Serie über den Endpunkt aus P2 anlegen. Entsperrt `VERSION_1_2_0.md` **5.4**.
|
||||
- [ ] **A3** Optional, nur falls P-Backlog gezogen wird: Kurs-Kategorie-Auswahl (erst wenn das **Web** sie bekommt — sonst keine Parität).
|
||||
- [x] **A1** **Logo/Avatar-Upload in den App-Editoren** (NativePHP Camera/Photo-Picker → multipart an die neuen Endpunkte aus P1). Entsperrt `VERSION_1_2_0.md` **4.6** (Meetup-Logo) + **7.1** (Referenten-Avatar) **+ Kurs-Logo**. Umgesetzt: gemeinsamer `HandlesImageUpload`-Concern (Camera `getPhoto`/`pickImages` → `#[OnNative]`-Events, je Editor per `imageUploadKey()` korreliert), wiederverwendbare `<x-image-picker>`-Komponente, drei multipart-Saloon-Requests (`UploadMeetupLogo/UploadLecturerAvatar/UploadCourseLogoRequest` über die Basis `UploadMediaRequest`, Feld `file`), `PortalWriter::uploadMeetupLogo/uploadLecturerAvatar/uploadCourseLogo` (mit Datei-Lesbarkeits-Guard + Cache-Invalidierung). **Zweistufig**: Stammdaten per JSON anlegen → ID aus der `data`-gewrappten Response → Bild separat hochladen; schlägt der Upload fehl, bleibt der Datensatz gespeichert (Warn-Toast). Tests: je Editor Create-/Edit-Upload + „kein Bild → kein Upload".
|
||||
- [x] **A2** **Recurrence-UI im `event-editor`** (Wiederhol-Typ/Tag/Position/Ende) → Serie über den Endpunkt aus P2 anlegen. Entsperrt `VERSION_1_2_0.md` **5.4**. Umgesetzt: `EventForm` um `repeats`/`recurrence_type`/`recurrence_day_of_week`/`recurrence_day_position`/`recurrence_end_date` erweitert; UI nur beim **Anlegen** (Serie aus einzelnen Terminen). **Bewusst nur die Typen exponiert, die das Portal real expandiert** — `weekly`, `monthly` und `custom` (N-ter Wochentag im Monat); `daily`/`yearly` weggelassen, da `ExpandRecurrenceSeries` für sie auf `addMonth` zurückfällt (siehe Log). Client-Validierung spiegelt die Portal-Bedingung (Typ + Enddatum ≥ Start, custom braucht Wochentag + Position). Tests: weekly/custom-Serie-Payload, Pflichtfelder, Enddatum-vor-Start, Einzeltermin sendet keine `recurrence_*`.
|
||||
- [ ] **A3** Optional, nur falls P-Backlog gezogen wird: Kurs-Kategorie-Auswahl (erst wenn das **Web** sie bekommt — sonst keine Parität). **Weiter offen** (Web hat keine Kategorie-Verwaltung).
|
||||
|
||||
> **Regressionsfix (App, nicht im ursprünglichen A-Scope):** Da P0 die Course/CourseEvent-Write-**und** den `my-course-events`-Lese-Endpunkt auf `JsonResource` (= `data`-Wrapper) gehoben hat, las `PortalApi::myCourseEvents()` das `data` nicht aus (alle anderen mine-Pfade tun es). Auf den `json('data')`-Extraktor umgestellt + die vier betroffenen App-Test-Mocks `data`-gewrappt. Die übrigen Course-Lesepfade (`courses`/`course`/`myCourses` = `index`/`show`) blieben rohe Arrays → unverändert.
|
||||
|
||||
---
|
||||
|
||||
@@ -152,3 +154,5 @@ Begründung: P0 (Konsistenz-Fundament) ist Voraussetzung für saubere Doku und b
|
||||
| 2026-06-15 | **P0–P4 umgesetzt** (Portal-Teil abgeschlossen) | Course/CourseEvent auf FormRequest+Policy+Resource gehoben; Media-Upload-API (Meetup-Logo, Lecturer-Avatar, Course-Logo) via dedizierte Endpunkte + gemeinsame `UploadMediaRequest`; Recurrence-Expansion als geteilte Action `ExpandRecurrenceSeries` (Web + API, harte Obergrenze 100); Scramble-Doku verifiziert (`scramble:analyze` fehlerfrei). 130 Tests grün, Pint sauber |
|
||||
| 2026-06-15 | **API-Antworten für Course/CourseEvent jetzt `data`-gewrappt** (Resource) | Konsistenz mit Meetup/Venue/Lecturer (alle `data`-gewrappt). Bedeutet eine Shape-Änderung ggü. dem alten rohen JSON — wird in Phase A (Mobile-App) nachgezogen. Bestehende Tests entsprechend aktualisiert |
|
||||
| 2026-06-15 | **`recurrence_type` ohne `recurrence_end_date` ⇒ Einzeltermin** | Serie wird nur erzeugt, wenn beide Felder gesetzt sind (wie der Web-Serien-Modus mit Pflicht-Enddatum). Sonst Backward-Compatible: ein einzelnes Event inkl. der übergebenen `recurrence_*`-Spalten |
|
||||
| 2026-06-15 | **Phase A (App-Nachzug) umgesetzt: A1 (Upload) + A2 (Recurrence)** | Mobile-App `einundzwanzig-mobile-app` zieht die in P1/P2 entsperrten Fähigkeiten nach. Logo/Avatar/Kurs-Logo per NativePHP-Camera + zweistufigem multipart-Upload; Recurrence-UI im Termin-Editor. 311 App-Tests grün, Pint + Larastan (Level 7) sauber. A3 (Kategorien) bleibt offen, solange das Web keine Kategorie-Verwaltung hat |
|
||||
| 2026-06-15 | **App exponiert nur `weekly`/`monthly`/`custom`, nicht `daily`/`yearly`** | `ExpandRecurrenceSeries::handle()` behandelt nur Weekly (`addWeek`) gesondert; daily/monthly/yearly fallen alle auf `addMonth` zurück. „daily"/„yearly" würden also still als monatliche Kadenz expandieren — irreführend. Die App bietet daher nur die Typen an, die real die erwartete Kadenz erzeugen (custom = N-ter Wochentag via `dayOfWeek`+`dayPosition`). Optionaler Portal-Fix: daily (`addDay`) + yearly (`addYear`) in der Action ergänzen, dann kann die App sie nachziehen |
|
||||
|
||||
Reference in New Issue
Block a user