'use client'; import { useState } from 'react'; import { DatabaseIcon, KeyIcon, LinkIcon, TableIcon } from 'lucide-react'; import { Badge } from '@kit/ui/badge'; import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card'; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@kit/ui/dialog'; import { Separator } from '@kit/ui/separator'; import { getTableDetailsAction } from '../_lib/server/table-server-actions'; interface TableColumn { name: string; type: string; nullable: boolean; defaultValue: string | null; isPrimaryKey: boolean; isForeignKey: boolean; referencedTable: string | null; referencedColumn: string | null; } interface TableInfo { name: string; schema: string; sourceFile: string; topic: string; columns: TableColumn[]; foreignKeys: Array<{ name: string; columns: string[]; referencedTable: string; referencedColumns: string[]; onDelete: string; onUpdate: string; }>; indexes: Array<{ name: string; columns: string[]; unique: boolean; type: string; }>; } interface TableSummary { name: string; schema: string; sourceFile: string; topic: string; } interface TableBrowserProps { searchTerm: string; tables: TableSummary[]; } const topicColors: Record = { accounts: 'bg-green-100 text-green-800', teams: 'bg-teal-100 text-teal-800', billing: 'bg-yellow-100 text-yellow-800', configuration: 'bg-blue-100 text-blue-800', auth: 'bg-indigo-100 text-indigo-800', notifications: 'bg-pink-100 text-pink-800', permissions: 'bg-orange-100 text-orange-800', general: 'bg-gray-100 text-gray-800', }; const typeColors: Record = { uuid: 'bg-purple-100 text-purple-800', text: 'bg-blue-100 text-blue-800', 'character varying': 'bg-blue-100 text-blue-800', boolean: 'bg-green-100 text-green-800', integer: 'bg-orange-100 text-orange-800', 'timestamp with time zone': 'bg-gray-100 text-gray-800', jsonb: 'bg-yellow-100 text-yellow-800', 'USER-DEFINED': 'bg-red-100 text-red-800', }; export function TableBrowser({ searchTerm, tables }: TableBrowserProps) { const [selectedTable, setSelectedTable] = useState(null); const [tableDetails, setTableDetails] = useState(null); const [loading, setLoading] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false); // Filter tables based on search term const filteredTables = tables.filter( (table) => table.name.toLowerCase().includes(searchTerm.toLowerCase()) || table.topic.toLowerCase().includes(searchTerm.toLowerCase()) || table.sourceFile.toLowerCase().includes(searchTerm.toLowerCase()), ); // Group tables by topic const tablesByTopic = filteredTables.reduce( (acc, table) => { if (!acc[table.topic]) { acc[table.topic] = []; } acc[table.topic].push(table); return acc; }, {} as Record, ); const handleTableClick = async (tableName: string) => { setSelectedTable(tableName); setIsDialogOpen(true); setLoading(true); setTableDetails(null); try { const result = await getTableDetailsAction(tableName, 'public'); if (result.success && result.data) { setTableDetails(result.data); } else { console.error('Error fetching table details:', result.error); setTableDetails(null); } } catch (error) { console.error('Error fetching table details:', error); setTableDetails(null); } finally { setLoading(false); } }; const closeDialog = () => { setIsDialogOpen(false); setSelectedTable(null); setTableDetails(null); }; const getColumnTypeDisplay = (type: string) => { const cleanType = type.replace('character varying', 'varchar'); return cleanType; }; return (
{/* Summary */}

Total Tables

{filteredTables.length}

Topics

{Object.keys(tablesByTopic).length}

Schema

public

{/* Tables by Topic */} {Object.entries(tablesByTopic).map(([topic, topicTables]) => (
{topic.toUpperCase()} {topicTables.length} table{topicTables.length !== 1 ? 's' : ''}
{topicTables.map((table) => ( handleTableClick(table.name)} > {table.name}
Schema: {table.schema}
Source: {table.sourceFile}
Click for details
))}
))} {/* Table Details Dialog */} Table Details: {selectedTable}
{loading && (

Loading table details...

)} {selectedTable && tableDetails && ( <> {/* Columns */}

Columns ({tableDetails.columns.length})

{tableDetails.columns.map((column) => ( ))}
Name Type Nullable Default Constraints
{column.name} {getColumnTypeDisplay(column.type)} {column.nullable ? 'YES' : 'NO'} {column.defaultValue || '—'}
{column.isPrimaryKey && ( PK )} {column.isForeignKey && ( FK )}
{/* Foreign Keys */} {tableDetails.foreignKeys.length > 0 && ( <>

Foreign Keys ({tableDetails.foreignKeys.length})

{tableDetails.foreignKeys.map((fk) => (
{fk.columns.join(', ')} {fk.referencedTable}. {fk.referencedColumns.join(', ')} ON DELETE {fk.onDelete}
))}
)} {/* Indexes */} {tableDetails.indexes.length > 0 && ( <>

Indexes ({tableDetails.indexes.length})

{tableDetails.indexes.map((index) => (
{index.name} {index.unique && ( UNIQUE )} {index.type.toUpperCase()}
on {index.columns.join(', ')}
))}
)} )} {selectedTable && !tableDetails && !loading && (

No detailed information available for this table.

)}
{filteredTables.length === 0 && (

{searchTerm ? 'No tables match your search' : 'No tables found'}

)}
); }