Spaces:
Running
Running
import { useState, useRef } from "react"; | |
import { Settings, Moon, Sun, Globe, Shield, Database, Cloud } from "lucide-react"; | |
import { Button } from "@/components/ui/button"; | |
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; | |
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; | |
import { Label } from "@/components/ui/label"; | |
import { Separator } from "@/components/ui/separator"; | |
import { Input } from "@/components/ui/input"; | |
import { toast } from "@/components/ui/sonner"; | |
import { storage, STORAGE_KEYS } from "@/lib/storage"; | |
const SettingsPage = () => { | |
const [theme, setTheme] = useState(() => { | |
return storage.get<string>(STORAGE_KEYS.THEME) || (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); | |
}); | |
const [apiEndpoint, setApiEndpoint] = useState( | |
storage.get<string>(STORAGE_KEYS.API_ENDPOINT) || "http://localhost:8000" | |
); | |
const fileInputRef = useRef<HTMLInputElement>(null); | |
const handleThemeChange = (newTheme: string) => { | |
setTheme(newTheme); | |
const root = window.document.documentElement; | |
if (newTheme === "dark") { | |
root.classList.add("dark"); | |
} else if (newTheme === "light") { | |
root.classList.remove("dark"); | |
} else { | |
// System theme | |
if (window.matchMedia("(prefers-color-scheme: dark)").matches) { | |
root.classList.add("dark"); | |
} else { | |
root.classList.remove("dark"); | |
} | |
} | |
storage.set(STORAGE_KEYS.THEME, newTheme); | |
toast.success("Theme updated successfully"); | |
}; | |
const handleSaveEndpoint = () => { | |
storage.set(STORAGE_KEYS.API_ENDPOINT, apiEndpoint); | |
toast.success("API endpoint saved successfully"); | |
}; | |
const handleClearChats = () => { | |
storage.set(STORAGE_KEYS.CHATS, []); | |
toast.success("Chat history cleared successfully"); | |
}; | |
const handleClearSources = () => { | |
storage.set(STORAGE_KEYS.SOURCES, []); | |
toast.success("Sources cleared successfully"); | |
}; | |
const handleResetSettings = () => { | |
storage.remove(STORAGE_KEYS.THEME); | |
storage.remove(STORAGE_KEYS.API_ENDPOINT); | |
setApiEndpoint("http://localhost:8000"); | |
setTheme(window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); | |
toast.success("Settings reset to defaults"); | |
}; | |
const handleExportStorage = () => { | |
const data = storage.export(); | |
const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" }); | |
const url = URL.createObjectURL(blob); | |
const a = document.createElement("a"); | |
a.href = url; | |
a.download = "insight-storage-export.json"; | |
a.click(); | |
URL.revokeObjectURL(url); | |
toast.success("Storage exported successfully"); | |
}; | |
const handleImportStorage = (event: React.ChangeEvent<HTMLInputElement>) => { | |
const file = event.target.files?.[0]; | |
if (!file) return; | |
const reader = new FileReader(); | |
reader.onload = (e) => { | |
try { | |
const result = e.target?.result as string; | |
const data = JSON.parse(result); | |
if (storage.import(data)) { | |
toast.success("Storage imported successfully. Please refresh the page."); | |
} else { | |
toast.error("Failed to import storage."); | |
} | |
} catch { | |
toast.error("Invalid file format."); | |
} | |
}; | |
reader.readAsText(file); | |
event.target.value = ""; | |
}; | |
return ( | |
<div className="container mx-auto px-4 py-8"> | |
<div className="max-w-4xl mx-auto"> | |
<h1 className="text-2xl font-bold mb-6 text-gradient flex items-center"> | |
<Settings className="mr-2 h-5 w-5" /> | |
System Settings | |
</h1> | |
<div className="grid gap-6"> | |
<Card className="glass-effect"> | |
<CardHeader className="border-b border-border/40 pb-4"> | |
<CardTitle className="flex items-center"> | |
<Moon className="mr-2 h-4 w-4" /> | |
Appearance | |
</CardTitle> | |
<CardDescription> | |
Customize how Insight AI looks | |
</CardDescription> | |
</CardHeader> | |
<CardContent className="pt-4"> | |
<RadioGroup | |
value={theme} | |
onValueChange={handleThemeChange} | |
className="space-y-4" | |
> | |
<div className="flex items-center space-x-2"> | |
<RadioGroupItem value="light" id="light" /> | |
<Label htmlFor="light" className="flex items-center cursor-pointer"> | |
<Sun className="h-4 w-4 mr-2" /> | |
Light | |
</Label> | |
</div> | |
<div className="flex items-center space-x-2"> | |
<RadioGroupItem value="dark" id="dark" /> | |
<Label htmlFor="dark" className="flex items-center cursor-pointer"> | |
<Moon className="h-4 w-4 mr-2" /> | |
Dark | |
</Label> | |
</div> | |
<div className="flex items-center space-x-2"> | |
<RadioGroupItem value="system" id="system" /> | |
<Label htmlFor="system" className="flex items-center cursor-pointer"> | |
<Globe className="h-4 w-4 mr-2" /> | |
System | |
</Label> | |
</div> | |
</RadioGroup> | |
</CardContent> | |
</Card> | |
<Card className="glass-effect"> | |
<CardHeader className="border-b border-border/40 pb-4"> | |
<CardTitle className="flex items-center"> | |
<Database className="mr-2 h-4 w-4" /> | |
API Configuration | |
</CardTitle> | |
<CardDescription> | |
Configure connection to the financial rulings database | |
</CardDescription> | |
</CardHeader> | |
<CardContent className="space-y-4 pt-4"> | |
<div className="space-y-2"> | |
<Label htmlFor="api-endpoint">API Endpoint</Label> | |
<div className="flex space-x-2"> | |
<Input | |
id="api-endpoint" | |
value={apiEndpoint} | |
onChange={(e) => setApiEndpoint(e.target.value)} | |
placeholder="http://localhost:8000" | |
className="glass-input" | |
/> | |
<Button onClick={handleSaveEndpoint} variant="outline" className="tech-button"> | |
<Cloud className="h-4 w-4 mr-2" /> | |
Save | |
</Button> | |
</div> | |
</div> | |
</CardContent> | |
</Card> | |
<Card className="glass-effect"> | |
<CardHeader className="border-b border-border/40 pb-4"> | |
<CardTitle className="flex items-center"> | |
<Shield className="mr-2 h-4 w-4" /> | |
Privacy & Data | |
</CardTitle> | |
<CardDescription> | |
Manage your data and privacy settings | |
</CardDescription> | |
</CardHeader> | |
<CardContent className="pt-4 space-y-4"> | |
<div className="space-y-2"> | |
<h3 className="text-sm font-medium">Data Management</h3> | |
<div className="flex flex-wrap gap-3"> | |
<Button | |
variant="outline" | |
onClick={handleClearChats} | |
className="tech-button" | |
> | |
Clear Chat History | |
</Button> | |
<Button | |
variant="outline" | |
onClick={handleClearSources} | |
className="tech-button" | |
> | |
Clear Source References | |
</Button> | |
<Button | |
variant="destructive" | |
onClick={handleResetSettings} | |
className="tech-button" | |
> | |
Reset All Settings | |
</Button> | |
</div> | |
</div> | |
</CardContent> | |
</Card> | |
<Card className="glass-effect"> | |
<CardHeader className="border-b border-border/40 pb-4"> | |
<CardTitle className="flex items-center"> | |
<Cloud className="mr-2 h-4 w-4" /> | |
Storage Export / Import | |
</CardTitle> | |
<CardDescription> | |
Backup or restore your application data | |
</CardDescription> | |
</CardHeader> | |
<CardContent className="pt-4 space-y-4"> | |
<div className="flex flex-wrap gap-3"> | |
<Button | |
variant="outline" | |
onClick={handleExportStorage} | |
className="tech-button" | |
> | |
Export Storage | |
</Button> | |
<Button | |
variant="outline" | |
onClick={() => fileInputRef.current?.click()} | |
className="tech-button" | |
> | |
Import Storage | |
</Button> | |
<input | |
ref={fileInputRef} | |
type="file" | |
accept="application/json" | |
style={{ display: "none" }} | |
onChange={handleImportStorage} | |
/> | |
</div> | |
<div className="text-xs text-muted-foreground"> | |
Export will download your data as a JSON file. Import will overwrite your current data with the file contents. | |
</div> | |
</CardContent> | |
</Card> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default SettingsPage; | |