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
@@ -0,0 +1,21 @@
-- ═══════════════════════════════════════════════════════════════
-- 061: Collections (folders) + tags for «Мои материалы»
--
-- A user groups saved materials into named collections (folders). A material
-- belongs to at most one collection (collection_id, SET NULL when the folder
-- is deleted — the material stays, just becomes "uncategorised"). Optional
-- free-text tags for quick filtering.
-- ═══════════════════════════════════════════════════════════════
CREATE TABLE material_collections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
color TEXT,
sort_order INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX idx_material_collections_user ON material_collections(user_id, sort_order, id);
ALTER TABLE student_materials ADD COLUMN collection_id INTEGER REFERENCES material_collections(id) ON DELETE SET NULL;
ALTER TABLE student_materials ADD COLUMN tags TEXT;