feat: add task management features to calendar
This commit is contained in:
+50
-1
@@ -1,5 +1,5 @@
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
import type { Email, Mailbox, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, VacationResponse, Calendar, CalendarEvent, CalendarEventFilter, FileNode } from '@/lib/jmap/types';
|
||||
import type { Email, Mailbox, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, VacationResponse, Calendar, CalendarEvent, CalendarEventFilter, CalendarTask, FileNode } from '@/lib/jmap/types';
|
||||
import type { SieveScript, SieveCapabilities } from '@/lib/jmap/sieve-types';
|
||||
import { getDemoData, type DemoData } from './demo-data';
|
||||
import { generateDemoId } from './demo-utils';
|
||||
@@ -621,6 +621,55 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
return []; // no-op in demo
|
||||
}
|
||||
|
||||
// ── Calendar Tasks ────────────────────────────────────────────
|
||||
|
||||
async getCalendarTasks(calendarIds?: string[]): Promise<CalendarTask[]> {
|
||||
let tasks = this.data.calendarTasks || [];
|
||||
if (calendarIds) {
|
||||
tasks = tasks.filter(t => Object.keys(t.calendarIds).some(id => calendarIds.includes(id)));
|
||||
}
|
||||
return [...tasks];
|
||||
}
|
||||
|
||||
async createCalendarTask(task: Partial<CalendarTask>): Promise<CalendarTask> {
|
||||
const full: CalendarTask = {
|
||||
id: generateDemoId('task'),
|
||||
uid: generateDemoId('task-uid'),
|
||||
'@type': 'Task',
|
||||
calendarIds: task.calendarIds || { [this.data.calendars[0]?.id || 'cal-1']: true },
|
||||
title: task.title || '',
|
||||
description: task.description || '',
|
||||
due: task.due || null,
|
||||
start: task.start || null,
|
||||
duration: task.duration || null,
|
||||
timeZone: task.timeZone || null,
|
||||
showWithoutTime: task.showWithoutTime ?? true,
|
||||
progress: task.progress || 'needs-action',
|
||||
progressUpdated: null,
|
||||
priority: task.priority || 0,
|
||||
privacy: task.privacy || 'public',
|
||||
keywords: task.keywords || null,
|
||||
categories: task.categories || null,
|
||||
color: task.color || null,
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString(),
|
||||
recurrenceRules: task.recurrenceRules || null,
|
||||
alerts: task.alerts || null,
|
||||
relatedTo: task.relatedTo || null,
|
||||
};
|
||||
this.data.calendarTasks.push(full);
|
||||
return full;
|
||||
}
|
||||
|
||||
async updateCalendarTask(taskId: string, updates: Partial<CalendarTask>): Promise<void> {
|
||||
const task = this.data.calendarTasks.find(t => t.id === taskId);
|
||||
if (task) Object.assign(task, updates, { updated: new Date().toISOString() });
|
||||
}
|
||||
|
||||
async deleteCalendarTask(taskId: string): Promise<void> {
|
||||
this.data.calendarTasks = this.data.calendarTasks.filter(t => t.id !== taskId);
|
||||
}
|
||||
|
||||
// ── Sieve / Filters ──────────────────────────────────────────
|
||||
|
||||
getSieveAccountId(): string { return 'demo-account'; }
|
||||
|
||||
@@ -3,12 +3,13 @@ import { createDemoMailboxes } from './fixtures/mailboxes';
|
||||
import { createDemoEmails } from './fixtures/emails';
|
||||
import { createDemoContacts, createDemoAddressBooks } from './fixtures/contacts';
|
||||
import { createDemoCalendars, createDemoCalendarEvents } from './fixtures/calendars';
|
||||
import { createDemoCalendarTasks } from './fixtures/tasks';
|
||||
import { createDemoIdentities } from './fixtures/identities';
|
||||
import { createDemoSieveScripts, createDemoSieveCapabilities, createDemoSieveContent } from './fixtures/filters';
|
||||
import { createDemoFileNodes } from './fixtures/files';
|
||||
import { createDemoVacationResponse } from './fixtures/vacation';
|
||||
|
||||
import type { Email, Mailbox, ContactCard, AddressBook, Calendar, CalendarEvent, Identity, VacationResponse, FileNode } from '@/lib/jmap/types';
|
||||
import type { Email, Mailbox, ContactCard, AddressBook, Calendar, CalendarEvent, CalendarTask, Identity, VacationResponse, FileNode } from '@/lib/jmap/types';
|
||||
import type { SieveScript, SieveCapabilities } from '@/lib/jmap/sieve-types';
|
||||
|
||||
export interface DemoData {
|
||||
@@ -18,6 +19,7 @@ export interface DemoData {
|
||||
addressBooks: AddressBook[];
|
||||
calendars: Calendar[];
|
||||
calendarEvents: CalendarEvent[];
|
||||
calendarTasks: CalendarTask[];
|
||||
identities: Identity[];
|
||||
sieveScripts: SieveScript[];
|
||||
sieveCapabilities: SieveCapabilities;
|
||||
@@ -35,6 +37,7 @@ export function getDemoData(): DemoData {
|
||||
addressBooks: createDemoAddressBooks(),
|
||||
calendars: createDemoCalendars(),
|
||||
calendarEvents: createDemoCalendarEvents(),
|
||||
calendarTasks: createDemoCalendarTasks(),
|
||||
identities: createDemoIdentities(),
|
||||
sieveScripts: createDemoSieveScripts(),
|
||||
sieveCapabilities: createDemoSieveCapabilities(),
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import type { CalendarTask } from '@/lib/jmap/types';
|
||||
import { demoDate } from '../demo-utils';
|
||||
|
||||
export function createDemoCalendarTasks(): CalendarTask[] {
|
||||
return [
|
||||
{
|
||||
id: 'demo-task-1',
|
||||
calendarIds: { 'demo-calendar-personal': true },
|
||||
'@type': 'Task',
|
||||
uid: 'demo-task-uid-1',
|
||||
title: 'Buy groceries',
|
||||
description: 'Milk, bread, eggs, and vegetables',
|
||||
due: demoDate(0, 2),
|
||||
start: null,
|
||||
duration: null,
|
||||
timeZone: null,
|
||||
showWithoutTime: false,
|
||||
progress: 'needs-action',
|
||||
progressUpdated: null,
|
||||
priority: 0,
|
||||
privacy: 'public',
|
||||
keywords: null,
|
||||
categories: null,
|
||||
color: null,
|
||||
created: demoDate(-3),
|
||||
updated: demoDate(-1),
|
||||
recurrenceRules: null,
|
||||
alerts: null,
|
||||
relatedTo: null,
|
||||
},
|
||||
{
|
||||
id: 'demo-task-2',
|
||||
calendarIds: { 'demo-calendar-work': true },
|
||||
'@type': 'Task',
|
||||
uid: 'demo-task-uid-2',
|
||||
title: 'Prepare quarterly report',
|
||||
description: 'Compile Q4 metrics and send to team',
|
||||
due: demoDate(1, 4),
|
||||
start: null,
|
||||
duration: null,
|
||||
timeZone: null,
|
||||
showWithoutTime: false,
|
||||
progress: 'in-process',
|
||||
progressUpdated: demoDate(-1),
|
||||
priority: 1,
|
||||
privacy: 'public',
|
||||
keywords: null,
|
||||
categories: null,
|
||||
color: null,
|
||||
created: demoDate(-5),
|
||||
updated: demoDate(0),
|
||||
recurrenceRules: null,
|
||||
alerts: {
|
||||
'demo-alert-1': {
|
||||
'@type': 'Alert',
|
||||
trigger: { '@type': 'OffsetTrigger', offset: '-PT15M', relativeTo: 'start' },
|
||||
action: 'display',
|
||||
acknowledged: null,
|
||||
relatedTo: null,
|
||||
},
|
||||
},
|
||||
relatedTo: null,
|
||||
},
|
||||
{
|
||||
id: 'demo-task-3',
|
||||
calendarIds: { 'demo-calendar-personal': true },
|
||||
'@type': 'Task',
|
||||
uid: 'demo-task-uid-3',
|
||||
title: 'Schedule dentist appointment',
|
||||
description: '',
|
||||
due: demoDate(3),
|
||||
start: null,
|
||||
duration: null,
|
||||
timeZone: null,
|
||||
showWithoutTime: true,
|
||||
progress: 'needs-action',
|
||||
progressUpdated: null,
|
||||
priority: 5,
|
||||
privacy: 'public',
|
||||
keywords: null,
|
||||
categories: null,
|
||||
color: null,
|
||||
created: demoDate(-2),
|
||||
updated: demoDate(-2),
|
||||
recurrenceRules: null,
|
||||
alerts: null,
|
||||
relatedTo: null,
|
||||
},
|
||||
{
|
||||
id: 'demo-task-4',
|
||||
calendarIds: { 'demo-calendar-work': true },
|
||||
'@type': 'Task',
|
||||
uid: 'demo-task-uid-4',
|
||||
title: 'Review pull requests',
|
||||
description: 'Review open PRs from the team',
|
||||
due: demoDate(-1),
|
||||
start: null,
|
||||
duration: null,
|
||||
timeZone: null,
|
||||
showWithoutTime: true,
|
||||
progress: 'completed',
|
||||
progressUpdated: demoDate(0),
|
||||
priority: 0,
|
||||
privacy: 'public',
|
||||
keywords: null,
|
||||
categories: null,
|
||||
color: null,
|
||||
created: demoDate(-4),
|
||||
updated: demoDate(0),
|
||||
recurrenceRules: null,
|
||||
alerts: null,
|
||||
relatedTo: null,
|
||||
},
|
||||
{
|
||||
id: 'demo-task-5',
|
||||
calendarIds: { 'demo-calendar-personal': true },
|
||||
'@type': 'Task',
|
||||
uid: 'demo-task-uid-5',
|
||||
title: 'Pay electricity bill',
|
||||
description: '',
|
||||
due: demoDate(-2),
|
||||
start: null,
|
||||
duration: null,
|
||||
timeZone: null,
|
||||
showWithoutTime: true,
|
||||
progress: 'needs-action',
|
||||
progressUpdated: null,
|
||||
priority: 1,
|
||||
privacy: 'public',
|
||||
keywords: null,
|
||||
categories: null,
|
||||
color: null,
|
||||
created: demoDate(-7),
|
||||
updated: demoDate(-7),
|
||||
recurrenceRules: null,
|
||||
alerts: null,
|
||||
relatedTo: null,
|
||||
},
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user