Workspace Layout
El área autenticada (dashboard) tiene su propio layout con sidebar, top bar y breadcrumbs. Desde el refactor a rutas dinámicas, cada workspace se representa con una URL propia: /[locale]/workspaces/[workspaceId]/....
Estructura
Sección titulada «Estructura»apps/web/app/[locale]/workspaces/├── page.tsx ← Listado de workspaces└── [workspaceId]/ ├── layout.tsx ← Layout del workspace (validación + UI) ├── home/page.tsx ← Dashboard principal ├── kanban/page.tsx ← Tablero Kanban (primer board, funcional con DnD) ├── kanban/[boardId]/page.tsx ← Tablero Kanban de un board concreto ├── backlog/page.tsx ← Backlog del workspace (tareas sin columna) ├── calendar/page.tsx ← Calendario: tabs Equipo / Mi tiempo (time tracking) ├── teams/page.tsx ← Equipos (miembros, roles y estadísticas) ├── chat/page.tsx ← Chat (placeholder) ├── settings/page.tsx ← Ajustes del workspace (nombre, descripción, URL) └── profile/ ← Perfil dentro del contexto del workspace ├── page.tsx ← Perfil propio del usuario ├── edit/page.tsx ← Edición del perfil └── [id]/page.tsx ← Perfil público de otro usuarioLayout dinámico (app/[locale]/workspaces/[workspaceId]/layout.tsx)
Sección titulada «Layout dinámico (app/[locale]/workspaces/[workspaceId]/layout.tsx)»Server Component que:
- Lee
params.workspaceId. - Busca el usuario autenticado y sus membresías.
- Verifica que el usuario pertenezca al workspace solicitado.
- Redirige a
/[locale]/workspacessi no es miembro. - Renderiza
Sidebar+TopBar+{children}.
export default async function WorkspaceLayout({ children, params,}: { children: ReactNode; params: Promise<{ workspaceId: string }>;}) { const { workspaceId } = await params; // validación de membresía...}Listado de workspaces (app/[locale]/workspaces/page.tsx)
Sección titulada «Listado de workspaces (app/[locale]/workspaces/page.tsx)»Página pública para usuarios autenticados. Muestra las tarjetas de cada workspace y permite navegar al workspace seleccionado.
/es/workspaces/en/workspacesCrear workspace desde la UI (SO-06)
Sección titulada «Crear workspace desde la UI (SO-06)»Cuando el usuario no tiene workspaces, se muestra un estado vacío con un botón “Crear nuevo workspace”. También hay un botón al final del grid cuando ya existen workspaces.
Ambos botones abren un modal (components/workspace/CreateWorkspaceModal.tsx) con un formulario que pide:
- Nombre (requerido)
- Descripción (opcional)
Al enviar, la server action createWorkspaceAction en server/workspace/actions/workspace-actions.ts:
- Valida los datos con Zod
- Genera un slug único a partir del nombre
- Crea el workspace y al usuario como OWNER
- Redirige al dashboard del nuevo workspace
Componentes involucrados:
| Archivo | Propósito |
|---|---|
components/workspace/CreateWorkspaceModal.tsx |
Modal con formulario, dos variantes: empty (botón inline) y grid (tarjeta dashed) |
server/workspace/actions/workspace-actions.ts |
Server action createWorkspaceAction con validación Zod |
Datos de prueba
Sección titulada «Datos de prueba»Para desarrollo local, ejecutar el seeder de Prisma (desde la raíz del repositorio):
cd syncro/apps/webnpx prisma migrate deploynpx prisma db seedEsto crea:
- 5 usuarios (todos con contraseña
demo1234):demo@syncro.app,maria@syncro.app,carlos@syncro.app,ana@syncro.appypedro@syncro.app - Workspace “Proyecto Alpha”: con tablero (Por hacer → Completado), 9 tareas y 16 tareas de backlog
- Workspace “Diseño UX”: con tablero (Descubrimiento → Entregado), 6 tareas y 12 tareas de backlog
- Workspace “Marketing Digital”: con tablero (Pendiente → Publicado), 8 tareas y 12 tareas de backlog
- 40 tareas de backlog en total, con las 4 prioridades, asignadas y sin asignar, y descripciones variadas
Los usuarios y workspaces no se duplican al re-ejecutar (upsert por email/slug). Para resetear datos: npx prisma migrate reset.
Ver documentación completa del seeder en Seeder.
Sidebar (apps/web/components/layout/Sidebar.tsx)
Sección titulada «Sidebar (apps/web/components/layout/Sidebar.tsx)»Barra lateral de navegación con las siguientes características:
- Colapsable — Botón para colapsar/expandir (icono
ChevronLeft/ChevronRight) - Iconos + texto — Cada item muestra un icono de lucide-react y su label
- Indicador activo — El item de la ruta actual se resalta con color primary
- Responsive — En mobile se oculta y se muestra como overlay con hamburger button
- Links dinámicos — Cada link incluye el
workspaceId:/workspaces/[workspaceId]/home - Animación — Transición suave de 300ms al colapsar
Nav items
Sección titulada «Nav items»| Ruta | Icono | Descripción |
|---|---|---|
/home |
LayoutDashboard | Inicio del dashboard |
/kanban |
Columns3 | Tablero Kanban |
/calendar |
Calendar | Calendario y horas |
/teams |
Users | Gestión de equipos |
/chat |
MessageSquareText | Chat |
/settings |
Settings | Ajustes |
Sección “Boards” de la sidebar
Sección titulada «Sección “Boards” de la sidebar»Bajo el nav principal, la sidebar lista los boards del workspace con CRUD inline:
- Crear (
+) →createBoardAction, redirige al nuevo board - Renombrar (lápiz) → input inline →
renameBoardAction - Eliminar (papelera) →
deleteBoardActioncon confirmación - Cada board enlaza a
/kanban/[boardId]y se resalta el activo
Requiere listBoardsAction para cargar el listado inicial.
TopBar (apps/web/components/layout/TopBar.tsx)
Sección titulada «TopBar (apps/web/components/layout/TopBar.tsx)»Barra superior con:
- Breadcrumb del workspace activo:
Workspaces / Nombre del workspace - Toggle de idioma
- Toggle de tema
- Botón “Invitar” con dropdown: copiar enlace del workspace + placeholder para invitar miembros
- Menú de usuario
Nota: El componente
WorkspaceSelectorfue eliminado. Ahora el selector de workspaces está integrado en laTopBarcomo un dropdown inline en el breadcrumb (workspaces / {nombre} ▾).
Breadcrumbs (apps/web/components/layout/Breadcrumbs.tsx)
Sección titulada «Breadcrumbs (apps/web/components/layout/Breadcrumbs.tsx)»Navegación jerárquica que ignora el prefijo workspaces/[workspaceId] y muestra solo los segmentos de página (home, kanban, settings, etc.).
WelcomeBanner (apps/web/components/dashboard/WelcomeBanner.tsx)
Sección titulada «WelcomeBanner (apps/web/components/dashboard/WelcomeBanner.tsx)»Hero del dashboard con:
- Saludo personalizado: “Bienvenido, {nombre}!”
- Badge de estado de la aplicación
- Subtítulo informativo
RoadmapSection (apps/web/components/dashboard/RoadmapSection.tsx)
Sección titulada «RoadmapSection (apps/web/components/dashboard/RoadmapSection.tsx)»Grid de funcionalidades planificadas con:
RoadmapCard— Tarjeta con icono, título, descripción y badge de estadoBadge— Indicador visual del estado (planned/building/ready)
Páginas del workspace
Sección titulada «Páginas del workspace»Solo chat sigue siendo un placeholder (WorkspacePlaceholder). El resto son funcionales:
Kanban (kanban/page.tsx y kanban/[boardId]/page.tsx)
Sección titulada «Kanban (kanban/page.tsx y kanban/[boardId]/page.tsx)»Tablero Kanban completo con:
- Drag & drop de tareas entre columnas con
@dnd-kity optimistic updates - CRUD de columnas: crear, renombrar inline, selector de colores, eliminar (mín. 3, máx. 7)
- CRUD de boards desde la sidebar
- Toolbar: búsqueda, filtros de prioridad/asignado, ancho, densidad, tamaño de letra
- Prioridades, asignaciones con avatares, descripciones markdown y actividad
Ver documentación completa en Kanban Board y Backend del Kanban.
Backlog (backlog/page.tsx)
Sección titulada «Backlog (backlog/page.tsx)»- Lista las tareas del workspace sin columna (
columnId: null) - CRUD de tareas de backlog con
TaskFormDialog, ordenación por prioridad/asignado y búsqueda - Mover tareas al tablero desde
AddBacklogTaskDialog
Calendario (calendar/page.tsx)
Sección titulada «Calendario (calendar/page.tsx)»- Dos tabs: Equipo (grid mensual con tareas programadas y horas por día) y Mi tiempo (imputación semanal)
- Time tracking:
TimeEntry,estimatedHoursy métricas de desviación
Teams (teams/page.tsx)
Sección titulada «Teams (teams/page.tsx)»Gestión de miembros del workspace:
- Lista con avatar, nombre, email y estadísticas (
tasksCreated,tasksAssigned) - Roles editables por el OWNER (
RoleBadgeSelect): OWNER / ADMIN / MEMBER - Añadir miembro por email (
addMemberAction) con selector de rol, solo OWNER/ADMIN - Eliminar miembro con confirmación, solo OWNER/ADMIN
- Leyenda de roles (crown/shield/user)
Settings (settings/page.tsx)
Sección titulada «Settings (settings/page.tsx)»- Formulario para editar nombre y descripción del workspace
- URL del workspace (slug) visible pero no editable
- Botón “Copiar enlace” que copia
/{locale}/workspaces/{workspaceId}/homeal portapapeles con feedback visual - Sección de configuración de equipo (rol por defecto y toggle de invitaciones)
- Eliminar workspace: solo para rol OWNER, tipo “ELIMINAR” para confirmar (componente
DeleteWorkspaceDialog) - Sección de “Zona de peligro” con acciones irreversibles
Copiar enlace del workspace
Sección titulada «Copiar enlace del workspace»Disponible en dos lugares:
- Settings: botón inline junto a la URL del workspace
- TopBar: menú desplegable al hacer clic en “Invitar” → opción “Copiar enlace” con feedback de 2 segundos
Perfil dentro del workspace
Sección titulada «Perfil dentro del workspace»Las páginas de perfil viven bajo el layout del workspace para mantener la UI consistente (sidebar, topbar, breadcrumbs), pero los datos del perfil pertenecen al usuario, no al workspace.
profile/page.tsx— Muestra la información del usuario logueado.profile/edit/page.tsx— Formulario para editar el perfil. La server actioneditProfileActionactualiza el usuario globalmente y luego revalida la ruta del perfil.profile/[id]/page.tsx— Vista pública del perfil de otro usuario.
Estado del workspace en la URL
Sección titulada «Estado del workspace en la URL»Antes del refactor, el workspace activo se guardaba en una cookie (currentWorkspace) y el layout lo leía para saber en qué workspace estaba el usuario. Esto causaba problemas al abrir varias pestañas.
Después del refactor, el workspaceId forma parte de la URL. Esto permite:
- Compartir URLs directas a un workspace.
- Trabajar en varios workspaces simultáneamente sin conflictos.
- Simplificar el layout: ya no necesita leer cookies.
Archivos clave
Sección titulada «Archivos clave»| Archivo | Propósito |
|---|---|
app/[locale]/workspaces/page.tsx |
Listado de workspaces |
app/[locale]/workspaces/[workspaceId]/layout.tsx |
Layout dinámico con validación |
components/layout/Sidebar.tsx |
Barra lateral colapsable y responsive |
components/layout/TopBar.tsx |
Barra superior con breadcrumbs |
components/layout/Breadcrumbs.tsx |
Migas de pan |
components/workspace/CreateWorkspaceModal.tsx |
Modal de creación de workspace |
components/workspace/DeleteWorkspaceDialog.tsx |
Diálogo de eliminación con type-to-confirm |
components/kanban/KanbanBoard.tsx |
Tablero Kanban con DnD |
components/kanban/KanbanToolbar.tsx |
Barra de filtros y controles de vista |
components/kanban/KanbanColumn.tsx |
Columna dropeable del Kanban |
components/kanban/TaskCard.tsx |
Card de tarea sortable |
components/tasks/TaskFormDialog.tsx |
Modal de crear/editar tareas |
components/kanban/ConfirmDialog.tsx |
Modal de confirmación reutilizable |
components/kanban/TaskActivityDialog.tsx |
Historial de actividad de una tarea |
components/workspace/MemberList.tsx |
Lista de miembros con roles y stats |
components/calendar/CalendarGrid.tsx |
Grid mensual del equipo |
components/calendar/MyTimeView.tsx |
Vista personal de imputación de horas |
components/layout/WorkspacePlaceholder.tsx |
Placeholder para páginas en construcción |
components/dashboard/WelcomeBanner.tsx |
Hero del dashboard |
components/dashboard/RoadmapSection.tsx |
Grid de features planificadas |
components/dashboard/RoadmapCard.tsx |
Tarjeta individual del roadmap |
components/ui/Badge.tsx |
Etiqueta de estado |