);
}
function AuthedApp({ user, logout }) {
const store = useStore();
store.user = user; // expose signed-in identity (role + brand) to personas
store.logout = logout; // so the AppHeader account menu can sign out
const View = viewForRole(user.role);
return (
);
}
/* ---- Onboarding (spec Screen 2 — Tally detection: 2A confirm / 2B mismatch / 2C none) ----
Runs once right after signup. In the desktop app it probes the open Tally company
(tally_list_companies) and asks the backend /detect to compare it with the account
company; auto-polls every 5s so a company switch resolves without clicking. In the web
app (no Tally probe) it goes straight to the confirm-details step. */
function Onboarding({ onDone, logout }) {
const [state, setState] = React.useState("checking"); // checking|not_detected|mismatch|matched
const [info, setInfo] = React.useState({});
const [gstin, setGstin] = React.useState("");
const [busy, setBusy] = React.useState(false);
const [err, setErr] = React.useState("");
const invoke = () => (window.__TAURI__ && window.__TAURI__.core && window.__TAURI__.core.invoke) || null;
const probe = React.useCallback(async () => {
const inv = invoke();
let open_name = null, tally_online = false;
if (inv) {
try {
const xml = await inv("tally_list_companies");
const m = xml && xml.match(/ g || r.expected_gstin || "");
}, [onDone]);
React.useEffect(() => {
if (state === "matched") return; // confirming → stop detecting
probe();
const t = setInterval(probe, 5000);
return () => clearInterval(t);
}, [probe, state]);
const save = async () => {
setErr(""); setBusy(true);
try { await api.post("/api/desktop/company-details", { gstin: gstin.trim() }); onDone(); }
catch (e) { setErr((e && e.message) || "Couldn't save — check the GSTIN."); setBusy(false); }
};
const shell = (children) => (