import { useEffect, useState } from "react"; import { Button, Column, Grid, Link, NumberInput, RadioButton, RadioButtonGroup, Select, SelectItem, Table, TableBody, TableCell, TableContainer, TableHead, TableHeader, TableRow, TextArea, TextInput, Tab, TabList, TabPanel, TabPanels, Tabs, Tile, Toggle, } from "@carbon/react"; import { Add, Save, TrashCan } from "@carbon/icons-react"; import { confirm } from "@tauri-apps/plugin-dialog"; import { api } from "../lib/api"; import { INDIAN_STATES, type BankAccount, type GstRegistration, type Settings, type TaxType, } from "../lib/types"; import { emailError, ifscError, panError, phoneError, vendorGstinError } from "../lib/validators"; import { ImagePicker } from "../components/ImagePicker"; import { LogoBranding } from "../components/LogoBranding"; import DiagnosticsPanel from "../components/DiagnosticsPanel"; import { FontOverrideTable } from "../components/FontOverrideTable"; import { useToast } from "../components/ToastProvider"; import { SETTINGS_TABS, loadSettingsTab, saveSettingsTab } from "../lib/settingsTab"; import SeriesSettings from "./SeriesSettings"; const blankBank = (): BankAccount => ({ id: null, label: "", bankName: "", accountName: "", accountNo: "", branch: "", ifsc: "", isDefault: false, }); export default function AppSettings({ settings, onSaved, }: { settings: Settings; onSaved: () => void; }) { const [form, setForm] = useState({ ...settings }); const [diagnosticsOpen, setDiagnosticsOpen] = useState(false); const [banks, setBanks] = useState([]); const [editing, setEditing] = useState(null); const [busy, setBusy] = useState(false); const toast = useToast(); // One form state for every tab and one Save button, so edits survive switching tabs. const [tab, setTab] = useState(loadSettingsTab); const selectTab = (index: number) => { setTab(index); saveSettingsTab(index); }; const loadBanks = async () => { try { setBanks(await api.listBanks()); } catch (e) { toast.error("Could not load bank accounts", String(e)); } }; useEffect(() => { void loadBanks(); }, []); const set = (key: K, value: Settings[K]) => setForm((prev) => ({ ...prev, [key]: value })); const setBank = (key: K, value: BankAccount[K]) => setEditing((prev) => (prev ? { ...prev, [key]: value } : prev)); const registered = form.gstRegistration !== "unregistered"; const gstinProblem = vendorGstinError( form.gstRegistration, form.vendorGstin, form.vendorPan, form.vendorStateCode, ); const saveAll = async () => { if (gstinProblem) { selectTab(0); toast.error("Settings not saved", gstinProblem); return; } setBusy(true); try { await api.saveSettings(form); toast.success("Settings saved"); onSaved(); } catch (e) { toast.error("Could not save settings", String(e)); } finally { setBusy(false); } }; const saveBank = async () => { if (!editing) return; setBusy(true); try { const saved = await api.saveBank(editing); if (saved.isDefault || form.defaultBankId === null) { setForm((prev) => ({ ...prev, defaultBankId: saved.id })); } setEditing(null); await loadBanks(); toast.success("Bank account saved", "Click Save settings to keep a changed default account."); } catch (e) { toast.error("Could not save bank account", String(e)); } finally { setBusy(false); } }; const removeBank = async (bank: BankAccount) => { if (bank.id === null) return; const ok = await confirm(`Delete bank account “${bank.bankName}”?`, { title: "Delete bank account", kind: "warning", }); if (!ok) return; try { await api.deleteBank(bank.id); if (form.defaultBankId === bank.id) set("defaultBankId", null); await loadBanks(); } catch (e) { toast.error("Could not delete bank account", String(e)); } }; return (

Settings

Business details, branding, defaults and bank accounts.

selectTab(selectedIndex)}> Business Branding Bank & payments Invoice defaults Numbering Templates & export Fonts Data
{ set("gstRegistration", value as GstRegistration); if (value === "unregistered") set("defaultTaxType", "none"); else if (form.defaultTaxType === "none") set("defaultTaxType", "cgst_sgst"); }} > set("vendorName", e.target.value)} />