# Seminarhof โ€” product backlog Status legend: โœ… done ยท ๐ŸŸก partial ยท โฌœ todo ยท ๐Ÿ”ฎ phase 2 --- ## โœ… Shipped - **Enquiry table.** Public enquiries land in `enquiry` (not `booking`). Admin view at `/admin/enquiries` with approve / waitlist / decline. - **Approve flow.** Requires start + end date and a client โ€” either pick an existing one (searchable autosuggest) or create a new one (email required, defaults to the enquiry contact email; rejects duplicate emails). Creates a `reserved` booking linked via `booking.enquiry_id`. - **`organization` โ†’ `client` rename** (+ `client_member` already models multiple login users per client; one is seeded from the enquiry email). - **`course` โ†’ `event` rename** across the codebase. - **Enquiry required fields trimmed:** event name + contact email only; privacy-policy consent only (no T&C checkbox). - **Booking-detail "Enquiry" milestone** sources its date from the enquiry's real submission time, not the approval timestamp. --- ## Backlog (epics) ### E1 ยท Calendar / availability - โœ… **1.1** Hide `enquiry` status on public availability โ€” show it to admins/owners only. `getAvailability` reads the optional session and filters out enquiry-state bookings for non-staff; the legend drops the enquiry swatch when no such day is present. - โœ… **1.2** Half-house: render the day cell **split by a diagonal line** (two triangles, one per half-house booking) instead of the current dot. On hover, show **both** events being held that day. `getAvailability` returns a per-day `bookings[]` (deterministic order); `availability.tsx` renders the diagonal `linear-gradient` split when two half-house bookings share a day and lists both in the tooltip. Single half-house days keep the corner dot. - โฌœ **1.3** Hover โ†’ rich detail card for bookings that are **reserved AND public**. - โฌœ **1.4** Waitlist dot in the top-right corner of the day cell. *(Pairs with E6 โ€” the wishlist/waitlist data.)* - ๐Ÿ”ฎ **1.5** Google Calendar auto-sync. **Phase 2.** ### E2 ยท Actions hub + transition safety โœ… - โœ… **2.1** All booking actions consolidated into one header hub (`BookingActions.tsx`), beside Cancel booking. `BookingStatusPanel` is now display-only (timeline + next-action alert). - โœ… **2.2** Every transition goes through a two-click `ConfirmButton` (`ConfirmButton.tsx`): first click arms ("Confirm?", red); second commits; auto-reverts after 3s. Replaced the raw `window.confirm` for cancel too. ### E3 ยท Contract form + invoicing *(big rock; status-model redesign approved)* - ๐ŸŸก **3.1** Binding booking form scoped to a booking โ€” **core fields shipped**. Client opens a **passwordless magic link** (`/booking-form/$token`), lands logged-in as a provisioned `client` user, and fills name/company, billing address, phone, website, payment method (**Cash / bank transfer**), event basics + AGB/privacy consents. Submit is legally binding (stamps `contractResponse='approved'` + `agbAcceptedAt`/`privacyAcceptedAt`). Token = a short-lived signed JWT via **`@pikku/jose`** (no token table). Admin mints the link from the booking actions hub. *Deferred to E7/E5: the priced add-on groups (extras / room equipment / materials) that feed the final invoice.* - โœ… **3.2** Sending the contract โ†’ recorded response / binding. *(Lifecycle work โ€” `recordContractResponse` + the form submit both mark it binding.)* - โฌœ **3.3** Generate a **preview invoice PDF** from the form โ†’ email to client + admin โ†’ upload into files. *(Depends on E5 โ€” blocked.)* - โœ… **3.4** 14-day deadline for signing + sending the deposit. *(Lifecycle cron: deposit `dueOn=+14d`, day-7 reminder, day-14 overdue flag.)* - โฌœ **3.5** On bank receipt, admin uploads the **official invoice** and marks it paid โ†’ *deposit paid*. *(Mark-paid done via `recordDepositReceived`; the upload depends on E5 โ€” blocked.)* - โœ… **3.6** **Status-model redesign** โ€” done by the lifecycle work: enum is `enquiry โ†’ reserved โ†’ confirmed โ†’ ended` (+ `cancelled`); contract-signed + deposit-paid are sub-milestones inside `reserved`, shown as a timeline in `BookingStatusPanel`. *Auth note:* the magic link issues the same DB-session as password login (reuses `lib/session.ts`); `app_user` is reused by email, `client_member` links the user to the client org. `consumeBookingFormLink` rejects cancelled/ended bookings and re-checks org membership. ### E4 ยท Admin dashboard โœ… - โœ… Required-actions hub at `/admin` (the admin landing): four "needs action" cards โ€” pending enquiries (with a waitlist badge), contracts to send, contracts awaiting response, deposits outstanding โ€” plus an "upcoming events" column (confirmed, next 30 days). One read-only `getAdminDashboard` function (`pikkuFunc`, `isAdminOrOwner`) aggregates the buckets from existing booking / enquiry / invoice / contract data; each card row links to its workspace (enquiries list or booking detail). "Deposits outstanding" โ‰  overdue โ€” the 14-day deadline lands with E7. ### E5 ยท File uploads *(infra prerequisite for E3.3 + event images)* - โฌœ Upload invoices. - โฌœ Upload public event images. ### E6 ยท Multi-date / wishlist enquiries - โฌœ **6.1** Enquiry form allows multiple date options, sorted by priority. - โฌœ **6.2** "Wishlist" path when dates conflict: instead of approveโ†’booking, keep it as a wishlist with its own workspace table. *(Builds on the existing `waitlisted_at`.)* ### E7 ยท Post-confirmation editing rules + `finalizing` state - โฌœ **New lifecycle state `finalizing`** (badge "Finalizing"), entered automatically **14 days before** start: `confirmed โ†’ finalizing โ†’ ended`. Add the enum value, a `finalizing_at` column, and the auto-transition (cron). - โฌœ After *confirmed*, participants (count) / rooms (allocation) / diet are editable up to **14 days** before the event starts. - โฌœ Entering `finalizing` (the 14-day mark) โ†’ auto-create a project/base invoice. - โฌœ Edits after the 14-day mark are still allowed but produce a **differing** (delta) invoice. - โฌœ Allergies remain editable until the event ends, with no invoice change. ### E8 ยท Rename `completed` โ†’ `ended` โœ… - โœ… Status value `completed` โ†’ `ended` and column `completed_at` โ†’ `ended_at`, end-to-end (DB-first). Enum now: `enquiry โ†’ reserved โ†’ confirmed โ†’ ended` (+ `cancelled`); `finalizing` slots in before `ended` later via E7. ### E9 ยท Unified public site chrome - โฌœ Make the public enquiry / events / availability pages match the seminarhof-drawehn.de header + footer for one cohesive website/app feel. --- ## Next up Quick wins **E8** + **E2** are shipped. Suggested next sequence (per epic notes above): **E5** (file uploads โ€” unblocks E3.3) โ†’ **E3** (contract + invoicing, incl. status-model redesign) โ†’ **E4** (dashboard) โ†’ **E6 / E7 / E9**.