feat(tables): "Add row" opens the compose form for record-tables

When a new record's identity is composed from required fields that aren't
table columns (e.g. the risk register's project/discipline/sequence
tracking-number components), those can't be supplied by typing into the grid.
"+ Add row" now navigates to the compose form (<dir>/form.html) for such
tables — the form composes the tracking number and the server creates the
record. Simple tables (all required fields are columns) keep the inline
draft-row flow. Server mode only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-06-08 17:49:29 -05:00
parent af91916b58
commit 0326d46826

View file

@ -1,12 +1,15 @@
// add-row.js — inline new-row creation.
// add-row.js — new-row creation.
//
// Click "+ Add row" → append a draft row at the end of state.rows,
// focus its first editable cell, accumulate user typing into the
// drafts buffer like any other row. On row-blur, save.js detects the
// row.isNew flag and POSTs to <dir>/form.html (the form-create
// endpoint). The 201 response carries the new row's Location; we swap
// the synthetic url/yamlUrl for the real ones and the draft row
// becomes a normal saved row.
// Two paths, chosen by the table's schema:
// - Record-tables (identity composed from required fields that aren't
// columns — e.g. the risk register's tracking-number components): "+ Add
// row" navigates to the compose form (<dir>/form.html), the only place
// those components can be supplied. See needsComposeForm().
// - Simple tables (all required fields are columns): "+ Add row" appends a
// draft row at the end of state.rows, focuses its first editable cell, and
// accumulates typing into the drafts buffer like any other row. On
// row-blur, save.js detects row.isNew and POSTs to <dir>/form.html; the
// 201 Location swaps the synthetic url and the draft becomes a saved row.
//
// Synthetic identity: each new row gets a temporary "__new-<N>" url
// so rowKey() returns something unique for selection + draft tracking.
@ -34,8 +37,29 @@
return dir + 'form.html';
}
// Create-and-paint: the user-facing path.
// True when a new record's identity is composed from required fields that
// AREN'T table columns (e.g. the risk register's project/discipline/
// sequence tracking-number components). Such rows can't be created by
// typing into the grid — they need the compose form. Server mode only
// (the form handler is server-side).
function needsComposeForm() {
const ctx = app.context || {};
if (!app.state || app.state.source !== 'server') return false;
const req = (ctx.rowSchema && Array.isArray(ctx.rowSchema.required)) ? ctx.rowSchema.required : [];
if (!req.length) return false;
const colFields = {};
(ctx.columns || []).forEach(function (c) { if (c && c.field) colFields[c.field] = true; });
return req.some(function (f) { return !colFields[f]; });
}
// Create-and-paint: the user-facing path. Record-tables (composed identity)
// open the compose form directly — the grid can't supply their
// tracking-number components; simple tables append an inline draft row.
function invoke() {
if (needsComposeForm()) {
window.location.href = formCreateUrl();
return;
}
const key = createSilent();
if (typeof app.repaint === 'function') app.repaint();
focusNewRow(key);