Add invoice drafts, read-only issued state and keyboard shortcuts
Views stay mounted so switching tabs no longer loses a half-typed invoice. The form autosaves a draft after 1 s, drafts can be resumed or deleted, and after issuing the form is read-only with Export again, Duplicate and New invoice actions. A failed export keeps the issued invoice and offers a retry instead of issuing again. Adds Ctrl+N/S/Enter/L and a shortcut sheet; F5 and Ctrl+R are blocked in production builds.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Column,
|
||||
@@ -20,7 +20,7 @@ import { confirm } from "@tauri-apps/plugin-dialog";
|
||||
import { api } from "../lib/api";
|
||||
import type { InvoiceSeries } from "../lib/types";
|
||||
|
||||
export default function SeriesSettings() {
|
||||
export default function SeriesSettings({ active: visible }: { active: boolean }) {
|
||||
const [active, setActive] = useState<InvoiceSeries | null>(null);
|
||||
const [all, setAll] = useState<InvoiceSeries[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -31,14 +31,18 @@ export default function SeriesSettings() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [notice, setNotice] = useState<string | null>(null);
|
||||
|
||||
const seeded = useRef(false);
|
||||
|
||||
const load = async () => {
|
||||
try {
|
||||
const [current, list] = await Promise.all([api.getActiveSeries(), api.listSeries()]);
|
||||
setActive(current);
|
||||
setAll(list);
|
||||
if (current) {
|
||||
// Only seed the form once, so a refetch on re-entry does not overwrite what was typed.
|
||||
if (current && !seeded.current) {
|
||||
setPrefix(current.prefix);
|
||||
setPadding(current.padding);
|
||||
seeded.current = true;
|
||||
}
|
||||
setError(null);
|
||||
} catch (e) {
|
||||
@@ -48,9 +52,10 @@ export default function SeriesSettings() {
|
||||
}
|
||||
};
|
||||
|
||||
// The view stays mounted, so refetch every time it is shown again.
|
||||
useEffect(() => {
|
||||
void load();
|
||||
}, []);
|
||||
if (visible) void load();
|
||||
}, [visible]);
|
||||
|
||||
const startNew = async () => {
|
||||
const ok = await confirm(
|
||||
|
||||
Reference in New Issue
Block a user