Skip to content

Commit 03c50db

Browse files
authored
Merge pull request #316 from DevKor-github/codexd/preparation-templates
[codex] Add preparation templates
2 parents 5d8a826 + d43daf3 commit 03c50db

31 files changed

Lines changed: 1901 additions & 170 deletions
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
# Preparation Templates Frontend Contract
2+
3+
## Concepts
4+
Schedules now have one preparation source:
5+
6+
- `DEFAULT`: uses the user's fixed default preparation from `GET /users/preparations`.
7+
- `TEMPLATE`: uses a named preparation template from `GET /preparation-templates`.
8+
- `CUSTOM`: uses schedule-specific preparation steps.
9+
10+
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.
11+
12+
## Ordered Preparation Shape
13+
New APIs use ordered steps:
14+
15+
```json
16+
{
17+
"preparationId": "3fa85f64-5717-4562-b3fc-2c963f66afe5",
18+
"preparationName": "Shower",
19+
"preparationTime": 15,
20+
"orderIndex": 0
21+
}
22+
```
23+
24+
Rules:
25+
- Client provides UUIDs for templates and preparation steps.
26+
- `orderIndex` is zero-based and contiguous.
27+
- Arrays may be sent in any order; backend stores and returns by `orderIndex`.
28+
- Each list must contain 1-50 steps.
29+
- Each `preparationTime` must be 1-1440 minutes.
30+
- Total preparation time per list must be at most 1440 minutes.
31+
- Step names are trimmed; duplicate step names are allowed.
32+
- 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.
33+
34+
## Named Template APIs
35+
The fixed default preparation is not included in these endpoints.
36+
37+
### List Active Templates
38+
`GET /preparation-templates`
39+
40+
Returns active named templates with full steps. Deleted templates are excluded.
41+
42+
```json
43+
{
44+
"status": "success",
45+
"data": [
46+
{
47+
"templateId": "11111111-1111-1111-1111-111111111111",
48+
"templateName": "Work",
49+
"createdAt": "2026-05-14T02:10:00Z",
50+
"updatedAt": "2026-05-14T02:10:00Z",
51+
"deletedAt": null,
52+
"preparations": []
53+
}
54+
]
55+
}
56+
```
57+
58+
### Get Template Detail
59+
`GET /preparation-templates/{templateId}`
60+
61+
Works for active and soft-deleted templates owned by the user. Detail includes `deletedAt`.
62+
63+
### Create Template
64+
`POST /preparation-templates`
65+
66+
```json
67+
{
68+
"templateId": "11111111-1111-1111-1111-111111111111",
69+
"templateName": "Work",
70+
"preparations": [
71+
{
72+
"preparationId": "22222222-2222-2222-2222-222222222222",
73+
"preparationName": "Pack laptop",
74+
"preparationTime": 5,
75+
"orderIndex": 0
76+
}
77+
]
78+
}
79+
```
80+
81+
Active template names are unique per user after trimming and case-insensitive normalization. Deleted template names can be reused, but deleted template IDs cannot.
82+
83+
### Update Template
84+
`PUT /preparation-templates/{templateId}`
85+
86+
Full replace of name and steps. Deleted templates cannot be updated.
87+
88+
### Delete Template
89+
`DELETE /preparation-templates/{templateId}`
90+
91+
Soft-deletes the template. Existing schedules that already use the template keep using it. New schedule create/update cannot select it. Repeated delete succeeds.
92+
93+
## Schedule Create
94+
`POST /schedules`
95+
96+
Preparation source is inferred:
97+
98+
- Omit both `preparationTemplateId` and `customPreparations`: creates `DEFAULT`.
99+
- Send only `preparationTemplateId`: creates `TEMPLATE`.
100+
- Send only `customPreparations`: creates `CUSTOM`.
101+
- Send both: rejected.
102+
103+
Template mode:
104+
105+
```json
106+
{
107+
"scheduleId": "33333333-3333-3333-3333-333333333333",
108+
"placeId": "44444444-4444-4444-4444-444444444444",
109+
"placeName": "Office",
110+
"scheduleName": "Morning meeting",
111+
"moveTime": 20,
112+
"scheduleTime": "2026-06-01T09:30:00",
113+
"scheduleSpareTime": 10,
114+
"scheduleNote": "Bring laptop",
115+
"preparationTemplateId": "11111111-1111-1111-1111-111111111111"
116+
}
117+
```
118+
119+
Custom mode:
120+
121+
```json
122+
{
123+
"scheduleId": "33333333-3333-3333-3333-333333333333",
124+
"placeId": "44444444-4444-4444-4444-444444444444",
125+
"placeName": "Office",
126+
"scheduleName": "Morning meeting",
127+
"moveTime": 20,
128+
"scheduleTime": "2026-06-01T09:30:00",
129+
"customPreparations": [
130+
{
131+
"preparationId": "55555555-5555-5555-5555-555555555555",
132+
"preparationName": "Pack laptop",
133+
"preparationTime": 5,
134+
"orderIndex": 0
135+
}
136+
]
137+
}
138+
```
139+
140+
## Schedule Update
141+
`PUT /schedules/{scheduleId}`
142+
143+
If `preparationMode` is omitted, the current preparation source is preserved. This includes schedules linked to soft-deleted templates.
144+
145+
To change source:
146+
147+
```json
148+
{
149+
"placeId": "44444444-4444-4444-4444-444444444444",
150+
"placeName": "Office",
151+
"scheduleName": "Morning meeting",
152+
"moveTime": 20,
153+
"scheduleTime": "2026-06-01T09:30:00",
154+
"preparationMode": "DEFAULT"
155+
}
156+
```
157+
158+
```json
159+
{
160+
"placeId": "44444444-4444-4444-4444-444444444444",
161+
"placeName": "Office",
162+
"scheduleName": "Morning meeting",
163+
"moveTime": 20,
164+
"scheduleTime": "2026-06-01T09:30:00",
165+
"preparationMode": "TEMPLATE",
166+
"preparationTemplateId": "11111111-1111-1111-1111-111111111111"
167+
}
168+
```
169+
170+
```json
171+
{
172+
"placeId": "44444444-4444-4444-4444-444444444444",
173+
"placeName": "Office",
174+
"scheduleName": "Morning meeting",
175+
"moveTime": 20,
176+
"scheduleTime": "2026-06-01T09:30:00",
177+
"preparationMode": "CUSTOM",
178+
"customPreparations": [
179+
{
180+
"preparationId": "55555555-5555-5555-5555-555555555555",
181+
"preparationName": "Pack laptop",
182+
"preparationTime": 5,
183+
"orderIndex": 0
184+
}
185+
]
186+
}
187+
```
188+
189+
Mixed mode payloads are rejected:
190+
- `DEFAULT` with template ID or custom list.
191+
- `TEMPLATE` without template ID.
192+
- `TEMPLATE` with custom list.
193+
- `CUSTOM` without full custom list.
194+
- `CUSTOM` with template ID.
195+
196+
Started schedules cannot be edited.
197+
198+
## Schedule Responses
199+
Normal schedule list/detail responses include metadata, not full step lists:
200+
201+
```json
202+
{
203+
"scheduleId": "33333333-3333-3333-3333-333333333333",
204+
"scheduleName": "Morning meeting",
205+
"startedAt": null,
206+
"finishedAt": null,
207+
"preparationMode": "TEMPLATE",
208+
"preparationTemplateId": "11111111-1111-1111-1111-111111111111",
209+
"preparationTemplateName": "Work",
210+
"preparationTemplateDeleted": false,
211+
"preparationFrozen": false
212+
}
213+
```
214+
215+
For default and custom schedules, `preparationTemplateId` and `preparationTemplateName` are `null`.
216+
217+
For schedules linked to a deleted template:
218+
219+
```json
220+
{
221+
"preparationMode": "TEMPLATE",
222+
"preparationTemplateId": "11111111-1111-1111-1111-111111111111",
223+
"preparationTemplateName": "Work",
224+
"preparationTemplateDeleted": true
225+
}
226+
```
227+
228+
## Existing Compatibility Endpoints
229+
These remain linked-list shaped:
230+
231+
- `GET /users/preparations`
232+
- `PUT /users/preparations`
233+
- `GET /schedules/{scheduleId}/preparations`
234+
- `POST /schedules/{scheduleId}/preparations`
235+
- `PUT /schedules/{scheduleId}/preparations`
236+
237+
`POST/PUT /schedules/{scheduleId}/preparations` now means "make this schedule CUSTOM" and clears any template link. The request still uses `nextPreparationId`.
238+
239+
## Alarm Window
240+
`GET /schedules/alarm-window` continues to include full `preparations`, and now also includes the same preparation metadata fields as normal schedule responses.
241+
242+
## Error Codes To Handle
243+
- `PREPARATION_TEMPLATE_NOT_FOUND`: missing or cross-user template.
244+
- `PREPARATION_TEMPLATE_NAME_DUPLICATE`: active template name already exists.
245+
- `PREPARATION_TEMPLATE_LIMIT_EXCEEDED`: user already has 20 active named templates.
246+
- `PREPARATION_TEMPLATE_DELETED`: user tried to select or update an owned deleted template.
247+
- `PREPARATION_STEP_ID_CONFLICT`: provided step ID belongs to another preparation resource.
248+
- `INVALID_INPUT`: malformed mode combination, ordering, list size, duration, or linked-list payload.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Clean Transition Plan For Preparation Templates And Schedule Preparation Modes
2+
3+
## Context
4+
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.
5+
6+
This issue tracks the clean transition work after the initial implementation lands.
7+
8+
## Product Model To Preserve
9+
- Every user has one fixed default preparation set.
10+
- The fixed default has no custom name and is managed through the existing `/users/preparations` compatibility endpoints.
11+
- Users can create up to 20 active named preparation templates.
12+
- Schedules can use exactly one preparation source: `DEFAULT`, `TEMPLATE`, or `CUSTOM`.
13+
- Named templates are soft-deleted with `deletedAt`.
14+
- Soft-deleted templates are hidden from future selection but remain resolvable for schedules that already reference them.
15+
- Started schedules are frozen. Their response keeps the original preparation source metadata, but steps are read from schedule snapshot rows.
16+
17+
## New Frontend Contract To Roll Out
18+
Template APIs:
19+
- `GET /preparation-templates`: active named templates only, excluding fixed default, with full ordered steps.
20+
- `GET /preparation-templates/{templateId}`: owner-only direct lookup, including deleted templates and `deletedAt`.
21+
- `POST /preparation-templates`: create a named template with client-provided template and step UUIDs.
22+
- `PUT /preparation-templates/{templateId}`: full replace of name and steps; deleted templates are immutable.
23+
- `DELETE /preparation-templates/{templateId}`: soft delete; repeated delete succeeds.
24+
25+
Ordered step shape:
26+
```json
27+
{
28+
"preparationId": "uuid",
29+
"preparationName": "Pack laptop",
30+
"preparationTime": 5,
31+
"orderIndex": 0
32+
}
33+
```
34+
35+
Schedule create modes:
36+
- Neither `preparationTemplateId` nor `customPreparations`: create `DEFAULT`.
37+
- Only `preparationTemplateId`: create `TEMPLATE`.
38+
- Only `customPreparations`: create `CUSTOM`.
39+
- Both fields: reject.
40+
41+
Schedule update modes:
42+
- Omit `preparationMode`: keep current source unchanged.
43+
- `DEFAULT`: no template ID, no custom list.
44+
- `TEMPLATE`: requires active owned template ID, no custom list.
45+
- `CUSTOM`: requires full custom list, no template ID.
46+
47+
Schedule response metadata:
48+
```json
49+
{
50+
"preparationMode": "TEMPLATE",
51+
"preparationTemplateId": "uuid-or-null",
52+
"preparationTemplateName": "Work",
53+
"preparationTemplateDeleted": false,
54+
"preparationFrozen": false,
55+
"startedAt": null,
56+
"finishedAt": null
57+
}
58+
```
59+
60+
## Compatibility Endpoints To Keep During Transition
61+
Keep these linked-list shaped:
62+
- `GET /users/preparations`
63+
- `PUT /users/preparations`
64+
- `GET /schedules/{scheduleId}/preparations`
65+
- `POST /schedules/{scheduleId}/preparations`
66+
- `PUT /schedules/{scheduleId}/preparations`
67+
68+
Compatibility behavior:
69+
- Existing request/response shape uses `nextPreparationId`.
70+
- Backend stores/maintains `orderIndex` internally.
71+
- Backend synthesizes `nextPreparationId` from order on compatibility reads.
72+
- Old schedule-preparation POST/PUT maps the schedule to `CUSTOM`, clears any template link, and replaces schedule-specific rows.
73+
74+
## Migration And Rollout Checklist
75+
Phase 1: Backend compatibility release
76+
- [ ] Add `order_index` to `preparation_user` and `preparation_schedule`.
77+
- [ ] Backfill existing rows.
78+
- [ ] Keep `next_preparation_id` during transition.
79+
- [ ] Add `preparation_mode` to `schedule`.
80+
- [ ] Add nullable `preparation_template_id` to `schedule`.
81+
- [ ] Migrate old `is_change = true` schedules to `CUSTOM`.
82+
- [ ] Migrate old `is_change = false` schedules to `DEFAULT`.
83+
- [ ] Document the compromise that historical started default snapshots with `is_change = true` become `CUSTOM` because source intent cannot be reliably recovered.
84+
- [ ] Add template tables and endpoints.
85+
- [ ] Add schedule response metadata.
86+
- [ ] Keep old endpoints working.
87+
88+
Phase 2: Frontend adoption
89+
- [ ] Update template picker to show local fixed default option plus named templates from `/preparation-templates`.
90+
- [ ] Treat missing `preparationTemplateId` plus no custom list as fixed default on create.
91+
- [ ] Use `preparationMode` for schedule update source changes.
92+
- [ ] Use ordered `customPreparations` for new custom schedule create/update.
93+
- [ ] Continue using old linked-list endpoints only where necessary.
94+
- [ ] Show deleted linked templates as disabled/unavailable when `preparationTemplateDeleted = true`.
95+
- [ ] Prevent selecting deleted templates from the picker.
96+
- [ ] Generate new step UUIDs when copying a template into custom schedule steps.
97+
- [ ] Respect `preparationFrozen = true` by disabling preparation edits on started schedules.
98+
99+
Phase 3: Monitoring and validation
100+
- [ ] Verify old app versions can still onboard and update `/users/preparations`.
101+
- [ ] Verify old app versions can still create schedule-specific preparations via `/schedules/{id}/preparations`.
102+
- [ ] Verify new app can create `DEFAULT`, `TEMPLATE`, and `CUSTOM` schedules.
103+
- [ ] Verify template updates refresh not-started, not-finished template-mode schedules.
104+
- [ ] Verify default preparation updates refresh not-started, not-finished default-mode schedules.
105+
- [ ] Verify started schedules keep frozen snapshot steps.
106+
- [ ] Verify deleted templates remain visible through schedule metadata and direct detail lookup.
107+
- [ ] Verify account deletion removes named templates and template steps.
108+
- [ ] Verify privacy/account deletion docs mention named templates.
109+
110+
Phase 4: Cleanup after client migration
111+
- [ ] Stop documenting `isChange` for new clients.
112+
- [ ] Remove client use of linked-list `nextPreparationId` from new app code.
113+
- [ ] Add a versioned ordered preparation read endpoint if frontend needs ordered schedule/default reads without linked-list compatibility.
114+
- [ ] Once old clients are no longer supported, remove or fully ignore `is_change`.
115+
- [ ] Once old linked-list endpoints are retired, remove `next_preparation_id` from `preparation_user` and `preparation_schedule`.
116+
- [ ] Remove compatibility synthesis code for `nextPreparationId`.
117+
- [ ] Simplify repository queries to rely only on `order_index`.
118+
119+
## Edge Cases To Test Explicitly
120+
- Creating a template with duplicate active name after trim/case normalization is rejected.
121+
- Creating a template after deleting another template with the same name succeeds.
122+
- Creating a template with a deleted template's same ID is rejected.
123+
- Deleting a template twice succeeds.
124+
- Updating a deleted template is rejected.
125+
- Selecting an owned deleted template for schedule create/update returns a deleted-specific error.
126+
- Selecting another user's template behaves as not found.
127+
- Schedule detail edit with omitted `preparationMode` preserves a deleted template reference.
128+
- Schedule linked to deleted template can switch to default, active template, or custom.
129+
- Custom schedule update can reuse its own step IDs and reorder them.
130+
- Custom schedule update cannot use step IDs from templates, fixed default, or another schedule.
131+
- Template update can reuse its own step IDs and reorder them.
132+
- Fixed default update can reuse its own step IDs.
133+
- Malformed linked-list payloads are rejected: cycles, multiple heads, disconnected nodes, unknown next IDs, duplicate IDs.
134+
- Malformed ordered payloads are rejected: duplicate/gapped indexes, duplicate IDs, empty list, more than 50 steps, total duration over 1440.
135+
- Equal-time step content changes still refresh notifications without leaving duplicate scheduled tasks.
136+
137+
## Open Implementation Notes
138+
- Confirm whether DB-level active-name uniqueness can be enforced cleanly in the deployed MySQL version; service-level validation is still required.
139+
- Confirm native alarm payload refresh behavior when notification time does not change but step names/order do.
140+
- Keep `docs/preparation-templates-frontend.md` aligned with actual endpoint behavior.

0 commit comments

Comments
 (0)