"use client"; import { useState } from "react"; import * as React from "react"; import { useSession } from "next-auth/react"; import { Download, Upload, User, Database, AlertTriangle, Shield, } 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 { 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"; import { Textarea } from "~/components/ui/textarea"; export default function SettingsPage() { 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("Your profile has been successfully updated."); void refetchProfile(); }, onError: (error: { message: string }) => { toast.error(`Error updating profile: ${error.message}`); }, }); const exportDataQuery = api.settings.exportData.useQuery(undefined, { enabled: false, }); // Handle export data success/error React.useEffect(() => { if (exportDataQuery.data && !exportDataQuery.isFetching) { // Create and download the backup file 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("Your data backup has been downloaded."); } if (exportDataQuery.error) { toast.error(`Error exporting data: ${exportDataQuery.error.message}`); } }, [exportDataQuery.data, exportDataQuery.isFetching, exportDataQuery.error]); const importDataMutation = api.settings.importData.useMutation({ onSuccess: (result) => { toast.success( `Data imported successfully! Imported ${result.imported.clients} clients, ${result.imported.businesses} businesses, and ${result.imported.invoices} invoices.`, ); setImportData(""); setIsImportDialogOpen(false); void refetchProfile(); }, onError: (error: { message: string }) => { toast.error(`Error importing data: ${error.message}`); }, }); const deleteDataMutation = api.settings.deleteAllData.useMutation({ onSuccess: () => { toast.success("Your account data has been permanently deleted."); setDeleteConfirmText(""); }, onError: (error: { message: string }) => { toast.error(`Error deleting data: ${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. Please check your backup file format."); } }; const handleDeleteAllData = () => { if (deleteConfirmText !== "DELETE ALL DATA") { toast.error("Please type 'DELETE ALL DATA' to confirm."); return; } deleteDataMutation.mutate({ confirmText: deleteConfirmText }); }; // Set initial name value when profile loads if (profile && !name && profile.name) { setName(profile.name); } return (
Manage your account and data preferences
Download all your clients, businesses, and invoices as a JSON backup file.
Restore your data from a previous backup file.
This will permanently delete all your clients, businesses, invoices, and related data. This action cannot be undone.
This action cannot be undone. This will permanently delete all your:
Type{" "} DELETE ALL DATA {" "} to confirm:
setDeleteConfirmText(e.target.value)} placeholder="Type: DELETE ALL DATA" className="font-mono" />