feat(wishes): трекер пожеланий по улучшению системы
Любой авторизованный пользователь подаёт пожелание (заголовок, категория, описание); видит только свои. Админ видит все, фильтрует по статусу, ведёт по статусам (новое → запланировано → в работе → готово / отклонено) и пишет ответ автору. Автор получает уведомление при смене статуса (pushNotif). Бэкенд: миграция 080 (таблица wishes), wishController (list/create/update/remove с валидацией и whitelist категорий/статусов), routes/wishes (PATCH — только админ, DELETE — автор«новое»/админ, проверка в хендлере), смонтировано в server.js. Тесты 15/15. Фронт: страница /wishes (форма + список со статус-бейджами; у админа — фильтры, смена статуса, ответ, удаление), пункт «Пожелания» в сайдбаре (все роли), фиче-флаг feature_wishes_enabled (тумблер в админ-модулях + whitelist + FEATURE_HREFS; админ видит всегда). Клиентские врапперы LS.wish*. ⚠️ Живой БД нужен npm run migrate (080). lint:routes 0; node --check всех файлов + инлайна. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -191,6 +191,11 @@ async function kickMember(classId, userId) { return req('DELETE', `/classes/
|
||||
async function regenerateInviteCode(classId) { return req('POST', `/classes/${classId}/new-code`); }
|
||||
async function classJournal(classId) { return req('GET', `/classes/${classId}/journal`); }
|
||||
async function classOutstanding(classId) { return req('GET', `/classes/${classId}/outstanding`); }
|
||||
/* ── Пожелания по улучшению ── */
|
||||
async function wishesList(params = {}) { const q = new URLSearchParams(params).toString(); return req('GET', '/wishes' + (q ? '?' + q : '')); }
|
||||
async function wishCreate(data) { return req('POST', '/wishes', data); }
|
||||
async function wishUpdate(id, data) { return req('PATCH', `/wishes/${id}`, data); }
|
||||
async function wishDelete(id) { return req('DELETE', `/wishes/${id}`); }
|
||||
async function createAssignment(classId, data) { return req('POST', `/classes/${classId}/assignments`, data); }
|
||||
async function createDirectAssignment(data) { return req('POST', '/assignments', data); }
|
||||
async function updateAssignment(id, data) { return req('PUT', `/assignments/${id}`, data); }
|
||||
@@ -853,6 +858,7 @@ const FEATURE_HREFS = {
|
||||
quantik: ['/quantik', '/quantik.html'],
|
||||
theory: ['/theory', '/theory.html'],
|
||||
sitemap: ['/sitemap', '/sitemap.html'],
|
||||
wishes: ['/wishes', '/wishes.html'],
|
||||
};
|
||||
/* Контейнеры виджетов-модулей (дашборд и т.п.) — прячем блок целиком, а не только
|
||||
ссылку, иначе остаётся пустой блок (напр. виджет флеш-карт #w-flashcard).
|
||||
@@ -1127,6 +1133,7 @@ window.LS = {
|
||||
getQuestions, createQuestion, duplicateQuestion, updateQuestion, deleteQuestion, importQuestions,
|
||||
getClasses, createClass, getClassDetail, updateClass, deleteClass, kickMember, addClassMember, createAssignment, createDirectAssignment, updateAssignment, deleteAssignment,
|
||||
regenerateInviteCode, classJournal, classOutstanding,
|
||||
wishesList, wishCreate, wishUpdate, wishDelete,
|
||||
joinClass, myClasses, getStudents, classFeed,
|
||||
getAnnouncements, createAnnouncement, deleteAnnouncement,
|
||||
getNotifications, markNotifRead, markAllNotifsRead, connectSSE,
|
||||
|
||||
Reference in New Issue
Block a user