Ir al contenido

Configurar Recurrencia

Configura tareas para que se generen automáticamente según un patrón de repetición.

TipoDescripciónConfiguración
DiariaRepetir cada N díasIntervalo: 1-365 días
SemanalRepetir cada N semanas en días específicosIntervalo: 1-52 semanas, Días: Lun-Dom
MensualRepetir cada N mesesIntervalo: 1-12 meses
AnualRepetir cada N añosIntervalo: 1-10 años

Fecha de Finalización: Define cuándo dejar de generar nuevas instancias. Opcional.

Máximo de Ocurrencias: Limita cuántas instancias se generarán en total. Opcional.

Estado Activo/Pausado:

  • Activa: Genera nuevas instancias automáticamente
  • Pausada: Mantiene configuración pero no genera instancias

Las nuevas instancias de tareas se generan automáticamente:

  • Frecuencia: Diariamente a las 00:00 UTC
  • Mecanismo: Edge Function generate-recurring-tasks
  • Criterios: Solo tareas con recurrencia activa que cumplan condiciones

Al Crear Tarea:

  1. Abrir formulario de nueva tarea
  2. Ir a pestaña “Recurrencia”
  3. Activar switch “Hacer recurrente”
  4. Configurar parámetros
  5. Ver vista previa de próximas fechas
  6. Guardar

Al Editar Tarea:

  1. Abrir tarea existente
  2. Click en “Editar”
  3. Ir a pestaña “Recurrencia”
  4. Modificar configuración
  5. Guardar cambios

Desde Vista Tareas Recurrentes:

  1. Navegar a pestaña “Recurrentes”
  2. Ver todas las tareas con recurrencia
  3. Click en “Pausar” o “Reanudar” para cambiar estado
  4. Expandir para ver próximas fechas
Detalles Técnicos
  • Archivo: TaskRecurrenceConfig.tsx
  • Ruta: src/features/projects/sub-modules/tasks/components/TaskRecurrenceConfig.tsx
type RecurrenceType = 'daily' | 'weekly' | 'monthly' | 'yearly';
interface TaskRecurrence {
id: string;
task_id: string;
recurrence_type: RecurrenceType;
recurrence_interval: number;
recurrence_days: number[] | null; // 0=Domingo, 1=Lunes, etc.
recurrence_end_date: string | null;
max_occurrences: number | null;
last_generated_date: string | null;
is_active: boolean;
created_at: string;
updated_at: string;
}

Diaria:

{
recurrence_type: 'daily',
recurrence_interval: 1, // cada 1 día
recurrence_days: null,
recurrence_end_date: '2024-12-31',
max_occurrences: null
}

Semanal:

{
recurrence_type: 'weekly',
recurrence_interval: 2, // cada 2 semanas
recurrence_days: [1, 3, 5], // Lunes, Miércoles, Viernes
recurrence_end_date: null,
max_occurrences: 20
}

Mensual:

{
recurrence_type: 'monthly',
recurrence_interval: 1, // cada mes
recurrence_days: null,
recurrence_end_date: null,
max_occurrences: 12
}
const saveMutation = useMutation({
mutationFn: async () => {
const configData = {
task_id: taskId,
recurrence_type: recurrenceType,
recurrence_interval: interval,
recurrence_days: recurrenceType === 'weekly' ? selectedDays : null,
recurrence_end_date: endDate || null,
max_occurrences: maxOccurrences || null,
is_active: isActive,
};
if (existingConfig) {
// Actualizar
await supabase
.from('task_recurrence_config')
.update(configData)
.eq('id', existingConfig.id);
} else {
// Crear
await supabase
.from('task_recurrence_config')
.insert(configData);
}
}
});
// Ejecutada diariamente por Supabase Cron
// Genera nuevas instancias de tareas recurrentes
const generateRecurringTasks = async () => {
// 1. Obtener configuraciones activas
const { data: configs } = await supabase
.from('task_recurrence_config')
.select('*, task:tasks(*)')
.eq('is_active', true);
for (const config of configs) {
// 2. Calcular próxima fecha según tipo
const nextDate = calculateNextOccurrence(config);
// 3. Verificar condiciones
if (shouldGenerate(config, nextDate)) {
// 4. Crear nueva instancia
await supabase.from('tasks').insert({
...config.task,
id: undefined, // Nuevo ID
due_date: nextDate,
status: 'pending',
completed_at: null,
progress: 0
});
// 5. Actualizar last_generated_date
await supabase
.from('task_recurrence_config')
.update({ last_generated_date: nextDate })
.eq('id', config.id);
}
}
};

Muestra vista previa de próximas fechas:

export function RecurrencePreview({ config, count = 5 }) {
const nextOccurrences = calculateNextOccurrences(config, count);
return (
<div className="space-y-2">
<p className="text-sm font-medium">Próximas {count} fechas:</p>
<ul className="space-y-1">
{nextOccurrences.map((date, i) => (
<li key={i} className="text-sm text-muted-foreground">
{format(date, "PPP", { locale: es })}
</li>
))}
</ul>
</div>
);
}
CREATE TABLE task_recurrence_config (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
recurrence_type VARCHAR(20) NOT NULL,
recurrence_interval INTEGER NOT NULL,
recurrence_days INTEGER[],
recurrence_end_date TIMESTAMPTZ,
max_occurrences INTEGER,
last_generated_date TIMESTAMPTZ,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(task_id)
);
const WEEKDAYS = [
{ label: 'Lun', value: 1 },
{ label: 'Mar', value: 2 },
{ label: 'Mié', value: 3 },
{ label: 'Jue', value: 4 },
{ label: 'Vie', value: 5 },
{ label: 'Sáb', value: 6 },
{ label: 'Dom', value: 0 },
];