# Database Direct Edit System

## Overview
A full-featured database management interface for administrators (admin/superuser roles only) that allows direct CRUD operations on all database tables with advanced spreadsheet-like functionality, comprehensive filtering, sorting, and inline editing capabilities.

**Status**: 📋 Planned (Not Yet Implemented)

**Target Users**: Administrators and Superusers only

**Security Level**: Requires AAL2 (MFA verification)

---

## Table of Contents
1. [Architecture & Security](#architecture--security)
2. [Core Components](#core-components)
3. [Feature Specifications](#feature-specifications)
4. [Database Requirements](#database-requirements)
5. [Implementation Roadmap](#implementation-roadmap)
6. [Dependencies](#dependencies)
7. [Safety & UX Guidelines](#safety--ux-guidelines)

---

## Architecture & Security

### Access Control
- **Role Restriction**: Only `admin` and `superuser` roles can access
- **AAL2 Enforcement**: All database operations require MFA verification via `useSuperuserGuard()`
- **Audit Logging**: Every modification logged to `admin_audit_log`
- **Warning Banner**: Display risk disclaimer about direct database access
- **Read-Only Mode**: Toggle to prevent accidental edits

### Route Structure
```
/admin/database              # Main database browser (table list)
/admin/database/:tableName   # Spreadsheet editor for specific table
```

### Navigation
- Add "Database Editor" link to AdminSidebar
- Icon: `Database` from lucide-react
- Position: Below "User Management"
- Visibility: Superuser only (`isSuperuser()`)

---

## Core Components

### File Structure
```
src/
├── pages/admin/
│   └── AdminDatabase.tsx                    # Main page with routing
│
├── components/admin/database/
│   ├── index.ts                             # Barrel exports
│   ├── DatabaseTableBrowser.tsx             # Table selector & overview
│   ├── DatabaseTableEditor.tsx              # Main spreadsheet editor (TanStack Table)
│   ├── DatabaseTableFilters.tsx             # Advanced filtering UI
│   ├── DatabaseColumnConfig.tsx             # Column visibility/order management
│   ├── DatabaseRowEditor.tsx                # Detailed row editor dialog
│   ├── DatabaseBulkActions.tsx              # Bulk edit/delete operations
│   ├── DatabaseExportImport.tsx             # CSV/JSON export/import
│   ├── DatabaseSchemaViewer.tsx             # Table schema & ERD viewer
│   ├── DatabaseCellEditors.tsx              # Type-specific cell editors
│   └── types.ts                             # TypeScript definitions
│
├── hooks/
│   ├── useTableSchema.ts                    # Fetch table schema from Supabase
│   ├── useTableData.ts                      # Fetch/edit table data with optimistic updates
│   ├── useDatabaseAudit.ts                  # Audit logging utilities
│   └── useDatabaseValidation.ts             # Validation functions
│
└── lib/
    ├── database/
    │   ├── cellEditors.tsx                  # Cell editor component factory
    │   ├── filterFunctions.ts               # Custom filter functions per data type
    │   ├── validationRules.ts               # Validation rules per column type
    │   └── schemaParser.ts                  # Parse Supabase schema to table config
    └── utils/
        ├── csvExport.ts                     # CSV export utilities
        └── jsonImport.ts                    # JSON import/validation
```

---

## Feature Specifications

### Phase 1: Table Browser & Navigation

#### DatabaseTableBrowser Component
**Purpose**: Display all database tables with metadata and quick navigation

**Features**:
- **Table List Display**:
  - Grid or list view toggle
  - Show table name, row count, size, last modified
  - Search/filter tables by name
  - Sort by name, row count, or date
  
- **Table Categorization**:
```typescript
const tableCategories = {
  auth: {
    color: 'red',
    tables: ['profiles', 'user_roles', 'user_preferences', 'user_sessions'],
    icon: 'Shield'
  },
  content: {
    color: 'yellow',
    tables: ['parks', 'rides', 'companies', 'ride_models', 'locations'],
    icon: 'MapPin'
  },
  submissions: {
    color: 'green',
    tables: ['content_submissions', 'submission_items', 'photo_submissions'],
    icon: 'FileText'
  },
  moderation: {
    color: 'blue',
    tables: ['reports', 'admin_audit_log', 'review_reports'],
    icon: 'Flag'
  },
  versioning: {
    color: 'purple',
    tables: ['park_versions', 'ride_versions', 'company_versions'],
    icon: 'History'
  },
  system: {
    color: 'gray',
    tables: ['admin_settings', 'notification_logs', 'rate_limits'],
    icon: 'Settings'
  }
}
```

- **Quick Stats Cards**:
  - Total tables count
  - Total rows across all tables
  - Database size
  - Last modified timestamp

- **Table Actions**:
  - Click table to open editor
  - Quick view schema (hover tooltip)
  - Export table data
  - View recent changes (from versions tables)

**Data Fetching**:
```typescript
// Use Supabase RPC to get table metadata
const { data: tables } = await supabase.rpc('get_table_metadata')

interface TableMetadata {
  table_name: string;
  row_count: bigint;
  total_size: string;
  last_modified: string;
  category?: string;
}
```

---

### Phase 2: Spreadsheet-Style Table Editor

#### DatabaseTableEditor Component
**Core Technology**: TanStack Table v8 with advanced features

#### 2.1 Data Grid Display

**Features**:
- **Virtual Scrolling**: Handle 10,000+ rows efficiently using `@tanstack/react-virtual`
- **Sticky Headers**: Column headers remain visible on scroll
- **Row Numbers**: Display row index in first column
- **Column Resizing**: Drag column borders to resize
- **Column Reordering**: Drag-drop column headers to reorder
- **Row Selection**: 
  - Single click to select row
  - Shift+Click for range selection
  - Ctrl+Click for multi-selection
  - Checkbox column for bulk selection
- **Zebra Striping**: Alternate row colors for readability
- **Cell Highlighting**: Hover effect on cells
- **Responsive Design**: Horizontal scroll on smaller screens

**Implementation**:
```tsx
const table = useReactTable({
  data: tableData,
  columns: dynamicColumns,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getSortedRowModel: getSortedRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
  enableRowSelection: true,
  enableMultiSort: true,
  enableColumnResizing: true,
  columnResizeMode: 'onChange',
  state: {
    sorting,
    columnFilters,
    columnVisibility,
    rowSelection,
    pagination
  }
})
```

#### 2.2 Inline Editing

**Cell Editor Types** (auto-detected from column type):

| Data Type | Editor Component | Features |
|-----------|------------------|----------|
| `text`, `varchar` | `<Input>` | Text input with validation |
| `integer`, `bigint`, `numeric` | `<Input type="number">` | Number input with min/max |
| `boolean` | `<Switch>` | Toggle switch |
| `timestamp`, `date` | `<DatePicker>` | Calendar popup with time |
| `uuid` | `<Select>` or `<Input>` | FK lookup or manual entry |
| `jsonb`, `json` | `<Textarea>` + JSON validator | Syntax highlighting |
| `enum` | `<Select>` | Dropdown with allowed values |
| `text[]` | `<TagInput>` | Multi-value chips |

**Edit Flow**:
1. Click cell → Enter edit mode
2. Show appropriate editor component
3. Type/select new value
4. Press Enter to save OR Escape to cancel
5. Optimistic update → DB save → Rollback on error
6. Visual feedback: Yellow (editing) → Green (saved) → Red (error)

**Validation**:
- Check NOT NULL constraints
- Validate data type compatibility
- Verify foreign key references exist
- Check max length for strings
- Validate JSON syntax
- Custom validation rules per table

**Implementation**:
```tsx
const EditableCell = ({ getValue, row, column, table }) => {
  const initialValue = getValue()
  const [value, setValue] = useState(initialValue)
  const [isEditing, setIsEditing] = useState(false)
  
  const handleSave = async () => {
    const meta = table.options.meta
    await meta?.updateData(row.index, column.id, value)
    setIsEditing(false)
  }
  
  return (
    <div onClick={() => setIsEditing(true)}>
      {isEditing ? (
        <Input 
          value={value} 
          onChange={(e) => setValue(e.target.value)}
          onBlur={handleSave}
          onKeyDown={(e) => {
            if (e.key === 'Enter') handleSave()
            if (e.key === 'Escape') setIsEditing(false)
          }}
        />
      ) : (
        <span>{value}</span>
      )}
    </div>
  )
}
```

#### 2.3 Advanced Filtering

**Filter Panel** (collapsible left sidebar):

**Filter Builder UI**:
- Add filter button
- Each filter row contains:
  - Column selector dropdown
  - Operator selector (based on column type)
  - Value input (type-appropriate)
  - Remove filter button
- AND/OR logic toggle between filters
- Clear all filters button
- Save filter preset (with name)
- Load saved preset

**Filter Types by Data Type**

**Text Columns**:
- Contains
- Equals
- Starts with
- Ends with
- Regex match
- Is empty / Is not empty

**Number Columns**:
- Equals
- Greater than (>)
- Less than (<)
- Greater than or equal (>=)
- Less than or equal (<=)
- Between (min, max)
- Is null / Is not null

**Date Columns**:
- Before
- After
- Between (start, end)
- Is null
- Last N days
- Next N days

**Boolean Columns**:
- Is true
- Is false
- Is null

**UUID Columns**:
- Equals
- In list (comma-separated)
- Is null

**JSON Columns**:
- Path exists
- Path equals value
- Contains key

**Quick Filters Bar** (above table):
- Chips showing active filters
- Click chip to edit filter
- X to remove filter
- "5 filters active" counter

**Implementation**:
```tsx
interface ColumnFilter {
  id: string;          // column name
  operator: FilterOperator;
  value: any;
  logicGate?: 'AND' | 'OR';
}

const filterFunctions = {
  text: {
    contains: (row, id, filterValue) => 
      row.getValue(id)?.toLowerCase().includes(filterValue.toLowerCase()),
    equals: (row, id, filterValue) => 
      row.getValue(id) === filterValue,
    startsWith: (row, id, filterValue) => 
      row.getValue(id)?.startsWith(filterValue),
    // ... more
  },
  number: {
    equals: (row, id, filterValue) => row.getValue(id) === filterValue,
    gt: (row, id, filterValue) => row.getValue(id) > filterValue,
    // ... more
  }
}
```

#### 2.4 Sorting & Pagination

**Sorting**:
- Click column header to sort ascending
- Click again to sort descending
- Click third time to remove sort
- Shift+Click to add multi-column sort
- Sort indicators: ↑ (asc), ↓ (desc)
- Multi-sort shows order numbers (1, 2, 3)
- Custom sort functions for complex types (JSON, arrays)

**Pagination**:
- Rows per page selector: 25, 50, 100, 500, 1000
- Page navigation: First, Prev, Next, Last
- Page input: "Jump to page X"
- Display: "Showing 1-25 of 1,234 rows"
- "Load all" button (with warning for tables > 1000 rows)
- Virtual scrolling for large datasets

**Implementation**:
```tsx
const [pagination, setPagination] = useState({
  pageIndex: 0,
  pageSize: 50
})

const table = useReactTable({
  // ...
  getPaginationRowModel: getPaginationRowModel(),
  onPaginationChange: setPagination,
  state: { pagination }
})

// Pagination controls
<div className="flex items-center gap-2">
  <Button onClick={() => table.setPageIndex(0)}>First</Button>
  <Button onClick={() => table.previousPage()}>Prev</Button>
  <Input 
    type="number" 
    value={table.getState().pagination.pageIndex + 1}
    onChange={(e) => table.setPageIndex(Number(e.target.value) - 1)}
  />
  <span>of {table.getPageCount()}</span>
  <Button onClick={() => table.nextPage()}>Next</Button>
  <Button onClick={() => table.setPageIndex(table.getPageCount() - 1)}>Last</Button>
</div>
```

#### 2.5 Column Management

**Column Config Panel** (collapsible right sidebar):

**Features**:
- **Column Visibility**:
  - Checkbox list of all columns
  - Toggle individual columns on/off
  - "Show all" / "Hide all" buttons
  - Search column names
  
- **Column Reordering**:
  - Drag-drop list to reorder columns
  - Visual drag handle icon
  - Smooth reorder animation

- **Column Pinning**:
  - Pin columns to left (e.g., ID, name)
  - Pin columns to right (e.g., actions)
  - Unpin button

- **Column Presets**:
  - Save current column layout with name
  - Load saved layouts
  - Default layouts per table:
    - "Essential" (ID, name, status, dates)
    - "All" (show everything)
    - "Metadata" (created_at, updated_at, created_by)

- **Reset to Default**:
  - Restore original column order/visibility

**Implementation**:
```tsx
const [columnVisibility, setColumnVisibility] = useState({})
const [columnOrder, setColumnOrder] = useState([])
const [columnPinning, setColumnPinning] = useState({ left: ['id'], right: [] })

const table = useReactTable({
  // ...
  state: {
    columnVisibility,
    columnOrder,
    columnPinning
  },
  onColumnVisibilityChange: setColumnVisibility,
  onColumnOrderChange: setColumnOrder,
  onColumnPinningChange: setColumnPinning
})
```

#### 2.6 Search & Navigation

**Global Search**:
- Search input above table
- Searches all visible columns
- Highlight matching text in cells
- "X matches found" counter
- Next/Prev navigation between matches
- Clear search button

**Quick Jump**:
- "Jump to row by ID" input
- Autofocus and scroll to row
- Highlight target row

**Keyboard Navigation**:
- Arrow keys: Navigate cells
- Tab: Move to next editable cell
- Enter: Edit current cell / Save edit
- Escape: Cancel edit
- Page Up/Down: Scroll page
- Home/End: Jump to first/last row
- Ctrl+F: Focus search
- Ctrl+C: Copy cell value
- Del: Delete selected rows (with confirmation)

---

### Phase 3: Advanced Features

#### 3.1 Bulk Operations (DatabaseBulkActions)

**Features**:
- **Row Selection**: Checkboxes in first column
- **Selection Actions**:
  - Select all on page
  - Select all matching filter
  - Deselect all
  - Invert selection
  
- **Bulk Edit**:
  - Select column to edit
  - Set value for all selected rows
  - Preview changes before applying
  - Apply button with confirmation

- **Bulk Delete**:
  - Delete all selected rows
  - Show count: "Delete 23 rows?"
  - Confirmation dialog with row preview
  - Cannot be undone warning

- **Batch Update**:
  - Apply formula/function to column
  - Examples:
    - Set status to 'approved' for all pending
    - Increment version number by 1
    - Update timestamps to NOW()

- **Action History**:
  - Show last 10 bulk operations
  - Display: operation, row count, timestamp, user
  - Undo last operation (if safe and within 5 minutes)

**UI**:
```tsx
<div className="bulk-actions-toolbar">
  <span>{selectedRows.length} rows selected</span>
  <Button onClick={handleBulkEdit}>Edit Selected</Button>
  <Button onClick={handleBulkDelete} variant="destructive">Delete Selected</Button>
  <Button onClick={handleDeselectAll} variant="ghost">Deselect All</Button>
</div>
```

#### 3.2 Export/Import (DatabaseExportImport)

**Export Features**:
- **Export Formats**:
  - CSV (with headers)
  - JSON (array of objects)
  - SQL INSERT statements
  
- **Export Scope**:
  - All rows
  - Filtered rows only
  - Selected rows only
  - Visible columns only
  
- **Export Options**:
  - Include/exclude NULL values
  - Date format selection
  - JSON pretty print
  - SQL: Include CREATE TABLE statement
  
- **Batch Export**:
  - Export all tables as ZIP file
  - Progress indicator for large exports

**Import Features**:
- **Import Formats**:
  - CSV (with column mapping UI)
  - JSON (validate schema first)
  
- **Import Modes**:
  - Insert (add new rows)
  - Update (match by ID)
  - Upsert (insert or update)
  
- **Import Validation**:
  - Dry-run preview
  - Show errors before import
  - Skip invalid rows option
  
- **Import Progress**:
  - Progress bar for large files
  - Success/error counts
  - Download error log

**Implementation**:
```tsx
const exportToCSV = (data: any[], columns: Column[]) => {
  const headers = columns.map(col => col.header).join(',')
  const rows = data.map(row => 
    columns.map(col => JSON.stringify(row[col.id] ?? '')).join(',')
  ).join('\n')
  
  const csv = `${headers}\n${rows}`
  downloadFile(csv, 'table_export.csv', 'text/csv')
}

const importFromCSV = async (file: File) => {
  const parsed = Papa.parse(file, { header: true })
  
  // Validate schema
  const validationErrors = validateImportData(parsed.data, tableSchema)
  if (validationErrors.length > 0) {
    showErrorDialog(validationErrors)
    return
  }
  
  // Insert rows
  for (const row of parsed.data) {
    await supabase.from(tableName).insert(row)
  }
}
```

#### 3.3 Schema Viewer (DatabaseSchemaViewer)

**Features**:
- **Table Structure Display**:
  - Column list with details:
    - Name
    - Data type
    - Nullable (✓/✗)
    - Default value
    - Max length (for text)
  - Primary key indicator (🔑)
  - Foreign key indicators (→ target_table)
  - Unique constraint indicators
  - Index indicators (📊)
  
- **Relationships View**:
  - Visual diagram (ERD style)
  - Show foreign key relationships
  - Click table to navigate
  - Zoom/pan controls
  
- **Constraints**:
  - Primary keys
  - Foreign keys (with ON DELETE/UPDATE actions)
  - Unique constraints
  - Check constraints
  - Indexes (btree, gin, gist)
  
- **RLS Policies** (read-only):
  - List all policies for table
  - Show policy name, operation, USING clause
  - Syntax highlighting for SQL
  
- **SQL DDL View**:
  - Show CREATE TABLE statement
  - Show CREATE INDEX statements
  - Show RLS policies
  - Copy to clipboard button
  - Syntax highlighting

**Implementation**:
```tsx
const SchemaViewer = ({ tableName }: { tableName: string }) => {
  const { data: schema } = useTableSchema(tableName)
  
  return (
    <Tabs>
      <TabsList>
        <TabsTrigger value="columns">Columns</TabsTrigger>
        <TabsTrigger value="relationships">Relationships</TabsTrigger>
        <TabsTrigger value="constraints">Constraints</TabsTrigger>
        <TabsTrigger value="rls">RLS Policies</TabsTrigger>
        <TabsTrigger value="sql">SQL DDL</TabsTrigger>
      </TabsList>
      
      <TabsContent value="columns">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Name</TableHead>
              <TableHead>Type</TableHead>
              <TableHead>Nullable</TableHead>
              <TableHead>Default</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {schema.columns.map(col => (
              <TableRow key={col.name}>
                <TableCell>
                  {col.isPrimaryKey && '🔑 '}
                  {col.name}
                </TableCell>
                <TableCell>{col.type}</TableCell>
                <TableCell>{col.isNullable ? '✓' : '✗'}</TableCell>
                <TableCell><code>{col.default}</code></TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </TabsContent>
      
      {/* Other tabs */}
    </Tabs>
  )
}
```

#### 3.4 Row Editor Dialog (DatabaseRowEditor)

**Purpose**: Detailed form-based editor for complex edits

**Features**:
- **Trigger**: Double-click row to open
- **Layout**: Vertical form with all columns
- **Field Types**:
  - Text: `<Textarea>` for long text
  - JSON: Syntax-highlighted editor with validation
  - Foreign Keys: Searchable `<Combobox>` with preview
  - Images: Preview thumbnail + URL input
  - Markdown: Split view (editor + preview)
  - Arrays: Chip input for multi-value
  - Timestamps: Calendar + time picker
  
- **Actions**:
  - Save: Update row
  - Save & Close: Update and return to table
  - Clone Row: Duplicate with new ID
  - Delete Row: With confirmation
  - Cancel: Discard changes
  
- **Validation**:
  - Real-time field validation
  - Show error messages inline
  - Disable save until valid
  
- **Metadata Section**:
  - Show created_at, updated_at, created_by
  - Show version history (if versioned table)
  - Link to audit log entries

**Implementation**:
```tsx
<Dialog open={isOpen} onOpenChange={setIsOpen}>
  <DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto">
    <DialogHeader>
      <DialogTitle>Edit Row: {rowId}</DialogTitle>
    </DialogHeader>
    
    <form onSubmit={handleSave}>
      {columns.map(col => (
        <div key={col.name} className="space-y-2">
          <Label>{col.name}</Label>
          {renderFieldEditor(col, formData[col.name], handleChange)}
          {errors[col.name] && (
            <p className="text-sm text-destructive">{errors[col.name]}</p>
          )}
        </div>
      ))}
      
      <DialogFooter>
        <Button variant="ghost" onClick={() => setIsOpen(false)}>Cancel</Button>
        <Button variant="outline" onClick={handleClone}>Clone Row</Button>
        <Button variant="destructive" onClick={handleDelete}>Delete</Button>
        <Button type="submit">Save</Button>
      </DialogFooter>
    </form>
  </DialogContent>
</Dialog>
```

#### 3.5 Real-time Updates

**Features**:
- **Realtime Subscriptions**:
  - Subscribe to table changes via Supabase Realtime
  - Listen for INSERT, UPDATE, DELETE events
  - Show notification when another admin edits
  
- **Conflict Detection**:
  - Detect concurrent edits on same row
  - Show warning: "User X is editing this row"
  - Lock row to prevent conflicts (optional)
  
- **Auto-refresh**:
  - Toggle auto-refresh on/off
  - Refresh interval: 10s, 30s, 60s
  - Manual refresh button
  
- **Change Notifications**:
  - Toast notification: "Table updated by Admin (3 rows changed)"
  - Click to refresh table
  - Dismiss notification

**Implementation**:
```tsx
useEffect(() => {
  const channel = supabase
    .channel(`table:${tableName}`)
    .on('postgres_changes', 
      { 
        event: '*', 
        schema: 'public', 
        table: tableName 
      },
      (payload) => {
        toast.info(`Table updated: ${payload.eventType}`)
        refetch() // Refetch table data
      }
    )
    .subscribe()
  
  return () => {
    supabase.removeChannel(channel)
  }
}, [tableName])
```

#### 3.6 Audit Trail Integration

**Features**:
- **Automatic Logging**:
  - Every edit logged to `admin_audit_log`
  - Log: admin_user_id, table, row_id, action, old_values, new_values, timestamp
  
- **Recent Changes Panel** (sidebar):
  - Show last 20 changes on current table
  - Display: timestamp, user, action, affected columns
  - Click to view full details
  
- **Change Details Dialog**:
  - Show before/after diff
  - Highlight changed fields
  - Show user who made change
  - Link to row in table
  
- **Revert Functionality**:
  - Revert single change (if safe)
  - Check for dependent changes
  - Confirmation dialog
  - Cannot revert if related data deleted

**Implementation**:
```tsx
const logDatabaseEdit = async (
  tableName: string,
  rowId: string,
  operation: 'INSERT' | 'UPDATE' | 'DELETE',
  oldValues: any,
  newValues: any
) => {
  await supabase.rpc('log_database_direct_edit', {
    _table_name: tableName,
    _row_id: rowId,
    _operation: operation,
    _old_values: oldValues,
    _new_values: newValues
  })
}

// In updateData function:
const handleUpdate = async (rowIndex, columnId, value) => {
  const row = tableData[rowIndex]
  const oldValue = row[columnId]
  
  // Update database
  await supabase.from(tableName).update({ [columnId]: value }).eq('id', row.id)
  
  // Log audit trail
  await logDatabaseEdit(
    tableName,
    row.id,
    'UPDATE',
    { [columnId]: oldValue },
    { [columnId]: value }
  )
}
```

---

## Database Requirements

### Supabase RPC Functions

#### 1. get_table_metadata()
Returns metadata for all tables in public schema.

```sql
CREATE OR REPLACE FUNCTION get_table_metadata()
RETURNS TABLE (
  table_name text,
  row_count bigint,
  total_size text,
  last_modified timestamp with time zone
) 
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = 'public'
AS $$
BEGIN
  RETURN QUERY
  SELECT 
    t.tablename::text as table_name,
    COALESCE(c.reltuples::bigint, 0) as row_count,
    pg_size_pretty(pg_total_relation_size(t.schemaname||'.'||t.tablename)) as total_size,
    COALESCE(
      (SELECT MAX(created_at) FROM information_schema.tables WHERE table_name = t.tablename),
      NOW()
    ) as last_modified
  FROM pg_tables t
  LEFT JOIN pg_class c ON c.relname = t.tablename
  WHERE t.schemaname = 'public'
  ORDER BY t.tablename;
END;
$$;

-- Grant execute to admins only
REVOKE EXECUTE ON FUNCTION get_table_metadata() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION get_table_metadata() TO authenticated;

-- RLS policy: only admins/superusers can execute
CREATE POLICY "admin_only_table_metadata" ON ... -- (handled in calling code)
```

#### 2. get_table_schema(table_name text)
Returns detailed schema information for a specific table.

```sql
CREATE OR REPLACE FUNCTION get_table_schema(_table_name text)
RETURNS jsonb
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = 'public'
AS $$
DECLARE
  schema_info jsonb;
BEGIN
  -- Get column information
  SELECT jsonb_agg(
    jsonb_build_object(
      'name', column_name,
      'type', data_type,
      'isNullable', is_nullable = 'YES',
      'maxLength', character_maximum_length,
      'numericPrecision', numeric_precision,
      'defaultValue', column_default,
      'isPrimaryKey', EXISTS (
        SELECT 1 FROM information_schema.table_constraints tc
        JOIN information_schema.key_column_usage kcu
          ON tc.constraint_name = kcu.constraint_name
        WHERE tc.table_name = _table_name
          AND tc.constraint_type = 'PRIMARY KEY'
          AND kcu.column_name = c.column_name
      ),
      'foreignKey', (
        SELECT jsonb_build_object(
          'table', ccu.table_name,
          'column', ccu.column_name
        )
        FROM information_schema.table_constraints tc
        JOIN information_schema.key_column_usage kcu
          ON tc.constraint_name = kcu.constraint_name
        JOIN information_schema.constraint_column_usage ccu
          ON ccu.constraint_name = tc.constraint_name
        WHERE tc.table_name = _table_name
          AND tc.constraint_type = 'FOREIGN KEY'
          AND kcu.column_name = c.column_name
        LIMIT 1
      ),
      'enumValues', (
        SELECT array_agg(enumlabel)
        FROM pg_enum e
        JOIN pg_type t ON t.oid = e.enumtypid
        WHERE t.typname = c.udt_name
      )
    )
  )
  INTO schema_info
  FROM information_schema.columns c
  WHERE table_name = _table_name
    AND table_schema = 'public';
  
  RETURN schema_info;
END;
$$;
```

#### 3. log_database_direct_edit(...)
Logs all direct database edits to audit trail.

```sql
CREATE OR REPLACE FUNCTION log_database_direct_edit(
  _table_name text,
  _row_id uuid,
  _operation text,
  _old_values jsonb,
  _new_values jsonb
)
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = 'public'
AS $$
BEGIN
  INSERT INTO admin_audit_log (
    admin_user_id,
    target_user_id,
    action,
    details
  ) VALUES (
    auth.uid(),
    NULL, -- No specific target user for direct DB edits
    'direct_database_edit',
    jsonb_build_object(
      'table', _table_name,
      'row_id', _row_id,
      'operation', _operation,
      'old_values', _old_values,
      'new_values', _new_values,
      'timestamp', NOW()
    )
  );
END;
$$;
```

### RLS Policies

All tables must enforce AAL2 for admin/superuser direct edits:

```sql
-- Example policy for parks table
CREATE POLICY "admin_direct_edit_requires_aal2"
ON parks
FOR ALL
USING (
  (
    EXISTS (
      SELECT 1 FROM user_roles
      WHERE user_id = auth.uid()
      AND role IN ('admin', 'superuser')
    )
  )
  AND (
    (auth.jwt() ->> 'aal') = 'aal2'
    OR NOT EXISTS (
      SELECT 1 FROM auth.mfa_factors
      WHERE user_id = auth.uid()
      AND status = 'verified'
    )
  )
);

-- Apply to all tables:
-- parks, rides, companies, ride_models, locations, profiles, etc.
```

---

## Implementation Roadmap

### Sprint 1: Foundation (Days 1-3)
**Goal**: Set up routes, navigation, and basic table browser

**Tasks**:
1. ✅ Create `/admin/database` route in router
2. ✅ Create `AdminDatabase.tsx` page component
3. ✅ Add "Database Editor" link to AdminSidebar (superuser only)
4. ✅ Implement access control:
   - Check `isAdmin()` or `isSuperuser()`
   - Enforce AAL2 with `useSuperuserGuard()`
   - Redirect if unauthorized
5. ✅ Create `DatabaseTableBrowser.tsx` component
6. ✅ Implement `get_table_metadata()` RPC function
7. ✅ Fetch and display table list with metadata
8. ✅ Add table categorization and color coding
9. ✅ Implement table search/filter
10. ✅ Add warning banner about direct DB access

**Deliverables**:
- Working `/admin/database` page
- Table list with categories
- Navigation from sidebar
- AAL2 enforcement

---

### Sprint 2: Core Editor (Days 4-7)
**Goal**: Build main spreadsheet editor with basic editing

**Tasks**:
1. ✅ Install dependencies: `@tanstack/react-virtual`
2. ✅ Create `/admin/database/:tableName` route
3. ✅ Create `DatabaseTableEditor.tsx` component
4. ✅ Implement `get_table_schema()` RPC function
5. ✅ Fetch table schema and generate dynamic columns
6. ✅ Set up TanStack Table with:
   - Core row model
   - Pagination
   - Row selection
7. ✅ Implement basic inline editing:
   - Text fields
   - Number fields
   - Boolean switches
8. ✅ Add optimistic updates with rollback
9. ✅ Implement save/error handling
10. ✅ Add visual feedback (yellow → green → red)
11. ✅ Create `log_database_direct_edit()` RPC function
12. ✅ Integrate audit logging on all edits

**Deliverables**:
- Working table editor page
- Inline editing for basic types
- Audit logging functional

---

### Sprint 3: Advanced Editing (Days 8-10)
**Goal**: Complete all cell editors, filtering, sorting, column config

**Tasks**:
1. ✅ Implement all cell editor types:
   - Date/Timestamp picker
   - UUID foreign key selector
   - JSON editor with validation
   - Enum dropdown
   - Array/tag input
2. ✅ Create `DatabaseTableFilters.tsx` component
3. ✅ Implement filter builder UI
4. ✅ Add filter functions for all data types
5. ✅ Integrate TanStack Table filtering
6. ✅ Add quick filters bar
7. ✅ Implement saved filter presets
8. ✅ Add multi-column sorting (Shift+Click)
9. ✅ Create `DatabaseColumnConfig.tsx` component
10. ✅ Implement column visibility toggle
11. ✅ Add column reordering (drag-drop)
12. ✅ Implement column pinning
13. ✅ Add column layout presets
14. ✅ Implement keyboard navigation
15. ✅ Add global search

**Deliverables**:
- All cell editors working
- Advanced filtering UI
- Multi-column sorting
- Column management panel
- Keyboard shortcuts

---

### Sprint 4: Bulk & Export (Days 11-12)
**Goal**: Add bulk operations and import/export functionality

**Tasks**:
1. ✅ Install dependencies: `papaparse`
2. ✅ Create `DatabaseBulkActions.tsx` component
3. ✅ Implement bulk edit functionality
4. ✅ Implement bulk delete with confirmation
5. ✅ Add batch update feature
6. ✅ Create action history with undo
7. ✅ Create `DatabaseExportImport.tsx` component
8. ✅ Implement CSV export
9. ✅ Implement JSON export
10. ✅ Implement SQL export
11. ✅ Add export options (scope, format)
12. ✅ Implement CSV import with validation
13. ✅ Implement JSON import
14. ✅ Add import preview/dry-run
15. ✅ Create batch export (all tables as ZIP)

**Deliverables**:
- Bulk operations working
- Export in multiple formats
- Import with validation
- Action history with undo

---

### Sprint 5: Polish & Safety (Days 13-14)
**Goal**: Add schema viewer, safety features, and final polish

**Tasks**:
1. ✅ Create `DatabaseSchemaViewer.tsx` component
2. ✅ Implement columns view
3. ✅ Add relationships/ERD view
4. ✅ Display constraints and indexes
5. ✅ Show RLS policies (read-only)
6. ✅ Add SQL DDL view with syntax highlighting
7. ✅ Implement read-only mode toggle
8. ✅ Add confirmation dialogs for all destructive actions
9. ✅ Implement real-time updates (Supabase Realtime)
10. ✅ Create recent changes panel
11. ✅ Add change details dialog with diff view
12. ✅ Implement revert functionality (if safe)
13. ✅ Add all safety warnings and checks
14. ✅ Full testing (manual + automated if time)
15. ✅ Write documentation (this file)

**Deliverables**:
- Complete schema viewer
- All safety features
- Real-time updates
- Full testing
- Documentation

---

## Dependencies

### Required npm Packages

```bash
# Virtual scrolling for large tables
npm install @tanstack/react-virtual

# JSON viewer/editor
npm install react-json-view-lite

# Syntax highlighting
npm install prismjs
npm install @types/prismjs --save-dev

# CSV parsing/generation
npm install papaparse
npm install @types/papaparse --save-dev
```

### Already Installed (✅)
- `@tanstack/react-table` - Table management
- `lucide-react` - Icons
- `react-hook-form` - Form handling
- `zod` - Validation
- `date-fns` - Date utilities
- `sonner` - Toast notifications

---

## Safety & UX Guidelines

### Safety Features Checklist

#### Confirmation Dialogs
- [ ] Delete single row → "Delete this row?"
- [ ] Delete multiple rows → "Delete X rows? This cannot be undone."
- [ ] Bulk edit → "Update X rows?"
- [ ] Drop table → "⚠️ DANGER: Delete entire table?"
- [ ] Import data → "Import X rows? Existing data may be overwritten."

#### Validation
- [ ] Enforce NOT NULL constraints
- [ ] Check data type compatibility
- [ ] Validate foreign key references exist
- [ ] Check max length for strings
- [ ] Validate JSON syntax
- [ ] Prevent orphan records (check dependencies)

#### Access Control
- [ ] AAL2 enforcement on page load
- [ ] Re-check AAL2 before each destructive operation
- [ ] Show MFA prompt if session drops to AAL1
- [ ] Log all operations to audit trail

#### Error Handling
- [ ] Rollback optimistic updates on error
- [ ] Show clear error messages
- [ ] Prevent partial updates (use transactions)
- [ ] Handle foreign key constraint violations gracefully

#### Data Integrity
- [ ] Warn before editing critical tables (auth, profiles)
- [ ] Prevent editing system columns (id, created_at, etc.)
- [ ] Check for circular foreign key dependencies
- [ ] Validate enum values against allowed list

---

### UX Best Practices

#### Loading States
```tsx
// Skeleton loader for table
{isLoading && <TableSkeleton rows={10} cols={8} />}

// Cell-level loading (during save)
{isSaving && <Spinner className="w-4 h-4" />}

// Progress bar for bulk operations
<Progress value={progress} max={total} />
```

#### Error Feedback
```tsx
// Toast notifications
toast.error('Failed to save: Foreign key violation')
toast.success('Successfully updated 5 rows')
toast.warning('Table has unsaved changes')

// Inline validation
<Input 
  error={errors.email?.message}
  className={errors.email && 'border-destructive'}
/>
```

#### Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `Ctrl+F` | Focus search |
| `Ctrl+S` | Save current row |
| `Ctrl+Z` | Undo last change |
| `Del` | Delete selected rows |
| `Ctrl+E` | Toggle edit mode |
| `Escape` | Cancel edit / Close dialog |
| `Enter` | Save edit / Submit form |
| `Arrow keys` | Navigate cells |
| `Tab` | Move to next cell |
| `Shift+Tab` | Move to previous cell |
| `Page Up/Down` | Scroll page |
| `Home/End` | Jump to first/last row |
| `?` | Show keyboard shortcuts help |

#### Responsive Design
- Horizontal scroll for tables on mobile
- Collapsible filter/column panels
- Touch-friendly cell editors (larger tap targets)
- Stack filters vertically on small screens
- Hide less important columns by default on mobile

#### Dark Mode Support
```css
/* Table grid */
.table-cell {
  @apply border-border/40;
  @apply hover:bg-accent/50;
}

/* Cell states */
.cell-editing { @apply bg-yellow-500/20 dark:bg-yellow-500/10; }
.cell-saved { @apply bg-green-500/20 dark:bg-green-500/10; }
.cell-error { @apply bg-red-500/20 dark:bg-red-500/10; }

/* Syntax highlighting */
.json-editor {
  @apply bg-background text-foreground;
}
```

---

## Security Considerations

### Authentication & Authorization
1. **AAL2 Enforcement**: All database operations require MFA verification
2. **Role-Based Access**: Only admin/superuser roles can access
3. **Session Monitoring**: Detect if session drops to AAL1 during use
4. **Token Refresh**: Handle token expiration gracefully

### Data Protection
1. **Audit Logging**: Every edit logged with user, timestamp, before/after values
2. **RLS Policies**: Enforce row-level security even in admin UI
3. **Input Sanitization**: Validate all user input before DB writes
4. **SQL Injection Prevention**: Use parameterized queries (Supabase handles this)

### Rate Limiting
```sql
-- Prevent abuse: max 1000 edits per admin per hour
CREATE POLICY "rate_limit_admin_edits"
ON admin_audit_log
FOR INSERT
WITH CHECK (
  (SELECT COUNT(*) FROM admin_audit_log 
   WHERE admin_user_id = auth.uid() 
   AND created_at > NOW() - INTERVAL '1 hour'
   AND action = 'direct_database_edit') < 1000
);
```

### Dangerous Operations Warning
Display prominent warnings for:
- Editing auth-related tables (profiles, user_roles)
- Deleting > 100 rows at once
- Importing > 1000 rows
- Editing production environment (if detected)

---

## Testing Strategy

### Manual Testing Checklist
- [ ] AAL2 enforcement blocks AAL1 users
- [ ] Non-admin users cannot access page
- [ ] All table categories display correctly
- [ ] Table search/filter works
- [ ] Table editor loads data correctly
- [ ] Inline editing saves successfully
- [ ] Optimistic updates rollback on error
- [ ] All cell editor types work
- [ ] Filtering works for all data types
- [ ] Multi-column sorting works
- [ ] Column visibility toggle works
- [ ] Column reordering works
- [ ] Pagination works correctly
- [ ] Global search highlights matches
- [ ] Keyboard navigation works
- [ ] Bulk edit updates multiple rows
- [ ] Bulk delete confirms and deletes
- [ ] Export CSV/JSON/SQL works
- [ ] Import validates and inserts data
- [ ] Schema viewer displays correctly
- [ ] Real-time updates show notifications
- [ ] Audit trail logs all edits
- [ ] Recent changes panel displays edits
- [ ] Revert functionality works
- [ ] Confirmation dialogs show for destructive actions
- [ ] Read-only mode prevents edits
- [ ] Dark mode styling looks good

### Automated Testing (Future)
```typescript
// Example Playwright test
test('admin can edit table cell', async ({ page }) => {
  await loginAsAdmin(page)
  await page.goto('/admin/database/parks')
  
  // Click cell to edit
  await page.click('[data-cell="name-0"]')
  await page.fill('input', 'New Park Name')
  await page.press('input', 'Enter')
  
  // Wait for save
  await page.waitForSelector('.cell-saved')
  
  // Verify in database
  const park = await getParkByName('New Park Name')
  expect(park).toBeTruthy()
})
```

---

## Future Enhancements

### Phase 2 Features (Post-MVP)
1. **SQL Query Console**:
   - Raw SQL query interface
   - Query history
   - Result set export
   - Query templates/snippets
   
2. **Visual Query Builder**:
   - Drag-drop JOIN builder
   - Visual WHERE clause builder
   - Preview results
   
3. **Scheduled Jobs**:
   - Schedule bulk updates
   - Automated cleanup scripts
   - Email reports
   
4. **Advanced Analytics**:
   - Table usage statistics
   - Most edited tables
   - Edit frequency heatmap
   - Admin activity dashboard
   
5. **Version Control Integration**:
   - Git-like diff view
   - Branch/merge for schema changes
   - Rollback to previous states
   
6. **Collaboration Features**:
   - Live presence indicators (who's viewing)
   - Comments on rows
   - Change proposals (before applying)
   - Team chat in context

---

## Troubleshooting

### Common Issues

**Issue**: "AAL2 required" error blocks access
- **Solution**: Ensure user has MFA enrolled and verified. Prompt to verify MFA if session is at AAL1.

**Issue**: Table loads slowly (> 5s)
- **Solution**: Add pagination, reduce pageSize, add indexes to frequently filtered columns.

**Issue**: Foreign key dropdown shows too many options
- **Solution**: Add search/autocomplete to dropdown, paginate results, show only recent 100.

**Issue**: JSON editor loses formatting on save
- **Solution**: Ensure JSON is pretty-printed before display, preserve whitespace.

**Issue**: Real-time updates cause table to jump
- **Solution**: Use optimistic updates, throttle refresh notifications, show "refresh available" button instead of auto-refresh.

**Issue**: Export fails for large tables (> 10k rows)
- **Solution**: Stream export instead of loading all in memory, show progress bar, split into chunks.

---

## Related Documentation
- [DATABASE_ARCHITECTURE.md](./DATABASE_ARCHITECTURE.md) - Database schema and RLS policies
- [ARCHITECTURE_OVERVIEW.md](./ARCHITECTURE_OVERVIEW.md) - System architecture
- [ADMIN_FEATURES.md](./ADMIN_FEATURES.md) - Other admin features (if exists)

---

## Change Log

| Date | Version | Changes | Author |
|------|---------|---------|--------|
| 2025-01-XX | 1.0.0 | Initial documentation | System |

---

## Approval & Sign-off

**Status**: 📋 Planned (awaiting approval)

**Reviewed By**: _Pending_

**Approved By**: _Pending_

**Implementation Start Date**: _TBD_

**Expected Completion**: _TBD_ (14 days after start)

---

*End of Document*
