From 9328bf31022c670d4b53405b7f510bbffd2b7ce8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 21:58:03 +0000 Subject: [PATCH] Split SettingsPage.tsx (627 LOC) into 6 smaller components Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- .../settings/BackendAutoConfigCard.tsx | 72 ++++ .../settings/DatabaseActionsCard.tsx | 84 ++++ src/components/settings/DatabaseStatsCard.tsx | 55 +++ src/components/settings/SchemaHealthCard.tsx | 67 +++ .../settings/StorageBackendCard.tsx | 157 +++++++ src/components/settings/StorageInfoCard.tsx | 40 ++ src/pages/SettingsPage.tsx | 400 ++---------------- 7 files changed, 522 insertions(+), 353 deletions(-) create mode 100644 src/components/settings/BackendAutoConfigCard.tsx create mode 100644 src/components/settings/DatabaseActionsCard.tsx create mode 100644 src/components/settings/DatabaseStatsCard.tsx create mode 100644 src/components/settings/SchemaHealthCard.tsx create mode 100644 src/components/settings/StorageBackendCard.tsx create mode 100644 src/components/settings/StorageInfoCard.tsx diff --git a/src/components/settings/BackendAutoConfigCard.tsx b/src/components/settings/BackendAutoConfigCard.tsx new file mode 100644 index 0000000..e932227 --- /dev/null +++ b/src/components/settings/BackendAutoConfigCard.tsx @@ -0,0 +1,72 @@ +import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { CloudCheck, CloudSlash } from '@phosphor-icons/react' + +interface BackendAutoConfigCardProps { + envVarSet: boolean + flaskUrl: string + flaskConnectionStatus: 'unknown' | 'connected' | 'failed' + testingConnection: boolean + onTestConnection: () => Promise +} + +export function BackendAutoConfigCard({ + envVarSet, + flaskUrl, + flaskConnectionStatus, + testingConnection, + onTestConnection +}: BackendAutoConfigCardProps) { + if (!envVarSet) return null + + return ( + + + + + Backend Auto-Configured + + + Flask backend is configured via environment variable + + + +
+
+ Backend URL + {flaskUrl} +
+
+ Configuration Source + VITE_FLASK_BACKEND_URL +
+
+ Status + {flaskConnectionStatus === 'connected' && ( + + + Connected + + )} + {flaskConnectionStatus === 'failed' && ( + + + Connection Failed + + )} + {flaskConnectionStatus === 'unknown' && ( + + )} +
+
+
+
+ ) +} diff --git a/src/components/settings/DatabaseActionsCard.tsx b/src/components/settings/DatabaseActionsCard.tsx new file mode 100644 index 0000000..a2b4fce --- /dev/null +++ b/src/components/settings/DatabaseActionsCard.tsx @@ -0,0 +1,84 @@ +import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Database, Download, Upload, Trash } from '@phosphor-icons/react' + +interface DatabaseActionsCardProps { + onExport: () => Promise + onImport: (event: React.ChangeEvent) => Promise + onSeed: () => Promise + onClear: () => Promise +} + +export function DatabaseActionsCard({ + onExport, + onImport, + onSeed, + onClear +}: DatabaseActionsCardProps) { + return ( + + + Database Actions + + Backup, restore, or reset your database + + + +
+

Export Database

+

+ Download your database as a file for backup or transfer to another device +

+ +
+ +
+

Import Database

+

+ Restore a previously exported database file +

+ +
+ +
+

Sample Data

+

+ Add sample code snippets to get started (only if database is empty) +

+ +
+ +
+

Clear All Data

+

+ Permanently delete all snippets and templates. This cannot be undone. +

+ +
+
+
+ ) +} diff --git a/src/components/settings/DatabaseStatsCard.tsx b/src/components/settings/DatabaseStatsCard.tsx new file mode 100644 index 0000000..b9b5b2b --- /dev/null +++ b/src/components/settings/DatabaseStatsCard.tsx @@ -0,0 +1,55 @@ +import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card' +import { Database } from '@phosphor-icons/react' + +interface DatabaseStatsCardProps { + loading: boolean + stats: { + snippetCount: number + templateCount: number + storageType: 'indexeddb' | 'localstorage' | 'none' + databaseSize: number + } | null + formatBytes: (bytes: number) => string +} + +export function DatabaseStatsCard({ loading, stats, formatBytes }: DatabaseStatsCardProps) { + return ( + + + + + Database Statistics + + + Information about your local database storage + + + + {loading ? ( +

Loading...

+ ) : stats ? ( +
+
+ Snippets + {stats.snippetCount} +
+
+ Templates + {stats.templateCount} +
+
+ Storage Type + {stats.storageType} +
+
+ Database Size + {formatBytes(stats.databaseSize)} +
+
+ ) : ( +

Failed to load statistics

+ )} +
+
+ ) +} diff --git a/src/components/settings/SchemaHealthCard.tsx b/src/components/settings/SchemaHealthCard.tsx new file mode 100644 index 0000000..bdb0cff --- /dev/null +++ b/src/components/settings/SchemaHealthCard.tsx @@ -0,0 +1,67 @@ +import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Alert, AlertDescription } from '@/components/ui/alert' +import { Warning, FirstAid, CheckCircle } from '@phosphor-icons/react' + +interface SchemaHealthCardProps { + schemaHealth: 'unknown' | 'healthy' | 'corrupted' + checkingSchema: boolean + onClear: () => Promise + onCheckSchema: () => Promise +} + +export function SchemaHealthCard({ + schemaHealth, + checkingSchema, + onClear, + onCheckSchema +}: SchemaHealthCardProps) { + if (schemaHealth === 'unknown') return null + + if (schemaHealth === 'corrupted') { + return ( + + + + + Schema Corruption Detected + + + Your database schema is outdated or corrupted and needs to be repaired + + + + + + The database schema is missing required tables or columns (likely due to namespace feature addition). + This can cause errors when loading or saving snippets. Click the button below to wipe and recreate the database with the correct schema. + + +
+ + +
+
+
+ ) + } + + return ( + + + + + Schema Healthy + + + Your database schema is up to date and functioning correctly + + + + ) +} diff --git a/src/components/settings/StorageBackendCard.tsx b/src/components/settings/StorageBackendCard.tsx new file mode 100644 index 0000000..80d8d1c --- /dev/null +++ b/src/components/settings/StorageBackendCard.tsx @@ -0,0 +1,157 @@ +import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Alert, AlertDescription } from '@/components/ui/alert' +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' +import { Database, CloudArrowUp, CloudCheck, CloudSlash, Upload, Download } from '@phosphor-icons/react' +import { type StorageBackend } from '@/lib/storage' + +interface StorageBackendCardProps { + storageBackend: StorageBackend + flaskUrl: string + flaskConnectionStatus: 'unknown' | 'connected' | 'failed' + testingConnection: boolean + envVarSet: boolean + onStorageBackendChange: (backend: StorageBackend) => void + onFlaskUrlChange: (url: string) => void + onTestConnection: () => Promise + onSaveConfig: () => Promise + onMigrateToFlask: () => Promise + onMigrateToIndexedDB: () => Promise +} + +export function StorageBackendCard({ + storageBackend, + flaskUrl, + flaskConnectionStatus, + testingConnection, + envVarSet, + onStorageBackendChange, + onFlaskUrlChange, + onTestConnection, + onSaveConfig, + onMigrateToFlask, + onMigrateToIndexedDB, +}: StorageBackendCardProps) { + return ( + + + + + Storage Backend + + + Choose where your snippets are stored + + + + {envVarSet && ( + + + + + Storage backend is configured via VITE_FLASK_BACKEND_URL environment variable and cannot be changed here. + + + + )} + + onStorageBackendChange(value as StorageBackend)} + disabled={envVarSet} + > +
+ +
+ +

+ Store snippets locally in your browser. Data persists on this device only. +

+
+
+ +
+ +
+ +

+ Store snippets on a Flask backend server. Data is accessible from any device. +

+
+
+
+ + {storageBackend === 'flask' && ( +
+
+ +
+ onFlaskUrlChange(e.target.value)} + disabled={envVarSet} + /> + +
+ {flaskConnectionStatus === 'connected' && ( +
+ + Connected successfully +
+ )} + {flaskConnectionStatus === 'failed' && ( +
+ + Connection failed +
+ )} +
+ +
+ + +
+
+ )} + +
+ +
+
+
+ ) +} diff --git a/src/components/settings/StorageInfoCard.tsx b/src/components/settings/StorageInfoCard.tsx new file mode 100644 index 0000000..41d7c76 --- /dev/null +++ b/src/components/settings/StorageInfoCard.tsx @@ -0,0 +1,40 @@ +import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card' +import { Alert, AlertDescription } from '@/components/ui/alert' + +interface StorageInfoCardProps { + storageType?: 'indexeddb' | 'localstorage' | 'none' +} + +export function StorageInfoCard({ storageType }: StorageInfoCardProps) { + return ( + + + Storage Information + + How your data is stored + + + + + + {storageType === 'indexeddb' ? ( + <> + IndexedDB is being used for storage. This provides better performance and + larger storage capacity compared to localStorage. Your data persists locally in your browser. + + ) : storageType === 'localstorage' ? ( + <> + localStorage is being used for storage. IndexedDB is not available in your + browser. Note that localStorage has a smaller storage limit (typically 5-10MB). + + ) : ( + <> + No persistent storage detected. Your data will be lost when you close the browser. + + )} + + + + + ) +} diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index c3eb23e..9633197 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -1,22 +1,20 @@ import { useState, useEffect } from 'react' import { motion } from 'framer-motion' -import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card' -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { Database, Download, Upload, Trash, CloudArrowUp, CloudCheck, CloudSlash, FirstAid, CheckCircle, Warning } from '@phosphor-icons/react' import { getDatabaseStats, exportDatabase, importDatabase, clearDatabase, seedDatabase, getAllSnippets, validateDatabaseSchema } from '@/lib/db' import { toast } from 'sonner' -import { Alert, AlertDescription } from '@/components/ui/alert' import { - getStorageConfig, saveStorageConfig, loadStorageConfig, FlaskStorageAdapter, type StorageBackend } from '@/lib/storage' -import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' import { PersistenceSettings } from '@/components/demo/PersistenceSettings' +import { SchemaHealthCard } from '@/components/settings/SchemaHealthCard' +import { BackendAutoConfigCard } from '@/components/settings/BackendAutoConfigCard' +import { StorageBackendCard } from '@/components/settings/StorageBackendCard' +import { DatabaseStatsCard } from '@/components/settings/DatabaseStatsCard' +import { StorageInfoCard } from '@/components/settings/StorageInfoCard' +import { DatabaseActionsCard } from '@/components/settings/DatabaseActionsCard' export function SettingsPage() { const [stats, setStats] = useState<{ @@ -271,356 +269,52 @@ export function SettingsPage() {
- {schemaHealth === 'corrupted' && ( - - - - - Schema Corruption Detected - - - Your database schema is outdated or corrupted and needs to be repaired - - - - - - The database schema is missing required tables or columns (likely due to namespace feature addition). - This can cause errors when loading or saving snippets. Click the button below to wipe and recreate the database with the correct schema. - - -
- - -
-
-
- )} + - {schemaHealth === 'healthy' && ( - - - - - Schema Healthy - - - Your database schema is up to date and functioning correctly - - - - )} - - {envVarSet && ( - - - - - Backend Auto-Configured - - - Flask backend is configured via environment variable - - - -
-
- Backend URL - {flaskUrl} -
-
- Configuration Source - VITE_FLASK_BACKEND_URL -
-
- Status - {flaskConnectionStatus === 'connected' && ( - - - Connected - - )} - {flaskConnectionStatus === 'failed' && ( - - - Connection Failed - - )} - {flaskConnectionStatus === 'unknown' && ( - - )} -
-
-
-
- )} + - - - - - Storage Backend - - - Choose where your snippets are stored - - - - {envVarSet && ( - - - - - Storage backend is configured via VITE_FLASK_BACKEND_URL environment variable and cannot be changed here. - - - - )} - - setStorageBackend(value as StorageBackend)} - disabled={envVarSet} - > -
- -
- -

- Store snippets locally in your browser. Data persists on this device only. -

-
-
- -
- -
- -

- Store snippets on a Flask backend server. Data is accessible from any device. -

-
-
-
+ { + setFlaskUrl(url) + setFlaskConnectionStatus('unknown') + }} + onTestConnection={handleTestConnection} + onSaveConfig={handleSaveStorageConfig} + onMigrateToFlask={handleMigrateToFlask} + onMigrateToIndexedDB={handleMigrateToIndexedDB} + /> - {storageBackend === 'flask' && ( -
-
- -
- { - setFlaskUrl(e.target.value) - setFlaskConnectionStatus('unknown') - }} - disabled={envVarSet} - /> - -
- {flaskConnectionStatus === 'connected' && ( -
- - Connected successfully -
- )} - {flaskConnectionStatus === 'failed' && ( -
- - Connection failed -
- )} -
+ -
- - -
-
- )} + -
- -
-
-
- - - - - - Database Statistics - - - Information about your local database storage - - - - {loading ? ( -

Loading...

- ) : stats ? ( -
-
- Snippets - {stats.snippetCount} -
-
- Templates - {stats.templateCount} -
-
- Storage Type - {stats.storageType} -
-
- Database Size - {formatBytes(stats.databaseSize)} -
-
- ) : ( -

Failed to load statistics

- )} -
-
- - - - Storage Information - - How your data is stored - - - - - - {stats?.storageType === 'indexeddb' ? ( - <> - IndexedDB is being used for storage. This provides better performance and - larger storage capacity compared to localStorage. Your data persists locally in your browser. - - ) : stats?.storageType === 'localstorage' ? ( - <> - localStorage is being used for storage. IndexedDB is not available in your - browser. Note that localStorage has a smaller storage limit (typically 5-10MB). - - ) : ( - <> - No persistent storage detected. Your data will be lost when you close the browser. - - )} - - - - - - - - Database Actions - - Backup, restore, or reset your database - - - -
-

Export Database

-

- Download your database as a file for backup or transfer to another device -

- -
- -
-

Import Database

-

- Restore a previously exported database file -

- -
- -
-

Sample Data

-

- Add sample code snippets to get started (only if database is empty) -

- -
- -
-

Clear All Data

-

- Permanently delete all snippets and templates. This cannot be undone. -

- -
-
-
+
)