79 lines
6.1 KiB
Markdown
79 lines
6.1 KiB
Markdown
# TODO: профиль ученика + ZIP-архив
|
||
|
||
## Задача
|
||
|
||
1. На `/students.html` у каждого ученика — кнопка **«Профиль»**, которая одним нажатием
|
||
отдаёт ZIP-архив со всеми данными ученика (фото, работы/файлы, записи журнала).
|
||
2. Сгенерированная страница `index.html` внутри архива должна быть оформлена
|
||
**полностью по дизайну `student-profile.html`** (тёмная/светлая тема, токены,
|
||
топбар, hero, статистика, образование, опыт, навыки, проекты, уроки, «о себе», футер).
|
||
3. Если в БД нет данных для заполнения блоков этой страницы — страница не должна
|
||
ломаться или показывать выдуманные данные: блоки рендерятся из того, что есть,
|
||
пустые разделы получают аккуратный empty-state, а **данные можно добавить позже**
|
||
(без правок кода).
|
||
|
||
## План работ
|
||
|
||
- [x] Разведка: `server.js` (экспорт `/api/export/student`, `createZipWriter`,
|
||
`renderStudentReport`), `public/students.html` + `public/js/students.js`,
|
||
`db/init.sql` + `db/migration.sql`, дизайн-макет `student-profile.html`.
|
||
- [x] `todo.md` (этот файл).
|
||
- [x] БД: у ученика появляются поля для будущего заполнения профиля —
|
||
`students.profile JSONB` и `students.photo_path` (`db/init.sql`, `db/migration.sql`).
|
||
- [x] Бэкап/восстановление: новые поля сохраняются и восстанавливаются
|
||
(`normalizeRestoreData`, `INSERT INTO students`).
|
||
- [x] API: `GET/PUT /api/students/:id/profile` (requireAuth + проверка филиала,
|
||
валидация структуры профиля, аудит `student.profile.update`).
|
||
- [x] Новый модуль `student-report.js`: ZIP-writer (перенесён из `server.js`) +
|
||
`renderStudentReport()` — страница профиля ученика по дизайну
|
||
`student-profile.html` (самодостаточный HTML+CSS+JS, Lucide через CDN с
|
||
fallback-заглушками, лайтбокс, ленты, тема, печать, анимации).
|
||
- [x] `server.js`: вызов рендера через модуль, убрать старый рендер из `server.js`,
|
||
расширить выборки экспорта (модули, статус ИИ, филиал, `students.profile`).
|
||
- [x] `public/students.html` + `public/js/students.js`: кнопка **«Профиль»**
|
||
(прямая выгрузка ZIP) и кнопка **«Данные»** (модальное окно заполнения данных
|
||
профиля: роль, статус, город, наставник, о себе, цитата, теги, контакты,
|
||
достижения, навыки, опыт, прогресс по модулям).
|
||
- [x] Архив: `index.html` + `photos/` + `files/` + `data.json` (метаданные профиля).
|
||
- [x] Проверка: `node --check`, генерация тестового ZIP и разбор `zipfile`
|
||
(структура + наличие разделов профиля в `index.html`).
|
||
|
||
## Решения и допущения
|
||
|
||
- «index.html» из задачи — это `index.html` внутри ZIP-архива (страница профиля
|
||
ученика). Именно он теперь оформлен по `student-profile.html`. Публичный
|
||
`public/index.html` (форма отправки записи) не переделывался: это отдельная
|
||
страница, и в задаче про неё данных нет.
|
||
- Кнопка называется «Профиль» (в задаче опечатка «Профаил»), подпись в подсказке:
|
||
«Скачать профиль ученика (ZIP: фото и работы)».
|
||
- Дизайн-макет использует Lucide-иконки через CDN; в архиве оставлен тот же подход,
|
||
потому что он полностью офлайн-деградирует (класс `no-icons`), как и в макете.
|
||
- Данные, которых нет в БД (опыт, навыки, контакты, достижения, прогресс по модулям,
|
||
фото), рендерятся из полей `students.profile`; пока их нет — показывается
|
||
empty-state с подсказкой, а заполнить их можно в админке («Данные» у ученика).
|
||
- В `renderStudentReport` добавлен раздел «Фотографии» (лента в стиле макета) —
|
||
без него фото из архива негде было бы посмотреть; ленты и лайтбокс — в стиле
|
||
`student-profile.html`.
|
||
|
||
## Как применить на существующей базе
|
||
|
||
```bash
|
||
docker compose exec -T db psql -U app -d whereldo \
|
||
-c "ALTER TABLE students ADD COLUMN IF NOT EXISTS photo_path VARCHAR(255);" \
|
||
-c "ALTER TABLE students ADD COLUMN IF NOT EXISTS profile JSONB;"
|
||
docker compose up -d --build app
|
||
```
|
||
|
||
(`db/migration.sql` уже содержит эти же ALTER-команды; на чистой базе `init.sql`
|
||
создаёт поля сразу.)
|
||
|
||
## Как проверить
|
||
|
||
```bash
|
||
# 1. Админка → «Ученики»: у каждого ученика кнопки «Профиль» и «Данные»
|
||
# 2. «Данные» → заполнить роль/био/навыки/контакты/достижения/опыт → Сохранить
|
||
# 3. «Профиль» → скачивается profile_<имя>_<дата>.zip:
|
||
# index.html (страница профиля по student-profile.html), photos/, files/, data.json
|
||
```
|
||
|