"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 (
{/* Header */}

Settings

Manage your account and data preferences

{/* Profile Section */} Profile Update your personal information
setName(e.target.value)} placeholder="Your full name" />

Email cannot be changed

{/* Data Statistics */} Your Data Overview of your account data
{dataStats?.clients ?? 0}
Clients
{dataStats?.businesses ?? 0}
Businesses
{dataStats?.invoices ?? 0}
Invoices
{/* Backup & Restore Section */} Backup & Restore Export your data for backup or import from a previous backup
{/* Export Data */}

Export Data

Download all your clients, businesses, and invoices as a JSON backup file.

{/* Import Data */}

Import Data

Restore your data from a previous backup file.

Import Backup Data Paste the contents of your backup JSON file below. This will add the data to your existing account.