fix(timeblocks): type errors from recurrence migration

- calendar/types: replace duplicate recurrenceRule with recurrenceDate on CalendarEvent; map it in timeBlockToCalendarEvent
- recurrence: drop stale Record casts now that LocalTimeBlock types isRecurrenceException and recurrenceDate
- todo: route recurrenceRule through TimeBlock in createTask/updateTask, load it from block in useTaskForm; accept labelIds via metadata; remove stale projectId casts
- calendar/events: include linkedBlockId/parentBlockId/recurrenceDate in createDraftEvent
- habits: drop unused db / LocalTimeBlock imports
- eslint-config: disable consistent-type-imports (parser conflict with .svelte.ts files)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-07 13:22:59 +02:00
parent af9b1f9369
commit ce04f43248
8 changed files with 26 additions and 11 deletions

View file

@ -195,7 +195,7 @@ export async function cleanupFutureInstances(templateBlockId: string): Promise<v
for (const instance of instances) {
if (instance.deletedAt) continue;
if ((instance as Record<string, unknown>).isRecurrenceException) continue;
if (instance.isRecurrenceException) continue;
if (instance.recurrenceDate && instance.recurrenceDate >= today) {
await deleteBlock(instance.id);
}
@ -255,7 +255,7 @@ export function expandTemplatesVirtually(
const existingKeys = new Set(
existingBlocks
.filter((b) => b.parentBlockId)
.map((b) => `${b.parentBlockId}|${(b as Record<string, unknown>).recurrenceDate}`)
.map((b) => `${b.parentBlockId}|${b.recurrenceDate}`)
);
const virtuals: VirtualTimeBlock[] = [];

View file

@ -377,6 +377,9 @@ export const eventsStore = {
icon: null,
isLive: false,
projectId: null,
linkedBlockId: null,
parentBlockId: null,
recurrenceDate: null,
};
return draftEvent;
},

View file

@ -59,7 +59,7 @@ export interface CalendarEvent {
projectId: string | null;
linkedBlockId: string | null;
parentBlockId: string | null;
recurrenceRule: string | null;
recurrenceDate: string | null;
}
export interface Calendar {
@ -107,5 +107,6 @@ export function timeBlockToCalendarEvent(
projectId: block.projectId,
linkedBlockId: block.linkedBlockId,
parentBlockId: block.parentBlockId,
recurrenceDate: block.recurrenceDate,
};
}

View file

@ -19,8 +19,6 @@ import {
materializeRecurringBlocks,
regenerateForBlock,
} from '$lib/data/time-blocks/recurrence';
import { db } from '$lib/data/database';
import type { LocalTimeBlock } from '$lib/data/time-blocks/types';
import type { LocalHabit, LocalHabitLog, HabitSchedule } from '../types';
export const habitsStore = {

View file

@ -47,7 +47,8 @@ export function useTaskForm() {
title = task.title;
description = task.description ?? '';
dueDate = task.dueDate ? task.dueDate.split('T')[0] : '';
// Load scheduled time from TimeBlock if scheduled
// Load scheduled time + recurrence from TimeBlock if scheduled
recurrenceRule = '';
if (task.scheduledBlockId) {
const block = await getBlock(task.scheduledBlockId);
if (block) {
@ -55,6 +56,7 @@ export function useTaskForm() {
dueTime = block.startDate.includes('T')
? block.startDate.split('T')[1]?.substring(0, 5)
: '';
recurrenceRule = block.recurrenceRule ?? '';
}
} else {
dueTime = '';
@ -63,7 +65,6 @@ export function useTaskForm() {
priority = task.priority;
status = task.status;
subtasks = task.subtasks ? [...task.subtasks] : [];
recurrenceRule = task.recurrenceRule ?? '';
effectiveDuration = task.estimatedDuration ?? null;
showDeleteConfirm = false;
isLoading = false;

View file

@ -19,7 +19,7 @@ import type {
export function toTask(local: LocalTask): Task {
return {
id: local.id,
projectId: (local as Record<string, unknown>).projectId as string | null | undefined,
projectId: local.projectId,
title: local.title,
description: local.description,
dueDate: local.dueDate,
@ -30,7 +30,6 @@ export function toTask(local: LocalTask): Task {
isCompleted: local.isCompleted,
completedAt: local.completedAt,
order: local.order,
recurrenceRule: local.recurrenceRule,
subtasks: local.subtasks ?? null,
metadata: local.metadata ?? null,
createdAt: local.createdAt ?? new Date().toISOString(),

View file

@ -21,6 +21,7 @@ export const tasksStore = {
subtasks?: Subtask[];
recurrenceRule?: string;
estimatedDuration?: number;
labelIds?: string[];
// Optional: schedule on calendar
scheduleStartDate?: string; // YYYY-MM-DD
scheduleStartTime?: string; // HH:mm
@ -49,6 +50,7 @@ export const tasksStore = {
sourceId: taskId,
title: data.title,
projectId: data.projectId ?? null,
recurrenceRule: data.recurrenceRule ?? null,
});
}
@ -62,12 +64,12 @@ export const tasksStore = {
scheduledBlockId,
estimatedDuration: data.estimatedDuration ?? null,
order: count,
recurrenceRule: data.recurrenceRule ?? null,
subtasks: data.subtasks,
metadata: data.labelIds && data.labelIds.length > 0 ? { labelIds: data.labelIds } : undefined,
};
if (data.projectId !== undefined) {
(newLocal as Record<string, unknown>).projectId = data.projectId;
newLocal.projectId = data.projectId;
}
await taskTable.add(newLocal);
@ -82,8 +84,11 @@ export const tasksStore = {
// Handle schedule changes via TimeBlock
const schedStartDate = data._scheduleStartDate as string | null | undefined;
const schedStartTime = data._scheduleStartTime as string | null | undefined;
const recurrenceRule = data.recurrenceRule as string | null | undefined;
delete data._scheduleStartDate;
delete data._scheduleStartTime;
// recurrenceRule lives on the TimeBlock — never on the task row
delete data.recurrenceRule;
if (schedStartDate !== undefined) {
if (schedStartDate) {
@ -103,6 +108,7 @@ export const tasksStore = {
endDate: endISO,
allDay: !schedStartTime,
title: (data.title as string) ?? task.title,
...(recurrenceRule !== undefined ? { recurrenceRule } : {}),
});
} else {
// Create new block
@ -116,6 +122,7 @@ export const tasksStore = {
sourceId: id,
title: (data.title as string) ?? task.title,
projectId: (data.projectId as string) ?? task.projectId ?? null,
recurrenceRule: recurrenceRule ?? null,
});
data.scheduledBlockId = blockId;
}
@ -133,6 +140,11 @@ export const tasksStore = {
await updateBlock(task.scheduledBlockId, { title: data.title as string });
}
// Update recurrence on existing TimeBlock when not also rescheduling
if (recurrenceRule !== undefined && schedStartDate === undefined && task.scheduledBlockId) {
await updateBlock(task.scheduledBlockId, { recurrenceRule });
}
await taskTable.update(id, {
...data,
updatedAt: new Date().toISOString(),

View file

@ -22,6 +22,7 @@ export const typescriptConfig = [
ecmaVersion: 2022,
sourceType: 'module',
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {