Merge branch 'phase/36-person-card-tabs'
ci / server (push) Failing after 4m1s
ci / client (push) Failing after 17s

This commit is contained in:
Leonid Pershin
2026-08-20 04:35:35 +03:00
29 changed files with 1458 additions and 280 deletions
+17 -16
View File
@@ -13,29 +13,30 @@
## Задачи
- [ ] Окно карточки: вкладки **Обзор / Одежда / Ноша / Сейчас**
- [ ] Обзор — прежнее: тело, навыки, черты, нужды, семья, расписание; текущее дело с обзора
- [x] Окно карточки: вкладки **Обзор / Одежда / Ноша / Сейчас**
- [x] Обзор — прежнее: тело, навыки, черты, нужды, семья, расписание; текущее дело с обзора
уходит на «Сейчас»
- [ ] Одежда: слои, цвет, полоска износа и подпись, короткая уместность к ситуации
- [ ] Ноша: сумка, несёшь/можешь, шкафчик или его отсутствие, кратко «ещё N дома» без полного
- [x] Одежда: слои, цвет, полоска износа и подпись (если сервер прислал `condition` /
`conditionLabel`), короткая уместность — прочерк до фазы 35
- [x] Ноша: сумка, несёшь/можешь, шкафчик или его отсутствие, кратко «ещё N дома» без полного
домашнего списка
- [ ] Сейчас: на территории школы — подпись действия (`ест`, `идёт`, `учится (математика)`,
- [x] Сейчас: на территории школы — подпись действия (`ест`, `идёт`, `учится (математика)`,
`переодевается`). Вне школы — «дома»
- [ ] История за сегодня под текущим делом: поиск, сортировка, пейджинг
- [ ] `GET /api/schools/{id}/people/{personId}/log` — страница и фильтр; не сокет, не лента
- [x] История за сегодня под текущим делом: поиск, сортировка, пейджинг
- [x] `GET /api/schools/{id}/people/{personId}/log` — страница и фильтр; не сокет, не лента
школы. Граница суток — шесть утра; skip ночи обнуляет вчерашнее
- [ ] Запись в лог: начало/конец действия, утренняя выдача одежды, переодевание. Хранит
работник, не `people.json`
- [ ] Vitest: переключение вкладок, пейджер лога, «дома» когда activity пустая и человек away
- [ ] `docs/protocol.md` — HTTP лога в том же коммите
- [ ] Запись в лог: начало/конец действия есть; утренняя выдача одежды — тип
`apparel-replaced` подхватывается, пишет фаза 34; переодевание — после фазы 35
- [x] Vitest: переключение вкладок, пейджер лога, «дома» когда activity пустая и человек away
- [x] `docs/protocol.md` — HTTP лога в том же коммите
## Тесты, без которых фаза не закрыта
- [ ] Лог не едет в `GET .../people/{id}` — только отдельным запросом
- [ ] Страница лога не пересекается; неизвестный человек — `404`
- [ ] После 6:00 записей вчера нет
- [ ] Вне школы вкладка «Сейчас» показывает «дома», не пусто
- [ ] Клиентский тест: вкладка «Одежда» не монтирует таблицу лога
- [x] Лог не едет в `GET .../people/{id}` — только отдельным запросом
- [x] Страница лога не пересекается; неизвестный человек — `404`
- [x] После 6:00 записей вчера нет
- [x] Вне школы вкладка «Сейчас» показывает «дома», не пусто
- [x] Клиентский тест: вкладка «Одежда» не монтирует таблицу лога
## Критерий готовности
+38 -2
View File
@@ -259,8 +259,9 @@ no Chemistry; a related tongue from the name set may sit beside the native at a
(01) and `conditionLabel` from the catalog bands (`целая` / `поношенная` / `порванная` /
`висит лохмотьями`). The client draws the bar and the caption; it does not compute thresholds.
`carried` is the bag — textbooks include `subject`. `carryMass` / `carryCapacity` are kilograms;
overload does not slow walking. Home and locker stay in `people.json` and are not on this card.
The people list does not include any of these fields.
overload does not slow walking. `hasLocker` is true when anything sits in a locker; `homeCount`
is how many items remain at home, not the list. The people list does not include any of these
fields. Today's history is a separate GET.
`hasAvatar` and `hasFullBody` tell the client whether PNG files already exist on disk for this
person. They are filled on the HTTP thread after the worker returns the card; generation does
@@ -313,11 +314,46 @@ not happen on this request.
],
"carryMass": 1.2,
"carryCapacity": 14,
"hasLocker": false,
"homeCount": 3,
"hasAvatar": false,
"hasFullBody": false
}
```
### `GET /api/schools/{id}/people/{personId}/log`
Today's history for one person: search, sort by time, page. Goes through the school's mailbox
because the log lives on the worker, not in `people.json` and not on a published snapshot.
Unknown school is `404` `unknown-school`; unknown person is `404` `unknown-person`. This is
not a socket feed and not a school-wide event list.
The day boundary is six in the morning — the same hour skip lands on. Crossing that hour, or
skipping an empty night, drops yesterday's rows. `?lang=ru|en` labels the captions. `q` is a
substring of the caption or type. `sort` is `time`. `dir` is `asc` or `desc` (default `desc`).
`page` starts at 1; `pageSize` defaults to 20 and is at most 100. A thousand rows do not
arrive in one response.
Row `type` values: `action-started`, `action-ended`, `apparel-replaced` (morning issue, when
phase 34 appends it), `apparel-changed` (dressing, when phase 35 appends it). `thingDef` is
the action or apparel def the caption was built from.
```json
{
"total": 2,
"page": 1,
"pageSize": 20,
"entries": [
{
"time": "2012-04-03T12:00:00Z",
"type": "action-started",
"label": "начал: Обед",
"thingDef": "EatLunch"
}
]
}
```
### `GET /api/schools/{id}/people/{personId}/portrait`
Returns a generated PNG when one exists. Query `kind=avatar|full` selects head-and-shoulders or