22 KiB
22 KiB
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; // 1–5
moodEvening: number | null; // 1–5
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; // 1–5
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'],
};