"use client"; import { useState } from "react"; import * as React from "react"; import { useSession } from "next-auth/react"; import { Download, Upload, User, Database, AlertTriangle, Shield, Settings, FileText, Users, Building, } from "lucide-react"; import { api } from "~/trpc/react"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Textarea } from "~/components/ui/textarea"; import { Badge } from "~/components/ui/badge"; import { toast } from "sonner"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "~/components/ui/alert-dialog"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "~/components/ui/dialog"; export function SettingsContent() { const { data: session } = useSession(); const [name, setName] = useState(""); const [deleteConfirmText, setDeleteConfirmText] = useState(""); const [importData, setImportData] = useState(""); const [isImportDialogOpen, setIsImportDialogOpen] = useState(false); // Queries const { data: profile, refetch: refetchProfile } = api.settings.getProfile.useQuery(); const { data: dataStats } = api.settings.getDataStats.useQuery(); // Mutations const updateProfileMutation = api.settings.updateProfile.useMutation({ onSuccess: () => { toast.success("Profile updated successfully"); void refetchProfile(); }, onError: (error: { message: string }) => { toast.error(`Failed to update profile: ${error.message}`); }, }); const exportDataQuery = api.settings.exportData.useQuery(undefined, { enabled: false, }); // Handle export data success/error React.useEffect(() => { if (exportDataQuery.data && !exportDataQuery.isFetching) { const blob = new Blob([JSON.stringify(exportDataQuery.data, null, 2)], { type: "application/json", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `beenvoice-backup-${new Date().toISOString().split("T")[0]}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toast.success("Data backup downloaded successfully"); } if (exportDataQuery.error) { toast.error(`Export failed: ${exportDataQuery.error.message}`); } }, [exportDataQuery.data, exportDataQuery.isFetching, exportDataQuery.error]); const importDataMutation = api.settings.importData.useMutation({ onSuccess: (result) => { toast.success( `Data imported successfully! Added ${result.imported.clients} clients, ${result.imported.businesses} businesses, and ${result.imported.invoices} invoices.`, ); setImportData(""); setIsImportDialogOpen(false); void refetchProfile(); }, onError: (error: { message: string }) => { toast.error(`Import failed: ${error.message}`); }, }); const deleteDataMutation = api.settings.deleteAllData.useMutation({ onSuccess: () => { toast.success("All data has been permanently deleted"); setDeleteConfirmText(""); }, onError: (error: { message: string }) => { toast.error(`Delete failed: ${error.message}`); }, }); const handleUpdateProfile = (e: React.FormEvent) => { e.preventDefault(); if (!name.trim()) { toast.error("Please enter your name"); return; } updateProfileMutation.mutate({ name: name.trim() }); }; const handleExportData = () => { void exportDataQuery.refetch(); }; // Type guard for backup data const isValidBackupData = ( data: unknown, ): data is { exportDate: string; version: string; user: { name?: string; email: string }; clients: Array<{ name: string; email?: string; phone?: string; addressLine1?: string; addressLine2?: string; city?: string; state?: string; postalCode?: string; country?: string; }>; businesses: Array<{ name: string; email?: string; phone?: string; addressLine1?: string; addressLine2?: string; city?: string; state?: string; postalCode?: string; country?: string; website?: string; taxId?: string; logoUrl?: string; isDefault?: boolean; }>; invoices: Array<{ invoiceNumber: string; businessName?: string; clientName: string; issueDate: Date; dueDate: Date; status?: string; totalAmount?: number; taxRate?: number; notes?: string; items: Array<{ date: Date; description: string; hours: number; rate: number; amount: number; position?: number; }>; }>; } => { return !!( data && typeof data === "object" && data !== null && "exportDate" in data && "version" in data && "user" in data && "clients" in data && "businesses" in data && "invoices" in data ); }; const handleImportData = () => { try { const parsedData: unknown = JSON.parse(importData); if (isValidBackupData(parsedData)) { importDataMutation.mutate(parsedData); } else { toast.error("Invalid backup file format"); } } catch { toast.error("Invalid JSON format. Please check your backup file."); } }; const handleDeleteAllData = () => { if (deleteConfirmText !== "delete all my data") { toast.error("Please type 'delete all my data' to confirm"); return; } deleteDataMutation.mutate({ confirmText: deleteConfirmText }); }; // Set initial name value when profile loads React.useEffect(() => { if (profile?.name && !name) { setName(profile.name); } }, [profile?.name, name]); const dataStatItems = [ { label: "Clients", value: dataStats?.clients ?? 0, icon: Users, color: "text-blue-600", bgColor: "bg-blue-100", }, { label: "Businesses", value: dataStats?.businesses ?? 0, icon: Building, color: "text-purple-600", bgColor: "bg-purple-100", }, { label: "Invoices", value: dataStats?.invoices ?? 0, icon: FileText, color: "text-emerald-600", bgColor: "bg-emerald-100", }, ]; return (
This will permanently delete all your clients, businesses, invoices, and related data. This action cannot be undone and your data cannot be recovered.