import { sql } from "drizzle-orm"; import { pgTable, text, varchar, timestamp, integer, boolean, json, date } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; // ADHD Settings Type export interface ADHDSettings { reducedAnimations: boolean; largerTargets: boolean; breakReminderInterval: number; // minutes singleTaskModeDefault: boolean; positiveMessagingLevel: 'minimal' | 'normal' | 'high'; hyperfocusProtection: boolean; hyperfocusMaxMinutes: number; visualTimerEnabled: boolean; quickWinThreshold: number; // minutes - tasks under this are "quick wins" energyCheckInsEnabled: boolean; } export const users = pgTable("users", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), username: text("username").notNull().unique(), email: text("email").notNull().unique(), // Added email password: text("password").notNull(), role: text("role").notNull().default("user"), // 'admin' | 'user' isActive: boolean("is_active").notNull().default(true), xp: integer("xp").notNull().default(0), level: integer("level").notNull().default(1), currentStreak: integer("current_streak").notNull().default(0), lastTaskDate: timestamp("last_task_date"), lastActive: timestamp("last_active"), // Track daily activity for streaks showOnLeaderboard: boolean("show_on_leaderboard").default(true).notNull(), // Privacy setting isSearchable: boolean("is_searchable").notNull().default(false), // Privacy setting apiKey: text("api_key"), // For MCP Server access aiEnabled: boolean("ai_enabled").notNull().default(true), // Feature flag per user routineConfig: json("routine_config").$type<{ morningTime: string, eveningTime: string, enabled: boolean }>().default({ morningTime: "09:00", eveningTime: "17:00", enabled: false }), lastMorningRoutine: timestamp("last_morning_routine"), lastEveningRoutine: timestamp("last_evening_routine"), // 2FA Fields is2faEnabled: boolean("is_2fa_enabled").notNull().default(false), otpCode: text("otp_code"), // The temporary 6-digit code otpExpiresAt: timestamp("otp_expires_at"), language: text("language").notNull().default("en"), // 'en' | 'de' workHours: json("work_hours").$type<{ start: string, end: string, days: number[] }>().default({ start: "09:00", end: "17:00", days: [1, 2, 3, 4, 5] }), // Deprecated in favor of availability? Keeping for now. availability: json("availability").$type<{ work: { start: string, end: string, days: number[] }, personal: { start: string, end: string, days: number[] } }>().default({ work: { start: "09:00", end: "17:00", days: [1, 2, 3, 4, 5] }, personal: { start: "18:00", end: "22:00", days: [1, 2, 3, 4, 5, 0, 6] } // Mon-Fri Evening + Weekend }), // ADHD Mode Fields adhdMode: boolean("adhd_mode").notNull().default(false), adhdSettings: json("adhd_settings").$type().default({ reducedAnimations: false, largerTargets: true, breakReminderInterval: 45, singleTaskModeDefault: false, positiveMessagingLevel: 'normal', hyperfocusProtection: true, hyperfocusMaxMinutes: 90, visualTimerEnabled: true, quickWinThreshold: 10, energyCheckInsEnabled: true, }), lastBreakAt: timestamp("last_break_at"), currentEnergyLevel: text("current_energy_level").default("medium"), // 'low' | 'medium' | 'high' todayEnergyCheckedIn: boolean("today_energy_checked_in").default(false), }); export const systemSettings = pgTable("system_settings", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), key: text("key").notNull().unique(), // e.g., 'registration_enabled' value: text("value").notNull(), // e.g., 'true' updatedAt: timestamp("updated_at").defaultNow(), }); export const labels = pgTable("labels", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), name: text("name").notNull(), color: text("color").notNull(), creatorId: varchar("creator_id").references(() => users.id), domain: text("domain").notNull().default("neutral"), // 'work' | 'personal' | 'neutral' }); export const sharedLabels = pgTable("shared_labels", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), labelId: varchar("label_id").references(() => labels.id).notNull(), sharedByUserId: varchar("shared_by_user_id").references(() => users.id).notNull(), sharedWithUserId: varchar("shared_with_user_id").references(() => users.id).notNull(), permission: text("permission").notNull().default("read"), // 'read' | 'write' createdAt: timestamp("created_at").defaultNow(), }); export const tasks = pgTable("tasks", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), title: text("title").notNull(), description: text("description"), status: text("status").notNull().default("todo"), // 'todo' | 'inProgress' | 'done' priority: text("priority").notNull().default("medium"), // 'low' | 'medium' | 'high' dueDate: timestamp("due_date"), timeTracked: integer("time_tracked").notNull().default(0), // in minutes isTracking: boolean("is_tracking").notNull().default(false), projectId: text("project_id"), notes: text("notes"), labelId: varchar("label_id").references(() => labels.id), energyLevel: text("energy_level").default("medium"), // 'low' | 'medium' | 'high' estimatedDuration: integer("estimated_duration"), // in minutes parentTaskId: varchar("parent_task_id").references((): any => tasks.id), // Self-reference for subtasks startDate: timestamp("start_date"), // For multi-day tasks dependencies: text("dependencies").array(), // Array of task IDs userId: varchar("user_id").references(() => users.id), // Added for ownership // Recurrence isRecurring: boolean("is_recurring").notNull().default(false), recurrenceInterval: text("recurrence_interval"), // 'daily', 'weekly', 'monthly', 'yearly' recurrenceIntervalValue: integer("recurrence_interval_value").default(1), recurrenceDays: integer("recurrence_days").array(), // 0=Sunday, 1=Monday etc. recurrenceEnd: timestamp("recurrence_end"), }); // Single Task Sharing export const sharedTasks = pgTable("shared_tasks", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), taskId: varchar("task_id").references(() => tasks.id).notNull(), sharedByUserId: varchar("shared_by_user_id").references(() => users.id).notNull(), sharedWithUserId: varchar("shared_with_user_id").references(() => users.id).notNull(), createdAt: timestamp("created_at").defaultNow(), }); // Global "Share All" Access export const userTaskAccess = pgTable("user_task_access", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), ownerId: varchar("owner_id").references(() => users.id).notNull(), viewerId: varchar("viewer_id").references(() => users.id).notNull(), createdAt: timestamp("created_at").defaultNow(), }); export const insertUserSchema = createInsertSchema(users).pick({ username: true, password: true, email: true, showOnLeaderboard: true, isSearchable: true, aiEnabled: true, language: true, workHours: true, availability: true, adhdMode: true, adhdSettings: true, }); export const registerSchema = insertUserSchema; export const loginSchema = z.object({ username: z.string().min(1, "Username is required"), password: z.string().min(1, "Password is required"), rememberMe: z.boolean().optional(), }); export type LoginUser = z.infer; export const insertSystemSettingsSchema = createInsertSchema(systemSettings).omit({ id: true, updatedAt: true, }); export const insertLabelSchema = createInsertSchema(labels).omit({ id: true, }); export const insertTaskSchema = createInsertSchema(tasks, { dueDate: z.coerce.date().nullable().optional(), startDate: z.coerce.date().nullable().optional(), }).omit({ id: true, userId: true, // We will set this server-side }); // Schema exports for sharing export const insertSharedTaskSchema = createInsertSchema(sharedTasks).omit({ id: true, createdAt: true, }); export const insertUserTaskAccessSchema = createInsertSchema(userTaskAccess).omit({ id: true, createdAt: true, }); export type InsertUser = z.infer; export type User = typeof users.$inferSelect; export type InsertSystemSettings = z.infer; export type SystemSettings = typeof systemSettings.$inferSelect; export type InsertLabel = z.infer; export type Label = typeof labels.$inferSelect; export type SharedLabel = typeof sharedLabels.$inferSelect; export type InsertTask = z.infer; export type Task = typeof tasks.$inferSelect; export type InsertSharedTask = z.infer; export type SharedTask = typeof sharedTasks.$inferSelect; export type InsertUserTaskAccess = z.infer; export type UserTaskAccess = typeof userTaskAccess.$inferSelect; export const notes = pgTable("notes", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), title: text("title"), content: text("content"), taskId: varchar("task_id").references(() => tasks.id), userId: varchar("user_id").references(() => users.id), createdAt: timestamp("created_at").defaultNow(), }); export const insertNoteSchema = createInsertSchema(notes).omit({ id: true, createdAt: true, }); export type InsertNote = z.infer; export const xpEvents = pgTable("xp_events", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id), amount: integer("amount").notNull(), source: text("source").notNull(), // 'task_completion', 'daily_streak', 'bonus' taskId: varchar("task_id"), details: json("details"), // For snapshotting task title etc. createdAt: timestamp("created_at").defaultNow(), }); export const goals = pgTable("goals", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id), title: text("title").notNull(), target: integer("target").notNull(), current: integer("current").notNull().default(0), type: text("type").notNull(), // 'weekly_tasks', 'total_xp', 'streak' deadline: timestamp("deadline"), completed: boolean("completed").default(false), createdAt: timestamp("created_at").defaultNow(), }); export const insertXpEventSchema = createInsertSchema(xpEvents).omit({ id: true, createdAt: true, }); export const insertGoalSchema = createInsertSchema(goals).omit({ id: true, current: true, completed: true, createdAt: true, }); export type InsertXpEvent = z.infer; export type XpEvent = typeof xpEvents.$inferSelect; export type InsertGoal = z.infer; export type Goal = typeof goals.$inferSelect; export type Note = typeof notes.$inferSelect; export const rewards = pgTable("rewards", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), title: text("title").notNull(), description: text("description"), cost: integer("cost").notNull(), icon: text("icon").notNull(), type: text("type").notNull().default("virtual"), // 'virtual', 'real_world', 'feature_unlock' isSystem: boolean("is_system").default(true), userId: varchar("user_id").references(() => users.id), // Nullable for system rewards }); export const userRewards = pgTable("user_rewards", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id), rewardId: varchar("reward_id").references(() => rewards.id), purchasedAt: timestamp("purchased_at").defaultNow(), }); export const insertRewardSchema = createInsertSchema(rewards).omit({ id: true, }); export type InsertReward = z.infer; export type Reward = typeof rewards.$inferSelect; export type UserReward = typeof userRewards.$inferSelect; export const insertUserRewardSchema = createInsertSchema(userRewards).omit({ id: true, purchasedAt: true, }); // Session table (managed by connect-pg-simple but defined here to avoid drizzle-kit deletion) export const session = pgTable("session", { sid: varchar("sid").primaryKey(), sess: json("sess").notNull(), expire: timestamp("expire", { precision: 6 }).notNull(), }); export const passwordResetTokens = pgTable("password_reset_tokens", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id).notNull(), token: text("token").notNull().unique(), expiresAt: timestamp("expires_at").notNull(), isUsed: boolean("is_used").default(false), createdAt: timestamp("created_at").defaultNow(), }); export const insertPasswordResetTokenSchema = createInsertSchema(passwordResetTokens).omit({ id: true, createdAt: true, }); export type InsertPasswordResetToken = z.infer; export type PasswordResetToken = typeof passwordResetTokens.$inferSelect; export type InsertUserReward = z.infer; // AI Chat Tables export const conversations = pgTable("conversations", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id).notNull(), title: text("title").notNull(), createdAt: timestamp("created_at").defaultNow(), updatedAt: timestamp("updated_at").defaultNow(), }); export const messages = pgTable("messages", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), conversationId: varchar("conversation_id").references(() => conversations.id).notNull(), role: text("role").notNull(), // 'user' | 'assistant' | 'system' content: text("content").notNull(), createdAt: timestamp("created_at").defaultNow(), }); export const insertConversationSchema = createInsertSchema(conversations).omit({ id: true, createdAt: true, updatedAt: true, }); export const insertMessageSchema = createInsertSchema(messages).omit({ id: true, createdAt: true, }); export type InsertConversation = z.infer; export type Conversation = typeof conversations.$inferSelect; export type InsertMessage = z.infer; export type Message = typeof messages.$inferSelect; export const auditLogs = pgTable("audit_logs", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id), // Nullable if system action (though usually we track actor) action: text("action").notNull(), // 'CREATE', 'UPDATE', 'DELETE', 'LOGIN', etc. entityType: text("entity_type").notNull(), // 'TASK', 'GOAL', 'USER', 'SYSTEM' entityId: text("entity_id"), // ID of the modified entity details: json("details"), // JSON object with changed fields source: text("source").notNull().default("USER"), // 'USER' | 'AI' | 'SYSTEM' createdAt: timestamp("created_at").defaultNow(), }); export const insertAuditLogSchema = createInsertSchema(auditLogs).omit({ id: true, createdAt: true, }); export type InsertAuditLog = z.infer; export type AuditLog = typeof auditLogs.$inferSelect; export const taskTimeLogs = pgTable("task_time_logs", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), taskId: varchar("task_id").references(() => tasks.id).notNull(), userId: varchar("user_id").references(() => users.id).notNull(), timeSpent: integer("time_spent").notNull(), // in minutes createdAt: timestamp("created_at").defaultNow(), // WHEN the time was logged }); export const insertTaskTimeLogSchema = createInsertSchema(taskTimeLogs).omit({ id: true, createdAt: true, }); export type InsertTaskTimeLog = z.infer; export type TaskTimeLog = typeof taskTimeLogs.$inferSelect; // ============================================ // ADHD FEATURE TABLES // ============================================ // Energy Logs - Track daily energy levels export const energyLogs = pgTable("energy_logs", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id).notNull(), energyLevel: text("energy_level").notNull(), // 'low' | 'medium' | 'high' notes: text("notes"), loggedAt: timestamp("logged_at").defaultNow(), }); export const insertEnergyLogSchema = createInsertSchema(energyLogs).omit({ id: true, loggedAt: true, }); export type InsertEnergyLog = z.infer; export type EnergyLog = typeof energyLogs.$inferSelect; // Break Logs - Track when users take breaks export const breakLogs = pgTable("break_logs", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id).notNull(), breakType: text("break_type").notNull(), // 'stretch' | 'water' | 'walk' | 'eyes' | 'snack' | 'breathe' | 'custom' durationMinutes: integer("duration_minutes").notNull().default(5), createdAt: timestamp("created_at").defaultNow(), }); export const insertBreakLogSchema = createInsertSchema(breakLogs).omit({ id: true, createdAt: true, }); export type InsertBreakLog = z.infer; export type BreakLog = typeof breakLogs.$inferSelect; // Body Doubling Sessions export const bodyDoublingSessions = pgTable("body_doubling_sessions", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), hostId: varchar("host_id").references(() => users.id).notNull(), title: text("title").notNull(), sessionType: text("session_type").notNull().default("focus"), // 'focus' | 'brainstorm' | 'admin' | 'creative' startsAt: timestamp("starts_at").notNull(), durationMinutes: integer("duration_minutes").notNull().default(50), maxParticipants: integer("max_participants").notNull().default(5), isPublic: boolean("is_public").notNull().default(true), status: text("status").notNull().default("scheduled"), // 'scheduled' | 'active' | 'completed' | 'cancelled' createdAt: timestamp("created_at").defaultNow(), }); export const insertBodyDoublingSessionSchema = createInsertSchema(bodyDoublingSessions).omit({ id: true, status: true, createdAt: true, }); export type InsertBodyDoublingSession = z.infer; export type BodyDoublingSession = typeof bodyDoublingSessions.$inferSelect; // Session Participants export const sessionParticipants = pgTable("session_participants", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), sessionId: varchar("session_id").references(() => bodyDoublingSessions.id).notNull(), userId: varchar("user_id").references(() => users.id).notNull(), joinedAt: timestamp("joined_at").defaultNow(), leftAt: timestamp("left_at"), }); export const insertSessionParticipantSchema = createInsertSchema(sessionParticipants).omit({ id: true, joinedAt: true, leftAt: true, }); export type InsertSessionParticipant = z.infer; export type SessionParticipant = typeof sessionParticipants.$inferSelect; // Daily Challenges for ADHD motivation export const dailyChallenges = pgTable("daily_challenges", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id).notNull(), challengeType: text("challenge_type").notNull(), // 'complete_tasks' | 'take_breaks' | 'quick_wins' | 'focus_time' | 'use_timer' title: text("title").notNull(), description: text("description"), target: integer("target").notNull(), progress: integer("progress").notNull().default(0), xpReward: integer("xp_reward").notNull(), challengeDate: date("challenge_date").notNull(), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").defaultNow(), }); export const insertDailyChallengeSchema = createInsertSchema(dailyChallenges).omit({ id: true, progress: true, completedAt: true, createdAt: true, }); export type InsertDailyChallenge = z.infer; export type DailyChallenge = typeof dailyChallenges.$inferSelect; // Focus Sessions - Track focused work periods export const focusSessions = pgTable("focus_sessions", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id).notNull(), taskId: varchar("task_id").references(() => tasks.id), sessionType: text("session_type").notNull().default("pomodoro"), // 'pomodoro' | 'five_minute' | 'deep_focus' | 'visual_timer' plannedMinutes: integer("planned_minutes").notNull(), actualMinutes: integer("actual_minutes"), wasCompleted: boolean("was_completed").default(false), startedAt: timestamp("started_at").defaultNow(), endedAt: timestamp("ended_at"), }); export const insertFocusSessionSchema = createInsertSchema(focusSessions).omit({ id: true, actualMinutes: true, wasCompleted: true, startedAt: true, endedAt: true, }); export type InsertFocusSession = z.infer; export type FocusSession = typeof focusSessions.$inferSelect; // Encouragements/Affirmations shown to users export const encouragementLogs = pgTable("encouragement_logs", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").references(() => users.id).notNull(), encouragementType: text("encouragement_type").notNull(), // 'task_complete' | 'returning' | 'struggling' | 'streak_broken' | 'milestone' message: text("message").notNull(), shownAt: timestamp("shown_at").defaultNow(), }); export type EncouragementLog = typeof encouragementLogs.$inferSelect;