chore: seminarhof customer project

This commit is contained in:
e2e
2026-06-22 09:44:35 +02:00
commit c3035e16ec
161 changed files with 18517 additions and 0 deletions

129
apps/app/src/specs.md Normal file
View File

@@ -0,0 +1,129 @@
# 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**.