feat(materials): Фаза 2 — коллекции (папки), поиск и фильтры

- Миграция 061: material_collections + student_materials.collection_id (ON DELETE SET NULL) + tags.
- API: CRUD коллекций (/api/materials/collections), GET /materials отдаёт {materials, collections}
  со счётчиками; PATCH /materials/:id принимает collection_id/tags. Хелперы в js/api.js.
- /my-materials: бар папок (Все/папки/Без папки/+папка) с фильтром, поиск по тексту, фильтр по типу,
  перенос материала в папку (select на карточке), создание/переименование/удаление папок.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-04 12:04:51 +03:00
parent fd3e5c47e8
commit 2c7e97406a
5 changed files with 251 additions and 35 deletions
+4
View File
@@ -1049,6 +1049,7 @@ window.LS = {
crGetMyHistory, crGetClassHistory, crGetSessionSummary, crExportChatUrl, crGetAllNotes, crDeleteHistory,
crAdminGetAllHistory, crAdminGetTeachersList,
listMaterials, saveMaterial, updateMaterial, deleteMaterial,
createMaterialCollection, updateMaterialCollection, deleteMaterialCollection,
escapeHtml, esc,
parseDate, fmtRelTime, safeHref,
initPage,
@@ -1247,6 +1248,9 @@ async function listMaterials() { return req('GET', '/materials'); }
async function saveMaterial(data) { return req('POST', '/materials', data); }
async function updateMaterial(id, d) { return req('PATCH', `/materials/${id}`, d); }
async function deleteMaterial(id) { return req('DELETE', `/materials/${id}`); }
async function createMaterialCollection(d) { return req('POST', '/materials/collections', d); }
async function updateMaterialCollection(id,d){ return req('PATCH', `/materials/collections/${id}`, d); }
async function deleteMaterialCollection(id) { return req('DELETE', `/materials/collections/${id}`); }
async function deleteFile(id) { return req('DELETE', `/files/${id}`); }
async function getFileAccess(id) { return req('GET', `/files/${id}/access`); }
async function assignFile(id, data) { return req('POST', `/files/${id}/assign`, data); }