Skip to content

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.

Materials page
  • 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

Tabla con columnas: nombre, tipo, tags, modificado, usos. Soporta drag-and-drop inline para mover items a carpetas. Filas clickables para editar.

  • 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)

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

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

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
  • Click en preview desde lista, grid o kanban abre un Sheet lateral 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)
  • 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: useDuplicateTemplate en use-materials.ts — invalida ['materials'] y ['lesson-templates'] tras exito
  • Toast de confirmacion al completar
  • 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 TreeNode dentro de FolderTreeSidebar
  • Los datos llegan desde el endpoint GET /teacher/materials/folder-tree que incluye itemCount por carpeta
  • Boton papelera en la toolbar abre TrashSheet con todos los items soft-deleted del profesor
  • Items organizados en tres secciones: carpetas, templates, recursos
  • Restaurar individual: boton por item — POST /teacher/materials/trash/restore con { 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 por deletionBatchId
  • Borrado permanente: icono de papelera por item — muestra ConfirmDialog antes de ejecutar POST /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 (acepta folderId opcional para filtrar)
  • Componente: TrashSheet (apps/web/src/components/materials/trash-sheet.tsx)
  • 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 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)

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

FeatureDescripcionEstadoImplementado
Emoji pickerEl campo de emoji de carpeta ahora usa un picker visual con busqueda y categoriasBatch 3
Limpieza de isActiveEliminado el campo isActive de los tipos TypeScript del frontend (types.ts), alineandolos con el schema de DB que solo usa deletedAtBatch 4
Filtrado de tags server-sideEl 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 grandesBatch 4
Filtro de recursos no vinculadosNuevo filtro para identificar recursos creados pero nunca vinculados a templates o sesionesBatch 4
Versionado de templatesSi 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/importBackup de la biblioteca de templates a JSON/CSVImplementado ✅
Busqueda avanzadaFiltros por fecha, tipo, tag combinadosImplementado ✅
Atajos de tecladoCmd+N para nueva carpeta, Delete para borrar, etc.Implementado ✅

BugDescripcionEstadoCorregido
Campo isActive inconsistenteLos tipos TypeScript en types.ts definan isActive: boolean pero la DB solo usa deletedAt. Se elimino isActive de los tiposBatch 4
N+1 en vista KanbanNOT A BUG: Each folder column fetches independently but React Query caching/deduplication handles repeated requests efficiently. This is the correct architecture for live KanbanN/A
Filtrado de tags client-sideEl filtrado por tags usaba logica OR post-query en el frontend. Ahora se ejecuta server-side con JOIN en la query SQLBatch 4
Busqueda cambia scope sin feedbackCuando 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 buttonBatch 4

MejoraDescripcionDificultadEstadoImplementado
Mover filtrado de tags al backendEl filtrado de tags ahora usa JOIN server-sideFacilBatch 4
Versionado de templatesGuardar snapshot del template al momento de aplicarse a una sesion. Mostrar en el historial de sesion que version del template se usoMedioImplementado ✅

ArchivoProposito
apps/web/src/routes/teacher/templates.lazy.tsxPagina principal
apps/web/src/components/materials/Componentes (breadcrumb, tree, vistas, editores, DnD, TrashSheet, BulkActionToolbar, BulkTagSheet)
apps/web/src/components/materials/template-preview.tsxPanel de preview de template (Sheet lateral, resources, markdown)
apps/web/src/components/materials/template-editor.tsxEditor de templates (incluye meetLink y defaultSummary)
apps/web/src/components/materials/trash-sheet.tsxPanel de papelera (restaurar individual/lote / borrado permanente)
apps/web/src/components/materials/folder-tree-sidebar.tsxSidebar con arbol de carpetas + item counts + filtros de tags
apps/web/src/components/materials/use-materials.tsHooks de TanStack Query y mutaciones (incluye useDuplicateTemplate, useTrashContents, useRestoreTrashItem, usePermanentDeleteTrashItem)
apps/api/src/services/scheduling/material-folder-service.tsCarpetas (CRUD, cascade delete, move, itemCount en folder-tree)
apps/api/src/services/scheduling/lesson-template-service.tsTemplates (CRUD, apply to session, duplicate)
apps/api/src/services/scheduling/resource-service.tsRecursos (CRUD, link/unlink, Drive)
EndpointMetodoProposito
/teacher/materialsGETContenido de carpeta (folders + templates + resources)
/teacher/materials/breadcrumb/:idGETRuta de navegacion
/teacher/materials/searchGETBusqueda global de templates y recursos
/teacher/materials/folder-treeGETArbol completo de carpetas
/teacher/materials/foldersPOSTCrear carpeta
/teacher/materials/folders/:idPATCH/DELETEEditar/borrar carpeta
/teacher/materials/folders/reorderPOSTReordenar carpetas
/teacher/materials/movePOSTMover items entre carpetas
/teacher/lesson-templatesGETLista todas las templates
/teacher/lesson-templatesPOSTCrear template
/teacher/lesson-templates/treeGETArbol de templates para picker
/teacher/lesson-templates/:idGETTemplate individual
/teacher/lesson-templates/:idPATCH/DELETEEditar/borrar template
/teacher/lesson-templates/:id/duplicatePOSTDuplicar template con sus recursos vinculados
/teacher/lesson-templates/:id/resourcesGETRecursos vinculados (usado por TemplatePreview)
/teacher/resourcesGETLista recursos con filtros
/teacher/resourcesPOSTCrear recurso
/teacher/resources/:idGETRecurso individual
/teacher/resources/:idPATCH/DELETEEditar/borrar recurso
/teacher/resources/:id/favoritePOSTToggle favorito
/teacher/materials/trashGETItems soft-deleted del profesor (opcional: folderId)
/teacher/materials/trash/restorePOSTRestaurar items (individual o por deletionBatchId)
/teacher/materials/trash/permanent-deletePOSTBorrado fisico permanente
/teacher/materials/bulk-deletePOSTBorrar multiples items a la vez
/teacher/materials/bulk-tagPOSTAnadir tags a multiples items a la vez
// Queries
useQuery({ queryKey: ['materials', 'contents', params], queryFn: ... })
useQuery({ queryKey: ['materials', 'breadcrumb', folderId], queryFn: ... })
useQuery({ queryKey: ['materials', 'folder-tree'], queryFn: ... })
useQuery({ queryKey: ['materials', 'trash'], queryFn: ... }) // TrashSheet
useQuery({ queryKey: ['template-resources', templateId], queryFn: ... }) // TemplatePreview
useQuery({ queryKey: ['class-categories'], queryFn: ... })
useQuery({ queryKey: ['tags'], queryFn: ... })
useQuery({ queryKey: ['drive-status'], queryFn: ... })
// Mutaciones clave
useDuplicateTemplate() // POST /teacher/lesson-templates/:id/duplicate
useRestoreTrashItem() // POST /teacher/materials/trash/restore
usePermanentDeleteTrashItem() // POST /teacher/materials/trash/permanent-delete