diff --git a/assets/style.css b/assets/style.css index 566571c..841de22 100644 --- a/assets/style.css +++ b/assets/style.css @@ -4303,10 +4303,24 @@ body { cursor: not-allowed; } -.list-action-button--center { +.list-header-center { + position: absolute; + top: 50%; left: 50%; - right: auto; transform: translate(-50%, -50%); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.list-header-center .list-action-button { + position: static; + transform: none; +} + +.list-action-button--small { + padding: 0.35rem 0.85rem; + font-size: 0.9rem; } .dialog-offset { @@ -4620,6 +4634,712 @@ body { } } +/* Dashboard layout (landlord + tenant) – no Tailwind */ +.dashboard-page { + min-height: 100vh; + background: var(--theme-bg); +} +.dashboard-header { + background: var(--theme-surface); + box-shadow: var(--theme-shadow); +} +.dashboard-header-inner { + max-width: 80rem; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; +} +@media (min-width: 640px) { + .dashboard-header-inner { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} +@media (min-width: 1024px) { + .dashboard-header-inner { + padding-left: 2rem; + padding-right: 2rem; + } +} +.dashboard-header-row { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 1rem; + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} +.dashboard-header-title { + font-size: 1.875rem; + font-weight: 700; + color: var(--theme-text); +} +.dashboard-header-subtitle { + margin-top: 0.25rem; + font-size: 0.875rem; + color: var(--theme-muted); +} +.dashboard-header-actions { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: stretch; + justify-content: flex-end; +} +.dashboard-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 8.5rem; + flex: 0 0 8.5rem; + padding: 0.5rem 0.75rem; + border-width: 2px; + border-style: solid; + font-size: 0.875rem; + font-weight: 500; + border-radius: 0.375rem; + text-decoration: none; + white-space: nowrap; + box-sizing: border-box; +} +.dashboard-btn--primary { + border-color: transparent; + color: #fff; + background: var(--theme-primary-fill); +} +.dashboard-btn--primary:hover { + background: var(--theme-primary-fill-hover); +} +.dashboard-btn--secondary { + border-color: var(--theme-border); + color: var(--theme-text); + background: var(--theme-surface); +} +.dashboard-btn--secondary:hover { + background: var(--landlord-surface-subtle); +} +.dashboard-btn--messages-new { + border-color: var(--theme-accent); + color: var(--theme-text); + background: var(--theme-surface); + box-shadow: var(--theme-shadow); + outline: 2px solid rgba(15, 109, 183, 0.3); + outline-offset: 2px; +} +.dashboard-btn--messages-new:hover { + background: var(--landlord-surface-subtle); +} +.dashboard-content { + max-width: 80rem; + margin-left: auto; + margin-right: auto; + padding: 1rem 1rem 2rem; +} +@media (min-width: 640px) { + .dashboard-content { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} +@media (min-width: 1024px) { + .dashboard-content { + padding-left: 2rem; + padding-right: 2rem; + } +} +.dashboard-stats-grid { + display: grid; + grid-template-columns: 1fr; + gap: 1.25rem; + margin-bottom: 2rem; +} +@media (min-width: 640px) { + .dashboard-stats-grid { + grid-template-columns: repeat(2, 1fr); + } +} +@media (min-width: 1024px) { + .dashboard-stats-grid { + grid-template-columns: repeat(4, 1fr); + } +} +.dashboard-two-col { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; +} +@media (min-width: 1024px) { + .dashboard-two-col { + grid-template-columns: repeat(2, 1fr); + } +} +.dashboard-card--full-width { + grid-column: 1 / -1; +} +.dashboard-table-wrap { + overflow-x: auto; +} +.dashboard-table { + width: 100%; + border-collapse: collapse; +} +.dashboard-table thead { + background: var(--landlord-surface-subtle); +} +.dashboard-table th, +.dashboard-table td { + padding: 0.75rem 1.5rem; + text-align: left; + font-size: 0.875rem; +} +.dashboard-table-th { + font-weight: 500; + color: var(--theme-muted); + text-transform: uppercase; + letter-spacing: 0.05em; +} +.dashboard-table-td { + color: var(--theme-text); + white-space: nowrap; +} +.dashboard-table tbody tr { + border-bottom: 1px solid var(--theme-border); +} +.dashboard-table tbody tr:last-child { + border-bottom: none; +} +.dashboard-table-empty { + text-align: center; + padding: 1rem 1.5rem; + color: var(--theme-muted); +} +.dashboard-card { + background: var(--theme-surface); + box-shadow: var(--theme-shadow); + border-radius: 0.5rem; + overflow: hidden; +} +.dashboard-card--no-overflow { + overflow: visible; +} +.dashboard-card-body { + padding: 1rem 1.25rem; +} +@media (min-width: 640px) { + .dashboard-card-body { + padding: 1.5rem; + } +} +.dashboard-stat-card { + display: flex; + align-items: center; +} +.dashboard-stat-icon-wrap { + flex-shrink: 0; +} +.dashboard-stat-icon { + width: 1.5rem; + height: 1.5rem; + color: var(--theme-muted); +} +.dashboard-stat-content { + margin-left: 1.25rem; + min-width: 0; + flex: 1; +} +.dashboard-stat-label { + font-size: 0.875rem; + font-weight: 500; + color: var(--theme-muted); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.dashboard-stat-value { + font-size: 1.125rem; + font-weight: 500; + color: var(--theme-text); +} +.dashboard-section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1rem; +} +.dashboard-section-title { + font-size: 1.125rem; + line-height: 1.5; + font-weight: 500; + color: var(--theme-text); +} +.dashboard-section-title--mb { + margin-bottom: 1rem; +} +.dashboard-property-grid { + display: grid; + grid-template-columns: 1fr; + gap: 1.5rem; +} +@media (min-width: 640px) { + .dashboard-property-grid { + grid-template-columns: repeat(2, 1fr); + } +} +@media (min-width: 1024px) { + .dashboard-property-grid { + grid-template-columns: repeat(4, 1fr); + } +} +.dashboard-section-link { + font-size: 0.875rem; + color: var(--theme-accent); + text-decoration: none; +} +.dashboard-section-link:hover { + color: var(--theme-accent-2); +} +.dashboard-list-spaced > * + * { + margin-top: 1rem; +} +.dashboard-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1rem; + background: var(--landlord-surface-subtle); + border-radius: 0.5rem; +} +.dashboard-row--yellow { + background: rgba(254, 249, 195, 0.6); +} +.dashboard-row--blue { + background: rgba(219, 234, 254, 0.6); +} +.dashboard-row--red { + background: rgba(254, 226, 226, 0.6); +} + +@media (prefers-color-scheme: dark) { + :is(.landlord-route, .tenant-route) .dashboard-row--yellow { + background: rgba(120, 100, 40, 0.5); + } + :is(.landlord-route, .tenant-route) .dashboard-row--blue { + background: rgba(30, 58, 100, 0.55); + } + :is(.landlord-route, .tenant-route) .dashboard-row--red { + background: rgba(100, 40, 40, 0.5); + } +} +:root[data-theme-mode="dark"] + :is(.landlord-route, .tenant-route) + .dashboard-row--yellow { + background: rgba(120, 100, 40, 0.5); +} +:root[data-theme-mode="dark"] + :is(.landlord-route, .tenant-route) + .dashboard-row--blue { + background: rgba(30, 58, 100, 0.55); +} +:root[data-theme-mode="dark"] + :is(.landlord-route, .tenant-route) + .dashboard-row--red { + background: rgba(100, 40, 40, 0.5); +} + +.dashboard-row-label { + font-size: 0.875rem; + font-weight: 500; + color: var(--theme-text); +} +.dashboard-row-muted { + font-size: 0.875rem; + color: var(--theme-muted); +} +.dashboard-row-meta { + font-size: 0.75rem; + color: var(--theme-muted); +} +.dashboard-row-meta.capitalize, +.dashboard-row-muted.capitalize { + text-transform: capitalize; +} +.dashboard-approval-details { + min-width: 0; +} +.dashboard-approval-details .dashboard-row-muted + .dashboard-row-meta, +.dashboard-approval-details .dashboard-row-label + .dashboard-row-muted { + margin-top: 0.125rem; +} +.dashboard-approval-details .dashboard-row-muted + .dashboard-row-muted, +.dashboard-approval-details .dashboard-row-meta + .dashboard-row-muted { + margin-top: 0.375rem; +} +.dashboard-row-value { + font-size: 0.875rem; + font-weight: 500; + color: var(--theme-text); +} +.dashboard-row-sub { + font-size: 0.75rem; + color: var(--theme-muted); +} +.dashboard-empty { + text-align: center; + padding: 1rem 0; +} +.dashboard-empty-text { + font-size: 0.875rem; + color: var(--theme-muted); +} +.dashboard-empty-link { + font-size: 0.875rem; + color: var(--theme-accent); + text-decoration: none; +} +.dashboard-empty-link:hover { + color: var(--theme-accent-2); +} +.dashboard-message-row { + display: flex; + align-items: flex-start; + gap: 0.75rem; +} +.dashboard-message-avatar { + flex-shrink: 0; + width: 2rem; + height: 2rem; + border-radius: 9999px; + background: var(--theme-border); + display: flex; + align-items: center; + justify-content: center; +} +.dashboard-message-avatar-text { + font-size: 0.75rem; + font-weight: 500; + color: var(--theme-text); +} +.dashboard-message-body { + flex: 1; + min-width: 0; +} +.dashboard-message-from { + font-size: 0.875rem; + font-weight: 500; + color: var(--theme-text); +} +.dashboard-message-preview { + font-size: 0.875rem; + color: var(--theme-muted); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.dashboard-message-meta { + font-size: 0.75rem; + color: var(--theme-muted); +} +.dashboard-message-dot { + flex-shrink: 0; + width: 0.5rem; + height: 0.5rem; + border-radius: 9999px; + background: var(--theme-accent); +} +.dashboard-badge { + display: inline-flex; + align-items: center; + padding: 0.125rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 500; +} +.dashboard-badge--green { + background: rgba(187, 247, 208, 0.8); + color: #166534; +} +.dashboard-badge--green:hover { + background: rgba(134, 239, 172, 0.9); +} +.dashboard-badge--red { + background: rgba(254, 226, 226, 0.8); + color: #b91c1c; +} +.dashboard-badge--red:hover { + background: rgba(252, 165, 165, 0.9); +} +.dashboard-badge--yellow { + background: rgba(254, 249, 195, 0.8); + color: #854d0e; +} +.dashboard-actions-inline { + display: flex; + gap: 0.5rem; +} +.dashboard-actions-inline .dashboard-badge { + cursor: pointer; + border: none; +} +.dashboard-quick-action { + display: block; + text-decoration: none; + color: inherit; +} +.dashboard-quick-action:hover .dashboard-card { + box-shadow: 0 20px 50px rgba(15, 38, 70, 0.2); +} +.dashboard-stat-icon--green { + color: var(--theme-success); +} +.dashboard-stat-icon--blue { + color: var(--theme-accent); +} +.dashboard-stat-icon--purple { + color: #7c3aed; +} +.dashboard-stat-icon--yellow { + color: #ca8a04; +} +.dashboard-badge--red-pill { + padding: 0.125rem 0.625rem; + border-radius: 9999px; + background: rgba(254, 226, 226, 0.8); + color: #b91c1c; + font-size: 0.75rem; + font-weight: 500; +} +.dashboard-error-page { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: var(--theme-bg); +} +.dashboard-error-inner { + text-align: center; +} +.dashboard-error-title { + font-size: 1.5rem; + font-weight: 700; + color: var(--theme-text); + margin-bottom: 1rem; +} +.dashboard-error-text { + color: var(--theme-muted); +} +.dashboard-error-link { + margin-top: 1rem; + display: inline-block; + color: var(--theme-accent); + text-decoration: none; +} +.dashboard-error-link:hover { + color: var(--theme-accent-2); +} +.dashboard-section-mb { + margin-bottom: 2rem; +} + +/* Tenant notification center */ +.dashboard-notification-header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 1rem; +} +.dashboard-notification-badge { + display: inline-flex; + align-items: center; + border-radius: 9999px; + background: var(--landlord-surface-subtle); + padding: 0.25rem 0.75rem; + font-size: 0.75rem; + font-weight: 600; + color: var(--theme-muted); +} +.dashboard-notification-grid { + margin-top: 1.5rem; + display: grid; + grid-template-columns: 1fr; + gap: 1rem; +} +@media (min-width: 1024px) { + .dashboard-notification-grid { + grid-template-columns: repeat(3, 1fr); + } +} +.dashboard-notification-card { + border-radius: 0.5rem; + border: 1px solid var(--theme-border); + padding: 1rem; + background: var(--theme-surface); +} +.dashboard-notification-card--highlight { + border-color: rgba(245, 158, 11, 0.5); + background: rgba(255, 251, 235, 0.8); +} +@media (prefers-color-scheme: dark) { + :is(.landlord-route, .tenant-route) .dashboard-notification-card--highlight { + border-color: rgba(180, 83, 9, 0.5); + background: rgba(69, 52, 24, 0.55); + } + :is(.landlord-route, .tenant-route) .dashboard-notification-icon--highlight { + background: rgba(69, 52, 24, 0.7); + color: var(--theme-accent); + } + :is(.landlord-route, .tenant-route) .dashboard-notification-count--highlight { + color: var(--theme-accent); + } + :is(.landlord-route, .tenant-route) .dashboard-notification-dot { + outline-color: rgba(69, 52, 24, 0.9); + } +} +:root[data-theme-mode="dark"] + :is(.landlord-route, .tenant-route) + .dashboard-notification-card--highlight { + border-color: rgba(180, 83, 9, 0.5); + background: rgba(69, 52, 24, 0.55); +} +:root[data-theme-mode="dark"] + :is(.landlord-route, .tenant-route) + .dashboard-notification-icon--highlight { + background: rgba(69, 52, 24, 0.7); + color: var(--theme-accent); +} +:root[data-theme-mode="dark"] + :is(.landlord-route, .tenant-route) + .dashboard-notification-count--highlight { + color: var(--theme-accent); +} +:root[data-theme-mode="dark"] + :is(.landlord-route, .tenant-route) + .dashboard-notification-dot { + outline-color: rgba(69, 52, 24, 0.9); +} +.dashboard-notification-card-inner { + display: flex; + align-items: center; + justify-content: space-between; +} +.dashboard-notification-icon-wrap { + display: flex; + align-items: center; + gap: 0.75rem; +} +.dashboard-notification-icon { + width: 2.5rem; + height: 2.5rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 9999px; + background: var(--landlord-surface-subtle); + color: var(--theme-muted); + position: relative; +} +.dashboard-notification-icon--highlight { + background: rgba(254, 243, 199, 0.9); + color: #b45309; +} +.dashboard-notification-icon--blue { + background: rgba(219, 234, 254, 0.8); + color: #1d4ed8; +} +.dashboard-notification-icon--green { + background: rgba(209, 250, 229, 0.8); + color: #047857; +} +.dashboard-notification-icon-svg { + width: 1.25rem; + height: 1.25rem; +} +.dashboard-notification-dot { + position: absolute; + right: -2px; + top: -2px; + width: 0.625rem; + height: 0.625rem; + border-radius: 9999px; + background: #f59e0b; + outline: 2px solid rgba(255, 251, 235, 0.9); + outline-offset: 0; +} +.dashboard-notification-count { + font-size: 0.875rem; + font-weight: 600; +} +.dashboard-notification-count--highlight { + color: #b45309; +} +.dashboard-notification-count--blue { + color: #1d4ed8; +} +.dashboard-notification-count--green { + color: #047857; +} +.dashboard-notification-footer { + margin-top: 0.75rem; +} +.dashboard-notification-footer a { + font-size: 0.75rem; + font-weight: 600; + color: var(--theme-accent); + text-decoration: none; +} +.dashboard-notification-footer a:hover { + color: var(--theme-accent-2); +} +.dashboard-notification-footer a.notification-link--blue { + color: #2563eb; +} +.dashboard-notification-footer a.notification-link--blue:hover { + color: #1d4ed8; +} +.dashboard-notification-footer a.notification-link--green { + color: #059669; +} +.dashboard-notification-footer a.notification-link--green:hover { + color: #047857; +} +.dashboard-invite-list { + margin-top: 0.75rem; +} +.dashboard-invite-list > * + * { + margin-top: 0.75rem; +} +.dashboard-invite-item { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 0.75rem; + border-radius: 0.5rem; + background: var(--landlord-surface-subtle); + padding: 0.75rem 1rem; +} +.dashboard-invite-item-label { + font-size: 0.875rem; + font-weight: 500; + color: var(--theme-text); +} +.dashboard-invite-item-address { + font-size: 0.75rem; + color: var(--theme-muted); + margin-top: 0.125rem; +} +.dashboard-invite-link { + font-size: 0.875rem; + font-weight: 600; + color: var(--theme-accent); + text-decoration: none; + flex-shrink: 0; +} +.dashboard-invite-link:hover { + color: var(--theme-accent-2); +} + .welcome-logo { left: 70%; } @@ -5025,6 +5745,13 @@ body { border: none; color: #fff; } +.theme-dialog-submit-button--danger { + background: var(--theme-danger, #b91c1c); + color: #fff; +} +.theme-dialog-submit-button--danger:hover { + filter: brightness(1.1); +} .theme-dialog-cancel-button { background: transparent; diff --git a/bootstrap-serve.ts b/bootstrap-serve.ts index 7516d17..1bd5d2b 100644 --- a/bootstrap-serve.ts +++ b/bootstrap-serve.ts @@ -2,7 +2,7 @@ * Entry point for the compiled server binary. * Sets FRESH_ROOT so the server finds _fresh/client and _fresh/server next to the binary, then starts Deno.serve. */ -import { join, dirname } from "https://deno.land/std@0.224.0/path/mod.ts"; +import { dirname, join } from "https://deno.land/std@0.224.0/path/mod.ts"; // Must run before importing the server so the bundle uses this root at load time const binDir = dirname(Deno.execPath()); diff --git a/claude_code_zai_env.sh b/claude_code_zai_env.sh new file mode 100644 index 0000000..00ee0b9 --- /dev/null +++ b/claude_code_zai_env.sh @@ -0,0 +1,205 @@ +#!/bin/bash + +set -euo pipefail + +# ======================== +# Define Constants +# ======================== +SCRIPT_NAME=$(basename "$0") +NODE_MIN_VERSION=18 +NODE_INSTALL_VERSION=22 +NVM_VERSION="v0.40.3" +CLAUDE_PACKAGE="@anthropic-ai/claude-code" +CONFIG_DIR="$HOME/.claude" +CONFIG_FILE="$CONFIG_DIR/settings.json" +API_BASE_URL="https://api.z.ai/api/anthropic" +API_KEY_URL="https://z.ai/manage-apikey/apikey-list" +API_TIMEOUT_MS=3000000 + +# ======================== +# Functions +# ======================== + +log_info() { + echo "🔹 $*" +} + +log_success() { + echo "✅ $*" +} + +log_error() { + echo "❌ $*" >&2 +} + +ensure_dir_exists() { + local dir="$1" + if [ ! -d "$dir" ]; then + mkdir -p "$dir" || { + log_error "Failed to create directory: $dir" + exit 1 + } + fi +} + +# ======================== +# Node.js Installation +# ======================== + +install_nodejs() { + local platform=$(uname -s) + + case "$platform" in + Linux|Darwin) + log_info "Installing Node.js on $platform..." + + # Install nvm + log_info "Installing nvm ($NVM_VERSION)..." + curl -s https://raw.githubusercontent.com/nvm-sh/nvm/"$NVM_VERSION"/install.sh | bash + + # Load nvm + log_info "Loading nvm environment..." + \. "$HOME/.nvm/nvm.sh" + + # Install Node.js + log_info "Installing Node.js $NODE_INSTALL_VERSION..." + nvm install "$NODE_INSTALL_VERSION" + + # Verify installation + node -v &>/dev/null || { + log_error "Node.js installation failed" + exit 1 + } + log_success "Node.js installed: $(node -v)" + log_success "npm version: $(npm -v)" + ;; + *) + log_error "Unsupported platform: $platform" + exit 1 + ;; + esac +} + +# ======================== +# Node.js Check +# ======================== + +check_nodejs() { + if command -v node &>/dev/null; then + current_version=$(node -v | sed 's/v//') + major_version=$(echo "$current_version" | cut -d. -f1) + + if [ "$major_version" -ge "$NODE_MIN_VERSION" ]; then + log_success "Node.js is already installed: v$current_version" + return 0 + else + log_info "Node.js v$current_version is installed but version < $NODE_MIN_VERSION. Upgrading..." + install_nodejs + fi + else + log_info "Node.js not found. Installing..." + install_nodejs + fi +} + +# ======================== +# Claude Code Installation +# ======================== + +install_claude_code() { + if command -v claude &>/dev/null; then + log_success "Claude Code is already installed: $(claude --version)" + else + log_info "Installing Claude Code..." + npm install -g "$CLAUDE_PACKAGE" || { + log_error "Failed to install claude-code" + exit 1 + } + log_success "Claude Code installed successfully" + fi +} + +configure_claude_json(){ + node --eval ' + const os = require("os"); + const fs = require("fs"); + const path = require("path"); + + const homeDir = os.homedir(); + const filePath = path.join(homeDir, ".claude.json"); + if (fs.existsSync(filePath)) { + const content = JSON.parse(fs.readFileSync(filePath, "utf-8")); + fs.writeFileSync(filePath, JSON.stringify({ ...content, hasCompletedOnboarding: true }, null, 2), "utf-8"); + } else { + fs.writeFileSync(filePath, JSON.stringify({ hasCompletedOnboarding: true }, null, 2), "utf-8"); + }' +} + +# ======================== +# API Key Configuration +# ======================== + +configure_claude() { + log_info "Configuring Claude Code..." + echo " You can get your API key from: $API_KEY_URL" + read -s -p "🔑 Please enter your Z.AI API key: " api_key + echo + + if [ -z "$api_key" ]; then + log_error "API key cannot be empty. Please run the script again." + exit 1 + fi + + ensure_dir_exists "$CONFIG_DIR" + + # Write settings.json + node --eval ' + const os = require("os"); + const fs = require("fs"); + const path = require("path"); + + const homeDir = os.homedir(); + const filePath = path.join(homeDir, ".claude", "settings.json"); + const apiKey = "'"$api_key"'"; + + const content = fs.existsSync(filePath) + ? JSON.parse(fs.readFileSync(filePath, "utf-8")) + : {}; + + fs.writeFileSync(filePath, JSON.stringify({ + ...content, + env: { + ANTHROPIC_AUTH_TOKEN: apiKey, + ANTHROPIC_BASE_URL: "'"$API_BASE_URL"'", + API_TIMEOUT_MS: "'"$API_TIMEOUT_MS"'", + CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: 1 + } + }, null, 2), "utf-8"); + ' || { + log_error "Failed to write settings.json" + exit 1 + } + + log_success "Claude Code configured successfully" +} + +# ======================== +# Main +# ======================== + +main() { + echo "🚀 Starting $SCRIPT_NAME" + + check_nodejs + install_claude_code + configure_claude_json + configure_claude + + echo "" + log_success "🎉 Installation completed successfully!" + echo "" + echo "🚀 You can now start using Claude Code with:" + echo " claude" +} + +main "$@" diff --git a/db/interfaces.ts b/db/interfaces.ts index cb9f6b9..44fe225 100644 --- a/db/interfaces.ts +++ b/db/interfaces.ts @@ -273,6 +273,33 @@ export interface MaintenanceRequest { resolved_on?: number | null; } +export interface PropertyExpense { + id: string; + property_id?: string | null; + category?: string | null; + amount?: number | null; + description?: string | null; + expense_date?: number | null; + payment_method?: string | null; + vendor?: string | null; + receipt_url?: string | null; + is_recurring?: boolean | null; + recurring_frequency?: string | null; + created_on?: number; + updated_on?: number; +} + +export interface RentChange { + id: string; + property_id?: string | null; + effective_date?: number | null; + monthly_rent?: number | null; + reason?: string | null; + previous_rent?: number | null; + created_by?: string | null; + created_on?: number; +} + export interface UserPin { user_id: string; pin: string; diff --git a/db/schema.ts b/db/schema.ts index 9cc8af0..e013f4b 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -274,6 +274,35 @@ export const payment_transactions = pgTable("payment_transactions", { created_on: bigint("created_on", { mode: "number" }), }); +// Property Expenses +export const property_expenses = pgTable("property_expenses", { + id: text("id").primaryKey(), + property_id: text("property_id").references(() => properties.id), + category: text("category"), // 'maintenance', 'repairs', 'utilities', 'insurance', 'taxes', 'supplies', 'services', 'other' + amount: doublePrecision("amount"), + description: text("description"), + expense_date: bigint("expense_date", { mode: "number" }), + payment_method: text("payment_method"), // 'card', 'ach', 'check', 'cash', 'transfer' + vendor: text("vendor"), // Vendor or service provider name + receipt_url: text("receipt_url"), // URL to receipt or invoice document + is_recurring: boolean("is_recurring").default(false), + recurring_frequency: text("recurring_frequency"), // 'monthly', 'quarterly', 'yearly' if recurring + created_on: bigint("created_on", { mode: "number" }), + updated_on: bigint("updated_on", { mode: "number" }), +}); + +// Rent Changes +export const rent_changes = pgTable("rent_changes", { + id: text("id").primaryKey(), + property_id: text("property_id").references(() => properties.id), + effective_date: bigint("effective_date", { mode: "number" }), + monthly_rent: doublePrecision("monthly_rent"), + reason: text("reason"), // Optional reason for the rent change + previous_rent: doublePrecision("previous_rent"), // Store the previous rent for reference + created_by: text("created_by").references(() => users.id), // User who made the change + created_on: bigint("created_on", { mode: "number" }), +}); + // Reminders export const reminders = pgTable("reminders", { id: text("id").primaryKey(), @@ -291,7 +320,9 @@ export const reminders = pgTable("reminders", { // Communication/Messages export const messages = pgTable("messages", { id: text("id").primaryKey(), - lease_id: text("lease_id").references(() => leases.id), + lease_id: text("lease_id").references(() => leases.id, { + onDelete: "set null", + }), 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' diff --git a/drizzle/0002_messages_lease_on_delete_set_null.sql b/drizzle/0002_messages_lease_on_delete_set_null.sql new file mode 100644 index 0000000..a638069 --- /dev/null +++ b/drizzle/0002_messages_lease_on_delete_set_null.sql @@ -0,0 +1,2 @@ +ALTER TABLE "messages" DROP CONSTRAINT IF EXISTS "messages_lease_id_leases_id_fk";--> statement-breakpoint +ALTER TABLE "messages" ADD CONSTRAINT "messages_lease_id_leases_id_fk" FOREIGN KEY ("lease_id") REFERENCES "public"."leases"("id") ON DELETE SET NULL ON UPDATE NO ACTION; diff --git a/drizzle/0002_powerful_justin_hammer.sql b/drizzle/0002_powerful_justin_hammer.sql new file mode 100644 index 0000000..f363ed7 --- /dev/null +++ b/drizzle/0002_powerful_justin_hammer.sql @@ -0,0 +1,20 @@ +CREATE TABLE "property_expenses" ( + "id" text PRIMARY KEY NOT NULL, + "property_id" text, + "category" text, + "amount" double precision, + "description" text, + "expense_date" bigint, + "payment_method" text, + "vendor" text, + "receipt_url" text, + "is_recurring" boolean DEFAULT false, + "recurring_frequency" text, + "created_on" bigint, + "updated_on" bigint +); +--> statement-breakpoint +ALTER TABLE "messages" DROP CONSTRAINT "messages_lease_id_leases_id_fk"; +--> statement-breakpoint +ALTER TABLE "property_expenses" ADD CONSTRAINT "property_expenses_property_id_properties_id_fk" FOREIGN KEY ("property_id") REFERENCES "public"."properties"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "messages" ADD CONSTRAINT "messages_lease_id_leases_id_fk" FOREIGN KEY ("lease_id") REFERENCES "public"."leases"("id") ON DELETE set null ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/0003_strong_namora.sql b/drizzle/0003_strong_namora.sql new file mode 100644 index 0000000..1b786e5 --- /dev/null +++ b/drizzle/0003_strong_namora.sql @@ -0,0 +1,13 @@ +CREATE TABLE "rent_changes" ( + "id" text PRIMARY KEY NOT NULL, + "property_id" text, + "effective_date" bigint, + "monthly_rent" double precision, + "reason" text, + "previous_rent" double precision, + "created_by" text, + "created_on" bigint +); +--> statement-breakpoint +ALTER TABLE "rent_changes" ADD CONSTRAINT "rent_changes_property_id_properties_id_fk" FOREIGN KEY ("property_id") REFERENCES "public"."properties"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "rent_changes" ADD CONSTRAINT "rent_changes_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..fa16f41 --- /dev/null +++ b/drizzle/meta/0002_snapshot.json @@ -0,0 +1,2272 @@ +{ + "id": "122ae23d-53bd-46bc-9109-8716f1780ae3", + "prevId": "6046c1bc-2d5f-4842-9d8a-037c18583ebf", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.financial_balances": { + "name": "financial_balances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "balance": { + "name": "balance", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "balance_date": { + "name": "balance_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "financial_balances_user_id_users_id_fk": { + "name": "financial_balances_user_id_users_id_fk", + "tableFrom": "financial_balances", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.incomes": { + "name": "incomes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "frequency": { + "name": "frequency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_date": { + "name": "received_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "incomes_user_id_users_id_fk": { + "name": "incomes_user_id_users_id_fk", + "tableFrom": "incomes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.lease_documents": { + "name": "lease_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "document_type": { + "name": "document_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_url": { + "name": "file_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signed_by_landlord": { + "name": "signed_by_landlord", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "signed_by_tenant": { + "name": "signed_by_tenant", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "signature_date": { + "name": "signature_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "lease_documents_lease_id_leases_id_fk": { + "name": "lease_documents_lease_id_leases_id_fk", + "tableFrom": "lease_documents", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.leases": { + "name": "leases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_address": { + "name": "property_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_date": { + "name": "start_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "rent": { + "name": "rent", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "security_deposit": { + "name": "security_deposit", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "default_payment_method_id": { + "name": "default_payment_method_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_status": { + "name": "lease_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_document_url": { + "name": "lease_document_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_schedule_id": { + "name": "payment_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "late_fee_rules": { + "name": "late_fee_rules", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_renewal": { + "name": "auto_renewal", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "approval_status": { + "name": "approval_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'pending_approval'" + }, + "approval_requested_at": { + "name": "approval_requested_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "approved_by": { + "name": "approved_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rejection_reason": { + "name": "rejection_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "leases_property_id_properties_id_fk": { + "name": "leases_property_id_properties_id_fk", + "tableFrom": "leases", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_tenant_id_users_id_fk": { + "name": "leases_tenant_id_users_id_fk", + "tableFrom": "leases", + "tableTo": "users", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_owner_id_users_id_fk": { + "name": "leases_owner_id_users_id_fk", + "tableFrom": "leases", + "tableTo": "users", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_default_payment_method_id_payment_methods_id_fk": { + "name": "leases_default_payment_method_id_payment_methods_id_fk", + "tableFrom": "leases", + "tableTo": "payment_methods", + "columnsFrom": [ + "default_payment_method_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_payment_schedule_id_rent_schedules_id_fk": { + "name": "leases_payment_schedule_id_rent_schedules_id_fk", + "tableFrom": "leases", + "tableTo": "rent_schedules", + "columnsFrom": [ + "payment_schedule_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_approved_by_users_id_fk": { + "name": "leases_approved_by_users_id_fk", + "tableFrom": "leases", + "tableTo": "users", + "columnsFrom": [ + "approved_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.maintenance_requests": { + "name": "maintenance_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_type": { + "name": "request_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "resolved_on": { + "name": "resolved_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "maintenance_requests_property_id_properties_id_fk": { + "name": "maintenance_requests_property_id_properties_id_fk", + "tableFrom": "maintenance_requests", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "maintenance_requests_tenant_id_users_id_fk": { + "name": "maintenance_requests_tenant_id_users_id_fk", + "tableFrom": "maintenance_requests", + "tableTo": "users", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sender_id": { + "name": "sender_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recipient_id": { + "name": "recipient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message_type": { + "name": "message_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_message_id": { + "name": "parent_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attachments_json": { + "name": "attachments_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_encrypted": { + "name": "content_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_read": { + "name": "is_read", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "messages_lease_id_leases_id_fk": { + "name": "messages_lease_id_leases_id_fk", + "tableFrom": "messages", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "messages_sender_id_users_id_fk": { + "name": "messages_sender_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "sender_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_recipient_id_users_id_fk": { + "name": "messages_recipient_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "recipient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.obligations": { + "name": "obligations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "frequency": { + "name": "frequency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "due_date": { + "name": "due_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "obligations_user_id_users_id_fk": { + "name": "obligations_user_id_users_id_fk", + "tableFrom": "obligations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_methods": { + "name": "payment_methods", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "method_type": { + "name": "method_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bank_name": { + "name": "bank_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_type": { + "name": "account_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "routing_number_encrypted": { + "name": "routing_number_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_number_encrypted": { + "name": "account_number_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "payment_methods_user_id_users_id_fk": { + "name": "payment_methods_user_id_users_id_fk", + "tableFrom": "payment_methods", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "payment_date": { + "name": "payment_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "payment_method": { + "name": "payment_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transaction_status": { + "name": "transaction_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_payment_id": { + "name": "stripe_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "payment_transactions_lease_id_leases_id_fk": { + "name": "payment_transactions_lease_id_leases_id_fk", + "tableFrom": "payment_transactions", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.properties": { + "name": "properties", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_1": { + "name": "address_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_2": { + "name": "address_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bedrooms": { + "name": "bedrooms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "bathrooms": { + "name": "bathrooms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "square_footage": { + "name": "square_footage", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lot_size": { + "name": "lot_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "year_built": { + "name": "year_built", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "listing_status": { + "name": "listing_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rental_status": { + "name": "rental_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "monthly_rent": { + "name": "monthly_rent", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "security_deposit": { + "name": "security_deposit", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "default_payment_method_id": { + "name": "default_payment_method_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_duration_months": { + "name": "lease_duration_months", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "amenities": { + "name": "amenities", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_photos_count": { + "name": "property_photos_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "properties_default_payment_method_id_payment_methods_id_fk": { + "name": "properties_default_payment_method_id_payment_methods_id_fk", + "tableFrom": "properties", + "tableTo": "payment_methods", + "columnsFrom": [ + "default_payment_method_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "properties_owner_id_users_id_fk": { + "name": "properties_owner_id_users_id_fk", + "tableFrom": "properties", + "tableTo": "users", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_expenses": { + "name": "property_expenses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expense_date": { + "name": "expense_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "payment_method": { + "name": "payment_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "receipt_url": { + "name": "receipt_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_recurring": { + "name": "is_recurring", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "recurring_frequency": { + "name": "recurring_frequency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_expenses_property_id_properties_id_fk": { + "name": "property_expenses_property_id_properties_id_fk", + "tableFrom": "property_expenses", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_media": { + "name": "property_media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "media_type": { + "name": "media_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_url": { + "name": "file_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_media_property_id_properties_id_fk": { + "name": "property_media_property_id_properties_id_fk", + "tableFrom": "property_media", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reminders": { + "name": "reminders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reminder_type": { + "name": "reminder_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "due_date": { + "name": "due_date", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "is_completed": { + "name": "is_completed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "reminders_user_id_users_id_fk": { + "name": "reminders_user_id_users_id_fk", + "tableFrom": "reminders", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reminders_lease_id_leases_id_fk": { + "name": "reminders_lease_id_leases_id_fk", + "tableFrom": "reminders", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rent_schedules": { + "name": "rent_schedules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rent_amount": { + "name": "rent_amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "due_day": { + "name": "due_day", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "late_fee_amount": { + "name": "late_fee_amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "grace_period_days": { + "name": "grace_period_days", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "payment_methods_allowed": { + "name": "payment_methods_allowed", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "rent_schedules_property_id_properties_id_fk": { + "name": "rent_schedules_property_id_properties_id_fk", + "tableFrom": "rent_schedules", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_configs": { + "name": "session_configs", + "schema": "", + "columns": { + "device_id": { + "name": "device_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "other_data": { + "name": "other_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_date": { + "name": "active_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "active_route": { + "name": "active_route", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "page_data": { + "name": "page_data", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_configs_user_id_users_id_fk": { + "name": "session_configs_user_id_users_id_fk", + "tableFrom": "session_configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.state_keys": { + "name": "state_keys", + "schema": "", + "columns": { + "seal_key": { + "name": "seal_key", + "type": "varchar(32)", + "primaryKey": true, + "notNull": true + }, + "state_id": { + "name": "state_id", + "type": "varchar(36)", + "primaryKey": false, + "notNull": true + }, + "cookie_hash": { + "name": "cookie_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenant_applications": { + "name": "tenant_applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicant_id": { + "name": "applicant_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_status": { + "name": "application_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_data_encrypted": { + "name": "application_data_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "document_urls_json": { + "name": "document_urls_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "submitted_on": { + "name": "submitted_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "reviewed_on": { + "name": "reviewed_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tenant_applications_property_id_properties_id_fk": { + "name": "tenant_applications_property_id_properties_id_fk", + "tableFrom": "tenant_applications", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tenant_applications_applicant_id_users_id_fk": { + "name": "tenant_applications_applicant_id_users_id_fk", + "tableFrom": "tenant_applications", + "tableTo": "users", + "columnsFrom": [ + "applicant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenant_invites": { + "name": "tenant_invites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "landlord_id": { + "name": "landlord_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "available_on": { + "name": "available_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "invited_emails": { + "name": "invited_emails", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tenant_invites_landlord_id_users_id_fk": { + "name": "tenant_invites_landlord_id_users_id_fk", + "tableFrom": "tenant_invites", + "tableTo": "users", + "columnsFrom": [ + "landlord_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tenant_invites_property_id_properties_id_fk": { + "name": "tenant_invites_property_id_properties_id_fk", + "tableFrom": "tenant_invites", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tenant_invites_lease_id_leases_id_fk": { + "name": "tenant_invites_lease_id_leases_id_fk", + "tableFrom": "tenant_invites", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_roles": { + "name": "user_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role_id": { + "name": "role_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_roles_user_id_users_id_fk": { + "name": "user_roles_user_id_users_id_fk", + "tableFrom": "user_roles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_roles_role_id_roles_id_fk": { + "name": "user_roles_role_id_roles_id_fk", + "tableFrom": "user_roles", + "tableTo": "roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_tokens": { + "name": "user_tokens", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_id": { + "name": "state_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_tokens_user_id_users_id_fk": { + "name": "user_tokens_user_id_users_id_fk", + "tableFrom": "user_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_tokens_pkey": { + "name": "user_tokens_pkey", + "columns": [ + "user_id", + "state_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_pins": { + "name": "user_pins", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pin": { + "name": "pin", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "modified_on": { + "name": "modified_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_pins_user_id_users_id_fk": { + "name": "user_pins_user_id_users_id_fk", + "tableFrom": "user_pins", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_1": { + "name": "address_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_2": { + "name": "address_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_type": { + "name": "user_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "onboarded_landlord": { + "name": "onboarded_landlord", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "onboarded_tenant": { + "name": "onboarded_tenant", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "banking_setup_complete": { + "name": "banking_setup_complete", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "notification_preferences_json": { + "name": "notification_preferences_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "payment_preferences_json": { + "name": "payment_preferences_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "onboarding_step": { + "name": "onboarding_step", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_on": { + "name": "started_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.web_tokens": { + "name": "web_tokens", + "schema": "", + "columns": { + "token_id": { + "name": "token_id", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "state_id": { + "name": "state_id", + "type": "varchar(36)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_value": { + "name": "token_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_on": { + "name": "expires_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "last_used_on": { + "name": "last_used_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "web_tokens_user_id_users_id_fk": { + "name": "web_tokens_user_id_users_id_fk", + "tableFrom": "web_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0003_snapshot.json b/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..30d1aaf --- /dev/null +++ b/drizzle/meta/0003_snapshot.json @@ -0,0 +1,2360 @@ +{ + "id": "2f6f6cf7-5da2-4b97-9d49-b34d77a7252b", + "prevId": "122ae23d-53bd-46bc-9109-8716f1780ae3", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.financial_balances": { + "name": "financial_balances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "balance": { + "name": "balance", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "balance_date": { + "name": "balance_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "financial_balances_user_id_users_id_fk": { + "name": "financial_balances_user_id_users_id_fk", + "tableFrom": "financial_balances", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.incomes": { + "name": "incomes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "frequency": { + "name": "frequency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_date": { + "name": "received_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "incomes_user_id_users_id_fk": { + "name": "incomes_user_id_users_id_fk", + "tableFrom": "incomes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.lease_documents": { + "name": "lease_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "document_type": { + "name": "document_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_url": { + "name": "file_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signed_by_landlord": { + "name": "signed_by_landlord", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "signed_by_tenant": { + "name": "signed_by_tenant", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "signature_date": { + "name": "signature_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "lease_documents_lease_id_leases_id_fk": { + "name": "lease_documents_lease_id_leases_id_fk", + "tableFrom": "lease_documents", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.leases": { + "name": "leases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_address": { + "name": "property_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_date": { + "name": "start_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "rent": { + "name": "rent", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "security_deposit": { + "name": "security_deposit", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "default_payment_method_id": { + "name": "default_payment_method_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_status": { + "name": "lease_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_document_url": { + "name": "lease_document_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_schedule_id": { + "name": "payment_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "late_fee_rules": { + "name": "late_fee_rules", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_renewal": { + "name": "auto_renewal", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "approval_status": { + "name": "approval_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'pending_approval'" + }, + "approval_requested_at": { + "name": "approval_requested_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "approved_by": { + "name": "approved_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rejection_reason": { + "name": "rejection_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "leases_property_id_properties_id_fk": { + "name": "leases_property_id_properties_id_fk", + "tableFrom": "leases", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_tenant_id_users_id_fk": { + "name": "leases_tenant_id_users_id_fk", + "tableFrom": "leases", + "tableTo": "users", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_owner_id_users_id_fk": { + "name": "leases_owner_id_users_id_fk", + "tableFrom": "leases", + "tableTo": "users", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_default_payment_method_id_payment_methods_id_fk": { + "name": "leases_default_payment_method_id_payment_methods_id_fk", + "tableFrom": "leases", + "tableTo": "payment_methods", + "columnsFrom": [ + "default_payment_method_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_payment_schedule_id_rent_schedules_id_fk": { + "name": "leases_payment_schedule_id_rent_schedules_id_fk", + "tableFrom": "leases", + "tableTo": "rent_schedules", + "columnsFrom": [ + "payment_schedule_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "leases_approved_by_users_id_fk": { + "name": "leases_approved_by_users_id_fk", + "tableFrom": "leases", + "tableTo": "users", + "columnsFrom": [ + "approved_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.maintenance_requests": { + "name": "maintenance_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_type": { + "name": "request_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "resolved_on": { + "name": "resolved_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "maintenance_requests_property_id_properties_id_fk": { + "name": "maintenance_requests_property_id_properties_id_fk", + "tableFrom": "maintenance_requests", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "maintenance_requests_tenant_id_users_id_fk": { + "name": "maintenance_requests_tenant_id_users_id_fk", + "tableFrom": "maintenance_requests", + "tableTo": "users", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sender_id": { + "name": "sender_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recipient_id": { + "name": "recipient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message_type": { + "name": "message_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_message_id": { + "name": "parent_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attachments_json": { + "name": "attachments_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_encrypted": { + "name": "content_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_read": { + "name": "is_read", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "messages_lease_id_leases_id_fk": { + "name": "messages_lease_id_leases_id_fk", + "tableFrom": "messages", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "messages_sender_id_users_id_fk": { + "name": "messages_sender_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "sender_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_recipient_id_users_id_fk": { + "name": "messages_recipient_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "recipient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.obligations": { + "name": "obligations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "frequency": { + "name": "frequency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "due_date": { + "name": "due_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "obligations_user_id_users_id_fk": { + "name": "obligations_user_id_users_id_fk", + "tableFrom": "obligations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_methods": { + "name": "payment_methods", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "method_type": { + "name": "method_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bank_name": { + "name": "bank_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_type": { + "name": "account_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "routing_number_encrypted": { + "name": "routing_number_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_number_encrypted": { + "name": "account_number_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "payment_methods_user_id_users_id_fk": { + "name": "payment_methods_user_id_users_id_fk", + "tableFrom": "payment_methods", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "payment_date": { + "name": "payment_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "payment_method": { + "name": "payment_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transaction_status": { + "name": "transaction_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_payment_id": { + "name": "stripe_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "payment_transactions_lease_id_leases_id_fk": { + "name": "payment_transactions_lease_id_leases_id_fk", + "tableFrom": "payment_transactions", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.properties": { + "name": "properties", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_1": { + "name": "address_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_2": { + "name": "address_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bedrooms": { + "name": "bedrooms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "bathrooms": { + "name": "bathrooms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "square_footage": { + "name": "square_footage", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lot_size": { + "name": "lot_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "year_built": { + "name": "year_built", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "listing_status": { + "name": "listing_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rental_status": { + "name": "rental_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "monthly_rent": { + "name": "monthly_rent", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "security_deposit": { + "name": "security_deposit", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "default_payment_method_id": { + "name": "default_payment_method_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_duration_months": { + "name": "lease_duration_months", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "amenities": { + "name": "amenities", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_photos_count": { + "name": "property_photos_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "properties_default_payment_method_id_payment_methods_id_fk": { + "name": "properties_default_payment_method_id_payment_methods_id_fk", + "tableFrom": "properties", + "tableTo": "payment_methods", + "columnsFrom": [ + "default_payment_method_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "properties_owner_id_users_id_fk": { + "name": "properties_owner_id_users_id_fk", + "tableFrom": "properties", + "tableTo": "users", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_expenses": { + "name": "property_expenses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expense_date": { + "name": "expense_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "payment_method": { + "name": "payment_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "receipt_url": { + "name": "receipt_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_recurring": { + "name": "is_recurring", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "recurring_frequency": { + "name": "recurring_frequency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_expenses_property_id_properties_id_fk": { + "name": "property_expenses_property_id_properties_id_fk", + "tableFrom": "property_expenses", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_media": { + "name": "property_media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "media_type": { + "name": "media_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_url": { + "name": "file_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_media_property_id_properties_id_fk": { + "name": "property_media_property_id_properties_id_fk", + "tableFrom": "property_media", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reminders": { + "name": "reminders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reminder_type": { + "name": "reminder_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "due_date": { + "name": "due_date", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "is_completed": { + "name": "is_completed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "reminders_user_id_users_id_fk": { + "name": "reminders_user_id_users_id_fk", + "tableFrom": "reminders", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reminders_lease_id_leases_id_fk": { + "name": "reminders_lease_id_leases_id_fk", + "tableFrom": "reminders", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rent_changes": { + "name": "rent_changes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "effective_date": { + "name": "effective_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "monthly_rent": { + "name": "monthly_rent", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previous_rent": { + "name": "previous_rent", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "rent_changes_property_id_properties_id_fk": { + "name": "rent_changes_property_id_properties_id_fk", + "tableFrom": "rent_changes", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "rent_changes_created_by_users_id_fk": { + "name": "rent_changes_created_by_users_id_fk", + "tableFrom": "rent_changes", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rent_schedules": { + "name": "rent_schedules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rent_amount": { + "name": "rent_amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "due_day": { + "name": "due_day", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "late_fee_amount": { + "name": "late_fee_amount", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "grace_period_days": { + "name": "grace_period_days", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "payment_methods_allowed": { + "name": "payment_methods_allowed", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "rent_schedules_property_id_properties_id_fk": { + "name": "rent_schedules_property_id_properties_id_fk", + "tableFrom": "rent_schedules", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_configs": { + "name": "session_configs", + "schema": "", + "columns": { + "device_id": { + "name": "device_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "other_data": { + "name": "other_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_date": { + "name": "active_date", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "active_route": { + "name": "active_route", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "page_data": { + "name": "page_data", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_configs_user_id_users_id_fk": { + "name": "session_configs_user_id_users_id_fk", + "tableFrom": "session_configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.state_keys": { + "name": "state_keys", + "schema": "", + "columns": { + "seal_key": { + "name": "seal_key", + "type": "varchar(32)", + "primaryKey": true, + "notNull": true + }, + "state_id": { + "name": "state_id", + "type": "varchar(36)", + "primaryKey": false, + "notNull": true + }, + "cookie_hash": { + "name": "cookie_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenant_applications": { + "name": "tenant_applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicant_id": { + "name": "applicant_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_status": { + "name": "application_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_data_encrypted": { + "name": "application_data_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "document_urls_json": { + "name": "document_urls_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "submitted_on": { + "name": "submitted_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "reviewed_on": { + "name": "reviewed_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tenant_applications_property_id_properties_id_fk": { + "name": "tenant_applications_property_id_properties_id_fk", + "tableFrom": "tenant_applications", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tenant_applications_applicant_id_users_id_fk": { + "name": "tenant_applications_applicant_id_users_id_fk", + "tableFrom": "tenant_applications", + "tableTo": "users", + "columnsFrom": [ + "applicant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenant_invites": { + "name": "tenant_invites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "landlord_id": { + "name": "landlord_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "property_id": { + "name": "property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "available_on": { + "name": "available_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "invited_emails": { + "name": "invited_emails", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tenant_invites_landlord_id_users_id_fk": { + "name": "tenant_invites_landlord_id_users_id_fk", + "tableFrom": "tenant_invites", + "tableTo": "users", + "columnsFrom": [ + "landlord_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tenant_invites_property_id_properties_id_fk": { + "name": "tenant_invites_property_id_properties_id_fk", + "tableFrom": "tenant_invites", + "tableTo": "properties", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tenant_invites_lease_id_leases_id_fk": { + "name": "tenant_invites_lease_id_leases_id_fk", + "tableFrom": "tenant_invites", + "tableTo": "leases", + "columnsFrom": [ + "lease_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_roles": { + "name": "user_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role_id": { + "name": "role_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_roles_user_id_users_id_fk": { + "name": "user_roles_user_id_users_id_fk", + "tableFrom": "user_roles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_roles_role_id_roles_id_fk": { + "name": "user_roles_role_id_roles_id_fk", + "tableFrom": "user_roles", + "tableTo": "roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_tokens": { + "name": "user_tokens", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_id": { + "name": "state_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_tokens_user_id_users_id_fk": { + "name": "user_tokens_user_id_users_id_fk", + "tableFrom": "user_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_tokens_pkey": { + "name": "user_tokens_pkey", + "columns": [ + "user_id", + "state_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_pins": { + "name": "user_pins", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pin": { + "name": "pin", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "modified_on": { + "name": "modified_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_pins_user_id_users_id_fk": { + "name": "user_pins_user_id_users_id_fk", + "tableFrom": "user_pins", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_1": { + "name": "address_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_2": { + "name": "address_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_type": { + "name": "user_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "onboarded_landlord": { + "name": "onboarded_landlord", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "onboarded_tenant": { + "name": "onboarded_tenant", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "banking_setup_complete": { + "name": "banking_setup_complete", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "notification_preferences_json": { + "name": "notification_preferences_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "payment_preferences_json": { + "name": "payment_preferences_json", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "onboarding_step": { + "name": "onboarding_step", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "encrypted_data": { + "name": "encrypted_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_on": { + "name": "started_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.web_tokens": { + "name": "web_tokens", + "schema": "", + "columns": { + "token_id": { + "name": "token_id", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "state_id": { + "name": "state_id", + "type": "varchar(36)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_value": { + "name": "token_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_on": { + "name": "expires_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_on": { + "name": "created_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_on": { + "name": "updated_on", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "last_used_on": { + "name": "last_used_on", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "web_tokens_user_id_users_id_fk": { + "name": "web_tokens_user_id_users_id_fk", + "tableFrom": "web_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 87bdbfc..ba509b0 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -15,6 +15,20 @@ "when": 1770386163089, "tag": "0001_wandering_wallflower", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1771310799998, + "tag": "0002_powerful_justin_hammer", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1771311449353, + "tag": "0003_strong_namora", + "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/helpers.ts b/helpers.ts index 041b01a..254c2fb 100644 --- a/helpers.ts +++ b/helpers.ts @@ -122,9 +122,6 @@ export async function decryptDataWithStoredPassword( ): Promise { const password = getEncryptionPassword(userId); if (!password) { - console.log( - "[Encryption] decryptDataWithStoredPassword: no PIN for userId, skipping decrypt", - ); return ""; } try { @@ -134,25 +131,15 @@ export async function decryptDataWithStoredPassword( parsed = JSON.parse(decoded) as unknown; } catch { // Not JSON: treat as legacy plain base64 payload - console.log( - "[Encryption] decryptDataWithStoredPassword: legacy base64, returning decoded", - ); return decoded; } if (isEnvelope(parsed)) { const out = await decryptEnvelope(password, parsed); - console.log( - "[Encryption] decryptDataWithStoredPassword: envelope decrypted ok", - ); return out; } // JSON but not an envelope: legacy - console.log( - "[Encryption] decryptDataWithStoredPassword: JSON payload, returning as-is", - ); return decoded; - } catch (e) { - console.log("[Encryption] decryptDataWithStoredPassword: failed", e); + } catch { return ""; } } @@ -197,18 +184,12 @@ const ENCRYPTION_KEY_PREFIX = "encryption_password_"; export function getEncryptionPassword(userId: string): string | null { if (!userId) { - console.log("[Encryption] getEncryptionPassword: no userId"); return null; } try { const pin = globalThis.localStorage?.getItem( ENCRYPTION_KEY_PREFIX + userId, ); - console.log( - "[Encryption] getEncryptionPassword:", - pin ? "PIN available" : "no PIN", - "key=" + ENCRYPTION_KEY_PREFIX + userId, - ); return pin; } catch { return null; @@ -221,16 +202,13 @@ export function setEncryptionPassword( password: string, ): boolean { if (!userId) { - console.log("[Encryption] setEncryptionPassword: no userId, skip"); return false; } try { const key = getUserEncryptionKey(userId); if (key) globalThis.localStorage?.setItem(key, password); - console.log("[Encryption] setEncryptionPassword: stored for key=" + key); return true; - } catch (e) { - console.log("[Encryption] setEncryptionPassword: failed", e); + } catch { return false; } } diff --git a/islands/ConfirmSubmitGuard.tsx b/islands/ConfirmSubmitGuard.tsx new file mode 100644 index 0000000..841bf9b --- /dev/null +++ b/islands/ConfirmSubmitGuard.tsx @@ -0,0 +1,89 @@ +import { useEffect, useRef, useState } from "preact/hooks"; +import { useFocusTrap } from "../utils/useFocusTrap.ts"; + +/** + * Listens for submit on forms with data-confirm-message and shows a themed + * confirm dialog instead of the browser's native confirm(). + * Add to any page and add data-confirm-message="..." (and optionally + * data-confirm-label="Confirm") to forms that should use the themed dialog. + */ +export default function ConfirmSubmitGuard() { + const [state, setState] = useState< + { + message: string; + confirmLabel: string; + form: HTMLFormElement | null; + } | null + >(null); + const modalRef = useRef(null); + useFocusTrap(state !== null, modalRef); + + useEffect(() => { + const handleSubmit = (e: Event) => { + const form = e.target as HTMLFormElement; + const message = form?.getAttribute?.("data-confirm-message"); + if (!message || !form) return; + e.preventDefault(); + e.stopPropagation(); + const confirmLabel = form.getAttribute("data-confirm-label") || "Confirm"; + setState({ message, confirmLabel, form }); + }; + + document.addEventListener("submit", handleSubmit, true); + return () => document.removeEventListener("submit", handleSubmit, true); + }, []); + + const handleConfirm = () => { + if (state?.form) { + const form = state.form; + setState(null); + form.submit(); + } else { + setState(null); + } + }; + + const handleCancel = () => { + setState(null); + }; + + if (!state) return null; + + return ( + + ); +} diff --git a/islands/FinancialCalendar.tsx b/islands/FinancialCalendar.tsx index 76aa1c3..76a5fcc 100644 --- a/islands/FinancialCalendar.tsx +++ b/islands/FinancialCalendar.tsx @@ -200,19 +200,10 @@ export default function FinancialCalendar( let encryptionPassword = getEncryptionPassword(userId); const hasEncrypted = obligations.some((o) => o.encrypted_data) || incomes.some((i) => i.encrypted_data); - console.log( - "[Calendar] processObligations: userId=" + - (userId ? "set" : "missing") + " pin=" + - (encryptionPassword ? "available" : "missing") + " hasEncrypted=" + - hasEncrypted, - ); // If no local PIN but we have encrypted data, try to fetch PIN from server (e.g. new device or calendar opened first) if (!encryptionPassword && hasEncrypted) { try { - console.log( - "[Calendar] processObligations: fetching PIN from /api/sync-pin/get", - ); const res = await fetch("/api/sync-pin/get", { credentials: "include", cache: "no-store", @@ -221,32 +212,14 @@ export default function FinancialCalendar( const data = await res.json(); if (data.pin && setEncryptionPassword(userId, data.pin)) { encryptionPassword = data.pin; - console.log( - "[Calendar] processObligations: PIN stored from server, using for decrypt", - ); - } else { - console.log( - "[Calendar] processObligations: server response ok but no pin or set failed", - ); } - } else { - console.log( - "[Calendar] processObligations: sync-pin/get status=" + - res.status, - ); } } catch (_e) { - console.log( - "[Calendar] processObligations: sync-pin/get fetch failed", - _e, - ); + // sync-pin/get fetch failed } } if (!encryptionPassword) { - console.log( - "[Calendar] processObligations: no PIN, using obligations as-is", - ); dispatch({ type: "SET_PROCESSED_OBLIGATIONS", payload: obligations }); return; } @@ -265,7 +238,6 @@ export default function FinancialCalendar( ); if (decryptedData) { const parsed = JSON.parse(decryptedData); - console.log("[Calendar] decrypted obligation JSON:", parsed); if (parsed.amount !== undefined) { processedObligation.amount = parsed.amount; } @@ -274,23 +246,14 @@ export default function FinancialCalendar( } decryptedCount++; } - } catch (error) { - console.error( - "[Calendar] Failed to decrypt obligation data:", - error, - ); + } catch { + // decrypt failed for this obligation } } updatedObligations.push(processedObligation); } - if (decryptedCount > 0) { - console.log( - "[Calendar] processObligations: decrypted " + decryptedCount + - " obligation(s) with PIN", - ); - } dispatch({ type: "SET_PROCESSED_OBLIGATIONS", payload: updatedObligations, @@ -310,17 +273,9 @@ export default function FinancialCalendar( let encryptionPassword = getEncryptionPassword(userId); const hasEncrypted = obligations.some((o) => o.encrypted_data) || incomes.some((i) => i.encrypted_data); - console.log( - "[Calendar] processIncomes: userId=" + (userId ? "set" : "missing") + - " pin=" + (encryptionPassword ? "available" : "missing") + - " hasEncrypted=" + hasEncrypted, - ); if (!encryptionPassword && hasEncrypted) { try { - console.log( - "[Calendar] processIncomes: fetching PIN from /api/sync-pin/get", - ); const res = await fetch("/api/sync-pin/get", { credentials: "include", cache: "no-store", @@ -329,29 +284,14 @@ export default function FinancialCalendar( const data = await res.json(); if (data.pin && setEncryptionPassword(userId, data.pin)) { encryptionPassword = data.pin; - console.log( - "[Calendar] processIncomes: PIN stored from server, using for decrypt", - ); - } else { - console.log( - "[Calendar] processIncomes: server response ok but no pin or set failed", - ); } - } else { - console.log( - "[Calendar] processIncomes: sync-pin/get status=" + res.status, - ); } } catch (_e) { - console.log( - "[Calendar] processIncomes: sync-pin/get fetch failed", - _e, - ); + // sync-pin/get fetch failed } } if (!encryptionPassword) { - console.log("[Calendar] processIncomes: no PIN, using incomes as-is"); dispatch({ type: "SET_PROCESSED_INCOMES", payload: incomes }); return; } @@ -370,7 +310,6 @@ export default function FinancialCalendar( ); if (decryptedData) { const parsed = JSON.parse(decryptedData); - console.log("[Calendar] decrypted income JSON:", parsed); if (parsed.amount !== undefined) { processedIncome.amount = parsed.amount; } @@ -379,20 +318,14 @@ export default function FinancialCalendar( } decryptedCount++; } - } catch (error) { - console.error("[Calendar] Failed to decrypt income data:", error); + } catch { + // decrypt failed for this income } } updatedIncomes.push(processedIncome); } - if (decryptedCount > 0) { - console.log( - "[Calendar] processIncomes: decrypted " + decryptedCount + - " income(s) with PIN", - ); - } dispatch({ type: "SET_PROCESSED_INCOMES", payload: updatedIncomes }); }; diff --git a/islands/IncomeList.tsx b/islands/IncomeList.tsx index 7dcf2ec..4867e8f 100644 --- a/islands/IncomeList.tsx +++ b/islands/IncomeList.tsx @@ -128,6 +128,124 @@ export default function IncomeList( processIncomes(); }, []); + async function handleExportIncomes() { + const exportData = processedIncomes.map((inc) => ({ + id: inc.id, + source: inc.source ?? "", + amount: inc.amount ?? 0, + frequency: inc.frequency ?? "", + received_date: inc.received_date + ? new Date(inc.received_date).toISOString().split("T")[0] + : null, + })); + const json = JSON.stringify(exportData, null, 2); + const blob = new Blob([json], { type: "application/json" }); + const suggestedName = `incomes-${ + new Date().toISOString().split("T")[0] + }.json`; + + const showSave = (globalThis as { showSaveFilePicker?: unknown }) + .showSaveFilePicker; + if (typeof showSave === "function") { + try { + const handle = await (showSave as (opts: { + suggestedName: string; + types: { description: string; accept: Record }[]; + }) => Promise)({ + suggestedName, + types: [{ + description: "JSON file", + accept: { "application/json": [".json"] }, + }], + }); + const writable = await handle.createWritable(); + await writable.write(blob); + await writable.close(); + } catch (err) { + if ((err as Error).name !== "AbortError") { + console.error("Export failed:", err); + fallbackDownload(blob, suggestedName); + } + } + } else { + fallbackDownload(blob, suggestedName); + } + } + + function fallbackDownload(blob: Blob, filename: string) { + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + async function handleImportIncomes() { + let file: File; + const showOpen = (globalThis as { showOpenFilePicker?: unknown }) + .showOpenFilePicker; + if (typeof showOpen === "function") { + try { + const [handle] = await (showOpen as (opts: { + types: { description: string; accept: Record }[]; + multiple: boolean; + }) => Promise)({ + types: [{ + description: "JSON file", + accept: { "application/json": [".json"] }, + }], + multiple: false, + }); + file = await handle.getFile(); + } catch (err) { + if ((err as Error).name !== "AbortError") { + console.error("Import file picker failed:", err); + } + return; + } + } else { + const chosen = await new Promise((resolve) => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = "application/json,.json"; + input.onchange = () => resolve(input.files?.[0] ?? null); + input.click(); + }); + if (!chosen) return; + file = chosen; + } + + try { + const text = await file.text(); + const data = JSON.parse(text) as unknown; + const list = Array.isArray(data) + ? data + : (data as { incomes?: unknown[] })?.incomes; + if (!Array.isArray(list)) { + alert("Invalid file: expected a JSON array or { incomes: [] }"); + return; + } + const res = await fetch("/api/import/incomes", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(list), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + alert(err?.error ?? "Import failed"); + return; + } + const result = await res.json() as { added: number; skipped: number }; + if (result.added > 0 || result.skipped > 0) { + globalThis.location.reload(); + } + } catch (e) { + console.error("Import failed:", e); + alert("Invalid JSON or import failed."); + } + } + return ( <> {popupAddIncomeDialog} @@ -136,6 +254,22 @@ export default function IncomeList(

Your Incomes

+
+ + +
+ +
{ + e.preventDefault(); + handleRejectClick(e, approval.id); + }} + > + + +
+ + + ); + }) + ) + : ( +
+

+ No pending lease approvals +

+
+ )} + + + + + + + ); +} diff --git a/islands/LeaseApprovalConfirmDialog.tsx b/islands/LeaseApprovalConfirmDialog.tsx new file mode 100644 index 0000000..0b34796 --- /dev/null +++ b/islands/LeaseApprovalConfirmDialog.tsx @@ -0,0 +1,75 @@ +import { useRef } from "preact/hooks"; +import { useFocusTrap } from "../utils/useFocusTrap.ts"; + +export type ConfirmAction = "approve" | "reject"; + +interface LeaseApprovalConfirmDialogProps { + open: boolean; + action: ConfirmAction; + leaseId: string; + onConfirm: () => void; + onCancel: () => void; +} + +const MESSAGES: Record = { + approve: + "Are you sure you want to approve this lease? This will make the lease active and the tenant will be able to access the property.", + reject: "Are you sure you want to reject this lease request?", +}; + +const CONFIRM_LABELS: Record = { + approve: "Approve", + reject: "Reject", +}; + +export default function LeaseApprovalConfirmDialog({ + open, + action, + onConfirm, + onCancel, +}: LeaseApprovalConfirmDialogProps) { + const modalRef = useRef(null); + useFocusTrap(open, modalRef); + + if (!open) return null; + + return ( + + ); +} diff --git a/islands/MemberHome.tsx b/islands/MemberHome.tsx index 9bc1897..6b22ef9 100644 --- a/islands/MemberHome.tsx +++ b/islands/MemberHome.tsx @@ -25,16 +25,17 @@ export default function MemberHome( const loginPurpose = localStorage.getItem("login-purpose"); if (loginPurpose) { localStorage.removeItem("login-purpose"); + const tenantDone = Boolean(onboardingStatus?.onboarded_tenant); const destination = loginPurpose === "landlord" ? "/landlord/dashboard" : loginPurpose === "tenant" - ? "/tenant/dashboard" + ? (tenantDone ? "/tenant/dashboard" : "/tenant/onboarding") : ""; if (destination) { globalThis.location.href = destination; } } - }, []); + }, [onboardingStatus]); const tileIds = [ { id: "landlord-hub", label: "Landlord" }, @@ -60,7 +61,7 @@ export default function MemberHome( const tenantPrimary = tenantOnboarded ? { label: "Open Tenant Dashboard", href: "/tenant/dashboard" } - : { label: "Continue Tenant Setup", href: "/tenant/dashboard" }; + : { label: "Continue Tenant Setup", href: "/tenant/onboarding" }; const year = serverCalendarYear ?? new Date().getFullYear(); const month = serverCalendarMonth ?? new Date().getMonth() + 1; diff --git a/islands/ObligationList.tsx b/islands/ObligationList.tsx index 1181139..6d18638 100644 --- a/islands/ObligationList.tsx +++ b/islands/ObligationList.tsx @@ -182,9 +182,14 @@ export default function ObligationList( new Date().toISOString().split("T")[0] }.json`; - if (typeof globalThis.showSaveFilePicker === "function") { + const showSave = (globalThis as { showSaveFilePicker?: unknown }) + .showSaveFilePicker; + if (typeof showSave === "function") { try { - const handle = await globalThis.showSaveFilePicker({ + const handle = await (showSave as (opts: { + suggestedName: string; + types: { description: string; accept: Record }[]; + }) => Promise)({ suggestedName, types: [{ description: "JSON file", @@ -214,6 +219,71 @@ export default function ObligationList( URL.revokeObjectURL(url); } + async function handleImportObligations() { + let file: File; + const showOpen = (globalThis as { showOpenFilePicker?: unknown }) + .showOpenFilePicker; + if (typeof showOpen === "function") { + try { + const [handle] = await (showOpen as (opts: { + types: { description: string; accept: Record }[]; + multiple: boolean; + }) => Promise)({ + types: [{ + description: "JSON file", + accept: { "application/json": [".json"] }, + }], + multiple: false, + }); + file = await handle.getFile(); + } catch (err) { + if ((err as Error).name !== "AbortError") { + console.error("Import file picker failed:", err); + } + return; + } + } else { + const chosen = await new Promise((resolve) => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = "application/json,.json"; + input.onchange = () => resolve(input.files?.[0] ?? null); + input.click(); + }); + if (!chosen) return; + file = chosen; + } + + try { + const text = await file.text(); + const data = JSON.parse(text) as unknown; + const list = Array.isArray(data) + ? data + : (data as { obligations?: unknown[] })?.obligations; + if (!Array.isArray(list)) { + alert("Invalid file: expected a JSON array or { obligations: [] }"); + return; + } + const res = await fetch("/api/import/obligations", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(list), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + alert(err?.error ?? "Import failed"); + return; + } + const result = await res.json() as { added: number; skipped: number }; + if (result.added > 0 || result.skipped > 0) { + globalThis.location.reload(); + } + } catch (e) { + console.error("Import failed:", e); + alert("Invalid JSON or import failed."); + } + } + return ( <> {popupAddObligationDialog} @@ -222,13 +292,22 @@ export default function ObligationList(

Your Obligations

- +
+ + +
- -
+ + + +
+
+ + + +
+ + +
- +
+ +