@ai-matrx/records-ui 0.76.0 → 0.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4476,6 +4476,16 @@ var import_design_system13 = require("@ai-matrx/design-system");
4476
4476
  var import_jsx_runtime16 = require("react/jsx-runtime");
4477
4477
  var BATCH = 250;
4478
4478
  var SAMPLES = 40;
4479
+ var MAPPING_COLUMNS = "minmax(9rem, 11rem) minmax(14rem, 17rem) minmax(13rem, 1fr) minmax(8rem, 10rem)";
4480
+ var ABSENT = "__absent__";
4481
+ function obviousTitleColumn(columns) {
4482
+ const says = /^(title|name|record|customer|client|company|job|item|subject|label|description)\b|(\bname|\btitle|\bnumber|\bid)$/i;
4483
+ const named = columns.find((c) => says.test(c.header.trim()));
4484
+ if (named) return named.header;
4485
+ const text = columns.find((c) => c.type === "text");
4486
+ if (text) return text.header;
4487
+ return columns[0]?.header ?? null;
4488
+ }
4479
4489
  async function sha256(bytes) {
4480
4490
  const subtle = globalThis.crypto?.subtle;
4481
4491
  if (!subtle) return null;
@@ -4510,6 +4520,7 @@ function ImportWizard({ tableId, onDone, className }) {
4510
4520
  const [alreadyRan, setAlreadyRan] = (0, import_react26.useState)(null);
4511
4521
  const [columnsAdded, setColumnsAdded] = (0, import_react26.useState)(null);
4512
4522
  const [show, setShow] = (0, import_react26.useState)("all");
4523
+ const [ledger, setLedger] = (0, import_react26.useState)(null);
4513
4524
  const declared = fields.data ?? [];
4514
4525
  const take = (0, import_react26.useCallback)(
4515
4526
  async (file) => {
@@ -4520,6 +4531,7 @@ function ImportWizard({ tableId, onDone, className }) {
4520
4531
  setProposals([]);
4521
4532
  setAlreadyRan(null);
4522
4533
  setProgress(0);
4534
+ setLedger(null);
4523
4535
  try {
4524
4536
  const xlsx = file.name.toLowerCase().endsWith(".xlsx");
4525
4537
  const bytes = await file.arrayBuffer();
@@ -4546,6 +4558,11 @@ function ImportWizard({ tableId, onDone, className }) {
4546
4558
  for (const column of answered.data.columns) {
4547
4559
  if (column.matched) next[column.header] = column.field_key;
4548
4560
  }
4561
+ const titleKey2 = table.data?.title_field ?? null;
4562
+ if (titleKey2 && !Object.values(next).includes(titleKey2)) {
4563
+ const guess = obviousTitleColumn(answered.data.columns);
4564
+ if (guess) next[guess] = titleKey2;
4565
+ }
4549
4566
  setMapping(next);
4550
4567
  setPhase("ready");
4551
4568
  } catch (thrown) {
@@ -4553,7 +4570,7 @@ function ImportWizard({ tableId, onDone, className }) {
4553
4570
  setPhase("waiting");
4554
4571
  }
4555
4572
  },
4556
- [client, declared, tableId]
4573
+ [client, declared, tableId, table.data?.title_field]
4557
4574
  );
4558
4575
  const run = (0, import_react26.useCallback)(async () => {
4559
4576
  if (!plan) return;
@@ -4561,6 +4578,7 @@ function ImportWizard({ tableId, onDone, className }) {
4561
4578
  setOutcomes([]);
4562
4579
  setColumnsAdded(null);
4563
4580
  setProgress(0);
4581
+ setLedger(null);
4564
4582
  if (unmapped === "create" && rights.admin) {
4565
4583
  setPhase("declaring");
4566
4584
  const made = await client.importDeclareColumns({ table_id: tableId, rows, mapping });
@@ -4596,6 +4614,7 @@ function ImportWizard({ tableId, onDone, className }) {
4596
4614
  }
4597
4615
  const importId = opened.data.import_id;
4598
4616
  const collected = [];
4617
+ const told = { seen: 0, written: 0, duplicate: 0, refused: 0 };
4599
4618
  let take2 = opened.data.rows_per_call ?? BATCH;
4600
4619
  let at = 0;
4601
4620
  while (at < rows.length) {
@@ -4604,11 +4623,17 @@ function ImportWizard({ tableId, onDone, className }) {
4604
4623
  if (!answered.ok) {
4605
4624
  setProblem(answered.error.message);
4606
4625
  setOutcomes([...collected]);
4626
+ setLedger({ ...told, final: false });
4607
4627
  setPhase("ready");
4608
4628
  return;
4609
4629
  }
4610
4630
  collected.push(...answered.data.outcomes);
4611
4631
  setOutcomes([...collected]);
4632
+ told.seen += answered.data.rows_seen;
4633
+ told.written += answered.data.rows_written;
4634
+ told.duplicate += answered.data.rows_duplicate;
4635
+ told.refused += answered.data.rows_refused;
4636
+ setLedger({ ...told, final: false });
4612
4637
  at += batch.length;
4613
4638
  setProgress(Math.min(at, rows.length));
4614
4639
  if (answered.data.rows_per_call) take2 = answered.data.rows_per_call;
@@ -4621,6 +4646,13 @@ function ImportWizard({ tableId, onDone, className }) {
4621
4646
  }
4622
4647
  setSummary(finished.data.message);
4623
4648
  setProposals(finished.data.proposals);
4649
+ setLedger({
4650
+ seen: finished.data.rows_seen,
4651
+ written: finished.data.rows_written,
4652
+ duplicate: finished.data.rows_duplicate,
4653
+ refused: finished.data.rows_refused,
4654
+ final: true
4655
+ });
4624
4656
  setPhase("done");
4625
4657
  onDone?.(finished.data.rows_written);
4626
4658
  }, [client, dedupeKey, fileBytes, fileHash, fileName, format, mapping, onDuplicate, onDone, plan, rows, tableId, unmapped]);
@@ -4640,6 +4672,10 @@ function ImportWizard({ tableId, onDone, className }) {
4640
4672
  [outcomes, show]
4641
4673
  );
4642
4674
  const mappedCount = Object.keys(mapping).length;
4675
+ const titleKey = table.data?.title_field ?? null;
4676
+ const titleMappedTo = titleKey ? Object.entries(mapping).find(([, key]) => key === titleKey)?.[0] ?? null : null;
4677
+ const titleMissing = Boolean(titleKey) && titleMappedTo === null;
4678
+ const titleFieldLabel = (titleKey ? plan?.fields.find((f) => f.key === titleKey)?.label : null) || (titleKey ? declared.find((f) => f.key === titleKey)?.label : null) || "the name column";
4643
4679
  const busy = phase === "reading" || phase === "planning" || phase === "declaring" || phase === "writing";
4644
4680
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: (0, import_design_system13.cn)("flex min-w-0 flex-col gap-2 text-xs", className), children: [
4645
4681
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
@@ -4672,55 +4708,107 @@ function ImportWizard({ tableId, onDone, className }) {
4672
4708
  phase === "reading" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
4673
4709
  phase === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
4674
4710
  plan && parsed ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4675
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-full overflow-x-auto rounded border", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("table", { className: "w-full min-w-[50rem] table-fixed", children: [
4676
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("colgroup", { children: [
4677
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("col", { className: "w-[10rem]" }),
4678
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("col", { className: "w-[17rem]" }),
4679
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("col", { className: "w-[14rem]" }),
4680
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("col", { className: "w-[9rem]" })
4681
- ] }),
4682
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("thead", { className: "bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { children: [
4683
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Column in the file" }),
4684
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Goes to" }),
4685
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Kind" }),
4686
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "First values" })
4687
- ] }) }),
4688
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: plan.columns.map((column) => {
4711
+ titleKey ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-2 rounded border bg-muted/40 px-2 py-1.5", children: [
4712
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: "What each record is called" }),
4713
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4714
+ import_design_system13.Select,
4715
+ {
4716
+ value: titleMappedTo ?? "",
4717
+ disabled: busy,
4718
+ onValueChange: (chosen) => {
4719
+ setMapping((m) => {
4720
+ const next = { ...m };
4721
+ for (const [header, key] of Object.entries(next)) {
4722
+ if (key === titleKey) delete next[header];
4723
+ }
4724
+ if (chosen) next[chosen] = titleKey;
4725
+ return next;
4726
+ });
4727
+ },
4728
+ children: [
4729
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4730
+ import_design_system13.SelectTrigger,
4731
+ {
4732
+ size: "sm",
4733
+ "aria-label": "Which column each record is named by",
4734
+ className: "min-w-[14rem]",
4735
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.SelectValue, { placeholder: "\u2014 choose the column \u2014" })
4736
+ }
4737
+ ),
4738
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.SelectContent, { children: plan.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.SelectItem, { value: c.header, children: c.header }, c.header)) })
4739
+ ]
4740
+ }
4741
+ ),
4742
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-muted-foreground", children: titleMappedTo ? `goes into ${titleFieldLabel}, so every record this file makes has a name you can read in the grid and on a chip.` : `Nothing goes into ${titleFieldLabel} yet, so every record this file makes would land with no name at all. Pick the column that says what each row IS.` })
4743
+ ] }) : null,
4744
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-full overflow-x-auto rounded border", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "min-w-[52rem]", children: [
4745
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4746
+ "div",
4747
+ {
4748
+ className: "grid items-center gap-x-3 border-b bg-muted px-2 py-1 font-medium",
4749
+ style: { gridTemplateColumns: MAPPING_COLUMNS },
4750
+ children: [
4751
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "Column in the file" }),
4752
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "Goes to" }),
4753
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "Kind" }),
4754
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "First values" })
4755
+ ]
4756
+ }
4757
+ ),
4758
+ plan.columns.map((column) => {
4689
4759
  const to = mapping[column.header] ?? "";
4690
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-t align-top", children: [
4691
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "truncate px-2 py-1 font-medium", title: column.header, children: column.header }),
4692
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4693
- "select",
4694
- {
4695
- "aria-label": `Where ${column.header} goes`,
4696
- className: "w-full min-w-0 rounded border bg-background px-1 py-0.5",
4697
- value: to,
4698
- disabled: busy,
4699
- onChange: (e) => {
4700
- const chosen = e.target.value;
4701
- setMapping((m) => {
4702
- const next = { ...m };
4703
- if (chosen === "") delete next[column.header];
4704
- else next[column.header] = chosen;
4705
- return next;
4706
- });
4707
- },
4708
- children: [
4709
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "", children: unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014" }),
4710
- plan.fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: f.key, children: f.label || f.key }, f.field_id))
4711
- ]
4712
- }
4713
- ) }),
4714
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("td", { className: "px-2 py-1", children: [
4715
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Badge, { variant: column.matched ? "secondary" : "outline", className: "text-[11px] font-normal", children: typeWord(column.type) }),
4716
- column.unit ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
4717
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
4718
- column.ambiguous ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
4719
- column.collides_with ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null
4720
- ] }),
4721
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "truncate px-2 py-1 text-muted-foreground", children: (column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ") })
4722
- ] }, column.header);
4723
- }) })
4760
+ const absent = unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014";
4761
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4762
+ "div",
4763
+ {
4764
+ className: "grid items-start gap-x-3 border-t px-2 py-1",
4765
+ style: { gridTemplateColumns: MAPPING_COLUMNS },
4766
+ children: [
4767
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate pt-1.5 font-medium", title: column.header, children: column.header }),
4768
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4769
+ import_design_system13.Select,
4770
+ {
4771
+ value: to || ABSENT,
4772
+ disabled: busy,
4773
+ onValueChange: (chosen) => {
4774
+ setMapping((m) => {
4775
+ const next = { ...m };
4776
+ if (chosen === ABSENT) delete next[column.header];
4777
+ else next[column.header] = chosen;
4778
+ return next;
4779
+ });
4780
+ },
4781
+ children: [
4782
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4783
+ import_design_system13.SelectTrigger,
4784
+ {
4785
+ size: "sm",
4786
+ "aria-label": `Where ${column.header} goes`,
4787
+ "data-goes-to": column.header,
4788
+ className: "w-full min-w-[14rem]",
4789
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.SelectValue, {})
4790
+ }
4791
+ ),
4792
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_design_system13.SelectContent, { children: [
4793
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.SelectItem, { value: ABSENT, children: absent }),
4794
+ plan.fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.SelectItem, { value: f.key, children: f.label || f.key }, f.field_id))
4795
+ ] })
4796
+ ]
4797
+ }
4798
+ ),
4799
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "pt-1", children: [
4800
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Badge, { variant: column.matched ? "secondary" : "outline", className: "text-[11px] font-normal", children: typeWord(column.type) }),
4801
+ column.unit ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
4802
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
4803
+ column.ambiguous ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
4804
+ column.collides_with ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null
4805
+ ] }),
4806
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate pt-1.5 text-muted-foreground", children: (column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ") })
4807
+ ]
4808
+ },
4809
+ column.header
4810
+ );
4811
+ })
4724
4812
  ] }) }),
4725
4813
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Separator, {}),
4726
4814
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-3", children: [
@@ -4778,13 +4866,18 @@ function ImportWizard({ tableId, onDone, className }) {
4778
4866
  ] })
4779
4867
  ] }),
4780
4868
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
4781
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Button, { size: "sm", disabled: busy || !rights.write || rows.length === 0, onClick: () => void run(), children: phase === "declaring" ? "Adding the columns\u2026" : phase === "writing" ? `Writing\u2026 ${progress} of ${rows.length}` : `Import ${rows.length} row${rows.length === 1 ? "" : "s"}` }),
4782
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-muted-foreground", children: [
4783
- mappedCount,
4784
- " of ",
4785
- plan.columns.length,
4786
- " columns go into this table",
4787
- fileHash ? "" : " \xB7 this browser cannot recognise the file again, so importing it twice would write it twice"
4869
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4870
+ import_design_system13.Button,
4871
+ {
4872
+ size: "sm",
4873
+ disabled: busy || !rights.write || rows.length === 0 || titleMissing,
4874
+ onClick: () => void run(),
4875
+ children: phase === "declaring" ? "Adding the columns\u2026" : phase === "writing" ? `Writing\u2026 ${progress} of ${rows.length}` : `Import ${rows.length} row${rows.length === 1 ? "" : "s"}`
4876
+ }
4877
+ ),
4878
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: (0, import_design_system13.cn)("text-muted-foreground", titleMissing && "text-destructive"), children: [
4879
+ titleMissing ? `Nothing goes into ${titleFieldLabel} yet, so these records would have no name. Choose the column above, then this will run.` : `${mappedCount} of ${plan.columns.length} columns go into this table`,
4880
+ fileHash || titleMissing ? "" : " \xB7 this browser cannot recognise the file again, so importing it twice would write it twice"
4788
4881
  ] })
4789
4882
  ] })
4790
4883
  ] }) : null,
@@ -4802,27 +4895,31 @@ function ImportWizard({ tableId, onDone, className }) {
4802
4895
  ] }) : null
4803
4896
  ] }) : null,
4804
4897
  alreadyRan ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: alreadyRan }) : null,
4805
- outcomes.length > 0 || summary ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4898
+ outcomes.length > 0 || summary || ledger ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4806
4899
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Separator, {}),
4807
4900
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
4808
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { children: [
4809
- counts.landed,
4810
- " landed \xB7 ",
4811
- counts.duplicate,
4812
- " already here \xB7 ",
4813
- counts.refused,
4814
- " refused"
4815
- ] }),
4816
- counts.refused + counts.duplicate > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4901
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: ledger ? `${ledger.written} landed \xB7 ${ledger.duplicate} already here \xB7 ${ledger.refused} refused, of ${ledger.seen} row${ledger.seen === 1 ? "" : "s"} offered` : `${counts.landed} landed \xB7 ${counts.duplicate} already here \xB7 ${counts.refused} refused` }),
4902
+ ledger && !ledger.final && phase !== "writing" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-destructive", children: "The run stopped here, so the rest of the file was never offered \u2014 these are the rows that were dealt with before it stopped, not the whole file." }) : null,
4903
+ (ledger ? ledger.refused + ledger.duplicate : counts.refused + counts.duplicate) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4817
4904
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: (0, import_design_system13.cn)("underline underline-offset-2", show === "all" && "font-medium"), onClick: () => setShow("all"), children: "everything that did not land" }),
4818
4905
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: (0, import_design_system13.cn)("underline underline-offset-2", show === "refused" && "font-medium"), onClick: () => setShow("refused"), children: "refused" }),
4819
4906
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: (0, import_design_system13.cn)("underline underline-offset-2", show === "duplicate" && "font-medium"), onClick: () => setShow("duplicate"), children: "already here" })
4820
4907
  ] }) : null
4821
4908
  ] }),
4822
4909
  summary ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: summary }) : null,
4910
+ ledger && show === "all" && ledger.refused + ledger.duplicate > interesting.length ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "text-muted-foreground", children: [
4911
+ interesting.length,
4912
+ " of the ",
4913
+ ledger.refused + ledger.duplicate,
4914
+ " row",
4915
+ ledger.refused + ledger.duplicate === 1 ? " that" : "s that",
4916
+ " did not land",
4917
+ interesting.length === 1 ? " is" : " are",
4918
+ " listed below by their line in your file \u2014 the store keeps the rest with this run and does not hand every one of them back at once."
4919
+ ] }) : null,
4823
4920
  interesting.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "max-h-64 overflow-auto rounded border", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("table", { className: "w-full", children: [
4824
4921
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("thead", { className: "sticky top-0 bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { children: [
4825
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Row" }),
4922
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Line in your file" }),
4826
4923
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "What happened" }),
4827
4924
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "The row in your file" })
4828
4925
  ] }) }),
@@ -6201,8 +6298,8 @@ function TableSettings({
6201
6298
  const table = (0, import_react36.useTable)(tableId);
6202
6299
  const fields = (0, import_react36.useFields)(tableId);
6203
6300
  const rights = useTableRights(table.data);
6204
- const mutation = (0, import_react36.useRecordMutation)();
6205
6301
  const shape = (0, import_react36.useFieldMutation)();
6302
+ const archive = (0, import_react36.useTableArchive)(tableId);
6206
6303
  const [editing, setEditing] = (0, import_react35.useState)(null);
6207
6304
  const [askingToDelete, setAskingToDelete] = (0, import_react35.useState)(false);
6208
6305
  const [askingToRemove, setAskingToRemove] = (0, import_react35.useState)(null);
@@ -6227,10 +6324,16 @@ function TableSettings({
6227
6324
  table.reload();
6228
6325
  }
6229
6326
  };
6230
- const deleteTable = async () => {
6231
- const gone = await mutation.remove({ record_id: tableId });
6232
- setAskingToDelete(false);
6233
- if (gone !== null) onDeleted?.();
6327
+ const startArchive = async () => {
6328
+ const finished = await archive.start();
6329
+ if (finished?.done && finished.table_archived) {
6330
+ setAskingToDelete(false);
6331
+ onDeleted?.();
6332
+ }
6333
+ };
6334
+ const askToArchive = async () => {
6335
+ setAskingToDelete(true);
6336
+ await archive.preview();
6234
6337
  };
6235
6338
  if (enriching) {
6236
6339
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className, children: [
@@ -6266,7 +6369,6 @@ function TableSettings({
6266
6369
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-muted-foreground", children: rows.length }),
6267
6370
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "outline", className: "ml-auto", onClick: () => setEditing("new"), children: "Add a field" })
6268
6371
  ] }),
6269
- mutation.error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RefusalNotice, { error: mutation.error }) : null,
6270
6372
  shape.error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RefusalNotice, { error: shape.error }) : null,
6271
6373
  askingToRemove ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-wrap items-center gap-2 rounded border border-destructive/40 px-2 py-1.5", children: [
6272
6374
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-destructive", children: [
@@ -6357,21 +6459,32 @@ function TableSettings({
6357
6459
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "font-medium", children: "This table" }),
6358
6460
  askingToDelete ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6359
6461
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("p", { className: "text-muted-foreground", children: [
6360
- "Deleting ",
6462
+ "Archiving ",
6361
6463
  table.data ? tableName(table.data) : "this table",
6362
6464
  " takes it out of everyone's list, with its ",
6363
6465
  rows.length,
6364
6466
  " field",
6365
6467
  rows.length === 1 ? "" : "s",
6366
- " and every record in it.",
6367
- table.data?.retention_days ? ` The store keeps them for ${table.data.retention_days} days, so it can still be restored.` : " The store keeps them, so it can still be restored."
6468
+ " and",
6469
+ " ",
6470
+ archive.progress ? `its ${archive.progress.remaining.toLocaleString()} record${archive.progress.remaining === 1 ? "" : "s"}` : "every record in it",
6471
+ ".",
6472
+ table.data?.retention_days ? ` The store keeps them for ${table.data.retention_days} days, so it can still be brought back.` : " The store keeps them, so it can still be brought back."
6368
6473
  ] }),
6369
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex gap-1", children: [
6370
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "destructive", disabled: mutation.saving, onClick: () => void deleteTable(), children: mutation.saving ? "Deleting\u2026" : "Delete this table" }),
6474
+ archive.progress && (archive.running || archive.passes > 0) ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("p", { className: archive.error ? "text-destructive" : "text-muted-foreground", children: [
6475
+ archive.progress.total - archive.progress.remaining === archive.progress.total ? `All ${archive.progress.total.toLocaleString()} record${archive.progress.total === 1 ? "" : "s"} are put away.` : `${(archive.progress.total - archive.progress.remaining).toLocaleString()} of ${archive.progress.total.toLocaleString()} record${archive.progress.total === 1 ? "" : "s"} put away so far.`,
6476
+ archive.slowedDown ? " The store is busy, so it is taking smaller passes \u2014 it will still finish." : ""
6477
+ ] }) : null,
6478
+ archive.interrupted && archive.progress ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-muted-foreground", children: archive.error ? `That pass did not finish, so the rest is still here. ${archive.progress.archived_total.toLocaleString()} record${archive.progress.archived_total === 1 ? " is" : "s are"} already archived and ${archive.progress.remaining.toLocaleString()} ${archive.progress.remaining === 1 ? "is" : "are"} left. Nothing was lost \u2014 carry on and it picks up where it stopped.` : `Stopped. ${archive.progress.archived_total.toLocaleString()} record${archive.progress.archived_total === 1 ? " is" : "s are"} archived and ${archive.progress.remaining.toLocaleString()} ${archive.progress.remaining === 1 ? "is" : "are"} left. Carry on whenever you like \u2014 it picks up where it stopped.` }) : null,
6479
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex gap-1", children: archive.running ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6480
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "destructive", disabled: true, children: "Archiving\u2026" }),
6481
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "ghost", onClick: () => archive.stop(), children: "Stop" })
6482
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6483
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "destructive", onClick: () => void startArchive(), children: archive.interrupted ? "Carry on archiving" : "Archive this table" }),
6371
6484
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "ghost", onClick: () => setAskingToDelete(false), children: "Keep it" })
6372
- ] })
6373
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "outline", onClick: () => setAskingToDelete(true), children: "Delete this table" }) }),
6374
- mutation.error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RefusalNotice, { error: mutation.error }) : null
6485
+ ] }) })
6486
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system18.Button, { size: "sm", variant: "outline", onClick: () => void askToArchive(), children: "Archive this table" }) }),
6487
+ archive.error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RefusalNotice, { error: archive.error }) : null
6375
6488
  ] })
6376
6489
  ] });
6377
6490
  }
@@ -7671,6 +7784,7 @@ function ViewSwitcher({
7671
7784
  )
7672
7785
  ] });
7673
7786
  }
7787
+ var UNUSUAL_FIELD_NOTE = "an unusual column to use here";
7674
7788
  function offerableFields(all, want) {
7675
7789
  return all.filter((field) => {
7676
7790
  const kind = editorKindFor(field);
@@ -7806,10 +7920,7 @@ function FieldPicker({
7806
7920
  children: [
7807
7921
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: "", children: "Choose a field" }),
7808
7922
  offerable.map((field) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: field.key, children: fieldName(field) }, field.key)),
7809
- unusual ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("option", { value: unusual.key, children: [
7810
- fieldName(unusual),
7811
- " \u2014 an unusual column to use here"
7812
- ] }, unusual.key) : null
7923
+ unusual ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: unusual.key, children: `${fieldName(unusual)} \u2014 ${UNUSUAL_FIELD_NOTE}` }, unusual.key) : null
7813
7924
  ]
7814
7925
  }
7815
7926
  ),
@@ -11622,14 +11733,41 @@ function SubscriptionsPanel({ tableId, className }) {
11622
11733
  }
11623
11734
 
11624
11735
  // src/FormBuilder.tsx
11625
- var import_react81 = require("react");
11626
- var import_react82 = require("@ai-matrx/records/react");
11736
+ var import_react83 = require("react");
11737
+ var import_react84 = require("@ai-matrx/records/react");
11738
+
11739
+ // src/publish-gate.ts
11740
+ var import_react79 = require("react");
11741
+ var import_react80 = require("@ai-matrx/records/react");
11742
+ var PUBLISH_NEEDS_THE_STORE_ON = "This organization has its record store switched off, so publishing would hand you a link that opens nothing. An owner or an administrator turns it on under Database settings, on the unified data screen \u2014 everything you have already built here is kept and starts working the moment they do.";
11743
+ function usePublishGate() {
11744
+ const client = (0, import_react80.useRecordsClient)();
11745
+ const [storeOpen, setStoreOpen] = (0, import_react79.useState)(null);
11746
+ (0, import_react79.useEffect)(() => {
11747
+ let alive = true;
11748
+ void (async () => {
11749
+ const answered = await client.storeIsOpen();
11750
+ if (!alive) return;
11751
+ setStoreOpen(answered.ok ? answered.data : null);
11752
+ })();
11753
+ return () => {
11754
+ alive = false;
11755
+ };
11756
+ }, [client]);
11757
+ return {
11758
+ storeOpen,
11759
+ blocked: storeOpen === false,
11760
+ why: storeOpen === false ? PUBLISH_NEEDS_THE_STORE_ON : null
11761
+ };
11762
+ }
11763
+
11764
+ // src/FormBuilder.tsx
11627
11765
  var import_records10 = require("@ai-matrx/records");
11628
11766
  var import_design_system40 = require("@ai-matrx/design-system");
11629
11767
 
11630
11768
  // src/FormRunner.tsx
11631
- var import_react79 = require("react");
11632
- var import_react80 = require("@ai-matrx/records/react");
11769
+ var import_react81 = require("react");
11770
+ var import_react82 = require("@ai-matrx/records/react");
11633
11771
  var import_design_system39 = require("@ai-matrx/design-system");
11634
11772
  var import_jsx_runtime42 = require("react/jsx-runtime");
11635
11773
  function FormRunner(props) {
@@ -11639,10 +11777,10 @@ function FormRunner(props) {
11639
11777
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ConnectedFormRunner, { ...props });
11640
11778
  }
11641
11779
  function ConnectedFormRunner(props) {
11642
- const client = (0, import_react80.useOptionalRecordsClient)();
11643
- const fields = (0, import_react80.useFields)(props.form.subject);
11780
+ const client = (0, import_react82.useOptionalRecordsClient)();
11781
+ const fields = (0, import_react82.useFields)(props.form.subject);
11644
11782
  const { form, className } = props;
11645
- const submit = (0, import_react79.useCallback)(
11783
+ const submit = (0, import_react81.useCallback)(
11646
11784
  async (values) => {
11647
11785
  if (!client) {
11648
11786
  return {
@@ -11665,7 +11803,7 @@ function ConnectedFormRunner(props) {
11665
11803
  },
11666
11804
  [client, form]
11667
11805
  );
11668
- const evaluate = (0, import_react79.useCallback)(
11806
+ const evaluate = (0, import_react81.useCallback)(
11669
11807
  async (expr, values) => {
11670
11808
  if (!client) return null;
11671
11809
  const answered = await client.ruleEval({ expr, values });
@@ -11699,16 +11837,16 @@ function FormStage({
11699
11837
  connected = false
11700
11838
  }) {
11701
11839
  const host = useRecordsUi();
11702
- const [answers, setAnswers] = (0, import_react79.useState)({});
11703
- const [at, setAt] = (0, import_react79.useState)(0);
11704
- const [error, setError] = (0, import_react79.useState)(null);
11705
- const [refusal, setRefusal] = (0, import_react79.useState)(null);
11706
- const [writing, setWriting] = (0, import_react79.useState)(false);
11707
- const [done, setDone] = (0, import_react79.useState)(null);
11708
- const [hidden, setHidden] = (0, import_react79.useState)({});
11709
- const decoy = (0, import_react79.useRef)("");
11710
- const stage = (0, import_react79.useRef)(null);
11711
- const questions = (0, import_react79.useMemo)(() => {
11840
+ const [answers, setAnswers] = (0, import_react81.useState)({});
11841
+ const [at, setAt] = (0, import_react81.useState)(0);
11842
+ const [error, setError] = (0, import_react81.useState)(null);
11843
+ const [refusal, setRefusal] = (0, import_react81.useState)(null);
11844
+ const [writing, setWriting] = (0, import_react81.useState)(false);
11845
+ const [done, setDone] = (0, import_react81.useState)(null);
11846
+ const [hidden, setHidden] = (0, import_react81.useState)({});
11847
+ const decoy = (0, import_react81.useRef)("");
11848
+ const stage = (0, import_react81.useRef)(null);
11849
+ const questions = (0, import_react81.useMemo)(() => {
11712
11850
  const byKey = new Map((fields ?? []).map((f) => [f.key, f]));
11713
11851
  return (form.questions ?? []).map((q) => {
11714
11852
  const field = byKey.get(q.field) ?? null;
@@ -11722,7 +11860,7 @@ function FormStage({
11722
11860
  };
11723
11861
  });
11724
11862
  }, [fields, form.questions]);
11725
- (0, import_react79.useEffect)(() => {
11863
+ (0, import_react81.useEffect)(() => {
11726
11864
  let cancelled = false;
11727
11865
  const conditional = questions.filter((q) => q.showIf);
11728
11866
  if (conditional.length === 0 || !evaluate) return;
@@ -11756,7 +11894,7 @@ function FormStage({
11756
11894
  const v = answers[q.key];
11757
11895
  return q.required && (v === void 0 || v === null || v === "");
11758
11896
  });
11759
- const submit = (0, import_react79.useCallback)(async () => {
11897
+ const submit = (0, import_react81.useCallback)(async () => {
11760
11898
  if (preview) {
11761
11899
  setDone("preview");
11762
11900
  return;
@@ -11793,7 +11931,7 @@ function FormStage({
11793
11931
  if (event.shiftKey) retreat();
11794
11932
  else advance();
11795
11933
  }
11796
- (0, import_react79.useEffect)(() => {
11934
+ (0, import_react81.useEffect)(() => {
11797
11935
  const input = stage.current?.querySelector(
11798
11936
  "input:not([tabindex='-1']), textarea, select, [role='combobox']"
11799
11937
  );
@@ -11887,7 +12025,7 @@ function Question({
11887
12025
  onChange,
11888
12026
  upload
11889
12027
  }) {
11890
- const [uploadError, setUploadError] = (0, import_react79.useState)(null);
12028
+ const [uploadError, setUploadError] = (0, import_react81.useState)(null);
11891
12029
  const field = question.field;
11892
12030
  if (!field) return null;
11893
12031
  const id = `form-${field.key}`;
@@ -11944,23 +12082,24 @@ function whyNotAskable(fields, hidden = []) {
11944
12082
  // src/FormBuilder.tsx
11945
12083
  var import_jsx_runtime43 = require("react/jsx-runtime");
11946
12084
  function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
11947
- const client = (0, import_react82.useRecordsClient)();
12085
+ const client = (0, import_react84.useRecordsClient)();
12086
+ const publishGate = usePublishGate();
11948
12087
  const host = useRecordsUi();
11949
12088
  const claimSeed = useSeedGuard();
11950
- const table = (0, import_react82.useTable)(tableId);
12089
+ const table = (0, import_react84.useTable)(tableId);
11951
12090
  const rights = useTableRights(table.data);
11952
- const fields = (0, import_react82.useFields)(tableId);
11953
- const [forms, setForms] = (0, import_react81.useState)(null);
11954
- const [error, setError] = (0, import_react81.useState)(null);
11955
- const [activeId, setActiveId] = (0, import_react81.useState)(activeFormId ?? null);
11956
- const [draft, setDraft] = (0, import_react81.useState)(null);
11957
- const [saving, setSaving] = (0, import_react81.useState)(false);
11958
- const [saved, setSaved] = (0, import_react81.useState)(null);
11959
- const [publishing, setPublishing] = (0, import_react81.useState)(false);
11960
- const [copied, setCopied] = (0, import_react81.useState)(false);
11961
- const [shownUrl, setShownUrl] = (0, import_react81.useState)(null);
12091
+ const fields = (0, import_react84.useFields)(tableId);
12092
+ const [forms, setForms] = (0, import_react83.useState)(null);
12093
+ const [error, setError] = (0, import_react83.useState)(null);
12094
+ const [activeId, setActiveId] = (0, import_react83.useState)(activeFormId ?? null);
12095
+ const [draft, setDraft] = (0, import_react83.useState)(null);
12096
+ const [saving, setSaving] = (0, import_react83.useState)(false);
12097
+ const [saved, setSaved] = (0, import_react83.useState)(null);
12098
+ const [publishing, setPublishing] = (0, import_react83.useState)(false);
12099
+ const [copied, setCopied] = (0, import_react83.useState)(false);
12100
+ const [shownUrl, setShownUrl] = (0, import_react83.useState)(null);
11962
12101
  const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
11963
- const load = (0, import_react81.useCallback)(async () => {
12102
+ const load = (0, import_react83.useCallback)(async () => {
11964
12103
  const answered = await client.forms({ table_id: tableId });
11965
12104
  if (!answered.ok) {
11966
12105
  setError(answered.error);
@@ -11987,17 +12126,17 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
11987
12126
  setError(null);
11988
12127
  setForms(mine);
11989
12128
  }, [client, tableId, seed, claimSeed]);
11990
- (0, import_react81.useEffect)(() => {
12129
+ (0, import_react83.useEffect)(() => {
11991
12130
  void load();
11992
12131
  }, [load]);
11993
- (0, import_react81.useEffect)(() => {
12132
+ (0, import_react83.useEffect)(() => {
11994
12133
  if (!forms || forms.length === 0) return;
11995
12134
  const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
11996
12135
  if (chosen.id !== activeId) setActiveId(chosen.id);
11997
12136
  setDraft(chosen);
11998
12137
  onActiveForm?.(chosen);
11999
12138
  }, [forms, activeFormId]);
12000
- const byKey = (0, import_react81.useMemo)(() => new Map((fields.data ?? []).map((f) => [f.key, f])), [fields.data]);
12139
+ const byKey = (0, import_react83.useMemo)(() => new Map((fields.data ?? []).map((f) => [f.key, f])), [fields.data]);
12001
12140
  function patch(change) {
12002
12141
  setDraft((prev) => prev ? { ...prev, ...change } : prev);
12003
12142
  setSaved(null);
@@ -12101,8 +12240,8 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
12101
12240
  {
12102
12241
  url: `${publicOrigin}${(0, import_records10.publicFormPath)(draft.id)}`,
12103
12242
  state: draft.state,
12104
- mayPublish: rights.structure,
12105
- why: rights.why("structure"),
12243
+ mayPublish: rights.structure && !publishGate.blocked,
12244
+ why: publishGate.why ?? rights.why("structure"),
12106
12245
  busy: publishing,
12107
12246
  copied,
12108
12247
  shownUrl,
@@ -12400,13 +12539,13 @@ function groupLabel(groups) {
12400
12539
  }
12401
12540
 
12402
12541
  // src/chartFrame.tsx
12403
- var import_react83 = require("react");
12542
+ var import_react85 = require("react");
12404
12543
  var import_design_system41 = require("@ai-matrx/design-system");
12405
12544
  var import_jsx_runtime44 = require("react/jsx-runtime");
12406
12545
  function useMeasuredWidth(fallback = 480) {
12407
- const ref = (0, import_react83.useRef)(null);
12408
- const [width, setWidth] = (0, import_react83.useState)(fallback);
12409
- (0, import_react83.useEffect)(() => {
12546
+ const ref = (0, import_react85.useRef)(null);
12547
+ const [width, setWidth] = (0, import_react85.useState)(fallback);
12548
+ (0, import_react85.useEffect)(() => {
12410
12549
  const node = ref.current;
12411
12550
  if (!node) return;
12412
12551
  const apply = () => {
@@ -12436,7 +12575,7 @@ function ChartFrame({
12436
12575
  children
12437
12576
  }) {
12438
12577
  const [ref, width] = useMeasuredWidth();
12439
- const chartId = `records-chart-${(0, import_react83.useId)().replace(/:/g, "")}`;
12578
+ const chartId = `records-chart-${(0, import_react85.useId)().replace(/:/g, "")}`;
12440
12579
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
12441
12580
  "div",
12442
12581
  {
@@ -12530,24 +12669,24 @@ function isSignatureField(field) {
12530
12669
  }
12531
12670
 
12532
12671
  // src/DocTemplate.tsx
12533
- var import_react84 = require("react");
12534
- var import_react85 = require("@ai-matrx/records/react");
12672
+ var import_react86 = require("react");
12673
+ var import_react87 = require("@ai-matrx/records/react");
12535
12674
  var import_design_system42 = require("@ai-matrx/design-system");
12536
12675
  var import_jsx_runtime45 = require("react/jsx-runtime");
12537
12676
  function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
12538
- const client = (0, import_react85.useRecordsClient)();
12677
+ const client = (0, import_react87.useRecordsClient)();
12539
12678
  const claimSeed = useSeedGuard();
12540
- const table = (0, import_react85.useTable)(tableId);
12679
+ const table = (0, import_react87.useTable)(tableId);
12541
12680
  const rights = useTableRights(table.data);
12542
- const fields = (0, import_react85.useFields)(tableId);
12543
- const [templates, setTemplates] = (0, import_react84.useState)(null);
12544
- const [error, setError] = (0, import_react84.useState)(null);
12545
- const [activeId, setActiveId] = (0, import_react84.useState)(activeTemplateId ?? null);
12546
- const [draftName, setDraftName] = (0, import_react84.useState)("");
12547
- const [draftBody, setDraftBody] = (0, import_react84.useState)("");
12548
- const [unresolved, setUnresolved] = (0, import_react84.useState)([]);
12549
- const [saving, setSaving] = (0, import_react84.useState)(false);
12550
- const load = (0, import_react84.useCallback)(async () => {
12681
+ const fields = (0, import_react87.useFields)(tableId);
12682
+ const [templates, setTemplates] = (0, import_react86.useState)(null);
12683
+ const [error, setError] = (0, import_react86.useState)(null);
12684
+ const [activeId, setActiveId] = (0, import_react86.useState)(activeTemplateId ?? null);
12685
+ const [draftName, setDraftName] = (0, import_react86.useState)("");
12686
+ const [draftBody, setDraftBody] = (0, import_react86.useState)("");
12687
+ const [unresolved, setUnresolved] = (0, import_react86.useState)([]);
12688
+ const [saving, setSaving] = (0, import_react86.useState)(false);
12689
+ const load = (0, import_react86.useCallback)(async () => {
12551
12690
  const held = await client.docTemplates({ table_id: tableId });
12552
12691
  if (!held.ok) {
12553
12692
  setError(held.error);
@@ -12578,10 +12717,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
12578
12717
  setError(null);
12579
12718
  setTemplates(rows);
12580
12719
  }, [client, tableId, seed, fields.data]);
12581
- (0, import_react84.useEffect)(() => {
12720
+ (0, import_react86.useEffect)(() => {
12582
12721
  void load();
12583
12722
  }, [load]);
12584
- (0, import_react84.useEffect)(() => {
12723
+ (0, import_react86.useEffect)(() => {
12585
12724
  if (!templates || templates.length === 0) return;
12586
12725
  const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
12587
12726
  if (chosen.id !== activeId) setActiveId(chosen.id);
@@ -12589,7 +12728,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
12589
12728
  setDraftBody(chosen.body);
12590
12729
  onActiveTemplate?.(chosen);
12591
12730
  }, [templates, activeTemplateId]);
12592
- (0, import_react84.useEffect)(() => {
12731
+ (0, import_react86.useEffect)(() => {
12593
12732
  let cancelled = false;
12594
12733
  if (draftBody.trim() === "") {
12595
12734
  setUnresolved([]);
@@ -12709,19 +12848,19 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
12709
12848
  }
12710
12849
 
12711
12850
  // src/DocRender.tsx
12712
- var import_react86 = require("react");
12713
- var import_react87 = require("@ai-matrx/records/react");
12851
+ var import_react88 = require("react");
12852
+ var import_react89 = require("@ai-matrx/records/react");
12714
12853
  var import_design_system43 = require("@ai-matrx/design-system");
12715
12854
  var import_jsx_runtime46 = require("react/jsx-runtime");
12716
12855
  function DocRender({ templateId, recordId, filename, onRendered, className }) {
12717
- const client = (0, import_react87.useRecordsClient)();
12718
- const [preview, setPreview] = (0, import_react86.useState)(null);
12719
- const [renders, setRenders] = (0, import_react86.useState)(null);
12720
- const [showing, setShowing] = (0, import_react86.useState)(null);
12721
- const [error, setError] = (0, import_react86.useState)(null);
12722
- const [busy, setBusy] = (0, import_react86.useState)(null);
12723
- const paper = (0, import_react86.useRef)(null);
12724
- const load = (0, import_react86.useCallback)(async () => {
12856
+ const client = (0, import_react89.useRecordsClient)();
12857
+ const [preview, setPreview] = (0, import_react88.useState)(null);
12858
+ const [renders, setRenders] = (0, import_react88.useState)(null);
12859
+ const [showing, setShowing] = (0, import_react88.useState)(null);
12860
+ const [error, setError] = (0, import_react88.useState)(null);
12861
+ const [busy, setBusy] = (0, import_react88.useState)(null);
12862
+ const paper = (0, import_react88.useRef)(null);
12863
+ const load = (0, import_react88.useCallback)(async () => {
12725
12864
  const [body, held] = await Promise.all([
12726
12865
  client.docRenderBody({ template_id: templateId, record_id: recordId }),
12727
12866
  client.docRenders({ record_id: recordId })
@@ -12738,7 +12877,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
12738
12877
  setPreview(body.data);
12739
12878
  setRenders(held.data.filter((r) => r.template_id === templateId));
12740
12879
  }, [client, templateId, recordId]);
12741
- (0, import_react86.useEffect)(() => {
12880
+ (0, import_react88.useEffect)(() => {
12742
12881
  void load();
12743
12882
  }, [load]);
12744
12883
  async function freeze() {
@@ -12822,22 +12961,22 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
12822
12961
  }
12823
12962
 
12824
12963
  // src/SignBlock.tsx
12825
- var import_react88 = require("react");
12826
- var import_react89 = require("@ai-matrx/records/react");
12964
+ var import_react90 = require("react");
12965
+ var import_react91 = require("@ai-matrx/records/react");
12827
12966
  var import_design_system44 = require("@ai-matrx/design-system");
12828
- var import_react90 = require("@ai-matrx/records/react");
12967
+ var import_react92 = require("@ai-matrx/records/react");
12829
12968
  var import_jsx_runtime47 = require("react/jsx-runtime");
12830
12969
  function SignBlock({ tableId, recordId, render, className }) {
12831
- const client = (0, import_react89.useRecordsClient)();
12832
- const table = (0, import_react90.useTable)(tableId);
12970
+ const client = (0, import_react91.useRecordsClient)();
12971
+ const table = (0, import_react92.useTable)(tableId);
12833
12972
  const rights = useTableRights(table.data);
12834
- const fields = (0, import_react89.useFields)(tableId);
12835
- const [signatures, setSignatures] = (0, import_react88.useState)(null);
12836
- const [verdicts, setVerdicts] = (0, import_react88.useState)({});
12837
- const [error, setError] = (0, import_react88.useState)(null);
12838
- const [name, setName] = (0, import_react88.useState)("");
12839
- const [busy, setBusy] = (0, import_react88.useState)(false);
12840
- const load = (0, import_react88.useCallback)(async () => {
12973
+ const fields = (0, import_react91.useFields)(tableId);
12974
+ const [signatures, setSignatures] = (0, import_react90.useState)(null);
12975
+ const [verdicts, setVerdicts] = (0, import_react90.useState)({});
12976
+ const [error, setError] = (0, import_react90.useState)(null);
12977
+ const [name, setName] = (0, import_react90.useState)("");
12978
+ const [busy, setBusy] = (0, import_react90.useState)(false);
12979
+ const load = (0, import_react90.useCallback)(async () => {
12841
12980
  const held = await client.docSignatures({ record_id: recordId });
12842
12981
  if (!held.ok) {
12843
12982
  setError(held.error);
@@ -12852,7 +12991,7 @@ function SignBlock({ tableId, recordId, render, className }) {
12852
12991
  }
12853
12992
  setVerdicts(answers);
12854
12993
  }, [client, recordId]);
12855
- (0, import_react88.useEffect)(() => {
12994
+ (0, import_react90.useEffect)(() => {
12856
12995
  void load();
12857
12996
  }, [load]);
12858
12997
  async function sign(field) {
@@ -12959,23 +13098,23 @@ function SignBlock({ tableId, recordId, render, className }) {
12959
13098
  }
12960
13099
 
12961
13100
  // src/NotifyRuleEditor.tsx
12962
- var import_react91 = require("react");
12963
- var import_react92 = require("@ai-matrx/records/react");
13101
+ var import_react93 = require("react");
13102
+ var import_react94 = require("@ai-matrx/records/react");
12964
13103
  var import_design_system45 = require("@ai-matrx/design-system");
12965
13104
  var import_jsx_runtime48 = require("react/jsx-runtime");
12966
13105
  var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
12967
13106
  var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
12968
13107
  function NotifyRuleEditor({ tableId, seed, className }) {
12969
- const client = (0, import_react92.useRecordsClient)();
13108
+ const client = (0, import_react94.useRecordsClient)();
12970
13109
  const host = useRecordsUi();
12971
- const table = (0, import_react92.useTable)(tableId);
13110
+ const table = (0, import_react94.useTable)(tableId);
12972
13111
  const rights = useTableRights(table.data);
12973
- const [subscriptions, setSubscriptions] = (0, import_react91.useState)(null);
12974
- const [cadences, setCadences] = (0, import_react91.useState)([]);
12975
- const [views, setViews] = (0, import_react91.useState)(null);
12976
- const [error, setError] = (0, import_react91.useState)(null);
12977
- const [busy, setBusy] = (0, import_react91.useState)(false);
12978
- const load = (0, import_react91.useCallback)(async () => {
13112
+ const [subscriptions, setSubscriptions] = (0, import_react93.useState)(null);
13113
+ const [cadences, setCadences] = (0, import_react93.useState)([]);
13114
+ const [views, setViews] = (0, import_react93.useState)(null);
13115
+ const [error, setError] = (0, import_react93.useState)(null);
13116
+ const [busy, setBusy] = (0, import_react93.useState)(false);
13117
+ const load = (0, import_react93.useCallback)(async () => {
12979
13118
  const [held, offered] = await Promise.all([
12980
13119
  // THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
12981
13120
  // to them plus — only where they hold admin on this Table — anyone's over
@@ -12995,10 +13134,10 @@ function NotifyRuleEditor({ tableId, seed, className }) {
12995
13134
  if (host.savedViews) setViews(await host.savedViews());
12996
13135
  else setViews(null);
12997
13136
  }, [client, host, tableId]);
12998
- (0, import_react91.useEffect)(() => {
13137
+ (0, import_react93.useEffect)(() => {
12999
13138
  void load();
13000
13139
  }, [load]);
13001
- const write = (0, import_react91.useCallback)(
13140
+ const write = (0, import_react93.useCallback)(
13002
13141
  async (spec) => {
13003
13142
  const declared = await client.subscriptionDeclare({
13004
13143
  table_id: tableId,
@@ -13023,7 +13162,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
13023
13162
  },
13024
13163
  [client, tableId]
13025
13164
  );
13026
- (0, import_react91.useEffect)(() => {
13165
+ (0, import_react93.useEffect)(() => {
13027
13166
  if (!subscriptions || !seed || seed.length === 0) return;
13028
13167
  const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
13029
13168
  if (missing.length === 0) return;
@@ -13166,7 +13305,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
13166
13305
  }
13167
13306
 
13168
13307
  // src/ChartBlock.tsx
13169
- var import_react93 = require("react");
13308
+ var import_react95 = require("react");
13170
13309
  var import_recharts = require("recharts");
13171
13310
  var import_records11 = require("@ai-matrx/records");
13172
13311
  var import_core6 = require("@ai-matrx/records/core");
@@ -13191,7 +13330,7 @@ function ChartBlock({ block, subject, className }) {
13191
13330
  const measures = block.measures && block.measures.length > 0 ? block.measures : [{ op: "count" }];
13192
13331
  const primary = (0, import_records11.measureKey)(measures[0]);
13193
13332
  const series = [primary];
13194
- const points = (0, import_react93.useMemo)(() => {
13333
+ const points = (0, import_react95.useMemo)(() => {
13195
13334
  if (kind === "stuck") return [];
13196
13335
  const rows = block.rows ?? [];
13197
13336
  return rows.map((row) => ({
@@ -13203,7 +13342,7 @@ function ChartBlock({ block, subject, className }) {
13203
13342
  n: row.row_count
13204
13343
  }));
13205
13344
  }, [block.rows, kind, series.join("|")]);
13206
- const config = (0, import_react93.useMemo)(() => {
13345
+ const config = (0, import_react95.useMemo)(() => {
13207
13346
  const out = {};
13208
13347
  series.forEach((key, index) => {
13209
13348
  out[key] = {
@@ -13464,25 +13603,25 @@ function Drawing({
13464
13603
  }
13465
13604
 
13466
13605
  // src/DashboardCanvas.tsx
13467
- var import_react94 = require("react");
13468
- var import_react95 = require("@ai-matrx/records/react");
13606
+ var import_react96 = require("react");
13607
+ var import_react97 = require("@ai-matrx/records/react");
13469
13608
  var import_design_system47 = require("@ai-matrx/design-system");
13470
13609
  var import_jsx_runtime50 = require("react/jsx-runtime");
13471
13610
  function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13472
- const client = (0, import_react95.useRecordsClient)();
13611
+ const client = (0, import_react97.useRecordsClient)();
13473
13612
  const host = useRecordsUi();
13474
- const table = (0, import_react95.useTable)(tableId);
13613
+ const table = (0, import_react97.useTable)(tableId);
13475
13614
  const rights = useTableRights(table.data);
13476
- const fields = (0, import_react95.useFields)(tableId);
13477
- const [boards, setBoards] = (0, import_react94.useState)(null);
13478
- const [error, setError] = (0, import_react94.useState)(null);
13479
- const [activeId, setActiveId] = (0, import_react94.useState)(activeDashboardId ?? null);
13480
- const [run, setRun] = (0, import_react94.useState)(null);
13481
- const [running, setRunning] = (0, import_react94.useState)(false);
13482
- const [question, setQuestion] = (0, import_react94.useState)("");
13483
- const [asking, setAsking] = (0, import_react94.useState)(false);
13484
- const [scheduling, setScheduling] = (0, import_react94.useState)(false);
13485
- const load = (0, import_react94.useCallback)(async () => {
13615
+ const fields = (0, import_react97.useFields)(tableId);
13616
+ const [boards, setBoards] = (0, import_react96.useState)(null);
13617
+ const [error, setError] = (0, import_react96.useState)(null);
13618
+ const [activeId, setActiveId] = (0, import_react96.useState)(activeDashboardId ?? null);
13619
+ const [run, setRun] = (0, import_react96.useState)(null);
13620
+ const [running, setRunning] = (0, import_react96.useState)(false);
13621
+ const [question, setQuestion] = (0, import_react96.useState)("");
13622
+ const [asking, setAsking] = (0, import_react96.useState)(false);
13623
+ const [scheduling, setScheduling] = (0, import_react96.useState)(false);
13624
+ const load = (0, import_react96.useCallback)(async () => {
13486
13625
  const answered = await client.dashboards({ table_id: tableId });
13487
13626
  if (!answered.ok) {
13488
13627
  setError(answered.error);
@@ -13491,17 +13630,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13491
13630
  setError(null);
13492
13631
  setBoards(answered.data.map(dashboardFromSummary));
13493
13632
  }, [client, tableId]);
13494
- (0, import_react94.useEffect)(() => {
13633
+ (0, import_react96.useEffect)(() => {
13495
13634
  void load();
13496
13635
  }, [load]);
13497
- (0, import_react94.useEffect)(() => {
13636
+ (0, import_react96.useEffect)(() => {
13498
13637
  if (!boards || boards.length === 0) return;
13499
13638
  const chosen = boards.find((d) => d.id === (activeDashboardId ?? activeId)) ?? boards[0];
13500
13639
  if (chosen.id !== activeId) setActiveId(chosen.id);
13501
13640
  }, [boards, activeDashboardId]);
13502
- const board = (0, import_react94.useMemo)(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
13641
+ const board = (0, import_react96.useMemo)(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
13503
13642
  const filterKey = JSON.stringify(filter ?? {});
13504
- (0, import_react94.useEffect)(() => {
13643
+ (0, import_react96.useEffect)(() => {
13505
13644
  if (!activeId) {
13506
13645
  setRun(null);
13507
13646
  return;
@@ -13519,7 +13658,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13519
13658
  cancelled = true;
13520
13659
  };
13521
13660
  }, [client, activeId, filterKey]);
13522
- const declare2 = (0, import_react94.useCallback)(
13661
+ const declare2 = (0, import_react96.useCallback)(
13523
13662
  async (next, blocks) => {
13524
13663
  const written = await client.dashboardDeclare(
13525
13664
  dashboardDeclareArgs({ ...next, blocks }, tableId)
@@ -13540,8 +13679,8 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13540
13679
  [client, tableId, load, filter]
13541
13680
  );
13542
13681
  async function create() {
13543
- const groupable = (fields.data ?? []).filter((f) => f.type === "text" || f.type === "list");
13544
- const byField = groupable[0];
13682
+ const all = fields.data ?? [];
13683
+ const byField = offerableFields(all, "choice")[0] ?? all.find((f) => f.type === "text" || f.type === "list");
13545
13684
  const by = byField?.key;
13546
13685
  await declare2(
13547
13686
  {
@@ -13570,7 +13709,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13570
13709
  ]
13571
13710
  );
13572
13711
  }
13573
- const refresh = (0, import_react94.useCallback)(async () => {
13712
+ const refresh = (0, import_react96.useCallback)(async () => {
13574
13713
  if (!activeId) return;
13575
13714
  setRunning(true);
13576
13715
  const again = await client.dashboardRun({
@@ -13712,6 +13851,32 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13712
13851
  children: CHART_KINDS.map((kind) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("option", { value: kind, children: CHART_KIND_LABEL[kind] }, kind))
13713
13852
  }
13714
13853
  ),
13854
+ (board.blocks[index].groupBy ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
13855
+ GroupingPicker,
13856
+ {
13857
+ block: board.blocks[index],
13858
+ fields: fields.data ?? [],
13859
+ onPick: (key) => void declare2(
13860
+ board,
13861
+ board.blocks.map((b, i) => {
13862
+ if (i !== index) return b;
13863
+ const picked = (fields.data ?? []).find((f) => f.key === key);
13864
+ const wasNamedAfterItsColumn = (fields.data ?? []).some(
13865
+ (f) => b.title === `By ${fieldName(f)}`
13866
+ );
13867
+ return {
13868
+ ...b,
13869
+ groupBy: [key],
13870
+ // A block titled after the column it grouped by
13871
+ // must not keep saying "By Job Number" once it
13872
+ // groups by Status. A title somebody wrote
13873
+ // themselves is left alone.
13874
+ ...wasNamedAfterItsColumn && picked ? { title: `By ${fieldName(picked)}` } : {}
13875
+ };
13876
+ })
13877
+ )
13878
+ }
13879
+ ) : null,
13715
13880
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
13716
13881
  import_design_system47.Button,
13717
13882
  {
@@ -13730,10 +13895,49 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13730
13895
  ] })
13731
13896
  ] });
13732
13897
  }
13898
+ function groupingChoices(all) {
13899
+ const once = [];
13900
+ for (const field of all) if (!once.some((f) => f.key === field.key)) once.push(field);
13901
+ const sensible = offerableFields(once, "choice");
13902
+ const isSensible = (field) => sensible.some((s) => s.key === field.key);
13903
+ return [
13904
+ ...sensible.map((field) => ({ field, unusual: false })),
13905
+ ...once.filter((field) => !isSensible(field)).map((field) => ({ field, unusual: true }))
13906
+ ];
13907
+ }
13908
+ function GroupingPicker({
13909
+ block,
13910
+ fields,
13911
+ onPick
13912
+ }) {
13913
+ const choices = groupingChoices(fields);
13914
+ const current = block.groupBy?.[0] ?? "";
13915
+ if (choices.length === 0) {
13916
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "text-muted-foreground", children: "This table has no columns yet, so there is nothing to group this block by." });
13917
+ }
13918
+ const known = choices.some((c) => c.field.key === current);
13919
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
13920
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("label", { className: "text-muted-foreground", htmlFor: `block-group-${block.title}`, children: "Group by" }),
13921
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_design_system47.Select, { value: known ? current : "", onValueChange: onPick, children: [
13922
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
13923
+ import_design_system47.SelectTrigger,
13924
+ {
13925
+ size: "sm",
13926
+ id: `block-group-${block.title}`,
13927
+ "aria-label": `Which column ${block.title} is grouped by`,
13928
+ className: "h-7 min-w-[10rem] text-xs",
13929
+ children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_design_system47.SelectValue, { placeholder: "Choose a column" })
13930
+ }
13931
+ ),
13932
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_design_system47.SelectContent, { children: choices.map(({ field, unusual }) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_design_system47.SelectItem, { value: field.key, children: unusual ? `${fieldName(field)} \u2014 ${UNUSUAL_FIELD_NOTE}` : fieldName(field) }, field.key)) })
13933
+ ] }),
13934
+ known ? null : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "text-muted-foreground", children: "The column this block was grouped by is no longer on this table \u2014 choose another." })
13935
+ ] });
13936
+ }
13733
13937
 
13734
13938
  // src/FormsPanel.tsx
13735
- var import_react96 = require("react");
13736
- var import_react97 = require("@ai-matrx/records/react");
13939
+ var import_react98 = require("react");
13940
+ var import_react99 = require("@ai-matrx/records/react");
13737
13941
  var import_records12 = require("@ai-matrx/records");
13738
13942
  var import_design_system48 = require("@ai-matrx/design-system");
13739
13943
  var import_jsx_runtime51 = require("react/jsx-runtime");
@@ -13744,16 +13948,17 @@ function formSuggestion(tableName2) {
13744
13948
  return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
13745
13949
  }
13746
13950
  function FormsPanel({ tableId, className }) {
13747
- const client = (0, import_react97.useRecordsClient)();
13951
+ const client = (0, import_react99.useRecordsClient)();
13952
+ const publishGate = usePublishGate();
13748
13953
  const host = useRecordsUi();
13749
- const table = (0, import_react97.useTable)(tableId);
13954
+ const table = (0, import_react99.useTable)(tableId);
13750
13955
  const rights = useTableRights(table.data);
13751
- const [forms, setForms] = (0, import_react96.useState)(null);
13752
- const [error, setError] = (0, import_react96.useState)(null);
13753
- const [busy, setBusy] = (0, import_react96.useState)(null);
13754
- const [copied, setCopied] = (0, import_react96.useState)(null);
13755
- const [building, setBuilding] = (0, import_react96.useState)(false);
13756
- const load = (0, import_react96.useCallback)(async () => {
13956
+ const [forms, setForms] = (0, import_react98.useState)(null);
13957
+ const [error, setError] = (0, import_react98.useState)(null);
13958
+ const [busy, setBusy] = (0, import_react98.useState)(null);
13959
+ const [copied, setCopied] = (0, import_react98.useState)(null);
13960
+ const [building, setBuilding] = (0, import_react98.useState)(false);
13961
+ const load = (0, import_react98.useCallback)(async () => {
13757
13962
  const answered = await client.forms({ table_id: tableId });
13758
13963
  if (!answered.ok) {
13759
13964
  setError(answered.error);
@@ -13763,10 +13968,10 @@ function FormsPanel({ tableId, className }) {
13763
13968
  setError(null);
13764
13969
  setForms(answered.data);
13765
13970
  }, [client, tableId]);
13766
- (0, import_react96.useEffect)(() => {
13971
+ (0, import_react98.useEffect)(() => {
13767
13972
  void load();
13768
13973
  }, [load]);
13769
- const toggle = (0, import_react96.useCallback)(
13974
+ const toggle = (0, import_react98.useCallback)(
13770
13975
  async (form) => {
13771
13976
  setBusy(form.form_id);
13772
13977
  const wanted = form.published_at === null || form.closed_at !== null;
@@ -13780,8 +13985,8 @@ function FormsPanel({ tableId, className }) {
13780
13985
  },
13781
13986
  [client, load]
13782
13987
  );
13783
- const [shown, setShown] = (0, import_react96.useState)(null);
13784
- const copy = (0, import_react96.useCallback)(async (url, formId) => {
13988
+ const [shown, setShown] = (0, import_react98.useState)(null);
13989
+ const copy = (0, import_react98.useCallback)(async (url, formId) => {
13785
13990
  try {
13786
13991
  await navigator.clipboard.writeText(url);
13787
13992
  setCopied(formId);
@@ -13856,7 +14061,7 @@ function FormsPanel({ tableId, className }) {
13856
14061
  ] }),
13857
14062
  /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
13858
14063
  form.published_at ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_design_system48.Button, { size: "sm", variant: "outline", onClick: () => void copy(url, form.form_id), children: copied === form.form_id ? "Copied" : "Copy link" }) : null,
13859
- rights.structure ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
14064
+ rights.structure && !publishGate.blocked ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
13860
14065
  import_design_system48.Button,
13861
14066
  {
13862
14067
  size: "sm",
@@ -13867,6 +14072,7 @@ function FormsPanel({ tableId, className }) {
13867
14072
  }
13868
14073
  ) : null
13869
14074
  ] }),
14075
+ rights.structure && publishGate.why ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "mt-1.5 text-xs text-muted-foreground", children: publishGate.why }) : null,
13870
14076
  shown && shown === url ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
13871
14077
  "This browser would not let the page copy for you, so here it is to copy by hand:",
13872
14078
  " ",
@@ -13879,8 +14085,8 @@ function FormsPanel({ tableId, className }) {
13879
14085
  }
13880
14086
 
13881
14087
  // src/BookingBuilder.tsx
13882
- var import_react98 = require("react");
13883
- var import_react99 = require("@ai-matrx/records/react");
14088
+ var import_react100 = require("react");
14089
+ var import_react101 = require("@ai-matrx/records/react");
13884
14090
  var import_records13 = require("@ai-matrx/records");
13885
14091
  var import_design_system49 = require("@ai-matrx/design-system");
13886
14092
  var import_jsx_runtime52 = require("react/jsx-runtime");
@@ -13908,38 +14114,39 @@ function draftWindows(availability) {
13908
14114
  });
13909
14115
  }
13910
14116
  function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
13911
- const client = (0, import_react99.useRecordsClient)();
14117
+ const client = (0, import_react101.useRecordsClient)();
14118
+ const publishGate = usePublishGate();
13912
14119
  const host = useRecordsUi();
13913
- const table = (0, import_react99.useTable)(tableId);
14120
+ const table = (0, import_react101.useTable)(tableId);
13914
14121
  const rights = useTableRights(table.data);
13915
- const fields = (0, import_react99.useFields)(tableId);
13916
- const [existing, setExisting] = (0, import_react98.useState)(null);
13917
- const [loaded, setLoaded] = (0, import_react98.useState)(false);
13918
- const [error, setError] = (0, import_react98.useState)(null);
13919
- const [saving, setSaving] = (0, import_react98.useState)(false);
13920
- const [publishing, setPublishing] = (0, import_react98.useState)(false);
13921
- const [copied, setCopied] = (0, import_react98.useState)(false);
13922
- const [title, setTitle] = (0, import_react98.useState)("");
13923
- const [minutes, setMinutes] = (0, import_react98.useState)(30);
13924
- const [buffer, setBuffer] = (0, import_react98.useState)(0);
13925
- const [lead, setLead] = (0, import_react98.useState)(120);
13926
- const [perDay, setPerDay] = (0, import_react98.useState)(8);
13927
- const [days, setDays] = (0, import_react98.useState)(30);
13928
- const [windows, setWindows] = (0, import_react98.useState)(() => draftWindows(null));
13929
- const [asked, setAsked] = (0, import_react98.useState)([]);
13930
- const [confirmation, setConfirmation] = (0, import_react98.useState)("");
13931
- const [offer, setOffer] = (0, import_react98.useState)(null);
13932
- const [formId, setFormId] = (0, import_react98.useState)(bookingId ?? null);
14122
+ const fields = (0, import_react101.useFields)(tableId);
14123
+ const [existing, setExisting] = (0, import_react100.useState)(null);
14124
+ const [loaded, setLoaded] = (0, import_react100.useState)(false);
14125
+ const [error, setError] = (0, import_react100.useState)(null);
14126
+ const [saving, setSaving] = (0, import_react100.useState)(false);
14127
+ const [publishing, setPublishing] = (0, import_react100.useState)(false);
14128
+ const [copied, setCopied] = (0, import_react100.useState)(false);
14129
+ const [title, setTitle] = (0, import_react100.useState)("");
14130
+ const [minutes, setMinutes] = (0, import_react100.useState)(30);
14131
+ const [buffer, setBuffer] = (0, import_react100.useState)(0);
14132
+ const [lead, setLead] = (0, import_react100.useState)(120);
14133
+ const [perDay, setPerDay] = (0, import_react100.useState)(8);
14134
+ const [days, setDays] = (0, import_react100.useState)(30);
14135
+ const [windows, setWindows] = (0, import_react100.useState)(() => draftWindows(null));
14136
+ const [asked, setAsked] = (0, import_react100.useState)([]);
14137
+ const [confirmation, setConfirmation] = (0, import_react100.useState)("");
14138
+ const [offer, setOffer] = (0, import_react100.useState)(null);
14139
+ const [formId, setFormId] = (0, import_react100.useState)(bookingId ?? null);
13933
14140
  const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
13934
- const askable = (0, import_react98.useMemo)(
14141
+ const askable = (0, import_react100.useMemo)(
13935
14142
  () => askableFields(fields.data ?? [], STORE_ANSWERS_THESE),
13936
14143
  [fields.data]
13937
14144
  );
13938
- const leftOut = (0, import_react98.useMemo)(
14145
+ const leftOut = (0, import_react100.useMemo)(
13939
14146
  () => whyNotAskable(fields.data ?? [], STORE_ANSWERS_THESE),
13940
14147
  [fields.data]
13941
14148
  );
13942
- const load = (0, import_react98.useCallback)(async () => {
14149
+ const load = (0, import_react100.useCallback)(async () => {
13943
14150
  const answered = await client.bookings({ table_id: tableId });
13944
14151
  if (!answered.ok) {
13945
14152
  setError(answered.error);
@@ -13951,18 +14158,18 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
13951
14158
  setLoaded(true);
13952
14159
  if (mine) setFormId(mine.form_id);
13953
14160
  }, [client, tableId, bookingId]);
13954
- (0, import_react98.useEffect)(() => {
14161
+ (0, import_react100.useEffect)(() => {
13955
14162
  void load();
13956
14163
  }, [load]);
13957
- (0, import_react98.useEffect)(() => {
14164
+ (0, import_react100.useEffect)(() => {
13958
14165
  if (title !== "" || !table.data) return;
13959
14166
  setTitle(existing?.title ?? `Book a ${minutes}-minute ${table.data.name} appointment`);
13960
14167
  }, [table.data, existing]);
13961
- (0, import_react98.useEffect)(() => {
14168
+ (0, import_react100.useEffect)(() => {
13962
14169
  if (!existing) return;
13963
14170
  setMinutes(existing.slot_minutes);
13964
14171
  }, [existing]);
13965
- (0, import_react98.useEffect)(() => {
14172
+ (0, import_react100.useEffect)(() => {
13966
14173
  if (!offer) return;
13967
14174
  setWindows(draftWindows(offer));
13968
14175
  setMinutes(offer.slot_minutes);
@@ -13971,7 +14178,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
13971
14178
  setPerDay(offer.max_per_day);
13972
14179
  setDays(offer.days);
13973
14180
  }, [offer]);
13974
- const availability = (0, import_react98.useCallback)(
14181
+ const availability = (0, import_react100.useCallback)(
13975
14182
  () => ({
13976
14183
  slot_minutes: minutes,
13977
14184
  buffer_minutes: buffer,
@@ -14208,7 +14415,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
14208
14415
  children: copied ? "Copied" : "Copy link"
14209
14416
  }
14210
14417
  ),
14211
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
14418
+ publishGate.blocked ? null : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
14212
14419
  import_design_system49.Button,
14213
14420
  {
14214
14421
  size: "sm",
@@ -14217,15 +14424,15 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
14217
14424
  children: publishing ? "\u2026" : existing?.published_at && !existing.closed_at ? "Unpublish" : "Publish"
14218
14425
  }
14219
14426
  ),
14220
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("p", { className: "w-full text-xs text-muted-foreground", children: existing?.published_at && !existing.closed_at ? "Open. Anyone with this link can book a time." : "Not open yet \u2014 the link does not take bookings until you publish it." })
14427
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("p", { className: "w-full text-xs text-muted-foreground", children: publishGate.why ?? (existing?.published_at && !existing.closed_at ? "Open. Anyone with this link can book a time." : "Not open yet \u2014 the link does not take bookings until you publish it.") })
14221
14428
  ] }) : null
14222
14429
  ] })
14223
14430
  );
14224
14431
  }
14225
14432
 
14226
14433
  // src/BookingSlots.tsx
14227
- var import_react100 = require("react");
14228
- var import_react101 = require("@ai-matrx/records/react");
14434
+ var import_react102 = require("react");
14435
+ var import_react103 = require("@ai-matrx/records/react");
14229
14436
  var import_records14 = require("@ai-matrx/records");
14230
14437
  var import_design_system50 = require("@ai-matrx/design-system");
14231
14438
  var import_jsx_runtime53 = require("react/jsx-runtime");
@@ -14237,15 +14444,15 @@ function bookingSuggestion(tableName2) {
14237
14444
  var NO_ADMIN2 = "Opening a booking page lets people with no account take time in your calendar, so it needs the admin level on the table the appointments land in.";
14238
14445
  var STATE_WORDS = BOOKING_PAGE_STATE_LABEL;
14239
14446
  function BookingSlots({ tableId, className }) {
14240
- const client = (0, import_react101.useRecordsClient)();
14447
+ const client = (0, import_react103.useRecordsClient)();
14241
14448
  const host = useRecordsUi();
14242
- const [pages, setPages] = (0, import_react100.useState)(null);
14243
- const [error, setError] = (0, import_react100.useState)(null);
14244
- const [busy, setBusy] = (0, import_react100.useState)(null);
14245
- const [copied, setCopied] = (0, import_react100.useState)(null);
14246
- const [shown, setShown] = (0, import_react100.useState)(null);
14247
- const [building, setBuilding] = (0, import_react100.useState)(false);
14248
- const load = (0, import_react100.useCallback)(async () => {
14449
+ const [pages, setPages] = (0, import_react102.useState)(null);
14450
+ const [error, setError] = (0, import_react102.useState)(null);
14451
+ const [busy, setBusy] = (0, import_react102.useState)(null);
14452
+ const [copied, setCopied] = (0, import_react102.useState)(null);
14453
+ const [shown, setShown] = (0, import_react102.useState)(null);
14454
+ const [building, setBuilding] = (0, import_react102.useState)(false);
14455
+ const load = (0, import_react102.useCallback)(async () => {
14249
14456
  const answered = await client.bookings(tableId ? { table_id: tableId } : {});
14250
14457
  if (!answered.ok) {
14251
14458
  setError(answered.error);
@@ -14255,10 +14462,10 @@ function BookingSlots({ tableId, className }) {
14255
14462
  setError(null);
14256
14463
  setPages(answered.data);
14257
14464
  }, [client, tableId]);
14258
- (0, import_react100.useEffect)(() => {
14465
+ (0, import_react102.useEffect)(() => {
14259
14466
  void load();
14260
14467
  }, [load]);
14261
- const subjectIds = (0, import_react100.useMemo)(
14468
+ const subjectIds = (0, import_react102.useMemo)(
14262
14469
  () => Array.from(
14263
14470
  /* @__PURE__ */ new Set([
14264
14471
  ...(pages ?? []).map((p) => p.table_id),
@@ -14271,10 +14478,10 @@ function BookingSlots({ tableId, className }) {
14271
14478
  ),
14272
14479
  [pages, tableId]
14273
14480
  );
14274
- const levels = (0, import_react101.useMyLevels)(subjectIds);
14481
+ const levels = (0, import_react103.useMyLevels)(subjectIds);
14275
14482
  const levelsKnown = !levels.loading && Boolean(levels.data);
14276
14483
  const mayBuildHere = levelsKnown && Boolean(tableId) && levels.data?.[tableId] === "admin";
14277
- const toggle = (0, import_react100.useCallback)(
14484
+ const toggle = (0, import_react102.useCallback)(
14278
14485
  async (page) => {
14279
14486
  setBusy(page.form_id);
14280
14487
  const wanted = page.published_at === null || page.closed_at !== null;
@@ -14288,7 +14495,7 @@ function BookingSlots({ tableId, className }) {
14288
14495
  },
14289
14496
  [client, load]
14290
14497
  );
14291
- const copy = (0, import_react100.useCallback)(async (url, formId) => {
14498
+ const copy = (0, import_react102.useCallback)(async (url, formId) => {
14292
14499
  try {
14293
14500
  await navigator.clipboard.writeText(url);
14294
14501
  setCopied(formId);
@@ -14424,14 +14631,14 @@ function nextInWords(page) {
14424
14631
  }
14425
14632
 
14426
14633
  // src/CaptureSheet.tsx
14427
- var import_react104 = require("react");
14428
- var import_react105 = require("@ai-matrx/records/react");
14634
+ var import_react106 = require("react");
14635
+ var import_react107 = require("@ai-matrx/records/react");
14429
14636
  var import_design_system52 = require("@ai-matrx/design-system");
14430
14637
 
14431
14638
  // src/CaptureRun.tsx
14432
- var import_react102 = require("react");
14639
+ var import_react104 = require("react");
14433
14640
  var import_records15 = require("@ai-matrx/records");
14434
- var import_react103 = require("@ai-matrx/records/react");
14641
+ var import_react105 = require("@ai-matrx/records/react");
14435
14642
  var import_design_system51 = require("@ai-matrx/design-system");
14436
14643
  var import_jsx_runtime54 = require("react/jsx-runtime");
14437
14644
  function controlFor(field) {
@@ -14470,21 +14677,21 @@ function whereWeAre(timeoutMs = 4e3) {
14470
14677
  });
14471
14678
  }
14472
14679
  function CaptureRun({ sheetId, face: given, className }) {
14473
- const client = (0, import_react103.useRecordsClient)();
14680
+ const client = (0, import_react105.useRecordsClient)();
14474
14681
  const host = useRecordsUi();
14475
- const [face, setFace] = (0, import_react102.useState)(given);
14476
- const [loadFailed, setLoadFailed] = (0, import_react102.useState)(null);
14477
- const [at, setAt] = (0, import_react102.useState)(0);
14478
- const [answers, setAnswers] = (0, import_react102.useState)({});
14479
- const [files, setFiles] = (0, import_react102.useState)({});
14480
- const [missing, setMissing] = (0, import_react102.useState)(null);
14481
- const [done, setDone] = (0, import_react102.useState)(null);
14482
- const [counts, setCounts] = (0, import_react102.useState)({ waiting: 0, sending: 0, refused: 0, landed: 0 });
14483
- const [items, setItems] = (0, import_react102.useState)([]);
14484
- const [lastSynced, setLastSynced] = (0, import_react102.useState)(null);
14485
- const [sending, setSending] = (0, import_react102.useState)(false);
14486
- const queueRef = (0, import_react102.useRef)(null);
14487
- (0, import_react102.useEffect)(() => {
14682
+ const [face, setFace] = (0, import_react104.useState)(given);
14683
+ const [loadFailed, setLoadFailed] = (0, import_react104.useState)(null);
14684
+ const [at, setAt] = (0, import_react104.useState)(0);
14685
+ const [answers, setAnswers] = (0, import_react104.useState)({});
14686
+ const [files, setFiles] = (0, import_react104.useState)({});
14687
+ const [missing, setMissing] = (0, import_react104.useState)(null);
14688
+ const [done, setDone] = (0, import_react104.useState)(null);
14689
+ const [counts, setCounts] = (0, import_react104.useState)({ waiting: 0, sending: 0, refused: 0, landed: 0 });
14690
+ const [items, setItems] = (0, import_react104.useState)([]);
14691
+ const [lastSynced, setLastSynced] = (0, import_react104.useState)(null);
14692
+ const [sending, setSending] = (0, import_react104.useState)(false);
14693
+ const queueRef = (0, import_react104.useRef)(null);
14694
+ (0, import_react104.useEffect)(() => {
14488
14695
  const q2 = (0, import_records15.openCaptureQueue)({
14489
14696
  onChange: (c, all) => {
14490
14697
  setCounts(c);
@@ -14522,7 +14729,7 @@ function CaptureRun({ sheetId, face: given, className }) {
14522
14729
  void q2.sync().then(() => void q2.lastSyncedAt().then(setLastSynced));
14523
14730
  return () => q2.dispose();
14524
14731
  }, [client, host]);
14525
- (0, import_react102.useEffect)(() => {
14732
+ (0, import_react104.useEffect)(() => {
14526
14733
  if (given !== void 0) return;
14527
14734
  let cancelled = false;
14528
14735
  void client.captureOpen({ sheet_id: sheetId }).then((res) => {
@@ -14534,7 +14741,7 @@ function CaptureRun({ sheetId, face: given, className }) {
14534
14741
  cancelled = true;
14535
14742
  };
14536
14743
  }, [client, given, sheetId]);
14537
- const questions = (0, import_react102.useMemo)(() => {
14744
+ const questions = (0, import_react104.useMemo)(() => {
14538
14745
  const asked = face?.presentation?.questions ?? [];
14539
14746
  if (asked.length > 0) return asked;
14540
14747
  return (face?.fields ?? []).map((f) => ({
@@ -14544,11 +14751,11 @@ function CaptureRun({ sheetId, face: given, className }) {
14544
14751
  required: f.required
14545
14752
  }));
14546
14753
  }, [face]);
14547
- const fieldOf = (0, import_react102.useCallback)(
14754
+ const fieldOf = (0, import_react104.useCallback)(
14548
14755
  (key) => (face?.fields ?? []).find((f) => f.key === key),
14549
14756
  [face]
14550
14757
  );
14551
- const sync = (0, import_react102.useCallback)(async () => {
14758
+ const sync = (0, import_react104.useCallback)(async () => {
14552
14759
  const q2 = queueRef.current;
14553
14760
  if (!q2) return;
14554
14761
  setSending(true);
@@ -14561,7 +14768,7 @@ function CaptureRun({ sheetId, face: given, className }) {
14561
14768
  setSending(false);
14562
14769
  }
14563
14770
  }, []);
14564
- const answered = (0, import_react102.useCallback)(
14771
+ const answered = (0, import_react104.useCallback)(
14565
14772
  (key) => {
14566
14773
  if (files[key]) return true;
14567
14774
  const v = answers[key];
@@ -14832,21 +15039,21 @@ function AdHocCaptureSheet({
14832
15039
  attachmentField,
14833
15040
  className
14834
15041
  }) {
14835
- const client = (0, import_react105.useRecordsClient)();
15042
+ const client = (0, import_react107.useRecordsClient)();
14836
15043
  const host = useRecordsUi();
14837
- const table = (0, import_react105.useTable)(tableId);
14838
- const fields = (0, import_react105.useFields)(tableId);
14839
- const [mode, setMode] = (0, import_react104.useState)("reading");
14840
- const [reading, setReading] = (0, import_react104.useState)("");
14841
- const [note, setNote] = (0, import_react104.useState)("");
14842
- const [fileId, setFileId] = (0, import_react104.useState)(null);
14843
- const [pending, setPending] = (0, import_react104.useState)(null);
14844
- const [saved, setSaved] = (0, import_react104.useState)([]);
14845
- const [error, setError] = (0, import_react104.useState)(null);
14846
- const [uploadError, setUploadError] = (0, import_react104.useState)(null);
14847
- const [flushing, setFlushing] = (0, import_react104.useState)(false);
14848
- const queue = (0, import_react104.useRef)([]);
14849
- const keep = (0, import_react104.useCallback)(
15044
+ const table = (0, import_react107.useTable)(tableId);
15045
+ const fields = (0, import_react107.useFields)(tableId);
15046
+ const [mode, setMode] = (0, import_react106.useState)("reading");
15047
+ const [reading, setReading] = (0, import_react106.useState)("");
15048
+ const [note, setNote] = (0, import_react106.useState)("");
15049
+ const [fileId, setFileId] = (0, import_react106.useState)(null);
15050
+ const [pending, setPending] = (0, import_react106.useState)(null);
15051
+ const [saved, setSaved] = (0, import_react106.useState)([]);
15052
+ const [error, setError] = (0, import_react106.useState)(null);
15053
+ const [uploadError, setUploadError] = (0, import_react106.useState)(null);
15054
+ const [flushing, setFlushing] = (0, import_react106.useState)(false);
15055
+ const queue = (0, import_react106.useRef)([]);
15056
+ const keep = (0, import_react106.useCallback)(
14850
15057
  async (next) => {
14851
15058
  queue.current = next;
14852
15059
  setPending(next);
@@ -14854,7 +15061,7 @@ function AdHocCaptureSheet({
14854
15061
  },
14855
15062
  [host]
14856
15063
  );
14857
- (0, import_react104.useEffect)(() => {
15064
+ (0, import_react106.useEffect)(() => {
14858
15065
  let cancelled = false;
14859
15066
  void (async () => {
14860
15067
  const held = host.captureQueue ? await host.captureQueue.load() : [];
@@ -14866,7 +15073,7 @@ function AdHocCaptureSheet({
14866
15073
  cancelled = true;
14867
15074
  };
14868
15075
  }, [host]);
14869
- const resolved = (0, import_react104.useCallback)(() => {
15076
+ const resolved = (0, import_react106.useCallback)(() => {
14870
15077
  const all = fields.data ?? [];
14871
15078
  return {
14872
15079
  reading: readingField ?? all.find((f) => f.type === "range")?.key ?? null,
@@ -14874,7 +15081,7 @@ function AdHocCaptureSheet({
14874
15081
  attachment: attachmentField ?? all.find((f) => f.format === "file" || f.format === "image")?.key ?? null
14875
15082
  };
14876
15083
  }, [attachmentField, fields.data, noteField, readingField, table.data]);
14877
- const flush = (0, import_react104.useCallback)(async () => {
15084
+ const flush = (0, import_react106.useCallback)(async () => {
14878
15085
  if (flushing || queue.current.length === 0) return;
14879
15086
  setFlushing(true);
14880
15087
  const left = [];
@@ -15033,18 +15240,18 @@ function AdHocCaptureSheet({
15033
15240
  }
15034
15241
 
15035
15242
  // src/PortalShell.tsx
15036
- var import_react106 = require("react");
15037
- var import_react107 = require("@ai-matrx/records/react");
15243
+ var import_react108 = require("react");
15244
+ var import_react109 = require("@ai-matrx/records/react");
15038
15245
  var import_design_system53 = require("@ai-matrx/design-system");
15039
15246
  var import_jsx_runtime56 = require("react/jsx-runtime");
15040
15247
  function PortalShell({ tableId, form, resourceType = "record", className }) {
15041
- const client = (0, import_react107.useRecordsClient)();
15042
- const [card, setCard] = (0, import_react106.useState)(null);
15043
- const [reach, setReach] = (0, import_react106.useState)(null);
15044
- const [error, setError] = (0, import_react106.useState)(null);
15045
- const [open, setOpen] = (0, import_react106.useState)(null);
15046
- const [sending, setSending] = (0, import_react106.useState)(false);
15047
- const load = (0, import_react106.useCallback)(async () => {
15248
+ const client = (0, import_react109.useRecordsClient)();
15249
+ const [card, setCard] = (0, import_react108.useState)(null);
15250
+ const [reach, setReach] = (0, import_react108.useState)(null);
15251
+ const [error, setError] = (0, import_react108.useState)(null);
15252
+ const [open, setOpen] = (0, import_react108.useState)(null);
15253
+ const [sending, setSending] = (0, import_react108.useState)(false);
15254
+ const load = (0, import_react108.useCallback)(async () => {
15048
15255
  const who = await client.externalPrincipalCard();
15049
15256
  if (!who.ok) {
15050
15257
  setError(who.error);
@@ -15059,7 +15266,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
15059
15266
  }
15060
15267
  setReach(reached.data.map((row) => row.resource_id));
15061
15268
  }, [client, resourceType]);
15062
- (0, import_react106.useEffect)(() => {
15269
+ (0, import_react108.useEffect)(() => {
15063
15270
  void load();
15064
15271
  }, [load]);
15065
15272
  if (error) {
@@ -15117,9 +15324,9 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
15117
15324
  ] });
15118
15325
  }
15119
15326
  function PortalRow({ tableId, recordId }) {
15120
- const client = (0, import_react107.useRecordsClient)();
15121
- const [title, setTitle] = (0, import_react106.useState)(null);
15122
- (0, import_react106.useEffect)(() => {
15327
+ const client = (0, import_react109.useRecordsClient)();
15328
+ const [title, setTitle] = (0, import_react108.useState)(null);
15329
+ (0, import_react108.useEffect)(() => {
15123
15330
  let cancelled = false;
15124
15331
  void client.recordRead({ record_id: recordId }).then((answered) => {
15125
15332
  if (cancelled) return;
@@ -15139,18 +15346,18 @@ function PortalRow({ tableId, recordId }) {
15139
15346
  }
15140
15347
 
15141
15348
  // src/PublicViewPage.tsx
15142
- var import_react108 = require("react");
15143
- var import_react109 = require("@ai-matrx/records/react");
15349
+ var import_react110 = require("react");
15350
+ var import_react111 = require("@ai-matrx/records/react");
15144
15351
  var import_design_system54 = require("@ai-matrx/design-system");
15145
15352
  var import_jsx_runtime57 = require("react/jsx-runtime");
15146
15353
  function PublicViewPage({ slug, className }) {
15147
- const client = (0, import_react109.useRecordsClient)();
15148
- const [binding, setBinding] = (0, import_react108.useState)(null);
15149
- const [rows, setRows] = (0, import_react108.useState)(null);
15150
- const [fields, setFields] = (0, import_react108.useState)(null);
15151
- const [error, setError] = (0, import_react108.useState)(null);
15152
- const [gap, setGap] = (0, import_react108.useState)(null);
15153
- const load = (0, import_react108.useCallback)(async () => {
15354
+ const client = (0, import_react111.useRecordsClient)();
15355
+ const [binding, setBinding] = (0, import_react110.useState)(null);
15356
+ const [rows, setRows] = (0, import_react110.useState)(null);
15357
+ const [fields, setFields] = (0, import_react110.useState)(null);
15358
+ const [error, setError] = (0, import_react110.useState)(null);
15359
+ const [gap, setGap] = (0, import_react110.useState)(null);
15360
+ const load = (0, import_react110.useCallback)(async () => {
15154
15361
  const notice = await client.worldPublishGapNotice();
15155
15362
  if (notice.ok) setGap(notice.data);
15156
15363
  const resolved = await client.resolvePublishBinding({ slug });
@@ -15183,7 +15390,7 @@ function PublicViewPage({ slug, className }) {
15183
15390
  }
15184
15391
  setRows([{ id: found.resource_id, document: read.data.document, level: "viewer", hidden: read.data.hidden }]);
15185
15392
  }, [client, slug]);
15186
- (0, import_react108.useEffect)(() => {
15393
+ (0, import_react110.useEffect)(() => {
15187
15394
  void load();
15188
15395
  }, [load]);
15189
15396
  if (error) return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(RefusalNotice, { error, className });
@@ -15220,10 +15427,10 @@ function PublicRow({ row, fields }) {
15220
15427
  }
15221
15428
 
15222
15429
  // src/EmbedFrame.tsx
15223
- var import_react110 = require("react");
15224
- var import_react111 = require("@ai-matrx/records/react");
15430
+ var import_react112 = require("react");
15431
+ var import_react113 = require("@ai-matrx/records/react");
15225
15432
  var import_design_system55 = require("@ai-matrx/design-system");
15226
- var import_react112 = require("@ai-matrx/records/react");
15433
+ var import_react114 = require("@ai-matrx/records/react");
15227
15434
  var import_jsx_runtime58 = require("react/jsx-runtime");
15228
15435
  function EmbedFrame({
15229
15436
  tableId,
@@ -15233,18 +15440,18 @@ function EmbedFrame({
15233
15440
  embedUrl,
15234
15441
  className
15235
15442
  }) {
15236
- const client = (0, import_react111.useRecordsClient)();
15443
+ const client = (0, import_react113.useRecordsClient)();
15237
15444
  const host = useRecordsUi();
15238
- const table = (0, import_react112.useTable)(tableId);
15445
+ const table = (0, import_react114.useTable)(tableId);
15239
15446
  const rights = useTableRights(table.data);
15240
- const [origins, setOrigins] = (0, import_react110.useState)("");
15241
- const [secret, setSecret] = (0, import_react110.useState)(null);
15242
- const [tokenId, setTokenId] = (0, import_react110.useState)(null);
15243
- const [error, setError] = (0, import_react110.useState)(null);
15244
- const [busy, setBusy] = (0, import_react110.useState)(false);
15447
+ const [origins, setOrigins] = (0, import_react112.useState)("");
15448
+ const [secret, setSecret] = (0, import_react112.useState)(null);
15449
+ const [tokenId, setTokenId] = (0, import_react112.useState)(null);
15450
+ const [error, setError] = (0, import_react112.useState)(null);
15451
+ const [busy, setBusy] = (0, import_react112.useState)(false);
15245
15452
  const mode = formId ? "write" : "read";
15246
15453
  const parsed = origins.split(/[\s,]+/).map((o) => o.trim()).filter((o) => o.length > 0);
15247
- const issue = (0, import_react110.useCallback)(async () => {
15454
+ const issue = (0, import_react112.useCallback)(async () => {
15248
15455
  setBusy(true);
15249
15456
  setError(null);
15250
15457
  const minted = await client.anonTokenIssue({
@@ -15263,7 +15470,7 @@ function EmbedFrame({
15263
15470
  setTokenId(minted.data.token_id);
15264
15471
  host.notify?.success("Embed token issued. Copy it now \u2014 it is never shown again.");
15265
15472
  }, [client, formId, host, mode, parsed, recordId, savedViewId]);
15266
- const revoke = (0, import_react110.useCallback)(async () => {
15473
+ const revoke = (0, import_react112.useCallback)(async () => {
15267
15474
  if (!tokenId) return;
15268
15475
  setBusy(true);
15269
15476
  const done = await client.anonTokenRevoke({ token_id: tokenId });
@@ -15327,13 +15534,13 @@ function EmbedFrame({
15327
15534
  ] });
15328
15535
  }
15329
15536
  function useEmbedHandshake(args) {
15330
- const client = (0, import_react111.useRecordsClient)();
15331
- const [binding, setBinding] = (0, import_react110.useState)(null);
15332
- const [error, setError] = (0, import_react110.useState)(null);
15333
- const [loading, setLoading] = (0, import_react110.useState)(true);
15537
+ const client = (0, import_react113.useRecordsClient)();
15538
+ const [binding, setBinding] = (0, import_react112.useState)(null);
15539
+ const [error, setError] = (0, import_react112.useState)(null);
15540
+ const [loading, setLoading] = (0, import_react112.useState)(true);
15334
15541
  const origin = args.origin ?? (typeof location === "undefined" ? "" : location.origin);
15335
15542
  const { secret, requiredMode } = args;
15336
- (0, import_react110.useEffect)(() => {
15543
+ (0, import_react112.useEffect)(() => {
15337
15544
  let cancelled = false;
15338
15545
  setLoading(true);
15339
15546
  setError(null);
@@ -15355,7 +15562,7 @@ function useEmbedHandshake(args) {
15355
15562
  }
15356
15563
 
15357
15564
  // src/RecordsMount.tsx
15358
- var import_react113 = require("@ai-matrx/records/react");
15565
+ var import_react115 = require("@ai-matrx/records/react");
15359
15566
  var import_jsx_runtime59 = require("react/jsx-runtime");
15360
15567
  var STORE_DECIDES_REASON = "This host offers exactly what the record store says this person may do: the level comes back with the table on the read door, so a control they cannot use is never drawn in the first place.";
15361
15568
  function storeDecidesRights(_table) {
@@ -15369,7 +15576,7 @@ function RecordsMount({
15369
15576
  }) {
15370
15577
  const bound = { ...host ?? {} };
15371
15578
  void letTheStoreDecideRights;
15372
- return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react113.RecordsProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(RecordsUiProvider, { value: bound, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(RecordLabelProvider, { children }) }) });
15579
+ return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react115.RecordsProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(RecordsUiProvider, { value: bound, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(RecordLabelProvider, { children }) }) });
15373
15580
  }
15374
15581
  function personActor(userId) {
15375
15582
  return userId ? { actor: "user", user_id: userId } : { actor: "user" };
@@ -15388,8 +15595,8 @@ function recordsDataSource(client, fallbackSchema = "custom") {
15388
15595
  }
15389
15596
 
15390
15597
  // src/TablesHome.tsx
15391
- var import_react114 = require("react");
15392
- var import_react115 = require("@ai-matrx/records/react");
15598
+ var import_react116 = require("react");
15599
+ var import_react117 = require("@ai-matrx/records/react");
15393
15600
  var import_design_system56 = require("@ai-matrx/design-system");
15394
15601
 
15395
15602
  // src/createTable.ts
@@ -15525,15 +15732,15 @@ function laneFor(table) {
15525
15732
  return "organization";
15526
15733
  }
15527
15734
  function TablesHome({ onOpenTable, className }) {
15528
- const client = (0, import_react115.useRecordsClient)();
15529
- const tables = (0, import_react115.useTables)();
15530
- const [creating, setCreating] = (0, import_react114.useState)(false);
15531
- const [name, setName] = (0, import_react114.useState)("");
15532
- const [busy, setBusy] = (0, import_react114.useState)(false);
15533
- const [error, setError] = (0, import_react114.useState)(null);
15534
- const [importInto, setImportInto] = (0, import_react114.useState)(null);
15535
- const [boards, setBoards] = (0, import_react114.useState)(null);
15536
- (0, import_react114.useEffect)(() => {
15735
+ const client = (0, import_react117.useRecordsClient)();
15736
+ const tables = (0, import_react117.useTables)();
15737
+ const [creating, setCreating] = (0, import_react116.useState)(false);
15738
+ const [name, setName] = (0, import_react116.useState)("");
15739
+ const [busy, setBusy] = (0, import_react116.useState)(false);
15740
+ const [error, setError] = (0, import_react116.useState)(null);
15741
+ const [importInto, setImportInto] = (0, import_react116.useState)(null);
15742
+ const [boards, setBoards] = (0, import_react116.useState)(null);
15743
+ (0, import_react116.useEffect)(() => {
15537
15744
  let cancelled = false;
15538
15745
  void client.dashboards({}).then((result) => {
15539
15746
  if (cancelled) return;
@@ -15543,7 +15750,7 @@ function TablesHome({ onOpenTable, className }) {
15543
15750
  cancelled = true;
15544
15751
  };
15545
15752
  }, [client]);
15546
- const create = (0, import_react114.useCallback)(
15753
+ const create = (0, import_react116.useCallback)(
15547
15754
  async (mode) => {
15548
15755
  const trimmed = name.trim();
15549
15756
  if (!trimmed) return;
@@ -15667,8 +15874,8 @@ function TablesHome({ onOpenTable, className }) {
15667
15874
  }
15668
15875
 
15669
15876
  // src/TablePage.tsx
15670
- var import_react116 = require("react");
15671
- var import_react117 = require("@ai-matrx/records/react");
15877
+ var import_react118 = require("react");
15878
+ var import_react119 = require("@ai-matrx/records/react");
15672
15879
  var import_design_system57 = require("@ai-matrx/design-system");
15673
15880
  var import_jsx_runtime61 = require("react/jsx-runtime");
15674
15881
  function filterInWords(filter, fields) {
@@ -15750,24 +15957,24 @@ function TablePage({
15750
15957
  filter,
15751
15958
  className
15752
15959
  }) {
15753
- const client = (0, import_react117.useRecordsClient)();
15754
- const table = (0, import_react117.useTable)(tableId);
15960
+ const client = (0, import_react119.useRecordsClient)();
15961
+ const table = (0, import_react119.useTable)(tableId);
15755
15962
  const rights = useTableRights(table.data);
15756
- const pageFields = (0, import_react117.useFields)(filter ? tableId : null);
15963
+ const pageFields = (0, import_react119.useFields)(filter ? tableId : null);
15757
15964
  const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
15758
- const organizationId = (0, import_react117.useRecordsClient)().config.organizationId;
15759
- const [view, setView] = (0, import_react116.useState)(null);
15965
+ const organizationId = (0, import_react119.useRecordsClient)().config.organizationId;
15966
+ const [view, setView] = (0, import_react118.useState)(null);
15760
15967
  const opening = openingRail(activeRecordId);
15761
15968
  const opened = openingView(activeView, activeDashboardId);
15762
- const [asking, setAsking] = (0, import_react116.useState)(null);
15763
- const [layoutFromLink, setLayoutFromLink] = (0, import_react116.useState)(opened.layout);
15764
- const [surface, setSurface] = (0, import_react116.useState)({
15969
+ const [asking, setAsking] = (0, import_react118.useState)(null);
15970
+ const [layoutFromLink, setLayoutFromLink] = (0, import_react118.useState)(opened.layout);
15971
+ const [surface, setSurface] = (0, import_react118.useState)({
15765
15972
  main: opened.main,
15766
15973
  rail: opening.rail
15767
15974
  });
15768
15975
  const { main, rail } = surface;
15769
15976
  const setRail = (next) => setSurface((now) => ({ ...now, rail: next }));
15770
- const [openRecord, setOpenRecord] = (0, import_react116.useState)(opening.record);
15977
+ const [openRecord, setOpenRecord] = (0, import_react118.useState)(opening.record);
15771
15978
  const viewVersion = useRecordVersion(view?.id ?? null);
15772
15979
  const shownLayout = layoutFromLink ?? view?.layout ?? "grid";
15773
15980
  const press = (pressed) => {
@@ -15778,19 +15985,19 @@ function TablePage({
15778
15985
  }
15779
15986
  };
15780
15987
  const show = (next) => press({ rail: next });
15781
- (0, import_react116.useEffect)(() => {
15988
+ (0, import_react118.useEffect)(() => {
15782
15989
  if (!activeRecordId) return;
15783
15990
  setOpenRecord(activeRecordId);
15784
15991
  setSurface((now) => ({ ...now, rail: "record" }));
15785
15992
  }, [activeRecordId]);
15786
- (0, import_react116.useEffect)(() => {
15993
+ (0, import_react118.useEffect)(() => {
15787
15994
  const named = pageViewFromParam(activeView);
15788
15995
  if (named === null) return;
15789
15996
  const isLayout = named !== "dashboards" && named !== "archived";
15790
15997
  setLayoutFromLink(isLayout ? named : null);
15791
15998
  setSurface((now) => ({ ...now, main: isLayout ? "records" : named }));
15792
15999
  }, [activeView]);
15793
- const patchView = (0, import_react116.useCallback)(
16000
+ const patchView = (0, import_react118.useCallback)(
15794
16001
  async (patch) => {
15795
16002
  const current = view;
15796
16003
  if (!current) return;