Fix dark theme fill, theme toggle visibility and bank account creation
- The Carbon theme class is now also set on html, with a themed background and a full-height root, so the dark theme fills the whole window instead of stopping at the content height. An inline script in index.html applies the saved theme before first paint. - The header theme button gets a tooltip, inline padding and a max width so it is no longer clipped at the window edge. New Invoice also gets a labelled Dark mode toggle wired to the same handler. - A shared BankAccountModal replaces the inline editor in Settings and adds a Create bank account button under the bank selector on New Invoice, which selects the new account on save. Settings keeps one Add bank account button under the table. Bank name and account number are now required.
This commit is contained in:
@@ -72,6 +72,7 @@ import { withUserFonts } from "../lib/fontImport";
|
||||
import { ImagePicker } from "../components/ImagePicker";
|
||||
import { loadLogoImages, type LogoImages } from "../lib/logo";
|
||||
import ClientModal from "../components/ClientModal";
|
||||
import BankAccountModal from "../components/BankAccountModal";
|
||||
import { useToast } from "../components/ToastProvider";
|
||||
import { itemFromPreset } from "../lib/presets";
|
||||
|
||||
@@ -88,6 +89,9 @@ interface NewInvoiceProps {
|
||||
/** A draft to open as soon as the form is shown (set by "Duplicate" on the invoice page). */
|
||||
openDraftId?: number | null;
|
||||
onDraftOpened?: () => void;
|
||||
/** Current theme ("g10" | "g100") and its single toggle handler, owned by App. */
|
||||
theme?: string;
|
||||
onToggleTheme?: () => void;
|
||||
}
|
||||
|
||||
const AUTOSAVE_MS = 1000;
|
||||
@@ -98,7 +102,7 @@ const formatStamp = (iso: string) => {
|
||||
return Number.isNaN(d.getTime()) ? iso : `${d.toLocaleDateString("en-GB")} ${formatTime(d)}`;
|
||||
};
|
||||
|
||||
export default function NewInvoice({ settings, onSettingsChange, active, onActivate, openDraftId = null, onDraftOpened }: NewInvoiceProps) {
|
||||
export default function NewInvoice({ settings, onSettingsChange, active, onActivate, openDraftId = null, onDraftOpened, theme, onToggleTheme }: NewInvoiceProps) {
|
||||
const toast = useToast();
|
||||
const newItem = (): InvoiceItem => ({ ...EMPTY_ITEM, hsnSac: settings.defaultHsnSac });
|
||||
const blankState = (): InvoiceFormState => {
|
||||
@@ -131,6 +135,8 @@ export default function NewInvoice({ settings, onSettingsChange, active, onActiv
|
||||
const [presets, setPresets] = useState<ItemPreset[]>([]);
|
||||
const [clientModalOpen, setClientModalOpen] = useState(false);
|
||||
const newClientButton = useRef<HTMLButtonElement | null>(null);
|
||||
const [bankModalOpen, setBankModalOpen] = useState(false);
|
||||
const newBankButton = useRef<HTMLButtonElement | null>(null);
|
||||
const [clientId, setClientId] = useState<number | null>(null);
|
||||
const [clientName, setClientName] = useState("");
|
||||
const [clientAddress, setClientAddress] = useState("");
|
||||
@@ -678,6 +684,18 @@ export default function NewInvoice({ settings, onSettingsChange, active, onActiv
|
||||
// Yield the shared render worker to the export; the model changes back when it is done and re-renders.
|
||||
const pdf = useInvoicePdf({ model: busy ? null : previewModel, prefs: activePrefs });
|
||||
|
||||
const themeToggle = onToggleTheme ? (
|
||||
<Toggle
|
||||
id="themeToggle"
|
||||
size="sm"
|
||||
labelText="Dark mode"
|
||||
labelA="Off"
|
||||
labelB="On"
|
||||
toggled={theme === "g100"}
|
||||
onToggle={() => onToggleTheme()}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="voiced-page" hidden={!active}>
|
||||
@@ -696,8 +714,9 @@ export default function NewInvoice({ settings, onSettingsChange, active, onActiv
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
{locked ? null : (
|
||||
{locked ? themeToggle : (
|
||||
<div className="voiced-header-action" style={{ flexWrap: "wrap" }}>
|
||||
{themeToggle}
|
||||
{draftSaving ? (
|
||||
<InlineLoading status="active" description="Saving draft…" />
|
||||
) : savedAt && !unsaved ? (
|
||||
@@ -1094,6 +1113,16 @@ export default function NewInvoice({ settings, onSettingsChange, active, onActiv
|
||||
/>
|
||||
))}
|
||||
</Select>
|
||||
<Button
|
||||
ref={newBankButton}
|
||||
kind="ghost"
|
||||
size="sm"
|
||||
renderIcon={Add}
|
||||
disabled={locked}
|
||||
onClick={() => setBankModalOpen(true)}
|
||||
>
|
||||
Create bank account
|
||||
</Button>
|
||||
|
||||
<ImagePicker
|
||||
kind="signature"
|
||||
@@ -1206,6 +1235,17 @@ export default function NewInvoice({ settings, onSettingsChange, active, onActiv
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Modal>
|
||||
<BankAccountModal
|
||||
open={bankModalOpen}
|
||||
bank={null}
|
||||
launcherRef={newBankButton}
|
||||
onClose={() => setBankModalOpen(false)}
|
||||
onSaved={(saved) => {
|
||||
setBankModalOpen(false);
|
||||
setBankId(saved.id);
|
||||
api.listBanks().then(setBanks).catch(() => {});
|
||||
}}
|
||||
/>
|
||||
<ClientModal
|
||||
open={clientModalOpen}
|
||||
client={null}
|
||||
|
||||
Reference in New Issue
Block a user