Files
WhatIDo/todo.md
T
2026-09-19 13:06:21 +03:00

6.1 KiB
Raw Blame History

TODO: профиль ученика + ZIP-архив

Задача

  1. На /students.html у каждого ученика — кнопка «Профиль», которая одним нажатием отдаёт ZIP-архив со всеми данными ученика (фото, работы/файлы, записи журнала).
  2. Сгенерированная страница index.html внутри архива должна быть оформлена полностью по дизайну student-profile.html (тёмная/светлая тема, токены, топбар, hero, статистика, образование, опыт, навыки, проекты, уроки, «о себе», футер).
  3. Если в БД нет данных для заполнения блоков этой страницы — страница не должна ломаться или показывать выдуманные данные: блоки рендерятся из того, что есть, пустые разделы получают аккуратный empty-state, а данные можно добавить позже (без правок кода).

План работ

  • Разведка: server.js (экспорт /api/export/student, createZipWriter, renderStudentReport), public/students.html + public/js/students.js, db/init.sql + db/migration.sql, дизайн-макет student-profile.html.
  • todo.md (этот файл).
  • БД: у ученика появляются поля для будущего заполнения профиля — students.profile JSONB и students.photo_path (db/init.sql, db/migration.sql).
  • Бэкап/восстановление: новые поля сохраняются и восстанавливаются (normalizeRestoreData, INSERT INTO students).
  • API: GET/PUT /api/students/:id/profile (requireAuth + проверка филиала, валидация структуры профиля, аудит student.profile.update).
  • Новый модуль student-report.js: ZIP-writer (перенесён из server.js) + renderStudentReport() — страница профиля ученика по дизайну student-profile.html (самодостаточный HTML+CSS+JS, Lucide через CDN с fallback-заглушками, лайтбокс, ленты, тема, печать, анимации).
  • server.js: вызов рендера через модуль, убрать старый рендер из server.js, расширить выборки экспорта (модули, статус ИИ, филиал, students.profile).
  • public/students.html + public/js/students.js: кнопка «Профиль» (прямая выгрузка ZIP) и кнопка «Данные» (модальное окно заполнения данных профиля: роль, статус, город, наставник, о себе, цитата, теги, контакты, достижения, навыки, опыт, прогресс по модулям).
  • Архив: index.html + photos/ + files/ + data.json (метаданные профиля).
  • Проверка: 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.

Как применить на существующей базе

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 создаёт поля сразу.)

Как проверить

# 1. Админка → «Ученики»: у каждого ученика кнопки «Профиль» и «Данные»
# 2. «Данные» → заполнить роль/био/навыки/контакты/достижения/опыт → Сохранить
# 3. «Профиль» → скачивается profile_<имя>_<дата>.zip:
#    index.html (страница профиля по student-profile.html), photos/, files/, data.json