life-docs/shared/types.md
2026-03-20 09:32:20 -07:00

22 KiB
Raw Permalink Blame History

Life Management System — Shared Types Package

Overview

The shared/ workspace package (@life-platform/shared) provides the TypeScript contract between frontend and backend. All enums, entity interfaces, DTOs, filter shapes, response wrappers, and WebSocket event payloads are defined here. Both backend-api and frontend import from this package.

shared/
├── package.json             # name: "@life-platform/shared"
├── tsconfig.json
└── src/
    ├── index.ts             # Re-exports everything
    ├── enums.ts             # All enum definitions
    ├── types/
    │   ├── entities.ts      # Entity interfaces
    │   ├── dto.ts           # Create/Update DTO interfaces
    │   ├── filters.ts       # Filter/query interfaces
    │   ├── responses.ts     # Response wrappers
    │   └── events.ts        # WebSocket event payloads
    └── constants.ts         # Shared constants

Enums

// src/enums.ts

export enum DomainType {
  ClientWork = 'client_work',
  Software = 'software',
  SideProject = 'side_project',
  LifeAdmin = 'life_admin',
  Modeling = 'modeling',
  Escort = 'escort',
  Content = 'content',
  Physical = 'physical',
}

export enum TaskStatus {
  Backlog = 'backlog',
  Todo = 'todo',
  InProgress = 'in_progress',
  Blocked = 'blocked',
  Done = 'done',
  Cancelled = 'cancelled',
}

export enum TaskPriority {
  Critical = 'critical',
  High = 'high',
  Medium = 'medium',
  Low = 'low',
}

export enum EnergyLevel {
  High = 'high',
  Medium = 'medium',
  Low = 'low',
}

export enum GoalLevel {
  Yearly = 'yearly',
  Quarterly = 'quarterly',
  Monthly = 'monthly',
  Weekly = 'weekly',
}

export enum GoalStatus {
  Active = 'active',
  Completed = 'completed',
  Abandoned = 'abandoned',
  Paused = 'paused',
}

export enum HabitFrequency {
  Daily = 'daily',
  Weekly = 'weekly',
  Custom = 'custom',
}

export enum TimeOfDay {
  Morning = 'morning',
  Afternoon = 'afternoon',
  Evening = 'evening',
  Anytime = 'anytime',
}

export enum CheckInStatus {
  Done = 'done',
  Skipped = 'skipped',
  Partial = 'partial',
}

export enum BlockType {
  Work = 'work',
  Personal = 'personal',
  Health = 'health',
  Break = 'break',
  Transit = 'transit',
}

export enum BlockStatus {
  Planned = 'planned',
  InProgress = 'in_progress',
  Completed = 'completed',
  Skipped = 'skipped',
  Rescheduled = 'rescheduled',
}

export enum SourceType {
  Session = 'session',
  Subscription = 'subscription',
  Tip = 'tip',
  ContentSale = 'content_sale',
  Invoice = 'invoice',
  Other = 'other',
}

export enum MeasurementType {
  Weight = 'weight',
  Bust = 'bust',
  Waist = 'waist',
  Hips = 'hips',
  Height = 'height',
  BodyFatPct = 'body_fat_pct',
  Custom = 'custom',
}

export enum MedicalEntryType {
  HrtDose = 'hrt_dose',
  Appointment = 'appointment',
  LabResult = 'lab_result',
  Prescription = 'prescription',
  Note = 'note',
}

export enum ContentType {
  PhotoSet = 'photo_set',
  Video = 'video',
  Story = 'story',
  Post = 'post',
}

export enum ContentStatus {
  Idea = 'idea',
  Planned = 'planned',
  Created = 'created',
  Scheduled = 'scheduled',
  Published = 'published',
}

export enum ContentPlatform {
  OnlyFans = 'onlyfans',
  Instagram = 'instagram',
  Twitter = 'twitter',
  TikTok = 'tiktok',
  Other = 'other',
}

export enum SprintStatus {
  Planned = 'planned',
  Active = 'active',
  Completed = 'completed',
  Cancelled = 'cancelled',
}

export enum ContactType {
  Client = 'client',
  Agency = 'agency',
  Collaborator = 'collaborator',
  Provider = 'provider',
  Other = 'other',
}

export enum MessageRole {
  System = 'system',
  User = 'user',
  Assistant = 'assistant',
}

export enum VoiceState {
  Idle = 'idle',
  Listening = 'listening',
  Processing = 'processing',
  Speaking = 'speaking',
}

Entity Interfaces

// src/types/entities.ts

export interface BaseEntity {
  id: string;
  createdAt: string;   // ISO 8601 timestamptz
  updatedAt: string;
}

export interface SoftDeletableEntity extends BaseEntity {
  deletedAt: string | null;
}

export interface Domain extends BaseEntity {
  name: string;
  type: DomainType;
  slug: string;
  color: string;
  icon: string;
  description: string | null;
  isActive: boolean;
  sortOrder: number;
  config: Record<string, unknown>;
}

export interface Goal extends BaseEntity {
  domainId: string;
  parentId: string | null;
  title: string;
  description: string | null;
  level: GoalLevel;
  status: GoalStatus;
  targetDate: string | null;
  progressPct: number;
  sortOrder: number;
  domain?: Domain;
  parent?: Goal;
  children?: Goal[];
  tasks?: Task[];
}

export interface Task extends SoftDeletableEntity {
  domainId: string;
  goalId: string | null;
  sprintId: string | null;
  parentId: string | null;
  title: string;
  description: string | null;
  status: TaskStatus;
  priority: TaskPriority;
  energyLevel: EnergyLevel;
  estimatedMinutes: number | null;
  actualMinutes: number | null;
  dueDate: string | null;
  scheduledDate: string | null;
  isQuickWin: boolean;
  recurrenceRule: string | null;
  tags: string[];
  sortOrder: number;
  domain?: Domain;
  goal?: Goal;
  sprint?: Sprint;
  parent?: Task;
  subtasks?: Task[];
  subtaskCount?: number;
  subtaskCompletedCount?: number;
}

export interface Habit extends BaseEntity {
  domainId: string;
  goalId: string | null;
  name: string;
  description: string | null;
  frequency: HabitFrequency;
  frequencyConfig: Record<string, unknown>;
  timeOfDay: TimeOfDay;
  estimatedMinutes: number | null;
  isActive: boolean;
  streakCurrent: number;
  streakBest: number;
  domain?: Domain;
  goal?: Goal;
}

export interface HabitCheckIn extends BaseEntity {
  habitId: string;
  date: string;
  status: CheckInStatus;
  qualityRating: number | null;
  notes: string | null;
  completedAt: string | null;
}

export interface TimeBlock extends BaseEntity {
  domainId: string | null;
  taskId: string | null;
  habitId: string | null;
  title: string;
  date: string;
  startTime: string;   // HH:mm
  endTime: string;      // HH:mm
  blockType: BlockType;
  status: BlockStatus;
  notes: string | null;
  domain?: Domain;
  task?: Task;
  habit?: Habit;
}

export interface DailyPlan extends BaseEntity {
  date: string;
  energyLevel: EnergyLevel | null;
  focusDomains: string[];
  morningIntention: string | null;
  eveningReflection: string | null;
  moodMorning: number | null;    // 15
  moodEvening: number | null;    // 15
  timeBlocks?: TimeBlock[];
}

export interface IncomeEntry extends BaseEntity {
  domainId: string;
  date: string;
  amount: string;                 // Decrypted on read
  currency: string;
  sourceType: SourceType;
  description: string | null;     // Decrypted on read
  durationMinutes: number | null;
  contactId: string | null;
  domain?: Domain;
  contact?: Contact;
}

export interface BillableEntry extends BaseEntity {
  domainId: string;
  taskId: string | null;
  date: string;
  hours: string;                  // decimal
  rate: string;                   // decimal
  currency: string;
  description: string | null;
  isInvoiced: boolean;
  invoiceRef: string | null;
  domain?: Domain;
  task?: Task;
}

export interface Measurement extends BaseEntity {
  domainId: string;
  date: string;
  type: MeasurementType;
  value: string;                  // decimal
  unit: string;
  notes: string | null;
}

export interface MedicalEntry extends BaseEntity {
  domainId: string;
  date: string;
  type: MedicalEntryType;
  title: string;
  details: string | null;         // Decrypted on read
  provider: string | null;
  nextDate: string | null;
}

export interface Contact extends SoftDeletableEntity {
  domainId: string;
  name: string;
  contactType: ContactType;
  phone: string | null;           // Decrypted on read
  email: string | null;
  notes: string | null;           // Decrypted on read
  rating: number | null;          // 15
  isActive: boolean;
  lastInteraction: string | null;
  metadata: Record<string, unknown>;
  domain?: Domain;
}

export interface Sprint extends BaseEntity {
  domainId: string;
  name: string;
  startDate: string;
  endDate: string;
  status: SprintStatus;
  goal: string | null;
  retrospective: string | null;
  domain?: Domain;
  tasks?: Task[];
}

export interface ContentCalendarItem extends BaseEntity {
  domainId: string;
  title: string;
  contentType: ContentType;
  scheduledDate: string | null;
  platform: ContentPlatform | null;
  status: ContentStatus;
  notes: string | null;
  domain?: Domain;
}

export interface Conversation extends BaseEntity {
  title: string | null;
  model: string | null;
  isVoice: boolean;
  startedAt: string;
  archivedAt: string | null;
}

export interface Message {
  id: string;
  conversationId: string;
  role: MessageRole;
  content: string;
  model: string | null;
  tokenCount: number | null;
  durationMs: number | null;
  metadata: Record<string, unknown>;
  parentId: string | null;
  createdAt: string;
}

DTO Interfaces

// src/types/dto.ts

// --- Domains ---

export interface CreateDomainDto {
  name: string;
  type: DomainType;
  slug: string;
  color: string;
  icon: string;
  description?: string;
  config?: Record<string, unknown>;
}

export interface UpdateDomainDto {
  name?: string;
  slug?: string;
  color?: string;
  icon?: string;
  description?: string;
  isActive?: boolean;
  sortOrder?: number;
  config?: Record<string, unknown>;
}

// --- Tasks ---

export interface CreateTaskDto {
  domainId: string;
  goalId?: string;
  sprintId?: string;
  parentId?: string;
  title: string;
  description?: string;
  status?: TaskStatus;
  priority?: TaskPriority;
  energyLevel?: EnergyLevel;
  estimatedMinutes?: number;
  dueDate?: string;
  scheduledDate?: string;
  isQuickWin?: boolean;
  recurrenceRule?: string;
  tags?: string[];
}

export interface UpdateTaskDto {
  domainId?: string;
  goalId?: string | null;
  sprintId?: string | null;
  parentId?: string | null;
  title?: string;
  description?: string;
  status?: TaskStatus;
  priority?: TaskPriority;
  energyLevel?: EnergyLevel;
  estimatedMinutes?: number;
  actualMinutes?: number;
  dueDate?: string | null;
  scheduledDate?: string | null;
  isQuickWin?: boolean;
  recurrenceRule?: string | null;
  tags?: string[];
  sortOrder?: number;
}

export interface UpdateTaskStatusDto {
  status: TaskStatus;
  actualMinutes?: number;
}

// --- Goals ---

export interface CreateGoalDto {
  domainId: string;
  parentId?: string;
  title: string;
  description?: string;
  level: GoalLevel;
  targetDate?: string;
}

export interface UpdateGoalDto {
  parentId?: string | null;
  title?: string;
  description?: string;
  level?: GoalLevel;
  status?: GoalStatus;
  targetDate?: string | null;
  progressPct?: number;
  sortOrder?: number;
}

// --- Habits ---

export interface CreateHabitDto {
  domainId: string;
  goalId?: string;
  name: string;
  description?: string;
  frequency?: HabitFrequency;
  frequencyConfig?: Record<string, unknown>;
  timeOfDay?: TimeOfDay;
  estimatedMinutes?: number;
}

export interface UpdateHabitDto {
  goalId?: string | null;
  name?: string;
  description?: string;
  frequency?: HabitFrequency;
  frequencyConfig?: Record<string, unknown>;
  timeOfDay?: TimeOfDay;
  estimatedMinutes?: number;
  isActive?: boolean;
}

export interface CreateCheckInDto {
  status: CheckInStatus;
  qualityRating?: number;
  notes?: string;
}

// --- Scheduling ---

export interface CreateTimeBlockDto {
  domainId?: string;
  taskId?: string;
  habitId?: string;
  title: string;
  date: string;
  startTime: string;
  endTime: string;
  blockType: BlockType;
  notes?: string;
}

export interface UpdateTimeBlockDto {
  domainId?: string | null;
  taskId?: string | null;
  habitId?: string | null;
  title?: string;
  date?: string;
  startTime?: string;
  endTime?: string;
  blockType?: BlockType;
  status?: BlockStatus;
  notes?: string;
}

export interface UpdateDailyPlanDto {
  energyLevel?: EnergyLevel;
  focusDomains?: string[];
  morningIntention?: string;
  eveningReflection?: string;
  moodMorning?: number;
  moodEvening?: number;
}

// --- Income ---

export interface CreateIncomeDto {
  domainId: string;
  date: string;
  amount: string;
  currency?: string;
  sourceType: SourceType;
  description?: string;
  durationMinutes?: number;
  contactId?: string;
}

export interface UpdateIncomeDto {
  date?: string;
  amount?: string;
  currency?: string;
  sourceType?: SourceType;
  description?: string;
  durationMinutes?: number;
  contactId?: string | null;
}

export interface CreateBillableDto {
  domainId: string;
  taskId?: string;
  date: string;
  hours: string;
  rate: string;
  currency?: string;
  description?: string;
}

export interface UpdateBillableDto {
  date?: string;
  hours?: string;
  rate?: string;
  description?: string;
  isInvoiced?: boolean;
  invoiceRef?: string;
}

// --- Health ---

export interface CreateMeasurementDto {
  domainId: string;
  date: string;
  type: MeasurementType;
  value: number;
  unit: string;
  notes?: string;
}

export interface CreateMedicalEntryDto {
  domainId: string;
  date: string;
  type: MedicalEntryType;
  title: string;
  details?: string;
  provider?: string;
  nextDate?: string;
}

export interface UpdateMedicalEntryDto {
  date?: string;
  type?: MedicalEntryType;
  title?: string;
  details?: string;
  provider?: string;
  nextDate?: string | null;
}

// --- Contacts ---

export interface CreateContactDto {
  domainId: string;
  name: string;
  contactType: ContactType;
  phone?: string;
  email?: string;
  notes?: string;
  rating?: number;
  metadata?: Record<string, unknown>;
}

export interface UpdateContactDto {
  name?: string;
  contactType?: ContactType;
  phone?: string;
  email?: string;
  notes?: string;
  rating?: number;
  isActive?: boolean;
  metadata?: Record<string, unknown>;
}

// --- Projects ---

export interface CreateSprintDto {
  domainId: string;
  name: string;
  startDate: string;
  endDate: string;
  goal?: string;
}

export interface UpdateSprintDto {
  name?: string;
  startDate?: string;
  endDate?: string;
  status?: SprintStatus;
  goal?: string;
  retrospective?: string;
}

export interface CreateContentDto {
  domainId: string;
  title: string;
  contentType: ContentType;
  scheduledDate?: string;
  platform?: ContentPlatform;
  status?: ContentStatus;
  notes?: string;
}

export interface UpdateContentDto {
  title?: string;
  contentType?: ContentType;
  scheduledDate?: string | null;
  platform?: ContentPlatform;
  status?: ContentStatus;
  notes?: string;
}

// --- Chat ---

export interface CreateConversationDto {
  title?: string;
  model?: string;
}

export interface UpdateConversationDto {
  title?: string;
  archivedAt?: string | null;
}

Filter Interfaces

// src/types/filters.ts

export interface PaginationParams {
  page?: number;
  limit?: number;
  sort?: string;
  order?: 'ASC' | 'DESC';
}

export interface TaskFilters extends PaginationParams {
  domainId?: string;
  goalId?: string;
  sprintId?: string;
  parentId?: string;
  status?: string;           // Comma-separated TaskStatus values
  priority?: string;         // Comma-separated TaskPriority values
  energyLevel?: string;      // Comma-separated EnergyLevel values
  isQuickWin?: boolean;
  overdue?: boolean;
  scheduledDate?: string;
  dueDateStart?: string;
  dueDateEnd?: string;
  search?: string;
  tags?: string;             // Comma-separated, AND logic
  includeSubtasks?: boolean;
}

export interface GoalFilters extends PaginationParams {
  domainId?: string;
  level?: string;            // Comma-separated GoalLevel values
  status?: string;           // Comma-separated GoalStatus values
  parentId?: string;
}

export interface HabitFilters extends PaginationParams {
  domainId?: string;
  isActive?: boolean;
  frequency?: HabitFrequency;
  timeOfDay?: TimeOfDay;
}

export interface CheckInFilters {
  startDate?: string;
  endDate?: string;
}

export interface TimeBlockFilters extends PaginationParams {
  date?: string;
  startDate?: string;
  endDate?: string;
  domainId?: string;
}

export interface IncomeFilters extends PaginationParams {
  domainId?: string;
  startDate?: string;
  endDate?: string;
  sourceType?: string;       // Comma-separated SourceType values
  currency?: string;
  contactId?: string;
}

export interface IncomeSummaryFilters {
  period: 'week' | 'month' | 'year';
  domainId?: string;
  startDate?: string;
  endDate?: string;
}

export interface BillableFilters extends PaginationParams {
  domainId?: string;
  isInvoiced?: boolean;
  startDate?: string;
  endDate?: string;
}

export interface MeasurementFilters extends PaginationParams {
  domainId?: string;
  type?: string;             // Comma-separated MeasurementType values
  startDate?: string;
  endDate?: string;
}

export interface MedicalFilters extends PaginationParams {
  domainId?: string;
  type?: string;             // Comma-separated MedicalEntryType values
  startDate?: string;
  endDate?: string;
}

export interface ContactFilters extends PaginationParams {
  domainId?: string;
  contactType?: string;      // Comma-separated ContactType values
  isActive?: boolean;
  search?: string;
}

export interface SprintFilters extends PaginationParams {
  domainId?: string;
  status?: string;           // Comma-separated SprintStatus values
}

export interface ContentFilters extends PaginationParams {
  domainId?: string;
  status?: string;           // Comma-separated ContentStatus values
  platform?: string;         // Comma-separated ContentPlatform values
  startDate?: string;
  endDate?: string;
}

export interface ConversationFilters extends PaginationParams {
  archived?: boolean;
  isVoice?: boolean;
}

export interface TrendFilters {
  metric: 'income' | 'tasks_completed' | 'habit_adherence' | 'time_tracked';
  period: 'day' | 'week' | 'month';
  domainId?: string;
  startDate?: string;
  endDate?: string;
}

Response Wrappers

// src/types/responses.ts

export interface PaginationMeta {
  page: number;
  limit: number;
  total: number;
  totalPages: number;
}

export interface PaginatedResponse<T> {
  data: T[];
  meta: PaginationMeta;
}

export interface ApiError {
  statusCode: number;
  error: string;
  message: string;
  details?: Array<{ field: string; message: string }>;
}

export interface TodayResponse {
  date: string;
  dailyPlan: {
    energyLevel: EnergyLevel | null;
    moodMorning: number | null;
    morningIntention: string | null;
    focusDomains: Domain[];
  };
  timeBlocks: TimeBlock[];
  priorityTasks: Task[];
  quickWins: Task[];
  habitsDueToday: Array<{
    habit: Habit;
    checkedIn: boolean;
    checkIn: HabitCheckIn | null;
  }>;
  domainStats: Array<{
    domain: Pick<Domain, 'id' | 'name' | 'color' | 'slug'>;
    tasksDueToday: number;
    tasksOverdue: number;
    incomeThisWeek: string;
    currentStreak: number;
    sprintProgress: number | null;
  }>;
  overdueTasks: Task[];
  completedToday: number;
  totalToday: number;
}

export interface NextActionResponse {
  task: Task & {
    domain: Pick<Domain, 'name' | 'color' | 'slug'>;
  };
  score: number;
  reason: string;
}

export interface AnalyticsOverview {
  period: string;
  tasks: {
    completed: number;
    created: number;
    overdue: number;
    byDomain: Array<{ domain: Pick<Domain, 'id' | 'name' | 'color'>; count: number }>;
  };
  habits: {
    adherenceRate: number;
    activeStreaks: number;
    longestStreak: number;
    byDomain: Array<{ domain: Pick<Domain, 'id' | 'name' | 'color'>; adherenceRate: number }>;
  };
  goals: {
    active: number;
    completed: number;
    averageProgress: number;
  };
  income: {
    total: string;
    currency: string;
    byDomain: Array<{ domain: Pick<Domain, 'id' | 'name' | 'color'>; total: string }>;
  };
}

export interface IncomeSummary {
  period: string;
  startDate: string;
  endDate: string;
  total: string;
  currency: string;
  byDomain: Array<{
    domain: Pick<Domain, 'name' | 'color'>;
    total: string;
    count: number;
  }>;
  bySourceType: Array<{
    sourceType: SourceType;
    total: string;
    count: number;
  }>;
}

export interface HabitStats {
  currentStreak: number;
  bestStreak: number;
  completionRate30d: number;
  completionRate7d: number;
  averageQuality: number;
  totalCheckIns: number;
  checkInsByStatus: Record<CheckInStatus, number>;
}

export interface TrendDataPoint {
  date: string;
  value: number;
}

export interface TrendResponse {
  metric: string;
  period: string;
  dataPoints: TrendDataPoint[];
}

WebSocket Event Payloads

// src/types/events.ts

// --- Chat Events ---

export interface ChatClientEvents {
  'conversation:join': { conversationId: string };
  'conversation:leave': { conversationId: string };
  'send_message': {
    conversationId: string;
    content: string;
    model?: string;
  };
}

export interface ChatServerEvents {
  'message_start': {
    messageId: string;
    conversationId: string;
  };
  'message_chunk': {
    messageId: string;
    chunk: string;
  };
  'message_end': {
    messageId: string;
    model: string;
    tokenCount: number;
    durationMs: number;
  };
  'message_error': {
    messageId: string;
    error: string;
  };
  'typing': {
    conversationId: string;
    isTyping: boolean;
  };
}

// --- Voice Events ---

export interface VoiceClientEvents {
  'voice:start': { conversationId: string };
  'voice:audio': { audio: string; segmentId: number }; // base64
  'voice:interrupt': {};
  'voice:stop': {};
}

export interface VoiceServerEvents {
  'voice:transcription': {
    text: string;
    segmentId: number;
    confidence: number;
  };
  'voice:assistant_text': {
    text: string;
    messageId: string;
  };
  'voice:audio_chunk': Buffer; // binary WAV
  'voice:audio_end': {
    messageId: string;
  };
  'voice:error': {
    error: string;
  };
  'voice:status': {
    state: VoiceState;
  };
}

Constants

// src/constants.ts

export const DEFAULT_PAGE_SIZE = 20;
export const MAX_PAGE_SIZE = 100;

export const DEFAULT_CURRENCY = 'GBP';

export const QUICK_WIN_THRESHOLD_MINUTES = 5;

export const STREAK_MILESTONES = [3, 7, 14, 30, 60, 100] as const;

export const DOMAIN_TYPE_TABS: Record<DomainType, string[]> = {
  [DomainType.ClientWork]:  ['tasks', 'goals', 'income', 'contacts'],
  [DomainType.Software]:    ['tasks', 'goals', 'sprints'],
  [DomainType.SideProject]: ['tasks', 'goals'],
  [DomainType.LifeAdmin]:   ['tasks', 'goals'],
  [DomainType.Modeling]:    ['tasks', 'goals', 'contacts', 'measurements'],
  [DomainType.Escort]:      ['tasks', 'goals', 'income', 'contacts'],
  [DomainType.Content]:     ['tasks', 'goals', 'income', 'content'],
  [DomainType.Physical]:    ['tasks', 'goals', 'habits', 'measurements', 'medical'],
};