Server actions para la gestión del tablero Kanban, columnas y tareas. Siguen el patrón de Clean Architecture: Action → Service → Repository .
Capa
Archivo
Action
server/board/actions/board-actions.ts
Service
server/board/service/board-service.ts
Repository
server/board/repository/board-repository.ts
Action
Descripción
getBoardAction(workspaceId)
Obtiene el primer board del workspace con columnas + tareas + asignados. Devuelve null si no es miembro
getBoardByIdAction(boardId)
Obtiene un board concreto por ID (usado en /kanban/[boardId]). Verifica membresía
listBoardsAction(workspaceId)
Lista los boards del workspace (para la sección “Boards” de la sidebar)
createBoardAction(workspaceId, locale)
Crea un board con 3 columnas por defecto (To Do, In Progress, Done). Verifica membresía
renameBoardAction(boardId, name)
Renombra un board. Valida nombre 1-50 caracteres
deleteBoardAction(boardId)
Elimina un board y devuelve { workspaceId, remainingCount }
El board se obtiene con eager loading de toda la jerarquía:
Board → Column[] → Task[] → TaskAssignee[] → User
→ Activities (TaskActivity[])
Al crear un board, se generan automáticamente 3 columnas con colores por defecto.
import { createBoardAction } from " @/server/board/actions/board-actions " ;
const result = await createBoardAction (workspaceId , locale);
// Board creado con columnas: To Do, In Progress, Done
` / ${ locale } /workspaces/ ${ workspaceId } /kanban/ ${ result . board . id } ` ,
Nota: Esta acción se usa desde el empty state del KanbanBoard (cuando un workspace no tiene ningún board) y desde el botón + de la sección “Boards” de la sidebar.
Capa
Archivo
Action
server/column/actions/column-actions.ts
Service
server/column/service/column-service.ts
Repository
server/column/repository/column-repository.ts
Action
Descripción
createColumnAction(boardId, workspaceId, name)
Crea columna. Valida máximo 7 columnas. Asigna color y posición automáticos
updateColumnNameAction(columnId, workspaceId, name)
Renombra columna
updateColumnColorAction(columnId, workspaceId, color)
Cambia color del header
deleteColumnAction(columnId, boardId, workspaceId)
Elimina columna. Valida mínimo 3 columnas. Normaliza posiciones
reorderColumnsAction(columns[], workspaceId)
Reordena columnas (reservado para futuro)
Regla
Implementación
Máximo 7 columnas
column-service.ts: validateCanAddColumn()
Mínimo 3 columnas
column-actions.ts: deleteColumnAction()
Solo miembros del workspace
Action verifica membresía con findUserWithProfile
Capa
Archivo
Action
server/task/actions/task-actions.ts
Service
server/task/service/task-service.ts
Repository
server/task/repository/task-repository.ts
Action
Descripción
createTaskAction(columnId, workspaceId, data)
Crea tarea con validación Zod. Asigna posición automática. Asigna usuario si se provee
updateTaskAction(taskId, workspaceId, data)
Actualiza título, descripción, prioridad, dueDate, startDate y estimatedHours
moveTaskAction(taskId, newColumnId, newPosition, workspaceId)
Mueve tarea entre columnas. Normaliza posiciones en origen y destino
moveTaskToBacklogAction(taskId, workspaceId)
Mueve una tarea al backlog (columnId = null)
deleteTaskAction(taskId, workspaceId)
Elimina tarea. Normaliza posiciones de la columna
toggleAssigneeAction(taskId, userId, workspaceId)
Asigna/desasigna usuario (toggle)
Regla
Implementación
Título requerido (1-200 chars)
Zod schema en createTaskAction
Descripción opcional (max 2000)
Zod schema en createTaskAction
Prioridad: LOW | MEDIUM | HIGH | URGENT
Tipo TaskPriority de Prisma + Zod enum
estimatedHours positivo
Zod schema en createTaskAction / updateTaskAction
Solo miembros del workspace
Action verifica membresía
Las tareas usan un campo position: Float reordenado como enteros consecutivos (0, 1, 2, ...). Cada vez que se mueve o elimina una tarea, se normalizan las posiciones de la columna afectada. Una tarea en backlog tiene columnId: null y position: 0.
Cada cambio en una tarea registra un TaskActivity (enum TaskAction: TASK_CREATED, TASK_UPDATED, TASK_MOVED, TASK_ASSIGNED, TASK_UNASSIGNED, TASK_DELETED). Con fieldName/oldValue/newValue se puede mostrar el cambio exacto (“prioridad: MEDIUM → HIGH”).
Dónde vive : server/task/service/task-service.ts registra el historial dentro de las mismas operaciones.
Cómo se lee : getBoardByIdWithDetails incluye activities de cada tarea para el TaskActivityDialog del frontend.
Action
Archivo
Descripción
getWorkspaceMembersAction(workspaceId)
server/workspace/actions/workspace-actions.ts
Lista miembros con rol para asignaciones
deleteWorkspaceAction(workspaceId, locale)
server/workspace/actions/workspace-actions.ts
Elimina workspace. Solo rol OWNER
Todas las server actions siguen esta estructura:
export async function ejemploAction ( param : string ) {
const userId = await requireAuthedUserId ();
const user = await findUserWithProfile (userId);
const isMember = user ?. memberships . some (
( m ) => m . workspace . id === workspaceId ,
if ( ! isMember) return { success: false , error: " NOT_MEMBER " };
revalidatePath ( " / " , " layout " );
return { success: true , data };
return { success: false , error: " ERROR_CODE " };
Validación de membresía en cada action (no hay middlewares)
Optimistic updates en el cliente: la UI se actualiza antes de la respuesta del servidor
revalidatePath para mantener el cache de Next.js sincronizado
Manejo de errores con códigos tipados (CreateColumnResult, UpdateColumnResult, etc.)