6.1 KiB
6.1 KiB
TODO: профиль ученика + ZIP-архив
Задача
- На
/students.htmlу каждого ученика — кнопка «Профиль», которая одним нажатием отдаёт ZIP-архив со всеми данными ученика (фото, работы/файлы, записи журнала). - Сгенерированная страница
index.htmlвнутри архива должна быть оформлена полностью по дизайнуstudent-profile.html(тёмная/светлая тема, токены, топбар, hero, статистика, образование, опыт, навыки, проекты, уроки, «о себе», футер). - Если в БД нет данных для заполнения блоков этой страницы — страница не должна ломаться или показывать выдуманные данные: блоки рендерятся из того, что есть, пустые разделы получают аккуратный 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