Purpose: Top navigation bar with app title and connection status
Location: app/components/Header.tsx
Props:
isConnected: boolean- Database connection statusdatabaseName?: string- Current database name
Visual:
- Height: 64px
- Background: white
- Border bottom: gray-200
- Contains: Logo/title (left), connection status badge (right)
Purpose: Navigation and table list
Location: app/components/Sidebar.tsx
Props:
tables: string[]- List of database tablesselectedTable?: string- Currently selected tableonTableSelect: (table: string) => void- Table selection handler
Visual:
- Width: 240px
- Background: gray-50
- Border right: gray-200
- Sticky positioning
- Scrollable content area
Purpose: Primary content area wrapper
Location: app/components/MainContent.tsx
Props:
children: ReactNode
Visual:
- Flex: 1 (takes remaining space)
- Padding: 24px
- Background: white
Purpose: Database connection input form
Location: app/components/ConnectionForm.tsx
Props:
onConnect: (config: DBConfig) => void- Submit handlerisConnecting: boolean- Loading state
Fields:
- Host (text input)
- Port (number input)
- Database (text input)
- Username (text input)
- Password (password input)
- Connect button
Visual:
- Card layout
- Centered on page
- Max width: 480px
- Form validation states
Purpose: Display current connection state
Location: app/components/ConnectionStatus.tsx
Props:
status: 'connected' | 'disconnected' | 'connecting'databaseName?: string
Visual:
- Badge component
- Green for connected
- Gray for disconnected
- Yellow for connecting (with spinner)
Purpose: Sidebar list of all database tables
Location: app/components/TableList.tsx
Props:
tables: string[]selectedTable?: stringonSelect: (table: string) => void
Visual:
- List items with hover states
- Active state for selected table
- Search/filter input at top
- Grouped by schema (optional)
Purpose: Display table data in grid format
Location: app/components/DataTable.tsx
Props:
columns: Column[]- Column definitionsdata: any[]- Row dataisLoading: booleanonSort?: (column: string) => void
Visual:
- Fixed header row
- Striped rows (alternating bg)
- Hover highlight on rows
- Column headers clickable for sorting
- Scrollable horizontal/vertical
Features:
- Pagination controls
- Column sorting indicators
- Loading skeleton state
- Empty state message
Purpose: Show table structure/metadata
Location: app/components/TableSchema.tsx
Props:
columns: ColumnInfo[]- Column metadataindexes?: Index[]- Table indexesconstraints?: Constraint[]- Table constraints
Visual:
- Collapsible panel
- Table format showing:
- Column name
- Data type
- Nullable
- Default value
- Primary key indicator
Purpose: SQL query input and execution
Location: app/components/QueryEditor.tsx
Props:
onExecute: (query: string) => voidisExecuting: boolean
Visual:
- Textarea with monospace font
- Syntax highlighting (optional)
- Execute button
- Query history dropdown (optional)
Purpose: Reusable button component
Location: app/components/ui/Button.tsx
Variants:
primary- Blue backgroundsecondary- Gray backgrounddanger- Red backgroundghost- Transparent background
Sizes:
sm- Small padding, text-smmd- Medium padding (default)lg- Large padding
Props:
variant?: 'primary' | 'secondary' | 'danger' | 'ghost'size?: 'sm' | 'md' | 'lg'disabled?: booleanisLoading?: booleanonClick?: () => void
Purpose: Form input field
Location: app/components/ui/Input.tsx
Types:
- text
- number
- password
- search
Props:
type?: stringlabel?: stringplaceholder?: stringerror?: stringvalue: stringonChange: (value: string) => void
Visual:
- Label above input
- Border on all sides
- Focus ring (blue)
- Error state (red border + message)
Purpose: Container with elevation
Location: app/components/ui/Card.tsx
Props:
children: ReactNodetitle?: stringclassName?: string
Visual:
- White background
- Rounded corners (lg)
- Shadow (md)
- Padding (lg)
- Optional header section
Purpose: Small status indicator
Location: app/components/ui/Badge.tsx
Variants:
success- Greenwarning- Yellowdanger- Redinfo- Gray
Props:
variant?: 'success' | 'warning' | 'danger' | 'info'children: ReactNode
Visual:
- Small, rounded pill
- Uppercase text
- Semi-bold font
Purpose: Loading indicator
Location: app/components/ui/Spinner.tsx
Sizes:
sm- 16pxmd- 24pxlg- 32px
Visual:
- Circular spinner
- Blue color (primary)
- Smooth animation
Purpose: Overlay dialog
Location: app/components/ui/Modal.tsx
Props:
isOpen: booleanonClose: () => voidtitle?: stringchildren: ReactNode
Visual:
- Centered on screen
- Dark backdrop overlay
- White card with shadow
- Close button (X) in top right
Purpose: Display when no data available
Location: app/components/EmptyState.tsx
Props:
icon?: ReactNodetitle: stringdescription?: stringaction?: ReactNode- Optional CTA button
Visual:
- Centered vertically/horizontally
- Gray icon
- Large title
- Muted description
Purpose: Display error states
Location: app/components/ErrorMessage.tsx
Props:
message: stringonRetry?: () => void
Visual:
- Red border card
- Red icon
- Error message text
- Optional retry button
Purpose: Navigate through pages of data
Location: app/components/Pagination.tsx
Props:
currentPage: numbertotalPages: numberonPageChange: (page: number) => void
Visual:
- Previous/Next buttons
- Page numbers (with ellipsis for many pages)
- Current page highlighted
<div className="flex h-screen">
<Sidebar tables={tables} onTableSelect={setSelectedTable} />
<div className="flex-1 flex flex-col">
<Header isConnected={true} databaseName="mydb" />
<MainContent>
{/* Page content */}
</MainContent>
</div>
</div>
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<ConnectionForm onConnect={handleConnect} />
</div>
<MainContent>
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-bold">users</h1>
<Button variant="secondary">Export</Button>
</div>
<TableSchema columns={schemaInfo} />
<DataTable
columns={columns}
data={rows}
isLoading={loading}
/>
<Pagination
currentPage={page}
totalPages={totalPages}
onPageChange={setPage}
/>
</MainContent>
<MainContent>
<Card title="SQL Query">
<QueryEditor onExecute={runQuery} />
</Card>
{results && (
<Card title="Results" className="mt-4">
<DataTable columns={resultColumns} data={results} />
</Card>
)}
</MainContent>
app/components/
├── ui/ # Primitive components
│ ├── Button.tsx
│ ├── Input.tsx
│ ├── Card.tsx
│ ├── Badge.tsx
│ ├── Spinner.tsx
│ └── Modal.tsx
├── Header.tsx
├── Sidebar.tsx
├── MainContent.tsx
├── TableList.tsx
├── ConnectionForm.tsx
├── ConnectionStatus.tsx
├── DataTable.tsx
├── TableSchema.tsx
├── QueryEditor.tsx
├── EmptyState.tsx
├── ErrorMessage.tsx
└── Pagination.tsx
- Button, Input, Card (primitives)
- Header, Sidebar, MainContent (layout)
- ConnectionForm
- EmptyState
- TableList
- DataTable
- Pagination
- Spinner, Badge
- TableSchema
- QueryEditor
- Modal
- ErrorMessage
Consider creating a app/styles/tokens.ts file:
export const colors = {
primary: '#2563eb',
success: '#16a34a',
warning: '#ca8a04',
danger: '#dc2626',
// ... etc
}
export const spacing = {
xs: '0.25rem',
sm: '0.5rem',
// ... etc
}