|
| 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. |
0 commit comments