Create categories and tags in place from every assignment picker

Category and tag inputs across the transaction editor, Analyse
corrections, and merchant defaults now mint missing entries without a
detour through the registry pages. A bare name lands under the kind's
root, "Parent / Name" targets that parent, and typing an existing name
selects it instead of duplicating. Enter only creates when nothing
matches, server rejections surface inline in the dropdown, and
assignment pickers offer leaf categories only — the shape the server
validates.

Mutations now return the accepted state so callers can select the id
the server just minted, and the revision-keyed remounts on Transactions
and the registry pages are gone: they closed the open modal and threw
away pending edits the moment any in-modal creation committed.
This commit is contained in:
Lars Nolden
2026-09-14 11:50:50 +02:00
parent f9e829e6ba
commit 1b3d7b22bb
8 changed files with 384 additions and 90 deletions
+12 -22
View File
@@ -136,13 +136,12 @@ function App() {
"/api/rebuild",
].includes(path);
try {
acceptState(
await request<State>(
path,
revisionless ? body : { revision: state.revision, ...body },
),
message,
const next = await request<State>(
path,
revisionless ? body : { revision: state.revision, ...body },
);
acceptState(next, message);
return next;
} catch (err) {
if (err instanceof APIError && err.status === 409) setConflict(true);
throw err;
@@ -352,7 +351,6 @@ function App() {
)}
{page === "transactions" && (
<Transactions
key={state.revision}
data={state.data}
filter={filter}
setFilter={setFilter}
@@ -361,7 +359,6 @@ function App() {
)}
{page === "categories" && (
<Registry
key={`categories-${state.revision}`}
entity="category"
data={state.data}
mutate={mutate}
@@ -371,24 +368,13 @@ function App() {
/>
)}
{page === "tags" && (
<Registry
key={`tags-${state.revision}`}
entity="tag"
data={state.data}
mutate={mutate}
/>
<Registry entity="tag" data={state.data} mutate={mutate} />
)}
{page === "merchants" && (
<Registry
key={`merchants-${state.revision}`}
entity="merchant"
data={state.data}
mutate={mutate}
/>
<Registry entity="merchant" data={state.data} mutate={mutate} />
)}
{page === "instruments" && (
<Registry
key={`instruments-${state.revision}`}
entity="instrument"
data={state.data}
mutate={mutate}
@@ -409,7 +395,11 @@ function App() {
/>
)}
{page === "classification" && (
<Classification state={state} acceptState={acceptState} />
<Classification
state={state}
acceptState={acceptState}
mutate={mutate}
/>
)}
{page === "settings" && (
<Settings