docs: design-document v1.8, dev-docs restructure, VitePress user docs scaffold, backlog update
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
165
dev-docs/API.md
Normal file
165
dev-docs/API.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Crewli API Contract
|
||||
|
||||
Base path: `/api/v1/`
|
||||
|
||||
Auth: Bearer token (Sanctum)
|
||||
|
||||
## Auth
|
||||
|
||||
- `POST /auth/login`
|
||||
- `POST /auth/logout`
|
||||
- `GET /auth/me`
|
||||
|
||||
## Organisations
|
||||
|
||||
- `GET /organisations` — list (super admin)
|
||||
- `POST /organisations` — create
|
||||
- `GET /organisations/{org}` — show
|
||||
- `PUT /organisations/{org}` — update
|
||||
- `GET /organisations/{org}/members` — members
|
||||
- `POST /organisations/{org}/invite` — invite user
|
||||
|
||||
## Events
|
||||
|
||||
- `GET /organisations/{org}/events` — list (top-level only by default)
|
||||
- `GET /organisations/{org}/events?include_children=true` — include sub-events nested in response
|
||||
- `GET /organisations/{org}/events?type=festival` — filter by event_type (festival|series|event)
|
||||
- `POST /organisations/{org}/events` — create (supports `parent_event_id` for sub-events)
|
||||
- `GET /organisations/{org}/events/{event}` — detail (includes children and parent if loaded)
|
||||
- `PUT /organisations/{org}/events/{event}` — update (does NOT accept `status` — use transition endpoint)
|
||||
- `POST /organisations/{org}/events/{event}/transition` — change event status via state machine (see below)
|
||||
- `GET /organisations/{org}/events/{event}/children` — list sub-events of a festival/series
|
||||
|
||||
### Event Status Transitions
|
||||
|
||||
`POST /organisations/{org}/events/{event}/transition`
|
||||
|
||||
Body: `{ "status": "published" }`
|
||||
|
||||
Enforces a state machine: only valid forward (and select backward) transitions are allowed.
|
||||
Returns 422 with `errors`, `current_status`, `requested_status`, and `allowed_transitions` when the transition is invalid or prerequisites are missing.
|
||||
|
||||
**Prerequisites checked:**
|
||||
- `→ published`: name, start_date, end_date required
|
||||
- `→ registration_open`: at least one time slot and one section required
|
||||
|
||||
**Festival cascade:** Transitioning a festival parent to `showday`, `teardown`, or `closed` automatically cascades to all children in an earlier status.
|
||||
|
||||
**EventResource** includes `allowed_transitions` (array of valid next statuses) so the frontend knows which buttons to show.
|
||||
|
||||
## Crowd Types
|
||||
|
||||
- `GET /organisations/{org}/crowd-types`
|
||||
- `POST /organisations/{org}/crowd-types`
|
||||
- `PUT /organisations/{org}/crowd-types/{type}`
|
||||
- `DELETE /organisations/{org}/crowd-types/{type}`
|
||||
|
||||
## Companies
|
||||
|
||||
- `GET /organisations/{org}/companies`
|
||||
- `POST /organisations/{org}/companies`
|
||||
- `PUT /organisations/{org}/companies/{company}`
|
||||
- `DELETE /organisations/{org}/companies/{company}`
|
||||
|
||||
## Section Categories
|
||||
|
||||
- `GET /organisations/{org}/section-categories` — distinct categories used across the organisation's events (for autocomplete). Returns `{ "data": ["Bar", "Podium", ...] }`
|
||||
|
||||
## Festival Sections
|
||||
|
||||
- `GET /events/{event}/sections`
|
||||
- `POST /events/{event}/sections`
|
||||
- `PUT /events/{event}/sections/{section}`
|
||||
- `DELETE /events/{event}/sections/{section}`
|
||||
- `POST /events/{event}/sections/reorder`
|
||||
|
||||
> **Festival context:** `{event}` can be a festival parent or a sub-event.
|
||||
> On a festival parent, sections are for operational planning (build-up, teardown).
|
||||
> For sub-events, `GET` automatically includes `cross_event` sections from the parent festival.
|
||||
> Shifts on cross_event sections must use the **parent festival's event_id** in API calls,
|
||||
> since the section's `event_id` points to the parent.
|
||||
|
||||
## Time Slots
|
||||
|
||||
- `GET /events/{event}/time-slots`
|
||||
- `POST /events/{event}/time-slots`
|
||||
- `PUT /events/{event}/time-slots/{timeSlot}`
|
||||
- `DELETE /events/{event}/time-slots/{timeSlot}`
|
||||
|
||||
> **Festival context:** `{event}` can be a festival parent or a sub-event.
|
||||
> Festival-level time slots (operational: build-up, teardown, transitions) are separate
|
||||
> from sub-event time slots (program-specific).
|
||||
>
|
||||
> `GET /events/{event}/time-slots` returns only the specified event's own time slots by
|
||||
> default. For sub-events, pass `?include_parent=true` to also include the parent festival's
|
||||
> time slots — each time slot is marked with a `source` field (`sub_event` or `festival`)
|
||||
> and includes `event_name` for display grouping. This parameter has no effect on festivals
|
||||
> or flat events.
|
||||
>
|
||||
> Shifts on sub-event sections may reference parent festival time slots (e.g. for build-up
|
||||
> shifts). The `time_slot_id` validation accepts time slots from the sub-event itself or
|
||||
> its parent festival.
|
||||
|
||||
## Shifts
|
||||
|
||||
- `GET /events/{event}/sections/{section}/shifts`
|
||||
- `POST /events/{event}/sections/{section}/shifts`
|
||||
- `PUT /events/{event}/sections/{section}/shifts/{shift}`
|
||||
- `DELETE /events/{event}/sections/{section}/shifts/{shift}`
|
||||
- `POST /events/{event}/sections/{section}/shifts/{shift}/assign`
|
||||
- `POST /events/{event}/sections/{section}/shifts/{shift}/claim`
|
||||
|
||||
> **Festival context:** When managing shifts on a `cross_event` section, the `{event}`
|
||||
> in the URL must be the parent festival's ID (matching `section.event_id`), not the
|
||||
> sub-event's ID.
|
||||
|
||||
## Persons
|
||||
|
||||
- `GET /events/{event}/persons`
|
||||
- `POST /events/{event}/persons`
|
||||
- `GET /events/{event}/persons/{person}`
|
||||
- `PUT /events/{event}/persons/{person}`
|
||||
- `POST /events/{event}/persons/{person}/approve`
|
||||
- `DELETE /events/{event}/persons/{person}`
|
||||
|
||||
## Crowd Lists
|
||||
|
||||
- `GET /events/{event}/crowd-lists`
|
||||
- `POST /events/{event}/crowd-lists`
|
||||
- `PUT /events/{event}/crowd-lists/{list}`
|
||||
- `DELETE /events/{event}/crowd-lists/{list}`
|
||||
- `POST /events/{event}/crowd-lists/{list}/persons`
|
||||
- `DELETE /events/{event}/crowd-lists/{list}/persons/{person}`
|
||||
|
||||
## Locations
|
||||
|
||||
- `GET /events/{event}/locations`
|
||||
- `POST /events/{event}/locations`
|
||||
- `PUT /events/{event}/locations/{location}`
|
||||
- `DELETE /events/{event}/locations/{location}`
|
||||
|
||||
## Person Tags (Organisation Settings)
|
||||
|
||||
- `GET /organisations/{org}/person-tags` — list active tags (ordered)
|
||||
- `POST /organisations/{org}/person-tags` — create tag
|
||||
- `PUT /organisations/{org}/person-tags/{tag}` — update tag
|
||||
- `DELETE /organisations/{org}/person-tags/{tag}` — deactivate tag (soft: sets `is_active = false`)
|
||||
- `GET /organisations/{org}/person-tag-categories` — distinct categories for autocomplete
|
||||
|
||||
## User Tag Assignments
|
||||
|
||||
- `GET /organisations/{org}/users/{user}/tags` — list all tags for user in organisation
|
||||
- `POST /organisations/{org}/users/{user}/tags` — assign a tag
|
||||
- `DELETE /organisations/{org}/users/{user}/tags/{tagAssignment}` — remove assignment
|
||||
- `PUT /organisations/{org}/users/{user}/tags/sync` — sync tags by source
|
||||
|
||||
> **Sync endpoint:** Replaces tags of the specified `source` only.
|
||||
> Body: `{ "tag_ids": ["ulid1", "ulid2"], "source": "self_reported" }`
|
||||
> Removes `self_reported` tags not in the list, adds new ones, leaves `organiser_assigned` untouched (and vice versa).
|
||||
|
||||
### Person list tag filtering
|
||||
|
||||
- `GET /events/{event}/persons?tag={person_tag_id}` — filter persons by single tag
|
||||
- `GET /events/{event}/persons?tags=ulid1,ulid2` — filter persons by multiple tags (AND logic: must have all)
|
||||
|
||||
_(Extend this contract per module as endpoints are implemented.)_
|
||||
Reference in New Issue
Block a user