{session.user.image && (
-

)}
diff --git a/src/components/tasks/BoardView/BoardTask.tsx b/src/components/tasks/BoardView/BoardTask.tsx
index 9737c61..f2ebba9 100644
--- a/src/components/tasks/BoardView/BoardTask.tsx
+++ b/src/components/tasks/BoardView/BoardTask.tsx
@@ -1,6 +1,6 @@
"use client";
-import { Task, EnergyLevel, TimePreference } from "@/types/task";
+import { Task, TimePreference } from "@/types/task";
import { useDraggable } from "@dnd-kit/core";
import { HiPencil, HiTrash, HiClock, HiLockClosed } from "react-icons/hi";
import { format, isToday, isTomorrow, isThisWeek, isThisYear } from "date-fns";
@@ -9,7 +9,6 @@ interface BoardTaskProps {
task: Task;
onEdit: (task: Task) => void;
onDelete: (taskId: string) => void;
- onInlineEdit: (task: Task) => void;
}
const energyLevelColors = {
@@ -65,7 +64,6 @@ export function BoardTask({
task,
onEdit,
onDelete,
- onInlineEdit,
}: BoardTaskProps) {
const { attributes, listeners, setNodeRef, transform, isDragging } =
useDraggable({
diff --git a/src/components/tasks/BoardView/BoardView.tsx b/src/components/tasks/BoardView/BoardView.tsx
index 1c720f4..e5a0a90 100644
--- a/src/components/tasks/BoardView/BoardView.tsx
+++ b/src/components/tasks/BoardView/BoardView.tsx
@@ -5,14 +5,13 @@ import { useProjectStore } from "@/store/project";
import { useTaskListViewSettings } from "@/store/taskListViewSettings";
import { useMemo } from "react";
import { Column } from "./Column";
-import { DndContext, DragEndEvent, DragOverEvent } from "@dnd-kit/core";
+import { DndContext, DragEndEvent} from "@dnd-kit/core";
interface BoardViewProps {
tasks: Task[];
onEdit: (task: Task) => void;
onDelete: (taskId: string) => void;
onStatusChange: (taskId: string, status: TaskStatus) => void;
- onInlineEdit: (task: Task) => void;
}
export function BoardView({
@@ -20,7 +19,6 @@ export function BoardView({
onEdit,
onDelete,
onStatusChange,
- onInlineEdit,
}: BoardViewProps) {
const { activeProject } = useProjectStore();
const { energyLevel, timePreference, tagIds, search } =
@@ -107,7 +105,6 @@ export function BoardView({
tasks={columns[status]}
onEdit={onEdit}
onDelete={onDelete}
- onInlineEdit={onInlineEdit}
/>
))}
diff --git a/src/components/tasks/BoardView/Column.tsx b/src/components/tasks/BoardView/Column.tsx
index 3b6acd9..f7403c3 100644
--- a/src/components/tasks/BoardView/Column.tsx
+++ b/src/components/tasks/BoardView/Column.tsx
@@ -9,7 +9,6 @@ interface ColumnProps {
tasks: Task[];
onEdit: (task: Task) => void;
onDelete: (taskId: string) => void;
- onInlineEdit: (task: Task) => void;
}
const statusColors = {
@@ -38,7 +37,6 @@ export function Column({
tasks,
onEdit,
onDelete,
- onInlineEdit,
}: ColumnProps) {
const { setNodeRef, isOver } = useDroppable({
id: status,
@@ -71,7 +69,6 @@ export function Column({
task={task}
onEdit={onEdit}
onDelete={onDelete}
- onInlineEdit={onInlineEdit}
/>
))}
diff --git a/src/components/tasks/TaskList.tsx b/src/components/tasks/TaskList.tsx
index 4699afc..66fb1a9 100644
--- a/src/components/tasks/TaskList.tsx
+++ b/src/components/tasks/TaskList.tsx
@@ -201,6 +201,7 @@ function StatusFilter({
interface EditableCellProps {
task: Task;
field: keyof Task;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
onSave: (task: Task) => void;
}
@@ -488,7 +489,7 @@ function EditableCell({ task, field, value, onSave }: EditableCellProps) {
const utcDate = new Date(
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())
);
- onSave({ ...task, [field]: utcDate.toISOString() });
+ onSave({ ...task, [field]: utcDate });
} else {
onSave({ ...task, [field]: undefined });
}
@@ -496,7 +497,7 @@ function EditableCell({ task, field, value, onSave }: EditableCellProps) {
}}
onClickOutside={() => setIsEditing(false)}
open={isEditing}
- onInputClick={(e: React.MouseEvent) => e.stopPropagation()}
+ onInputClick={() => {}}
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"
dateFormat="yyyy-MM-dd"
isClearable
@@ -805,7 +806,7 @@ export function TaskList({
setFilters,
resetFilters,
} = useTaskListViewSettings();
- const { activeProject, projects } = useProjectStore();
+ const { activeProject } = useProjectStore();
const handleSort = (column: typeof sortBy) => {
if (sortBy === column) {
diff --git a/src/components/tasks/TaskModal.tsx b/src/components/tasks/TaskModal.tsx
index b7090ab..25e7974 100644
--- a/src/components/tasks/TaskModal.tsx
+++ b/src/components/tasks/TaskModal.tsx
@@ -11,8 +11,7 @@ import {
Tag,
} from "@/types/task";
import { useProjectStore } from "@/store/project";
-import { Project } from "@/types/project";
-import { RRule, Frequency } from "rrule";
+import { RRule } from "rrule";
import { Switch } from "@/components/ui/switch";
import { format } from "date-fns";
diff --git a/src/lib/calendar/parser.ts b/src/lib/calendar/parser.ts
deleted file mode 100644
index 9951592..0000000
--- a/src/lib/calendar/parser.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import ICAL from "ical.js";
-import { zonedTimeToUtc, toZonedTime } from "date-fns-tz";
-import { v4 as uuidv4 } from "uuid";
-import { CalendarEvent, EventStatus, AttendeeStatus } from "@/types/calendar";
-
-export async function parseICalFeed(
- feedId: string,
- url: string
-): Promise
{
- try {
- const response = await fetch("/api/calendar", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ url }),
- });
-
- if (!response.ok) {
- const error = await response.json();
- throw new Error(
- error.error || `Failed to fetch calendar feed: ${response.statusText}`
- );
- }
-
- const { data: icalData } = await response.json();
- const jcalData = ICAL.parse(icalData);
- const comp = new ICAL.Component(jcalData);
- const vevents = comp.getAllSubcomponents("vevent");
-
- return vevents.map((vevent) => parseEvent(feedId, vevent));
- } catch (error) {
- console.error("Error parsing iCal feed:", error);
- throw error;
- }
-}
-
-function parseEvent(feedId: string, vevent: ICAL.Component): CalendarEvent {
- const event = new ICAL.Event(vevent);
- const timezone = event.startDate?.timezone || "UTC";
-
- const startDate = event.startDate
- ? toZonedTime(event.startDate.toJSDate(), timezone)
- : new Date();
-
- const endDate = event.endDate
- ? toZonedTime(event.endDate.toJSDate(), timezone)
- : new Date(startDate.getTime() + 3600000); // Default 1 hour duration
-
- const attendees = vevent.getAllProperties("attendee").map((attendee) => ({
- name: attendee.getParameter("cn"),
- email: attendee.getFirstValue(),
- status: parseAttendeeStatus(attendee.getParameter("partstat")),
- }));
-
- const organizer = vevent.getFirstProperty("organizer");
- const organizerInfo = organizer
- ? {
- name: organizer.getParameter("cn"),
- email: organizer.getFirstValue(),
- }
- : undefined;
-
- return {
- id: uuidv4(), // Generate a unique ID for the event
- feedId,
- uid: event.uid,
- title: event.summary || "Untitled Event",
- description: event.description,
- start: startDate,
- end: endDate,
- location: event.location,
- isRecurring: !!event.recurrenceId,
- recurrenceRule: event.recurrenceRule?.toString(),
- allDay: event.startDate?.isDate || false, // isDate true means it's an all-day event
- status: parseEventStatus(vevent.getFirstPropertyValue("status")),
- created: event.created?.toJSDate(),
- lastModified: event.lastModified?.toJSDate(),
- sequence: event.sequence,
- organizer: organizerInfo,
- attendees,
- };
-}
-
-function parseEventStatus(status?: string): EventStatus | undefined {
- if (!status) return undefined;
-
- switch (status.toUpperCase()) {
- case "CONFIRMED":
- return EventStatus.CONFIRMED;
- case "TENTATIVE":
- return EventStatus.TENTATIVE;
- case "CANCELLED":
- return EventStatus.CANCELLED;
- default:
- return undefined;
- }
-}
-
-function parseAttendeeStatus(status?: string): AttendeeStatus | undefined {
- if (!status) return undefined;
-
- switch (status.toUpperCase()) {
- case "ACCEPTED":
- return AttendeeStatus.ACCEPTED;
- case "TENTATIVE":
- return AttendeeStatus.TENTATIVE;
- case "DECLINED":
- return AttendeeStatus.DECLINED;
- case "NEEDS-ACTION":
- return AttendeeStatus.NEEDS_ACTION;
- default:
- return undefined;
- }
-}
diff --git a/src/lib/logger.ts b/src/lib/logger.ts
index accce05..f161014 100644
--- a/src/lib/logger.ts
+++ b/src/lib/logger.ts
@@ -15,14 +15,16 @@ class Logger {
);
}
- log(message: string, data?: any) {
- if (process.env.NODE_ENV === "development" && process.env.LOG_LEVEL === "debug") {
+ log(message: string, data?: Record) {
+ if (
+ process.env.NODE_ENV === "development" &&
+ process.env.LOG_LEVEL === "debug"
+ ) {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] ${message}${
data ? "\n" + JSON.stringify(data, null, 2) : ""
}\n`;
fs.appendFileSync(this.logFile, logMessage);
-
}
}
}
diff --git a/src/services/scheduling/CalendarServiceImpl.ts b/src/services/scheduling/CalendarServiceImpl.ts
index ebf53c4..e1fa2a8 100644
--- a/src/services/scheduling/CalendarServiceImpl.ts
+++ b/src/services/scheduling/CalendarServiceImpl.ts
@@ -1,7 +1,7 @@
-import { CalendarEvent, PrismaClient, Task } from "@prisma/client";
+import { CalendarEvent, PrismaClient } from "@prisma/client";
import { TimeSlot, Conflict } from "@/types/scheduling";
import { CalendarService } from "./CalendarService";
-import { isWithinInterval, areIntervalsOverlapping } from "date-fns";
+import { areIntervalsOverlapping } from "date-fns";
import { logger } from "@/lib/logger";
export class CalendarServiceImpl implements CalendarService {
@@ -29,16 +29,15 @@ export class CalendarServiceImpl implements CalendarService {
logger.log(`[DEBUG] Found ${events.length} calendar events in range`);
if (events.length > 0) {
- logger.log(
- "[DEBUG] Calendar events:",
- events.map((e) => ({
+ logger.log("[DEBUG] Calendar events:", {
+ events: events.map((e) => ({
id: e.id,
title: e.title,
start: e.start,
end: e.end,
feedId: e.feedId,
- }))
- );
+ })),
+ });
}
for (const event of events) {
@@ -84,15 +83,14 @@ export class CalendarServiceImpl implements CalendarService {
`[DEBUG] Found ${scheduledTasks.length} scheduled tasks to check`
);
if (scheduledTasks.length > 0) {
- logger.log(
- "[DEBUG] Scheduled tasks:",
- scheduledTasks.map((t) => ({
+ logger.log("[DEBUG] Scheduled tasks:", {
+ tasks: scheduledTasks.map((t) => ({
id: t.id,
title: t.title,
start: t.scheduledStart,
end: t.scheduledEnd,
- }))
- );
+ })),
+ });
}
for (const task of scheduledTasks) {
@@ -138,7 +136,9 @@ export class CalendarServiceImpl implements CalendarService {
return [];
}
- logger.log("[DEBUG] Fetching events for calendars:", selectedCalendarIds);
+ logger.log("[DEBUG] Fetching events for calendars:", {
+ calendarIds: selectedCalendarIds,
+ });
return this.prisma.calendarEvent.findMany({
where: {
feedId: {
diff --git a/src/services/scheduling/SchedulingService.ts b/src/services/scheduling/SchedulingService.ts
index 2e28e8f..b5b75e4 100644
--- a/src/services/scheduling/SchedulingService.ts
+++ b/src/services/scheduling/SchedulingService.ts
@@ -1,6 +1,5 @@
-import { PrismaClient, Task } from "@prisma/client";
+import { PrismaClient, Task, AutoScheduleSettings } from "@prisma/client";
import { TimeSlotManagerImpl, TimeSlotManager } from "./TimeSlotManager";
-import { TaskAnalyzer } from "./TaskAnalyzer";
import { CalendarServiceImpl } from "./CalendarServiceImpl";
import { useSettingsStore } from "@/store/settings";
import { addDays } from "date-fns";
@@ -11,13 +10,11 @@ const DEFAULT_TASK_DURATION = 30; // Default duration in minutes
export class SchedulingService {
private prisma: PrismaClient;
private calendarService: CalendarServiceImpl;
- private taskAnalyzer: TaskAnalyzer;
private settings: AutoScheduleSettings | null;
constructor(settings?: AutoScheduleSettings) {
this.prisma = new PrismaClient();
this.calendarService = new CalendarServiceImpl(this.prisma);
- this.taskAnalyzer = new TaskAnalyzer();
this.settings = settings || null;
}
@@ -53,15 +50,14 @@ export class SchedulingService {
}
async scheduleMultipleTasks(tasks: Task[]): Promise {
- logger.log(
- "Starting to schedule multiple tasks",
- tasks.map((t) => ({
+ logger.log("Starting to schedule multiple tasks", {
+ tasks: tasks.map((t) => ({
id: t.id,
title: t.title,
duration: t.duration || DEFAULT_TASK_DURATION,
dueDate: t.dueDate,
- }))
- );
+ })),
+ });
// Clear existing schedules for non-locked tasks
const tasksToSchedule = tasks.filter((t) => !t.scheduleLocked);
@@ -90,15 +86,14 @@ export class SchedulingService {
if (!b.dueDate) return -1;
return a.dueDate.getTime() - b.dueDate.getTime();
});
- logger.log(
- "Sorted tasks by due date",
- sortedTasks.map((t) => ({
+ logger.log("Sorted tasks by due date", {
+ tasks: sortedTasks.map((t) => ({
id: t.id,
title: t.title,
dueDate: t.dueDate,
duration: t.duration || DEFAULT_TASK_DURATION,
- }))
- );
+ })),
+ });
const timeSlotManager = this.getTimeSlotManager();
const updatedTasks: Task[] = [];
diff --git a/src/services/scheduling/SlotScorer.ts b/src/services/scheduling/SlotScorer.ts
index 48d14b1..5dd98ff 100644
--- a/src/services/scheduling/SlotScorer.ts
+++ b/src/services/scheduling/SlotScorer.ts
@@ -1,5 +1,5 @@
import { TimeSlot, SlotScore, EnergyLevel } from "@/types/scheduling";
-import { AutoScheduleSettings, Task, CalendarEvent } from "@prisma/client";
+import { AutoScheduleSettings, Task } from "@prisma/client";
import { getEnergyLevelForTime } from "@/lib/autoSchedule";
import { differenceInMinutes, differenceInHours } from "date-fns";
import { logger } from "@/lib/logger";
diff --git a/src/services/scheduling/TaskAnalyzer.ts b/src/services/scheduling/TaskAnalyzer.ts
deleted file mode 100644
index 8a2a585..0000000
--- a/src/services/scheduling/TaskAnalyzer.ts
+++ /dev/null
@@ -1,203 +0,0 @@
-import { Task, Project } from "@prisma/client";
-import { differenceInDays, differenceInMinutes } from "date-fns";
-
-export interface TaskDependency {
- taskId: string;
- type: "hard" | "soft"; // hard = must complete before, soft = should complete before
- reason: string;
-}
-
-export interface RecurrencePattern {
- type: "daily" | "weekly" | "monthly";
- interval: number;
- daysOfWeek?: number[];
- endDate?: Date;
- occurrences?: number;
-}
-
-export interface TaskPriority {
- score: number;
- factors: {
- dueDate: number;
- importance: number;
- dependencies: number;
- userInteraction: number;
- };
-}
-
-export interface TaskAnalysis {
- priority: TaskPriority;
- estimatedDuration: number;
- dependencies: TaskDependency[];
- recurrencePattern?: RecurrencePattern;
- complexity: "low" | "medium" | "high";
-}
-
-export class TaskAnalyzer {
- constructor(private completedTasks: Task[] = []) {}
-
- async analyzeTask(task: Task, project?: Project): Promise {
- return {
- priority: await this.calculatePriority(task),
- estimatedDuration: this.estimateDuration(task),
- dependencies: await this.analyzeDependencies(task),
- recurrencePattern: this.analyzeRecurringPattern(task),
- complexity: this.assessComplexity(task),
- };
- }
-
- private async calculatePriority(task: Task): Promise {
- const factors = {
- dueDate: this.calculateDueDatePriority(task),
- importance: this.calculateImportancePriority(task),
- dependencies: await this.calculateDependencyPriority(task),
- userInteraction: this.calculateUserInteractionPriority(task),
- };
-
- // Weighted average of factors
- const weights = {
- dueDate: 2.0,
- importance: 1.5,
- dependencies: 1.0,
- userInteraction: 0.5,
- };
-
- const totalWeight = Object.values(weights).reduce((a, b) => a + b, 0);
- const weightedSum = Object.entries(factors).reduce(
- (sum, [key, value]) => sum + value * weights[key as keyof typeof weights],
- 0
- );
-
- return {
- score: weightedSum / totalWeight,
- factors,
- };
- }
-
- private calculateDueDatePriority(task: Task): number {
- if (!task.dueDate) return 0.5; // Neutral priority if no due date
-
- const daysToDeadline = differenceInDays(task.dueDate, new Date());
- if (daysToDeadline < 0) return 1; // Highest priority if overdue
-
- // Exponential increase in priority as deadline approaches
- return Math.min(1, Math.exp(-daysToDeadline / 7)); // 7 days as half-life
- }
-
- private calculateImportancePriority(task: Task): number {
- // Factors that indicate importance:
- // 1. Task has dependencies (other tasks depend on it)
- // 2. Part of a project
- // 3. Has a specific energy level requirement
- // 4. Has been rescheduled multiple times
-
- let importance = 0.5; // Start with neutral importance
-
- if (task.projectId) importance += 0.2;
- if (task.energyLevel) importance += 0.1;
- if (task.lastScheduled) importance += 0.1;
-
- return Math.min(1, importance);
- }
-
- private async calculateDependencyPriority(task: Task): Promise {
- const dependencies = await this.analyzeDependencies(task);
- if (dependencies.length === 0) return 0.5;
-
- // Higher priority if task has many dependencies or hard dependencies
- const hardDependencies = dependencies.filter(
- (d) => d.type === "hard"
- ).length;
- return Math.min(
- 1,
- 0.5 + hardDependencies * 0.2 + dependencies.length * 0.1
- );
- }
-
- private calculateUserInteractionPriority(task: Task): number {
- // Factors that indicate user interest:
- // 1. Recently viewed/edited
- // 2. Manually scheduled before
- // 3. Has detailed description/notes
- // 4. Part of an active project
-
- let interactionScore = 0.5;
-
- if (task.scheduleLocked) interactionScore += 0.2; // User cares about timing
- if (task.description) interactionScore += 0.1; // User provided details
- if (task.preferredTime) interactionScore += 0.1; // User specified preference
-
- return Math.min(1, interactionScore);
- }
-
- private estimateDuration(task: Task): number {
- if (task.duration) return task.duration; // Use explicit duration if set
-
- // Find similar completed tasks
- const similarTasks = this.completedTasks.filter(
- (t) =>
- t.projectId === task.projectId || t.energyLevel === task.energyLevel
- );
-
- if (similarTasks.length > 0) {
- // Calculate average duration of similar tasks
- const totalDuration = similarTasks.reduce((sum, t) => {
- if (t.scheduledStart && t.scheduledEnd) {
- return sum + differenceInMinutes(t.scheduledEnd, t.scheduledStart);
- }
- return sum + (t.duration || 60);
- }, 0);
- return Math.round(totalDuration / similarTasks.length);
- }
-
- return 60; // Default to 1 hour if no better estimate available
- }
-
- private async analyzeDependencies(task: Task): Promise {
- const dependencies: TaskDependency[] = [];
-
- // For now, return empty array
- // This will be implemented when we add task dependency features
-
- return dependencies;
- }
-
- private analyzeRecurringPattern(task: Task): RecurrencePattern | undefined {
- if (!task.isRecurring || !task.recurrenceRule) return undefined;
-
- // Basic pattern detection - will be enhanced later
- if (task.recurrenceRule.includes("FREQ=DAILY")) {
- return {
- type: "daily",
- interval: 1,
- };
- }
-
- if (task.recurrenceRule.includes("FREQ=WEEKLY")) {
- return {
- type: "weekly",
- interval: 1,
- daysOfWeek: [task.scheduledStart?.getDay() || 0],
- };
- }
-
- return undefined;
- }
-
- private assessComplexity(task: Task): "low" | "medium" | "high" {
- let complexityScore = 0;
-
- // Factors that indicate complexity:
- if (task.duration && task.duration > 120) complexityScore++; // Long duration
- if (task.description?.length || 0 > 100) complexityScore++; // Detailed description
- if (task.projectId) complexityScore++; // Part of a project
- if (task.energyLevel === "high") complexityScore++; // Requires high energy
- if (task.isRecurring) complexityScore++; // Recurring task
-
- return complexityScore <= 1
- ? "low"
- : complexityScore <= 3
- ? "medium"
- : "high";
- }
-}
diff --git a/src/services/scheduling/TimeSlotManager.ts b/src/services/scheduling/TimeSlotManager.ts
index 442bb9c..46c46e4 100644
--- a/src/services/scheduling/TimeSlotManager.ts
+++ b/src/services/scheduling/TimeSlotManager.ts
@@ -6,7 +6,6 @@ import {
isWithinInterval,
setHours,
setMinutes,
- getHours,
getDay,
differenceInHours,
} from "date-fns";
@@ -104,14 +103,13 @@ export class TimeSlotManagerImpl implements TimeSlotManager {
logger.log(`[DEBUG] Found ${availableSlots.length} available slots`);
if (availableSlots.length > 0) {
- logger.log(
- "[DEBUG] Available slots:",
- availableSlots.map((slot) => ({
+ logger.log("[DEBUG] Available slots:", {
+ slots: availableSlots.map((slot) => ({
start: slot.start,
end: slot.end,
score: this.scoreSlot(slot),
- }))
- );
+ })),
+ });
}
// 4. Apply buffer times
@@ -130,9 +128,8 @@ export class TimeSlotManagerImpl implements TimeSlotManager {
return false;
}
- // Check for calendar conflicts with a dummy task
- const dummyTask = { id: "dummy" } as Task;
- const conflicts = await this.findConflicts(slot, dummyTask);
+ // Check for calendar conflicts
+ const conflicts = await this.findCalendarConflicts(slot);
return conflicts.length === 0;
}
@@ -283,7 +280,7 @@ export class TimeSlotManagerImpl implements TimeSlotManager {
);
}
- private async findConflicts(slot: TimeSlot, task: Task): Promise {
+ private async findCalendarConflicts(slot: TimeSlot): Promise {
const selectedCalendars = parseSelectedCalendars(
this.settings.selectedCalendars
);
@@ -293,7 +290,9 @@ export class TimeSlotManagerImpl implements TimeSlotManager {
return [];
}
- logger.log("[DEBUG] Checking conflicts with calendars:", selectedCalendars);
+ logger.log("[DEBUG] Checking conflicts with calendars:", {
+ calendarIds: selectedCalendars,
+ });
return this.calendarService.findConflicts(slot, selectedCalendars);
}
@@ -314,7 +313,7 @@ export class TimeSlotManagerImpl implements TimeSlotManager {
});
for (const slot of slots) {
- const conflicts = await this.findConflicts(slot, task);
+ const conflicts = await this.findCalendarConflicts(slot);
// Check for conflicts with other scheduled tasks
const hasTaskConflict = scheduledTasks.some(
@@ -335,11 +334,11 @@ export class TimeSlotManagerImpl implements TimeSlotManager {
...(hasTaskConflict
? [
{
- type: "task",
+ type: "task" as const,
start: slot.start,
end: slot.end,
title: "Conflict with another scheduled task",
- source: { type: "task", id: "conflict" },
+ source: { type: "task" as const, id: "conflict" },
},
]
: []),
@@ -350,10 +349,17 @@ export class TimeSlotManagerImpl implements TimeSlotManager {
return availableSlots;
}
+ // TODO: Buffer time implementation needs improvement:
+ // 1. Currently only checks if buffers fit within work hours but doesn't prevent scheduling in buffer times
+ // 2. Should check for conflicts during buffer periods
+ // 3. Consider adjusting slot times to include the buffers
+ // 4. Could factor buffer availability into slot scoring
private applyBufferTimes(slots: TimeSlot[]): TimeSlot[] {
return slots.map((slot) => {
const { beforeBuffer, afterBuffer } = this.calculateBufferTimes(slot);
- slot.hasBufferTime = true;
+ // Only mark as having buffer time if both buffers are within work hours
+ slot.hasBufferTime =
+ beforeBuffer.isWithinWorkHours && afterBuffer.isWithinWorkHours;
return slot;
});
}
diff --git a/src/store/calendar.ts b/src/store/calendar.ts
index 7cc1378..f1f54bb 100644
--- a/src/store/calendar.ts
+++ b/src/store/calendar.ts
@@ -9,7 +9,6 @@ import {
CalendarView,
CalendarViewState,
} from "@/types/calendar";
-import { parseICalFeed } from "@/lib/calendar/parser";
import { CalendarType } from "@/lib/calendar/init";
import { useTaskStore } from "@/store/task";
import { useSettingsStore } from "@/store/settings";
@@ -548,34 +547,6 @@ export const useCalendarStore = create()((set, get) => ({
await scheduleAllTasks();
return;
}
-
- // For iCal feeds, parse the feed
- if (feed.url) {
- const events = await parseICalFeed(feed.url, id);
-
- // Update events in database
- const response = await fetch(`/api/feeds/${id}/sync`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ events }),
- });
-
- if (!response.ok) {
- throw new Error("Failed to sync feed with database");
- }
-
- // Update feed sync status
- await get().updateFeed(id, {
- lastSync: new Date(),
- error: undefined,
- });
-
- // Reload events from database
- await get().loadFromDatabase();
- // Trigger auto-scheduling after event is created
- const { scheduleAllTasks } = useTaskStore.getState();
- await scheduleAllTasks();
- }
} catch (error) {
console.error("Failed to sync feed:", error);
// Update feed with error
@@ -736,18 +707,19 @@ export const useCalendarStore = create()((set, get) => ({
id: `${task.id}`,
feedId: "tasks",
title: task.title,
- description: task.description,
+ description: task.description || undefined,
start: new Date(task.scheduledStart),
end: new Date(task.scheduledEnd),
isRecurring: false,
+ isMaster: false,
allDay: false,
color: task.tags[0]?.color || "#4f46e5",
extendedProps: {
isTask: true,
taskId: task.id,
status: task.status,
- energyLevel: task.energyLevel,
- preferredTime: task.preferredTime,
+ energyLevel: task.energyLevel?.toString() || undefined,
+ preferredTime: task.preferredTime?.toString(),
tags: task.tags,
isAutoScheduled: true,
scheduleScore: task.scheduleScore,
@@ -770,20 +742,21 @@ export const useCalendarStore = create()((set, get) => ({
id: `${task.id}`,
feedId: "tasks",
title: task.title,
- description: task.description,
+ description: task.description || undefined,
start: eventDate,
end: task.duration
? new Date(eventDate.getTime() + task.duration * 60000)
: new Date(eventDate.getTime() + 3600000),
isRecurring: false,
+ isMaster: false,
allDay: true,
color: task.tags[0]?.color || "#4f46e5",
extendedProps: {
isTask: true,
taskId: task.id,
status: task.status,
- energyLevel: task.energyLevel,
- preferredTime: task.preferredTime,
+ energyLevel: task.energyLevel?.toString() || undefined,
+ preferredTime: task.preferredTime?.toString(),
tags: task.tags,
isAutoScheduled: false,
dueDate: task.dueDate
diff --git a/src/types/project.ts b/src/types/project.ts
index 05624cf..218e269 100644
--- a/src/types/project.ts
+++ b/src/types/project.ts
@@ -20,4 +20,4 @@ export interface NewProject {
status?: ProjectStatus;
}
-export interface UpdateProject extends Partial {}
+export type UpdateProject = Partial;
diff --git a/src/types/task.ts b/src/types/task.ts
index a4d6cb7..0626034 100644
--- a/src/types/task.ts
+++ b/src/types/task.ts
@@ -64,7 +64,7 @@ export interface UpdateTask
tagIds?: string[];
}
-export interface NewTag extends Omit {}
+export type NewTag = Omit;
export interface TaskFilters {
status?: TaskStatus[];