Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 248 additions & 0 deletions docs/preparation-templates-frontend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
# Preparation Templates Frontend Contract

## Concepts
Schedules now have one preparation source:

- `DEFAULT`: uses the user's fixed default preparation from `GET /users/preparations`.
- `TEMPLATE`: uses a named preparation template from `GET /preparation-templates`.
- `CUSTOM`: uses schedule-specific preparation steps.

Started schedules are frozen. Use `preparationFrozen` from schedule responses; it is `true` when `startedAt` is present. Frozen schedules still show their original `preparationMode`, but their preparation steps come from the schedule snapshot.

## Ordered Preparation Shape
New APIs use ordered steps:

```json
{
"preparationId": "3fa85f64-5717-4562-b3fc-2c963f66afe5",
"preparationName": "Shower",
"preparationTime": 15,
"orderIndex": 0
}
```

Rules:
- Client provides UUIDs for templates and preparation steps.
- `orderIndex` is zero-based and contiguous.
- Arrays may be sent in any order; backend stores and returns by `orderIndex`.
- Each list must contain 1-50 steps.
- Each `preparationTime` must be 1-1440 minutes.
- Total preparation time per list must be at most 1440 minutes.
- Step names are trimmed; duplicate step names are allowed.
- Step IDs must not be reused across other templates, other schedules, or the fixed default. Reusing the same step ID within the same resource update is allowed.

## Named Template APIs
The fixed default preparation is not included in these endpoints.

### List Active Templates
`GET /preparation-templates`

Returns active named templates with full steps. Deleted templates are excluded.

```json
{
"status": "success",
"data": [
{
"templateId": "11111111-1111-1111-1111-111111111111",
"templateName": "Work",
"createdAt": "2026-05-14T02:10:00Z",
"updatedAt": "2026-05-14T02:10:00Z",
"deletedAt": null,
"preparations": []
}
]
}
```

### Get Template Detail
`GET /preparation-templates/{templateId}`

Works for active and soft-deleted templates owned by the user. Detail includes `deletedAt`.

### Create Template
`POST /preparation-templates`

```json
{
"templateId": "11111111-1111-1111-1111-111111111111",
"templateName": "Work",
"preparations": [
{
"preparationId": "22222222-2222-2222-2222-222222222222",
"preparationName": "Pack laptop",
"preparationTime": 5,
"orderIndex": 0
}
]
}
```

Active template names are unique per user after trimming and case-insensitive normalization. Deleted template names can be reused, but deleted template IDs cannot.

### Update Template
`PUT /preparation-templates/{templateId}`

Full replace of name and steps. Deleted templates cannot be updated.

### Delete Template
`DELETE /preparation-templates/{templateId}`

Soft-deletes the template. Existing schedules that already use the template keep using it. New schedule create/update cannot select it. Repeated delete succeeds.

## Schedule Create
`POST /schedules`

Preparation source is inferred:

- Omit both `preparationTemplateId` and `customPreparations`: creates `DEFAULT`.
- Send only `preparationTemplateId`: creates `TEMPLATE`.
- Send only `customPreparations`: creates `CUSTOM`.
- Send both: rejected.

Template mode:

```json
{
"scheduleId": "33333333-3333-3333-3333-333333333333",
"placeId": "44444444-4444-4444-4444-444444444444",
"placeName": "Office",
"scheduleName": "Morning meeting",
"moveTime": 20,
"scheduleTime": "2026-06-01T09:30:00",
"scheduleSpareTime": 10,
"scheduleNote": "Bring laptop",
"preparationTemplateId": "11111111-1111-1111-1111-111111111111"
}
```

Custom mode:

```json
{
"scheduleId": "33333333-3333-3333-3333-333333333333",
"placeId": "44444444-4444-4444-4444-444444444444",
"placeName": "Office",
"scheduleName": "Morning meeting",
"moveTime": 20,
"scheduleTime": "2026-06-01T09:30:00",
"customPreparations": [
{
"preparationId": "55555555-5555-5555-5555-555555555555",
"preparationName": "Pack laptop",
"preparationTime": 5,
"orderIndex": 0
}
]
}
```

## Schedule Update
`PUT /schedules/{scheduleId}`

If `preparationMode` is omitted, the current preparation source is preserved. This includes schedules linked to soft-deleted templates.

To change source:

```json
{
"placeId": "44444444-4444-4444-4444-444444444444",
"placeName": "Office",
"scheduleName": "Morning meeting",
"moveTime": 20,
"scheduleTime": "2026-06-01T09:30:00",
"preparationMode": "DEFAULT"
}
```

```json
{
"placeId": "44444444-4444-4444-4444-444444444444",
"placeName": "Office",
"scheduleName": "Morning meeting",
"moveTime": 20,
"scheduleTime": "2026-06-01T09:30:00",
"preparationMode": "TEMPLATE",
"preparationTemplateId": "11111111-1111-1111-1111-111111111111"
}
```

```json
{
"placeId": "44444444-4444-4444-4444-444444444444",
"placeName": "Office",
"scheduleName": "Morning meeting",
"moveTime": 20,
"scheduleTime": "2026-06-01T09:30:00",
"preparationMode": "CUSTOM",
"customPreparations": [
{
"preparationId": "55555555-5555-5555-5555-555555555555",
"preparationName": "Pack laptop",
"preparationTime": 5,
"orderIndex": 0
}
]
}
```

Mixed mode payloads are rejected:
- `DEFAULT` with template ID or custom list.
- `TEMPLATE` without template ID.
- `TEMPLATE` with custom list.
- `CUSTOM` without full custom list.
- `CUSTOM` with template ID.

Started schedules cannot be edited.

## Schedule Responses
Normal schedule list/detail responses include metadata, not full step lists:

```json
{
"scheduleId": "33333333-3333-3333-3333-333333333333",
"scheduleName": "Morning meeting",
"startedAt": null,
"finishedAt": null,
"preparationMode": "TEMPLATE",
"preparationTemplateId": "11111111-1111-1111-1111-111111111111",
"preparationTemplateName": "Work",
"preparationTemplateDeleted": false,
"preparationFrozen": false
}
```

For default and custom schedules, `preparationTemplateId` and `preparationTemplateName` are `null`.

For schedules linked to a deleted template:

```json
{
"preparationMode": "TEMPLATE",
"preparationTemplateId": "11111111-1111-1111-1111-111111111111",
"preparationTemplateName": "Work",
"preparationTemplateDeleted": true
}
```

## Existing Compatibility Endpoints
These remain linked-list shaped:

- `GET /users/preparations`
- `PUT /users/preparations`
- `GET /schedules/{scheduleId}/preparations`
- `POST /schedules/{scheduleId}/preparations`
- `PUT /schedules/{scheduleId}/preparations`

`POST/PUT /schedules/{scheduleId}/preparations` now means "make this schedule CUSTOM" and clears any template link. The request still uses `nextPreparationId`.

## Alarm Window
`GET /schedules/alarm-window` continues to include full `preparations`, and now also includes the same preparation metadata fields as normal schedule responses.

## Error Codes To Handle
- `PREPARATION_TEMPLATE_NOT_FOUND`: missing or cross-user template.
- `PREPARATION_TEMPLATE_NAME_DUPLICATE`: active template name already exists.
- `PREPARATION_TEMPLATE_LIMIT_EXCEEDED`: user already has 20 active named templates.
- `PREPARATION_TEMPLATE_DELETED`: user tried to select or update an owned deleted template.
- `PREPARATION_STEP_ID_CONFLICT`: provided step ID belongs to another preparation resource.
- `INVALID_INPUT`: malformed mode combination, ordering, list size, duration, or linked-list payload.
140 changes: 140 additions & 0 deletions docs/preparation-templates-transition-issue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Clean Transition Plan For Preparation Templates And Schedule Preparation Modes

## Context
The backend is adding multiple named preparation templates while keeping the existing fixed default preparation flow. This transition needs to be intentionally staged because existing clients still use linked-list preparation payloads (`nextPreparationId`) and the old `isChange` concept, while new clients should use explicit schedule preparation modes and ordered preparation steps.

This issue tracks the clean transition work after the initial implementation lands.

## Product Model To Preserve
- Every user has one fixed default preparation set.
- The fixed default has no custom name and is managed through the existing `/users/preparations` compatibility endpoints.
- Users can create up to 20 active named preparation templates.
- Schedules can use exactly one preparation source: `DEFAULT`, `TEMPLATE`, or `CUSTOM`.
- Named templates are soft-deleted with `deletedAt`.
- Soft-deleted templates are hidden from future selection but remain resolvable for schedules that already reference them.
- Started schedules are frozen. Their response keeps the original preparation source metadata, but steps are read from schedule snapshot rows.

## New Frontend Contract To Roll Out
Template APIs:
- `GET /preparation-templates`: active named templates only, excluding fixed default, with full ordered steps.
- `GET /preparation-templates/{templateId}`: owner-only direct lookup, including deleted templates and `deletedAt`.
- `POST /preparation-templates`: create a named template with client-provided template and step UUIDs.
- `PUT /preparation-templates/{templateId}`: full replace of name and steps; deleted templates are immutable.
- `DELETE /preparation-templates/{templateId}`: soft delete; repeated delete succeeds.

Ordered step shape:
```json
{
"preparationId": "uuid",
"preparationName": "Pack laptop",
"preparationTime": 5,
"orderIndex": 0
}
```

Schedule create modes:
- Neither `preparationTemplateId` nor `customPreparations`: create `DEFAULT`.
- Only `preparationTemplateId`: create `TEMPLATE`.
- Only `customPreparations`: create `CUSTOM`.
- Both fields: reject.

Schedule update modes:
- Omit `preparationMode`: keep current source unchanged.
- `DEFAULT`: no template ID, no custom list.
- `TEMPLATE`: requires active owned template ID, no custom list.
- `CUSTOM`: requires full custom list, no template ID.

Schedule response metadata:
```json
{
"preparationMode": "TEMPLATE",
"preparationTemplateId": "uuid-or-null",
"preparationTemplateName": "Work",
"preparationTemplateDeleted": false,
"preparationFrozen": false,
"startedAt": null,
"finishedAt": null
}
```

## Compatibility Endpoints To Keep During Transition
Keep these linked-list shaped:
- `GET /users/preparations`
- `PUT /users/preparations`
- `GET /schedules/{scheduleId}/preparations`
- `POST /schedules/{scheduleId}/preparations`
- `PUT /schedules/{scheduleId}/preparations`

Compatibility behavior:
- Existing request/response shape uses `nextPreparationId`.
- Backend stores/maintains `orderIndex` internally.
- Backend synthesizes `nextPreparationId` from order on compatibility reads.
- Old schedule-preparation POST/PUT maps the schedule to `CUSTOM`, clears any template link, and replaces schedule-specific rows.

## Migration And Rollout Checklist
Phase 1: Backend compatibility release
- [ ] Add `order_index` to `preparation_user` and `preparation_schedule`.
- [ ] Backfill existing rows.
- [ ] Keep `next_preparation_id` during transition.
- [ ] Add `preparation_mode` to `schedule`.
- [ ] Add nullable `preparation_template_id` to `schedule`.
- [ ] Migrate old `is_change = true` schedules to `CUSTOM`.
- [ ] Migrate old `is_change = false` schedules to `DEFAULT`.
- [ ] Document the compromise that historical started default snapshots with `is_change = true` become `CUSTOM` because source intent cannot be reliably recovered.
- [ ] Add template tables and endpoints.
- [ ] Add schedule response metadata.
- [ ] Keep old endpoints working.

Phase 2: Frontend adoption
- [ ] Update template picker to show local fixed default option plus named templates from `/preparation-templates`.
- [ ] Treat missing `preparationTemplateId` plus no custom list as fixed default on create.
- [ ] Use `preparationMode` for schedule update source changes.
- [ ] Use ordered `customPreparations` for new custom schedule create/update.
- [ ] Continue using old linked-list endpoints only where necessary.
- [ ] Show deleted linked templates as disabled/unavailable when `preparationTemplateDeleted = true`.
- [ ] Prevent selecting deleted templates from the picker.
- [ ] Generate new step UUIDs when copying a template into custom schedule steps.
- [ ] Respect `preparationFrozen = true` by disabling preparation edits on started schedules.

Phase 3: Monitoring and validation
- [ ] Verify old app versions can still onboard and update `/users/preparations`.
- [ ] Verify old app versions can still create schedule-specific preparations via `/schedules/{id}/preparations`.
- [ ] Verify new app can create `DEFAULT`, `TEMPLATE`, and `CUSTOM` schedules.
- [ ] Verify template updates refresh not-started, not-finished template-mode schedules.
- [ ] Verify default preparation updates refresh not-started, not-finished default-mode schedules.
- [ ] Verify started schedules keep frozen snapshot steps.
- [ ] Verify deleted templates remain visible through schedule metadata and direct detail lookup.
- [ ] Verify account deletion removes named templates and template steps.
- [ ] Verify privacy/account deletion docs mention named templates.

Phase 4: Cleanup after client migration
- [ ] Stop documenting `isChange` for new clients.
- [ ] Remove client use of linked-list `nextPreparationId` from new app code.
- [ ] Add a versioned ordered preparation read endpoint if frontend needs ordered schedule/default reads without linked-list compatibility.
- [ ] Once old clients are no longer supported, remove or fully ignore `is_change`.
- [ ] Once old linked-list endpoints are retired, remove `next_preparation_id` from `preparation_user` and `preparation_schedule`.
- [ ] Remove compatibility synthesis code for `nextPreparationId`.
- [ ] Simplify repository queries to rely only on `order_index`.

## Edge Cases To Test Explicitly
- Creating a template with duplicate active name after trim/case normalization is rejected.
- Creating a template after deleting another template with the same name succeeds.
- Creating a template with a deleted template's same ID is rejected.
- Deleting a template twice succeeds.
- Updating a deleted template is rejected.
- Selecting an owned deleted template for schedule create/update returns a deleted-specific error.
- Selecting another user's template behaves as not found.
- Schedule detail edit with omitted `preparationMode` preserves a deleted template reference.
- Schedule linked to deleted template can switch to default, active template, or custom.
- Custom schedule update can reuse its own step IDs and reorder them.
- Custom schedule update cannot use step IDs from templates, fixed default, or another schedule.
- Template update can reuse its own step IDs and reorder them.
- Fixed default update can reuse its own step IDs.
- Malformed linked-list payloads are rejected: cycles, multiple heads, disconnected nodes, unknown next IDs, duplicate IDs.
- Malformed ordered payloads are rejected: duplicate/gapped indexes, duplicate IDs, empty list, more than 50 steps, total duration over 1440.
- Equal-time step content changes still refresh notifications without leaving duplicate scheduled tasks.

## Open Implementation Notes
- Confirm whether DB-level active-name uniqueness can be enforced cleanly in the deployed MySQL version; service-level validation is still required.
- Confirm native alarm payload refresh behavior when notification time does not change but step names/order do.
- Keep `docs/preparation-templates-frontend.md` aligned with actual endpoint behavior.
Loading
Loading