Mi Material
Ruta: /teacher/templates · Atajo: g t · Sidebar: Mi Material
Mi Material es un gestor de contenido inspirado en Google Drive para organizar lesson templates, recursos y carpetas en una estructura jerarquica. Soporta tres modos de vista, busqueda global, filtrado por tags, y drag-and-drop.
Que hay
Section titled “Que hay”Layout general
Section titled “Layout general”- Sidebar izquierdo (oculto en movil): Arbol de carpetas + seccion de filtro por tags
- Toolbar superior: Busqueda (debounced 300ms), filtro de tipo (todos/templates/recursos), dropdown de ordenacion, toggle de vista (lista/grid/kanban), boton “Anadir”
- Area principal: Contenido segun la vista seleccionada
Tres modos de vista
Section titled “Tres modos de vista”Tabla con columnas: nombre, tipo, tags, modificado, usos. Soporta drag-and-drop inline para mover items a carpetas. Filas clickables para editar.
Tarjetas en grid de 5 columnas (desktop), responsive hasta 2 columnas. Cada tarjeta muestra icono, nombre, tipo y preview de recursos vinculados.
Columnas horizontales scrollables, una por carpeta + columna “Sin carpeta”. Cada columna carga su contenido via query separada.
Navegacion por carpetas
Section titled “Navegacion por carpetas”- Breadcrumb en la parte superior: navega por la jerarquia de carpetas
- Arbol de carpetas en sidebar: expandible, muestra toda la estructura
- Profundidad maxima: 5 niveles
- Cada carpeta puede tener: nombre, color, emoji (seleccionado con picker visual)
Tipos de contenido
Section titled “Tipos de contenido”Lesson Templates:
- Nombre, descripcion, categorias, tags
- Notas por defecto (
defaultNotes) y resumen por defecto (defaultSummary) — ambos campos editables en el editor - Meet link (
meetLink) — campo URL editable en el editor, se auto-rellena al aplicar el template a una sesion - Recursos vinculados (junction table
template_resources) - Contador de usos
Recursos:
- Label, URL, descripcion
- Provider: link, drive, youtube, vimeo
- Tipo: material, homework, reference
- Tags, favorito, contador de usos, thumbnail
- Integracion con Google Drive Picker para importar
Emoji picker de carpeta
Section titled “Emoji picker de carpeta”El campo de emoji en el formulario de creacion/edicion de carpetas usa un componente de picker visual en lugar de un input de texto libre. Al hacer click, se abre un popover con:
- Grid de emojis agrupados por categoria (smileys, naturaleza, comida, objetos, simbolos, banderas)
- Input de busqueda para filtrar emojis por nombre (debounced 200ms)
- Click en un emoji lo selecciona e inserta en el campo
- El emoji seleccionado se previsualiza junto al nombre de la carpeta en el formulario
Componente: EmojiPicker en apps/web/src/components/ui/emoji-picker.tsx
Drag-and-drop
Section titled “Drag-and-drop”Implementado con dnd-kit. Se puede arrastrar items a:
- Carpetas en la lista/grid
- Segmentos del breadcrumb
- Nodos del arbol de carpetas en el sidebar
Panel de preview de templates
Section titled “Panel de preview de templates”- Click en preview desde lista, grid o kanban abre un
Sheetlateral sin entrar en modo edicion - Muestra: nombre, descripcion, categorias (con color), tags (con color), meet link (clickable), resumen por defecto (Markdown), notas por defecto (Markdown), recursos vinculados (via query independiente a
GET /teacher/lesson-templates/:id/resources), contador de usos - Boton “Editar template” al pie del panel para pasar directamente al editor completo
- Componente:
TemplatePreview(apps/web/src/components/materials/template-preview.tsx)
Duplicar template
Section titled “Duplicar template”- Accion “Duplicar” disponible en lista, grid y kanban desde el menu de acciones de cada template
- Copia el template completo incluyendo todos sus recursos vinculados (
template_resources) - Endpoint:
POST /teacher/lesson-templates/:id/duplicate - Hook:
useDuplicateTemplateenuse-materials.ts— invalida['materials']y['lesson-templates']tras exito - Toast de confirmacion al completar
Estadisticas de carpetas
Section titled “Estadisticas de carpetas”- El arbol de carpetas del sidebar muestra una pastilla con el conteo de items (templates + recursos) junto al nombre de cada carpeta
- Solo visible cuando
folder.itemCount > 0 - Renderizado en
TreeNodedentro deFolderTreeSidebar - Los datos llegan desde el endpoint
GET /teacher/materials/folder-treeque incluyeitemCountpor carpeta
Papelera y restauracion
Section titled “Papelera y restauracion”- Boton papelera en la toolbar abre
TrashSheetcon todos los items soft-deleted del profesor - Items organizados en tres secciones: carpetas, templates, recursos
- Restaurar individual: boton por item —
POST /teacher/materials/trash/restorecon{ type, id } - Restaurar lote (batch): cuando una carpeta fue eliminada junto con su contenido (cascade soft-delete), aparece un boton “Restaurar grupo” que restaura todo el lote via
{ batchId }— solo se muestra una vez pordeletionBatchId - Borrado permanente: icono de papelera por item — muestra
ConfirmDialogantes de ejecutarPOST /teacher/materials/trash/permanent-delete - Fechas de eliminacion mostradas en formato relativo (minutos, horas, dias)
- Estado vacio con ilustracion cuando la papelera no tiene items
- La vista de papelera usa
GET /teacher/materials/trash(aceptafolderIdopcional para filtrar) - Componente:
TrashSheet(apps/web/src/components/materials/trash-sheet.tsx)
Operaciones en masa
Section titled “Operaciones en masa”- Multi-select disponible en vistas lista y grid mediante checkboxes
- BulkActionToolbar aparece al seleccionar uno o mas items, con acciones: Mover, Etiquetar, Eliminar
- BulkTagSheet permite anadir tags a multiples items a la vez
- Endpoints:
POST /teacher/materials/bulk-delete,POST /teacher/materials/bulk-tag
Busqueda y filtrado
Section titled “Busqueda y filtrado”- Busqueda global: Cuando hay termino de busqueda, ignora la carpeta actual y busca en todo
- Filtro por tipo: Todos, Templates, Recursos
- Filtro por tags: Seleccion multiple con logica OR (client-side)
- Ordenacion: nombre, modificado, usos, tipo (asc/desc)
Integracion Google Drive
Section titled “Integracion Google Drive”Si el profesor tiene Drive conectado, puede importar archivos directamente:
- Abre Google Drive Picker nativo
- Crea recursos con deduplicacion por
providerItemId - Los recursos Drive vinculados a sesiones se auto-copian a carpetas de alumnos via
DriveCopyService
Que falta
Section titled “Que falta”| Feature | Descripcion | Estado | Implementado |
|---|---|---|---|
| Emoji picker | El campo de emoji de carpeta ahora usa un picker visual con busqueda y categorias | ✅ | Batch 3 |
| Limpieza de isActive | Eliminado el campo isActive de los tipos TypeScript del frontend (types.ts), alineandolos con el schema de DB que solo usa deletedAt | ✅ | Batch 4 |
| Filtrado de tags server-side | El filtrado por tags ahora se ejecuta en el backend con JOIN en la query SQL en vez de post-query client-side. Mejora rendimiento con bibliotecas grandes | ✅ | Batch 4 |
| Filtro de recursos no vinculados | Nuevo filtro para identificar recursos creados pero nunca vinculados a templates o sesiones | ✅ | Batch 4 |
| Versionado de templates | Si un template se aplica a varias sesiones y luego se edita, afecta al historial. No hay tracking de “template cambio en fecha X” | Implementado ✅ | |
| Export/import | Backup de la biblioteca de templates a JSON/CSV | Implementado ✅ | |
| Busqueda avanzada | Filtros por fecha, tipo, tag combinados | Implementado ✅ | |
| Atajos de teclado | Cmd+N para nueva carpeta, Delete para borrar, etc. | Implementado ✅ |
Que falla
Section titled “Que falla”| Bug | Descripcion | Estado | Corregido |
|---|---|---|---|
| Campo isActive inconsistente | Los tipos TypeScript en types.ts definan isActive: boolean pero la DB solo usa deletedAt. Se elimino isActive de los tipos | ✅ | Batch 4 |
| N+1 en vista Kanban | NOT A BUG: Each folder column fetches independently but React Query caching/deduplication handles repeated requests efficiently. This is the correct architecture for live Kanban | ✅ | N/A |
| Filtrado de tags client-side | El filtrado por tags usaba logica OR post-query en el frontend. Ahora se ejecuta server-side con JOIN en la query SQL | ✅ | Batch 4 |
| Busqueda cambia scope sin feedback | Cuando habia termino de busqueda, se ignoraba el folderId actual sin feedback. FIXED: Blue banner with Search icon shows “Buscando en todos los materiales” when search is active, with dismiss button | ✅ | Batch 4 |
Que cambiaria
Section titled “Que cambiaria”| Mejora | Descripcion | Dificultad | Estado | Implementado |
|---|---|---|---|---|
| Mover filtrado de tags al backend | El filtrado de tags ahora usa JOIN server-side | Facil | ✅ | Batch 4 |
| Versionado de templates | Guardar snapshot del template al momento de aplicarse a una sesion. Mostrar en el historial de sesion que version del template se uso | Medio | Implementado ✅ |
Referencia tecnica
Section titled “Referencia tecnica”Archivos clave
Section titled “Archivos clave”| Archivo | Proposito |
|---|---|
apps/web/src/routes/teacher/templates.lazy.tsx | Pagina principal |
apps/web/src/components/materials/ | Componentes (breadcrumb, tree, vistas, editores, DnD, TrashSheet, BulkActionToolbar, BulkTagSheet) |
apps/web/src/components/materials/template-preview.tsx | Panel de preview de template (Sheet lateral, resources, markdown) |
apps/web/src/components/materials/template-editor.tsx | Editor de templates (incluye meetLink y defaultSummary) |
apps/web/src/components/materials/trash-sheet.tsx | Panel de papelera (restaurar individual/lote / borrado permanente) |
apps/web/src/components/materials/folder-tree-sidebar.tsx | Sidebar con arbol de carpetas + item counts + filtros de tags |
apps/web/src/components/materials/use-materials.ts | Hooks de TanStack Query y mutaciones (incluye useDuplicateTemplate, useTrashContents, useRestoreTrashItem, usePermanentDeleteTrashItem) |
apps/api/src/services/scheduling/material-folder-service.ts | Carpetas (CRUD, cascade delete, move, itemCount en folder-tree) |
apps/api/src/services/scheduling/lesson-template-service.ts | Templates (CRUD, apply to session, duplicate) |
apps/api/src/services/scheduling/resource-service.ts | Recursos (CRUD, link/unlink, Drive) |
| Endpoint | Metodo | Proposito |
|---|---|---|
/teacher/materials | GET | Contenido de carpeta (folders + templates + resources) |
/teacher/materials/breadcrumb/:id | GET | Ruta de navegacion |
/teacher/materials/search | GET | Busqueda global de templates y recursos |
/teacher/materials/folder-tree | GET | Arbol completo de carpetas |
/teacher/materials/folders | POST | Crear carpeta |
/teacher/materials/folders/:id | PATCH/DELETE | Editar/borrar carpeta |
/teacher/materials/folders/reorder | POST | Reordenar carpetas |
/teacher/materials/move | POST | Mover items entre carpetas |
/teacher/lesson-templates | GET | Lista todas las templates |
/teacher/lesson-templates | POST | Crear template |
/teacher/lesson-templates/tree | GET | Arbol de templates para picker |
/teacher/lesson-templates/:id | GET | Template individual |
/teacher/lesson-templates/:id | PATCH/DELETE | Editar/borrar template |
/teacher/lesson-templates/:id/duplicate | POST | Duplicar template con sus recursos vinculados |
/teacher/lesson-templates/:id/resources | GET | Recursos vinculados (usado por TemplatePreview) |
/teacher/resources | GET | Lista recursos con filtros |
/teacher/resources | POST | Crear recurso |
/teacher/resources/:id | GET | Recurso individual |
/teacher/resources/:id | PATCH/DELETE | Editar/borrar recurso |
/teacher/resources/:id/favorite | POST | Toggle favorito |
/teacher/materials/trash | GET | Items soft-deleted del profesor (opcional: folderId) |
/teacher/materials/trash/restore | POST | Restaurar items (individual o por deletionBatchId) |
/teacher/materials/trash/permanent-delete | POST | Borrado fisico permanente |
/teacher/materials/bulk-delete | POST | Borrar multiples items a la vez |
/teacher/materials/bulk-tag | POST | Anadir tags a multiples items a la vez |
Queries y mutaciones (TanStack Query)
Section titled “Queries y mutaciones (TanStack Query)”// QueriesuseQuery({ queryKey: ['materials', 'contents', params], queryFn: ... })useQuery({ queryKey: ['materials', 'breadcrumb', folderId], queryFn: ... })useQuery({ queryKey: ['materials', 'folder-tree'], queryFn: ... })useQuery({ queryKey: ['materials', 'trash'], queryFn: ... }) // TrashSheetuseQuery({ queryKey: ['template-resources', templateId], queryFn: ... }) // TemplatePreviewuseQuery({ queryKey: ['class-categories'], queryFn: ... })useQuery({ queryKey: ['tags'], queryFn: ... })useQuery({ queryKey: ['drive-status'], queryFn: ... })
// Mutaciones claveuseDuplicateTemplate() // POST /teacher/lesson-templates/:id/duplicateuseRestoreTrashItem() // POST /teacher/materials/trash/restoreusePermanentDeleteTrashItem() // POST /teacher/materials/trash/permanent-delete