# 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 ```