Files
xavierk fbed18a0bd 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.
2026-10-04 04:21:47 +05:30

63 lines
2.0 KiB
TypeScript

import { useEffect, useRef } from "react";
export interface ShortcutHandlers {
onNew?: () => void;
onSaveDraft?: () => void;
onIssue?: () => void;
onAddLine?: () => void;
onHelp?: () => void;
}
const isTyping = (target: EventTarget | null) => {
const el = target as HTMLElement | null;
if (!el) return false;
return el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT" || el.isContentEditable;
};
/**
* Global keyboard shortcuts. Ctrl combinations work everywhere; the bare `?` is ignored
* while typing. F5 / Ctrl+R are blocked in production builds because a webview reload
* would discard in-memory state.
*/
export function useShortcuts(handlers: ShortcutHandlers) {
const ref = useRef(handlers);
ref.current = handlers;
useEffect(() => {
const onKey = (event: KeyboardEvent) => {
const ctrl = event.ctrlKey || event.metaKey;
const key = event.key.toLowerCase();
if (import.meta.env.PROD && (event.key === "F5" || (ctrl && key === "r"))) {
event.preventDefault();
return;
}
const run = (fn?: () => void) => {
event.preventDefault();
fn?.();
};
if (ctrl && !event.shiftKey && !event.altKey) {
if (key === "n") return run(ref.current.onNew);
if (key === "s") return run(ref.current.onSaveDraft);
if (key === "l") return run(ref.current.onAddLine);
if (key === "enter") return run(ref.current.onIssue);
}
if (!ctrl && !event.altKey && event.key === "?" && !isTyping(event.target)) {
run(ref.current.onHelp);
}
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, []);
}
export const SHORTCUT_LIST: { keys: string; action: string }[] = [
{ keys: "Ctrl + N", action: "New invoice" },
{ keys: "Ctrl + S", action: "Save draft" },
{ keys: "Ctrl + Enter", action: "Issue and export PDF" },
{ keys: "Ctrl + L", action: "Add line item" },
{ keys: "?", action: "Show this list" },
];