Files
Learn_System/plans/sim-builder/phase-6-sharing.md
T

91 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 6: Раздача / шаблоны / клон / программа
**Status:** ✅ Implemented (pending commit — за оркестратором)
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** fullstack
## Objective
Экосистема вокруг custom-sims: публикация, раздача классу, клонирование чужих,
старт из шаблонов, привязка к программе (учебник/тема).
## Tasks
- [x] Публикация: тумблер draft↔published. В билдере — кнопка «Опубликовать»/«Снять» (PUT status,
`setStatus`); в каталоге — кнопки на owner-карточке (publish/unpublish). Только владелец/admin.
- [x] Раздача классу: `POST /api/custom-sims/:id/share { classId }` (requireRole teacher,admin +
per-row ownership). РЕШЕНИЕ: published custom-sim И ТАК видна всем в каталоге, поэтому раздача =
авто-публикация (status→published) + ДОЛГОВЕЧНОЕ уведомление ученикам класса (`pushNotif`,
notifications-таблица + SSE) со ссылкой `/lab?sim=custom:<id>`. БЕЗ копии и БЕЗ content_access
(custom-sim не гейтится allowlist'ом 'sim' — тот гейтит только legacy lab_sims).
- [x] Клон: `POST /api/custom-sims/:id/clone` — копия spec вызвавшему как draft, title += « (копия)»,
version=1. Источник: своя (любая) ИЛИ чужая published. Кнопка «Клонировать к себе» на чужой
published-карточке (только для teacher/admin) → ведёт на `/sim-builder?id=<newId>`.
- [x] Шаблоны: 4 встроенных спеки в `TEMPLATES` (sim-builder.js): пустая, маятник, график y=f(x),
бросок. Кнопка «Шаблон» в тулбаре → модалка выбора → `loadFromSim` как новая симуляция.
«Создать из существующей» = clone (с чужой карточки).
- [x] Привязка к программе: переиспользован `lab_sim_links` с `sim_id='custom:<id>'` (sim_id —
TEXT, отдельная таблица не нужна). Эндпоинты на роутере custom-sims: GET `/:id/related`,
POST `/:id/links`, DELETE `/:id/links/:linkId` (владелец/admin управляет связями СВОЕЙ симуляции).
Клиент: `customSimRelated/AddLink/DelLink`. Backend + чтение готовы; UI-редактор связей в билдере
и чипы в каталоге — НЕ сделаны (см. Handoff: остаток).
- [x] Тесты: `backend/tests/custom-sims-share.test.js` (15 it): share (авто-publish +
durable-уведомление, 400/403/404), clone (новый владелец/draft/spec, чужой published OK, чужой
draft 403), links (привязка/удаление/related, ownership). seedRow-паттерн (class/members/textbook).
## Files to Modify/Create
- `backend/src/controllers/customSimController.js` — share/clone/publish (modify)
- `backend/src/routes/customSims.js` — роуты share/clone (modify)
- `backend/src/controllers/.../lab links` — связи для custom (reuse `lab.js` links, modify при необходимости)
- `frontend/sim-builder.html` / `frontend/js/labs/lab-glue.js` — шаблоны, кнопки публикации/клона/раздачи (modify)
- `js/api.js` — методы share/clone (modify)
- `backend/tests/custom-sims-share.test.js` (new)
## Acceptance Criteria
- Учитель публикует; другой учитель видит и клонирует к себе (draft).
- Выданная классу симуляция доступна ученикам класса (с уведомлением).
- Старт из шаблона создаёт рабочую заготовку. Привязка к учебнику показывает чип/кнопку.
## Notes
- Раздача — переиспользовать существующий механизм доступа/уведомлений, не строить новый.
- Решение копия-vs-ссылка зафиксировать в CONTEXT.md.
## Review Checklist
- [x] Все задачи выполнены (привязка к программе — backend+чтение; UI-редактор связей в Handoff)
- [x] Ownership на publish/share/clone/links покрыт тестами (чужой draft → 403, чужой published clone → ОК)
- [x] Ученик класса получает уведомление; не-владелец/не-свой-класс → 403
- [x] Reuse материалов/доступа/links, без дублей (pushNotif, lab_sim_links, паттерн share из materials)
## Handoff to Next Phase
### Что реализовано (Ф6)
- **Backend** (`customSimController.js` + `customSims.js`):
- `POST /:id/share { classId }` — авто-publish + `pushNotif(uid,'sim_shared',msg,'/lab?sim=custom:<id>')`
каждому ученику класса. Возвращает `{ ok, sent, status:'published' }`. requireRole(teacher,admin) +
ownership симуляции + проверка `classes.teacher_id`.
- `POST /:id/clone` → 201 `{ id }`. Источник: своя (любая) ИЛИ чужая published. status=draft, version=1,
title += ' (копия)', spec_json копируется как есть.
- `GET /:id/related` (auth, own/published) / `POST /:id/links` / `DELETE /:id/links/:linkId`
(owner/admin) — поверх `lab_sim_links`, `sim_id='custom:<id>'`. DELETE симуляции чистит её связи.
- **Решение копия-vs-доступ:** доступ (published виден всем) + уведомление; НЕ копия, НЕ content_access.
- **Клиент** (`js/api.js`): `customSimShare/Clone/Related/AddLink/DelLink` в `window.LS`.
- **Каталог** (`lab-glue.js`, IIFE `LabCustom`, аддитивно): owner-карточка — кнопки Раздать классу /
Опубликовать↔Снять; чужая published (для teacher/admin) — «Клонировать к себе». Делегат `data-act`
расширен (share/clone/publish/unpublish). Модалка выбора класса (`LS.getClasses`+`LS.modal`).
Публичное API: добавлены `LabCustom.share/clone/setStatus`.
- **Билдер** (`sim-builder.js`): тулбар — «Шаблон» (TEMPLATES×4: пустая/маятник/график/бросок →
`loadFromSim` как новая), «Раздать» (для сохранённой), publish-toggle (Опубликовать↔Снять).
Новые методы: `setStatus`, `openShareModal`, `openTemplateModal`. ICON.template/unpublish.
### Остаток (минимум по плану — не сделано, низкий приоритет)
- UI-редактор курикулумных связей в билдере (select учебника из `/api/access/catalog` + добавить/
удалить) и чипы «Связано с программой» в каталоге/на странице sim. Backend и `/related` готовы —
это чисто фронтовая надстройка. Обратный поиск «какие custom-sim привязаны к учебнику» (как
`/api/lab/links?kind=textbook&ref_id=`) для custom НЕ добавлен: `/api/lab/links` джойнит `lab_sims`,
а у custom строк там нет — при желании добавить отдельный bulk-эндпоинт или LEFT JOIN на custom_sims.
### Для Ф7 (доска онлайн-урока)
- Источник sim для доски: `LS.customSimGet(id)``sim.spec`; рендер — `window.SimEngine.mount(host, spec)`
(как в билдере/каталоге). Deep-link/идентификатор: `custom:<dbid>`; в LabRegistry — `customsim_<dbid>`
(resolveId). published-симуляция доступна всем (для учеников на доске — без доп. прав).
- Раздача классу уже шлёт уведомление со ссылкой `/lab?...` — на доске ссылку при необходимости
заменить на классную сессию; механизм `pushNotif` переиспользуем.