import { useState, useRef, useCallback, useEffect } from 'react'; import { Button } from '~/components/ui/Button'; import { ConfirmationDialog, SelectionDialog } from '~/components/ui/Dialog'; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '~/components/ui/Card'; import { motion } from 'framer-motion'; import { useDataOperations } from '~/lib/hooks/useDataOperations'; import { openDatabase } from '~/lib/persistence/db'; import { getAllChats, type Chat } from '~/lib/persistence/chats'; import { DataVisualization } from './DataVisualization'; import { classNames } from '~/utils/classNames'; import { toast } from 'react-toastify'; // Create a custom hook to connect to the boltHistory database function useBoltHistoryDB() { const [db, setDb] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const initDB = async () => { try { setIsLoading(true); const database = await openDatabase(); setDb(database || null); setIsLoading(false); } catch (err) { setError(err instanceof Error ? err : new Error('Unknown error initializing database')); setIsLoading(false); } }; initDB(); return () => { if (db) { db.close(); } }; }, []); return { db, isLoading, error }; } // Extend the Chat interface to include the missing properties interface ExtendedChat extends Chat { title?: string; updatedAt?: number; } // Helper function to create a chat label and description function createChatItem(chat: Chat): ChatItem { return { id: chat.id, // Use description as title if available, or format a short ID label: (chat as ExtendedChat).title || chat.description || `Chat ${chat.id.slice(0, 8)}`, // Format the description with message count and timestamp description: `${chat.messages.length} messages - Last updated: ${new Date((chat as ExtendedChat).updatedAt || Date.parse(chat.timestamp)).toLocaleString()}`, }; } interface SettingsCategory { id: string; label: string; description: string; } interface ChatItem { id: string; label: string; description: string; } export function DataTab() { // Use our custom hook for the boltHistory database const { db, isLoading: dbLoading } = useBoltHistoryDB(); const fileInputRef = useRef(null); const apiKeyFileInputRef = useRef(null); const chatFileInputRef = useRef(null); // State for confirmation dialogs const [showResetInlineConfirm, setShowResetInlineConfirm] = useState(false); const [showDeleteInlineConfirm, setShowDeleteInlineConfirm] = useState(false); const [showSettingsSelection, setShowSettingsSelection] = useState(false); const [showChatsSelection, setShowChatsSelection] = useState(false); // State for settings categories and available chats const [settingsCategories] = useState([ { id: 'core', label: 'Core Settings', description: 'User profile and main settings' }, { id: 'providers', label: 'Providers', description: 'API keys and provider configurations' }, { id: 'features', label: 'Features', description: 'Feature flags and settings' }, { id: 'ui', label: 'UI', description: 'UI configuration and preferences' }, { id: 'connections', label: 'Connections', description: 'External service connections' }, { id: 'debug', label: 'Debug', description: 'Debug settings and logs' }, { id: 'updates', label: 'Updates', description: 'Update settings and notifications' }, ]); const [availableChats, setAvailableChats] = useState([]); const [chatItems, setChatItems] = useState([]); // Data operations hook with boltHistory database const { isExporting, isImporting, isResetting, isDownloadingTemplate, handleExportSettings, handleExportSelectedSettings, handleExportAllChats, handleExportSelectedChats, handleImportSettings, handleImportChats, handleResetSettings, handleResetChats, handleDownloadTemplate, handleImportAPIKeys, handleExportAPIKeys, handleUndo, lastOperation, } = useDataOperations({ customDb: db || undefined, // Pass the boltHistory database, converting null to undefined onReloadSettings: () => window.location.reload(), onReloadChats: () => { // Reload chats after reset if (db) { getAllChats(db).then((chats) => { // Cast to ExtendedChat to handle additional properties const extendedChats = chats as ExtendedChat[]; setAvailableChats(extendedChats); setChatItems(extendedChats.map((chat) => createChatItem(chat))); }); } }, onResetSettings: () => setShowResetInlineConfirm(false), onResetChats: () => setShowDeleteInlineConfirm(false), }); // Loading states for operations not provided by the hook const [isDeleting, setIsDeleting] = useState(false); const [isImportingKeys, setIsImportingKeys] = useState(false); // Load available chats useEffect(() => { if (db) { console.log('Loading chats from boltHistory database', { name: db.name, version: db.version, objectStoreNames: Array.from(db.objectStoreNames), }); getAllChats(db) .then((chats) => { console.log('Found chats:', chats.length); // Cast to ExtendedChat to handle additional properties const extendedChats = chats as ExtendedChat[]; setAvailableChats(extendedChats); // Create ChatItems for selection dialog setChatItems(extendedChats.map((chat) => createChatItem(chat))); }) .catch((error) => { console.error('Error loading chats:', error); toast.error('Failed to load chats: ' + (error instanceof Error ? error.message : 'Unknown error')); }); } }, [db]); // Handle file input changes const handleFileInputChange = useCallback( (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { handleImportSettings(file); } }, [handleImportSettings], ); const handleAPIKeyFileInputChange = useCallback( (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { setIsImportingKeys(true); handleImportAPIKeys(file).finally(() => setIsImportingKeys(false)); } }, [handleImportAPIKeys], ); const handleChatFileInputChange = useCallback( (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { handleImportChats(file); } }, [handleImportChats], ); // Wrapper for reset chats to handle loading state const handleResetChatsWithState = useCallback(() => { setIsDeleting(true); handleResetChats().finally(() => setIsDeleting(false)); }, [handleResetChats]); return (
{/* Hidden file inputs */} {/* Reset Settings Confirmation Dialog */} setShowResetInlineConfirm(false)} title="Reset All Settings?" description="This will reset all your settings to their default values. This action cannot be undone." confirmLabel="Reset Settings" cancelLabel="Cancel" variant="destructive" isLoading={isResetting} onConfirm={handleResetSettings} /> {/* Delete Chats Confirmation Dialog */} setShowDeleteInlineConfirm(false)} title="Delete All Chats?" description="This will permanently delete all your chat history. This action cannot be undone." confirmLabel="Delete All" cancelLabel="Cancel" variant="destructive" isLoading={isDeleting} onConfirm={handleResetChatsWithState} /> {/* Settings Selection Dialog */} setShowSettingsSelection(false)} title="Select Settings to Export" items={settingsCategories} onConfirm={(selectedIds) => { handleExportSelectedSettings(selectedIds); setShowSettingsSelection(false); }} confirmLabel="Export Selected" /> {/* Chats Selection Dialog */} setShowChatsSelection(false)} title="Select Chats to Export" items={chatItems} onConfirm={(selectedIds) => { handleExportSelectedChats(selectedIds); setShowChatsSelection(false); }} confirmLabel="Export Selected" /> {/* Chats Section */}

Chats

{dbLoading ? (
Loading chats database...
) : (
Export All Chats
Export all your chats to a JSON file.
Export Selected Chats
Choose specific chats to export.
Import Chats
Import chats from a JSON file.
Delete All Chats
Delete all your chat history.
)}
{/* Settings Section */}

Settings

Export All Settings
Export all your settings to a JSON file.
Export Selected Settings
Choose specific settings to export.
Import Settings
Import settings from a JSON file.
Reset All Settings
Reset all settings to their default values.
{/* API Keys Section */}

API Keys

Export API Keys
Export your API keys to a JSON file.
Download Template
Download a template file for your API keys.
Import API Keys
Import API keys from a JSON file.
{/* Data Visualization */}

Data Usage

{/* Undo Last Operation */} {lastOperation && (
Last action: {lastOperation.type}
)}
); }