// db/schema.ts import { bigint, boolean, doublePrecision, integer, json, pgTable, text, varchar, } from "drizzle-orm/pg-core"; export const users = pgTable("users", { id: text("id").primaryKey(), // OAuth2 user ID name: text("name"), email: text("email"), phone: text("phone"), address_1: text("address_1"), address_2: text("address_2"), city: text("city"), state: text("state"), zip: text("zip"), country: text("country"), user_type: text("user_type"), // 'landlord', 'tenant', 'admin' onboarded_landlord: boolean("onboarded_landlord").default(false), onboarded_tenant: boolean("onboarded_tenant").default(false), email_verified: boolean("email_verified").default(false), banking_setup_complete: boolean("banking_setup_complete").default(false), notification_preferences_json: json("notification_preferences_json"), payment_preferences_json: json("payment_preferences_json"), onboarding_step: integer("onboarding_step").default(0), // Track onboarding progress encrypted_data: text("encrypted_data"), // Encrypted personal information started_on: bigint("started_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); export const roles = pgTable("roles", { id: text("id").primaryKey(), // e.g., 'admin', 'guest', 'client', 'owner' description: text("description"), }); export const userRoles = pgTable("user_roles", { id: text("id").primaryKey(), userId: text("user_id").references(() => users.id), roleId: text("role_id").references(() => roles.id), created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); export const stateKeys = pgTable("state_keys", { seal_key: varchar("seal_key", { length: 32 }).primaryKey(), state_id: varchar("state_id", { length: 36 }).notNull(), cookie_hash: varchar("cookie_hash", { length: 64 }).notNull(), created_on: bigint("created_on", { mode: "number" }).notNull(), }); export const userTokens = pgTable("user_tokens", { user_id: text("user_id").primaryKey().references(() => users.id), state_id: text("state_id").notNull(), access_token: text("access_token").notNull(), refresh_token: text("refresh_token").notNull(), created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); export const webTokens = pgTable("web_tokens", { token_id: varchar("token_id", { length: 64 }).primaryKey(), state_id: varchar("state_id", { length: 36 }).notNull(), user_id: text("user_id").notNull().references(() => users.id), token_type: text("token_type").notNull(), token_value: text("token_value").notNull(), expires_on: bigint("expires_on", { mode: "number" }).notNull(), created_on: bigint("created_on", { mode: "number" }).notNull(), updated_on: bigint("updated_on", { mode: "number" }).notNull(), last_used_on: bigint("last_used_on", { mode: "number" }), is_active: boolean("is_active").default(true), metadata: json("metadata"), }); // Obligations table export const obligations = pgTable("obligations", { id: text("id").primaryKey(), user_id: text("user_id").references(() => users.id), description: text("description"), amount: doublePrecision("amount"), frequency: text("frequency"), // e.g., 'monthly', 'yearly' due_date: bigint("due_date", { mode: "number" }), // unix timestamp encrypted_data: text("encrypted_data"), // Encrypted financial information created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); // Incomes table export const incomes = pgTable("incomes", { id: text("id").primaryKey(), user_id: text("user_id").references(() => users.id), source: text("source"), amount: doublePrecision("amount"), frequency: text("frequency"), // e.g., 'monthly', 'yearly' received_date: bigint("received_date", { mode: "number" }), // unix timestamp encrypted_data: text("encrypted_data"), // Encrypted financial information created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); // Financial balances table - stores balance-form data from FinancialCalendar export const financial_balances = pgTable("financial_balances", { id: text("id").primaryKey(), user_id: text("user_id").references(() => users.id), balance: doublePrecision("balance"), // balance amount balance_date: bigint("balance_date", { mode: "number" }), // date when balance was set (YYYY-MM-DD format) encrypted_data: text("encrypted_data"), // Encrypted financial information created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); export const properties = pgTable("properties", { id: text("id").primaryKey(), title: text("title"), description: text("description"), address_1: text("address_1"), address_2: text("address_2"), city: text("city"), state: text("state"), zip: text("zip"), country: text("country"), bedrooms: integer("bedrooms"), bathrooms: integer("bathrooms"), square_footage: integer("square_footage"), lot_size: integer("lot_size"), year_built: integer("year_built"), price: doublePrecision("price"), property_type: text("property_type"), listing_status: text("listing_status"), rental_status: text("rental_status"), // 'available', 'occupied', 'maintenance', 'draft' monthly_rent: doublePrecision("monthly_rent"), security_deposit: doublePrecision("security_deposit"), default_payment_method_id: text("default_payment_method_id").references(() => payment_methods.id ), lease_duration_months: integer("lease_duration_months"), amenities: text("amenities"), // JSON string of amenities property_photos_count: integer("property_photos_count").default(0), image_url: text("image_url"), encrypted_data: text("encrypted_data"), // Encrypted property information created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), owner_id: text("owner_id").references(() => users.id), }); // Rent Schedules & Rules export const rent_schedules = pgTable("rent_schedules", { id: text("id").primaryKey(), property_id: text("property_id").references(() => properties.id), rent_amount: doublePrecision("rent_amount"), due_day: integer("due_day"), // Day of month rent is due (1-31) late_fee_amount: doublePrecision("late_fee_amount"), grace_period_days: integer("grace_period_days").default(0), payment_methods_allowed: text("payment_methods_allowed"), // JSON array of allowed methods created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); export const leases = pgTable("leases", { id: text("id").primaryKey(), title: text("title"), property_id: text("property_id").references(() => properties.id), property_address: text("property_address"), tenant_id: text("tenant_id").references(() => users.id), owner_id: text("owner_id").references(() => users.id), start_date: bigint("start_date", { mode: "number" }), end_date: bigint("end_date", { mode: "number" }), rent: doublePrecision("rent"), security_deposit: doublePrecision("security_deposit"), default_payment_method_id: text("default_payment_method_id").references(() => payment_methods.id ), lease_status: text("lease_status"), lease_document_url: text("lease_document_url"), payment_schedule_id: text("payment_schedule_id").references(() => rent_schedules.id ), late_fee_rules: text("late_fee_rules"), // JSON string of late fee rules auto_renewal: boolean("auto_renewal").default(false), approval_status: text("approval_status").default("pending_approval"), // 'pending_approval', 'approved', 'rejected' approval_requested_at: bigint("approval_requested_at", { mode: "number" }), // Timestamp when tenant requested approval approved_at: bigint("approved_at", { mode: "number" }), // Timestamp when landlord approved approved_by: text("approved_by").references(() => users.id), // User ID of landlord who approved rejection_reason: text("rejection_reason"), // Reason for rejection if rejected encrypted_data: text("encrypted_data"), // Encrypted lease information created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); // Payment Methods & Banking export const payment_methods = pgTable("payment_methods", { id: text("id").primaryKey(), user_id: text("user_id").references(() => users.id), method_type: text("method_type"), // 'bank_account', 'credit_card' bank_name: text("bank_name"), account_type: text("account_type"), // 'checking', 'savings' routing_number_encrypted: text("routing_number_encrypted"), account_number_encrypted: text("account_number_encrypted"), is_default: boolean("is_default").default(false), created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); // Property Photos & Documents export const property_media = pgTable("property_media", { id: text("id").primaryKey(), property_id: text("property_id").references(() => properties.id), media_type: text("media_type"), // 'photo', 'video', 'document' file_url: text("file_url"), file_name: text("file_name"), display_order: integer("display_order").default(0), created_on: bigint("created_on", { mode: "number" }), }); // Lease Documents & Templates export const lease_documents = pgTable("lease_documents", { id: text("id").primaryKey(), lease_id: text("lease_id").references(() => leases.id), document_type: text("document_type"), // 'lease', 'addendum', 'receipt' file_url: text("file_url"), file_name: text("file_name"), signed_by_landlord: boolean("signed_by_landlord").default(false), signed_by_tenant: boolean("signed_by_tenant").default(false), signature_date: bigint("signature_date", { mode: "number" }), created_on: bigint("created_on", { mode: "number" }), }); // Tenant Applications export const tenant_applications = pgTable("tenant_applications", { id: text("id").primaryKey(), property_id: text("property_id").references(() => properties.id), applicant_id: text("applicant_id").references(() => users.id), application_status: text("application_status"), // 'pending', 'approved', 'rejected' application_data_encrypted: text("application_data_encrypted"), // Encrypted application details document_urls_json: json("document_urls_json"), // JSON array of document URLs submitted_on: bigint("submitted_on", { mode: "number" }), reviewed_on: bigint("reviewed_on", { mode: "number" }), }); export const tenant_invites = pgTable("tenant_invites", { id: text("id").primaryKey(), landlord_id: text("landlord_id").references(() => users.id), property_id: text("property_id").references(() => properties.id), lease_id: text("lease_id").references(() => leases.id), available_on: bigint("available_on", { mode: "number" }), invited_emails: text("invited_emails"), // JSON array of invited emails created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); // Payment Transactions export const payment_transactions = pgTable("payment_transactions", { id: text("id").primaryKey(), lease_id: text("lease_id").references(() => leases.id), amount: doublePrecision("amount"), payment_date: bigint("payment_date", { mode: "number" }), payment_method: text("payment_method"), // 'card', 'ach', 'check', 'cash' transaction_status: text("transaction_status"), // 'pending', 'completed', 'failed' stripe_payment_id: text("stripe_payment_id"), // External payment processor ID created_on: bigint("created_on", { mode: "number" }), }); // Reminders export const reminders = pgTable("reminders", { id: text("id").primaryKey(), user_id: text("user_id").references(() => users.id).notNull(), lease_id: text("lease_id").references(() => leases.id), reminder_type: text("reminder_type").notNull(), // 'rent_payment', 'maintenance_followup', 'custom', 'document_expiration' title: text("title").notNull(), description: text("description"), due_date: bigint("due_date", { mode: "number" }).notNull(), is_completed: boolean("is_completed").default(false), created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); // Communication/Messages export const messages = pgTable("messages", { id: text("id").primaryKey(), lease_id: text("lease_id").references(() => leases.id), sender_id: text("sender_id").references(() => users.id), recipient_id: text("recipient_id").references(() => users.id), message_type: text("message_type"), // 'general', 'maintenance', 'payment', 'notice' subject: text("subject"), parent_message_id: text("parent_message_id"), // For threading attachments_json: json("attachments_json"), // JSON array of file URLs/metadata content: text("content"), // Encrypted message content content_encrypted: text("content_encrypted"), // Encrypted message content is_read: boolean("is_read").default(false), created_on: bigint("created_on", { mode: "number" }), }); // Maintenance Requests export const maintenance_requests = pgTable("maintenance_requests", { id: text("id").primaryKey(), property_id: text("property_id").references(() => properties.id), tenant_id: text("tenant_id").references(() => users.id), request_type: text("request_type"), // 'plumbing', 'electrical', 'hvac', 'general' description: text("description"), // Encrypted request details status: text("status"), // 'open', 'in_progress', 'completed', 'cancelled' priority: text("priority"), // 'low', 'medium', 'high', 'urgent' created_on: bigint("created_on", { mode: "number" }), resolved_on: bigint("resolved_on", { mode: "number" }), }); // User Pins export const user_pins = pgTable("user_pins", { user_id: text("user_id").primaryKey().references(() => users.id), pin: text("pin").notNull(), // Encrypted pin/password created_on: bigint("created_on", { mode: "number" }).notNull(), modified_on: bigint("modified_on", { mode: "number" }).notNull(), }); // Session Config table export const session_configs = pgTable("session_configs", { device_id: text("device_id").primaryKey(), user_id: text("user_id").references(() => users.id), other_data: text("other_data"), active_date: bigint("active_date", { mode: "number" }), quiz_configs: json("quiz_configs"), // Array> active_route: text("active_route"), page_data: text("page_data"), active_quiz_index: integer("active_quiz_index"), active_question_index: integer("active_question_index"), }); // Quiz Config table export const quiz_configs = pgTable("quiz_configs", { quiz_config_id: text("quiz_config_id").primaryKey(), quiz_name: text("quiz_name"), owner_id: text("owner_id").references(() => users.id), quiz_mode: text("quiz_mode"), active_date: bigint("active_date", { mode: "number" }), tab_name: text("tab_name"), tab_index: integer("tab_index").default(0), list_index: integer("list_index").default(0), questions: json("questions"), // Array of question objects results: json("results"), // Array of result objects }); // Quiz Questions table (normalized for better querying) export const quiz_questions = pgTable("quiz_questions", { id: text("id").primaryKey(), quiz_id: text("quiz_id").references(() => quiz_configs.quiz_config_id), owner_id: text("owner_id").references(() => users.id), question_index: integer("question_index").default(0), language_1: text("language_1"), language_2: text("language_2"), text_1: text("text_1"), audio_file_1: text("audio_file_1"), text_2: text("text_2"), audio_file_2: text("audio_file_2"), order_num: integer("order_num"), background_file: text("background_file"), background_image_data: text("background_image_data"), video_file: text("video_file"), video_file_data: text("video_file_data"), }); // Quiz Results table (normalized for better querying) export const quiz_results = pgTable("quiz_results", { id: text("id").primaryKey(), quiz_config_id: text("quiz_config_id").references(() => quiz_configs.quiz_config_id ), user_id: text("user_id").references(() => users.id), success: boolean("success"), total_time: bigint("total_time", { mode: "number" }), help_count: integer("help_count"), completed_on: bigint("completed_on", { mode: "number" }), }); // Brain Question Progress table (for spaced repetition learning with mode progression) export const brain_question_progress = pgTable("brain_question_progress", { id: text("id").primaryKey(), // composite: `${user_id}_${question_id}` (mode-agnostic) user_id: text("user_id").references(() => users.id).notNull(), quiz_id: text("quiz_id").references(() => quiz_configs.quiz_config_id), question_id: text("question_id").notNull(), current_mode: text("current_mode").notNull().default("photo-see-bisaya"), // Current mode in progression mode_index: integer("mode_index").default(0), // Index in QUIZ_MODES array (0-5) attempts: integer("attempts").default(0), first_try_successes: integer("first_try_successes").default(0), consecutive_first_try: integer("consecutive_first_try").default(0), // Streak of first-try correct last_result: boolean("last_result"), // true = correct on first try next_due_at: bigint("next_due_at", { mode: "number" }), // timestamp for spaced repetition interval_days: integer("interval_days").default(1), // current spacing interval ease_factor: doublePrecision("ease_factor").default(2.5), // SM-2 algorithm factor is_review_only: boolean("is_review_only").default(false), // Card has mastered all modes created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), });