/** * rTasks Automerge document schemas. * * Granularity: one Automerge document per board/workspace. * DocId format: {space}:tasks:boards:{boardId} */ import type { DocSchema } from '../../shared/local-first/document'; // ── Document types ── export interface ClickUpTaskMeta { taskId: string; // ClickUp task ID listId: string; // ClickUp list ID url: string; // Direct link to ClickUp task lastSyncedAt: number; syncStatus: 'synced' | 'pending-push' | 'conflict' | 'push-failed'; contentHash: string; // SHA of title+desc+status+priority at last sync remoteHash?: string; // ClickUp state hash at last pull } export interface TaskItem { id: string; spaceId: string; title: string; description: string; status: string; priority: string | null; labels: string[]; assigneeId: string | null; createdBy: string | null; sortOrder: number; createdAt: number; updatedAt: number; clickup?: ClickUpTaskMeta; } export interface ClickUpBoardMeta { listId: string; // ClickUp list this board mirrors listName: string; workspaceId: string; // ClickUp team ID webhookId?: string; // For teardown on disconnect syncEnabled: boolean; statusMap: Record; // rTasks → ClickUp reverseStatusMap: Record; // ClickUp → rTasks } export interface BoardMeta { id: string; name: string; slug: string; description: string; icon: string | null; ownerDid: string | null; statuses: string[]; labels: string[]; createdAt: number; updatedAt: number; clickup?: ClickUpBoardMeta; } export interface BoardDoc { meta: { module: string; collection: string; version: number; spaceSlug: string; createdAt: number; }; board: BoardMeta; tasks: Record; } // ── Schema registration ── export const boardSchema: DocSchema = { module: 'tasks', collection: 'boards', version: 1, init: (): BoardDoc => ({ meta: { module: 'tasks', collection: 'boards', version: 1, spaceSlug: '', createdAt: Date.now(), }, board: { id: '', name: 'Untitled Board', slug: '', description: '', icon: null, ownerDid: null, statuses: ['TODO', 'IN_PROGRESS', 'DONE'], labels: [], createdAt: Date.now(), updatedAt: Date.now(), }, tasks: {}, }), }; // ── Helpers ── export function boardDocId(space: string, boardId: string) { return `${space}:tasks:boards:${boardId}` as const; } // ── ClickUp connection doc (one per space) ── export interface ClickUpConnectionDoc { meta: { module: string; collection: string; version: number; spaceSlug: string; createdAt: number; }; clickup?: { accessToken: string; // OAuth token or personal API token teamId: string; teamName: string; connectedAt: number; webhookSecret: string; // HMAC secret for webhook validation }; } export const clickupConnectionSchema: DocSchema = { module: 'tasks', collection: 'clickup-connection', version: 1, init: (): ClickUpConnectionDoc => ({ meta: { module: 'tasks', collection: 'clickup-connection', version: 1, spaceSlug: '', createdAt: Date.now(), }, }), }; export function clickupConnectionDocId(space: string) { return `${space}:tasks:clickup-connection` as const; } export function createTaskItem( id: string, spaceId: string, title: string, opts: Partial = {}, ): TaskItem { const now = Date.now(); return { id, spaceId, title, description: '', status: 'TODO', priority: null, labels: [], assigneeId: null, createdBy: null, sortOrder: 0, createdAt: now, updatedAt: now, ...opts, }; }