/** * rSocials Automerge document schemas. * * Granularity: one Automerge document per space. * DocId format: {space}:socials:data * * Images stay on filesystem, referenced by URL strings in the doc. */ import type { DocSchema } from '../../shared/local-first/document'; // ── Thread types ── export interface ThreadData { id: string; name: string; handle: string; title: string; tweets: string[]; imageUrl?: string | null; tweetImages?: Record | null; createdAt: number; updatedAt: number; } // ── Campaign types ── export interface CampaignPost { id: string; platform: string; postType: string; stepNumber: number; content: string; scheduledAt: string; status: string; hashtags: string[]; phase: number; phaseLabel: string; } export interface Campaign { id: string; title: string; description: string; duration: string; platforms: string[]; phases: { name: string; label: string; days: string }[]; posts: CampaignPost[]; createdAt: number; updatedAt: number; } // ── Campaign planner (flow canvas) types ── export type CampaignNodeType = 'post' | 'thread' | 'platform' | 'audience' | 'phase'; export interface PostNodeData { label: string; platform: string; postType: string; content: string; scheduledAt: string; status: 'draft' | 'scheduled' | 'published'; hashtags: string[]; } export interface ThreadNodeData { label: string; threadId: string; tweetCount: number; status: 'draft' | 'ready' | 'published'; preview: string; } export interface PlatformNodeData { label: string; platform: string; handle: string; } export interface AudienceNodeData { label: string; description: string; sizeEstimate: string; } export interface PhaseNodeData { label: string; dateRange: string; color: string; progress?: number; childNodeIds: string[]; size: { w: number; h: number }; } export interface CampaignPlannerNode { id: string; type: CampaignNodeType; position: { x: number; y: number }; data: PostNodeData | ThreadNodeData | PlatformNodeData | AudienceNodeData | PhaseNodeData; } export type CampaignEdgeType = 'publish' | 'sequence' | 'target'; export interface CampaignEdge { id: string; from: string; to: string; type: CampaignEdgeType; waypoint?: { x: number; y: number }; } export interface CampaignFlow { id: string; name: string; nodes: CampaignPlannerNode[]; edges: CampaignEdge[]; createdAt: number; updatedAt: number; createdBy: string | null; } // ── Document root ── export interface SocialsDoc { meta: { module: string; collection: string; version: number; spaceSlug: string; createdAt: number; }; threads: Record; campaigns: Record; campaignFlows: Record; activeFlowId: string; } // ── Schema registration ── export const socialsSchema: DocSchema = { module: 'socials', collection: 'data', version: 2, init: (): SocialsDoc => ({ meta: { module: 'socials', collection: 'data', version: 2, spaceSlug: '', createdAt: Date.now(), }, threads: {}, campaigns: {}, campaignFlows: {}, activeFlowId: '', }), migrate: (doc: SocialsDoc, _fromVersion: number): SocialsDoc => { if (!doc.campaignFlows) (doc as any).campaignFlows = {}; if (!doc.activeFlowId) (doc as any).activeFlowId = ''; if (doc.meta) doc.meta.version = 2; return doc; }, }; // ── Helpers ── export function socialsDocId(space: string) { return `${space}:socials:data` as const; }