@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.js CHANGED
@@ -4240,10 +4240,30 @@ import {
4240
4240
  importCsv,
4241
4241
  importXlsx
4242
4242
  } from "@ai-matrx/records/core";
4243
- import { Badge as Badge5, Button as Button11, Separator as Separator2, cn as cn12 } from "@ai-matrx/design-system";
4243
+ import {
4244
+ Badge as Badge5,
4245
+ Button as Button11,
4246
+ Select as Select3,
4247
+ SelectContent as SelectContent3,
4248
+ SelectItem as SelectItem3,
4249
+ SelectTrigger as SelectTrigger3,
4250
+ SelectValue as SelectValue3,
4251
+ Separator as Separator2,
4252
+ cn as cn12
4253
+ } from "@ai-matrx/design-system";
4244
4254
  import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
4245
4255
  var BATCH = 250;
4246
4256
  var SAMPLES = 40;
4257
+ var MAPPING_COLUMNS = "minmax(9rem, 11rem) minmax(14rem, 17rem) minmax(13rem, 1fr) minmax(8rem, 10rem)";
4258
+ var ABSENT = "__absent__";
4259
+ function obviousTitleColumn(columns) {
4260
+ const says = /^(title|name|record|customer|client|company|job|item|subject|label|description)\b|(\bname|\btitle|\bnumber|\bid)$/i;
4261
+ const named = columns.find((c) => says.test(c.header.trim()));
4262
+ if (named) return named.header;
4263
+ const text = columns.find((c) => c.type === "text");
4264
+ if (text) return text.header;
4265
+ return columns[0]?.header ?? null;
4266
+ }
4247
4267
  async function sha256(bytes) {
4248
4268
  const subtle = globalThis.crypto?.subtle;
4249
4269
  if (!subtle) return null;
@@ -4278,6 +4298,7 @@ function ImportWizard({ tableId, onDone, className }) {
4278
4298
  const [alreadyRan, setAlreadyRan] = useState13(null);
4279
4299
  const [columnsAdded, setColumnsAdded] = useState13(null);
4280
4300
  const [show, setShow] = useState13("all");
4301
+ const [ledger, setLedger] = useState13(null);
4281
4302
  const declared = fields.data ?? [];
4282
4303
  const take = useCallback8(
4283
4304
  async (file) => {
@@ -4288,6 +4309,7 @@ function ImportWizard({ tableId, onDone, className }) {
4288
4309
  setProposals([]);
4289
4310
  setAlreadyRan(null);
4290
4311
  setProgress(0);
4312
+ setLedger(null);
4291
4313
  try {
4292
4314
  const xlsx = file.name.toLowerCase().endsWith(".xlsx");
4293
4315
  const bytes = await file.arrayBuffer();
@@ -4314,6 +4336,11 @@ function ImportWizard({ tableId, onDone, className }) {
4314
4336
  for (const column of answered.data.columns) {
4315
4337
  if (column.matched) next[column.header] = column.field_key;
4316
4338
  }
4339
+ const titleKey2 = table.data?.title_field ?? null;
4340
+ if (titleKey2 && !Object.values(next).includes(titleKey2)) {
4341
+ const guess = obviousTitleColumn(answered.data.columns);
4342
+ if (guess) next[guess] = titleKey2;
4343
+ }
4317
4344
  setMapping(next);
4318
4345
  setPhase("ready");
4319
4346
  } catch (thrown) {
@@ -4321,7 +4348,7 @@ function ImportWizard({ tableId, onDone, className }) {
4321
4348
  setPhase("waiting");
4322
4349
  }
4323
4350
  },
4324
- [client, declared, tableId]
4351
+ [client, declared, tableId, table.data?.title_field]
4325
4352
  );
4326
4353
  const run = useCallback8(async () => {
4327
4354
  if (!plan) return;
@@ -4329,6 +4356,7 @@ function ImportWizard({ tableId, onDone, className }) {
4329
4356
  setOutcomes([]);
4330
4357
  setColumnsAdded(null);
4331
4358
  setProgress(0);
4359
+ setLedger(null);
4332
4360
  if (unmapped === "create" && rights.admin) {
4333
4361
  setPhase("declaring");
4334
4362
  const made = await client.importDeclareColumns({ table_id: tableId, rows, mapping });
@@ -4364,6 +4392,7 @@ function ImportWizard({ tableId, onDone, className }) {
4364
4392
  }
4365
4393
  const importId = opened.data.import_id;
4366
4394
  const collected = [];
4395
+ const told = { seen: 0, written: 0, duplicate: 0, refused: 0 };
4367
4396
  let take2 = opened.data.rows_per_call ?? BATCH;
4368
4397
  let at = 0;
4369
4398
  while (at < rows.length) {
@@ -4372,11 +4401,17 @@ function ImportWizard({ tableId, onDone, className }) {
4372
4401
  if (!answered.ok) {
4373
4402
  setProblem(answered.error.message);
4374
4403
  setOutcomes([...collected]);
4404
+ setLedger({ ...told, final: false });
4375
4405
  setPhase("ready");
4376
4406
  return;
4377
4407
  }
4378
4408
  collected.push(...answered.data.outcomes);
4379
4409
  setOutcomes([...collected]);
4410
+ told.seen += answered.data.rows_seen;
4411
+ told.written += answered.data.rows_written;
4412
+ told.duplicate += answered.data.rows_duplicate;
4413
+ told.refused += answered.data.rows_refused;
4414
+ setLedger({ ...told, final: false });
4380
4415
  at += batch.length;
4381
4416
  setProgress(Math.min(at, rows.length));
4382
4417
  if (answered.data.rows_per_call) take2 = answered.data.rows_per_call;
@@ -4389,6 +4424,13 @@ function ImportWizard({ tableId, onDone, className }) {
4389
4424
  }
4390
4425
  setSummary(finished.data.message);
4391
4426
  setProposals(finished.data.proposals);
4427
+ setLedger({
4428
+ seen: finished.data.rows_seen,
4429
+ written: finished.data.rows_written,
4430
+ duplicate: finished.data.rows_duplicate,
4431
+ refused: finished.data.rows_refused,
4432
+ final: true
4433
+ });
4392
4434
  setPhase("done");
4393
4435
  onDone?.(finished.data.rows_written);
4394
4436
  }, [client, dedupeKey, fileBytes, fileHash, fileName, format, mapping, onDuplicate, onDone, plan, rows, tableId, unmapped]);
@@ -4408,6 +4450,10 @@ function ImportWizard({ tableId, onDone, className }) {
4408
4450
  [outcomes, show]
4409
4451
  );
4410
4452
  const mappedCount = Object.keys(mapping).length;
4453
+ const titleKey = table.data?.title_field ?? null;
4454
+ const titleMappedTo = titleKey ? Object.entries(mapping).find(([, key]) => key === titleKey)?.[0] ?? null : null;
4455
+ const titleMissing = Boolean(titleKey) && titleMappedTo === null;
4456
+ const titleFieldLabel = (titleKey ? plan?.fields.find((f) => f.key === titleKey)?.label : null) || (titleKey ? declared.find((f) => f.key === titleKey)?.label : null) || "the name column";
4411
4457
  const busy = phase === "reading" || phase === "planning" || phase === "declaring" || phase === "writing";
4412
4458
  return /* @__PURE__ */ jsxs12("div", { className: cn12("flex min-w-0 flex-col gap-2 text-xs", className), children: [
4413
4459
  /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
@@ -4440,55 +4486,107 @@ function ImportWizard({ tableId, onDone, className }) {
4440
4486
  phase === "reading" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
4441
4487
  phase === "planning" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
4442
4488
  plan && parsed ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
4443
- /* @__PURE__ */ jsx16("div", { className: "w-full overflow-x-auto rounded border", children: /* @__PURE__ */ jsxs12("table", { className: "w-full min-w-[50rem] table-fixed", children: [
4444
- /* @__PURE__ */ jsxs12("colgroup", { children: [
4445
- /* @__PURE__ */ jsx16("col", { className: "w-[10rem]" }),
4446
- /* @__PURE__ */ jsx16("col", { className: "w-[17rem]" }),
4447
- /* @__PURE__ */ jsx16("col", { className: "w-[14rem]" }),
4448
- /* @__PURE__ */ jsx16("col", { className: "w-[9rem]" })
4449
- ] }),
4450
- /* @__PURE__ */ jsx16("thead", { className: "bg-muted", children: /* @__PURE__ */ jsxs12("tr", { children: [
4451
- /* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "Column in the file" }),
4452
- /* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "Goes to" }),
4453
- /* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "Kind" }),
4454
- /* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "First values" })
4455
- ] }) }),
4456
- /* @__PURE__ */ jsx16("tbody", { children: plan.columns.map((column) => {
4489
+ titleKey ? /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-2 rounded border bg-muted/40 px-2 py-1.5", children: [
4490
+ /* @__PURE__ */ jsx16("span", { className: "font-medium", children: "What each record is called" }),
4491
+ /* @__PURE__ */ jsxs12(
4492
+ Select3,
4493
+ {
4494
+ value: titleMappedTo ?? "",
4495
+ disabled: busy,
4496
+ onValueChange: (chosen) => {
4497
+ setMapping((m) => {
4498
+ const next = { ...m };
4499
+ for (const [header, key] of Object.entries(next)) {
4500
+ if (key === titleKey) delete next[header];
4501
+ }
4502
+ if (chosen) next[chosen] = titleKey;
4503
+ return next;
4504
+ });
4505
+ },
4506
+ children: [
4507
+ /* @__PURE__ */ jsx16(
4508
+ SelectTrigger3,
4509
+ {
4510
+ size: "sm",
4511
+ "aria-label": "Which column each record is named by",
4512
+ className: "min-w-[14rem]",
4513
+ children: /* @__PURE__ */ jsx16(SelectValue3, { placeholder: "\u2014 choose the column \u2014" })
4514
+ }
4515
+ ),
4516
+ /* @__PURE__ */ jsx16(SelectContent3, { children: plan.columns.map((c) => /* @__PURE__ */ jsx16(SelectItem3, { value: c.header, children: c.header }, c.header)) })
4517
+ ]
4518
+ }
4519
+ ),
4520
+ /* @__PURE__ */ jsx16("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.` })
4521
+ ] }) : null,
4522
+ /* @__PURE__ */ jsx16("div", { className: "w-full overflow-x-auto rounded border", children: /* @__PURE__ */ jsxs12("div", { className: "min-w-[52rem]", children: [
4523
+ /* @__PURE__ */ jsxs12(
4524
+ "div",
4525
+ {
4526
+ className: "grid items-center gap-x-3 border-b bg-muted px-2 py-1 font-medium",
4527
+ style: { gridTemplateColumns: MAPPING_COLUMNS },
4528
+ children: [
4529
+ /* @__PURE__ */ jsx16("span", { children: "Column in the file" }),
4530
+ /* @__PURE__ */ jsx16("span", { children: "Goes to" }),
4531
+ /* @__PURE__ */ jsx16("span", { children: "Kind" }),
4532
+ /* @__PURE__ */ jsx16("span", { children: "First values" })
4533
+ ]
4534
+ }
4535
+ ),
4536
+ plan.columns.map((column) => {
4457
4537
  const to = mapping[column.header] ?? "";
4458
- return /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
4459
- /* @__PURE__ */ jsx16("td", { className: "truncate px-2 py-1 font-medium", title: column.header, children: column.header }),
4460
- /* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: /* @__PURE__ */ jsxs12(
4461
- "select",
4462
- {
4463
- "aria-label": `Where ${column.header} goes`,
4464
- className: "w-full min-w-0 rounded border bg-background px-1 py-0.5",
4465
- value: to,
4466
- disabled: busy,
4467
- onChange: (e) => {
4468
- const chosen = e.target.value;
4469
- setMapping((m) => {
4470
- const next = { ...m };
4471
- if (chosen === "") delete next[column.header];
4472
- else next[column.header] = chosen;
4473
- return next;
4474
- });
4475
- },
4476
- children: [
4477
- /* @__PURE__ */ jsx16("option", { value: "", children: unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014" }),
4478
- plan.fields.map((f) => /* @__PURE__ */ jsx16("option", { value: f.key, children: f.label || f.key }, f.field_id))
4479
- ]
4480
- }
4481
- ) }),
4482
- /* @__PURE__ */ jsxs12("td", { className: "px-2 py-1", children: [
4483
- /* @__PURE__ */ jsx16(Badge5, { variant: column.matched ? "secondary" : "outline", className: "text-[11px] font-normal", children: typeWord(column.type) }),
4484
- column.unit ? /* @__PURE__ */ jsx16("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
4485
- /* @__PURE__ */ jsx16("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
4486
- column.ambiguous ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
4487
- column.collides_with ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null
4488
- ] }),
4489
- /* @__PURE__ */ jsx16("td", { className: "truncate px-2 py-1 text-muted-foreground", children: (column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ") })
4490
- ] }, column.header);
4491
- }) })
4538
+ const absent = unmapped === "ignore" ? "\u2014 leave it out \u2014" : "\u2014 offer it as a new column \u2014";
4539
+ return /* @__PURE__ */ jsxs12(
4540
+ "div",
4541
+ {
4542
+ className: "grid items-start gap-x-3 border-t px-2 py-1",
4543
+ style: { gridTemplateColumns: MAPPING_COLUMNS },
4544
+ children: [
4545
+ /* @__PURE__ */ jsx16("span", { className: "truncate pt-1.5 font-medium", title: column.header, children: column.header }),
4546
+ /* @__PURE__ */ jsxs12(
4547
+ Select3,
4548
+ {
4549
+ value: to || ABSENT,
4550
+ disabled: busy,
4551
+ onValueChange: (chosen) => {
4552
+ setMapping((m) => {
4553
+ const next = { ...m };
4554
+ if (chosen === ABSENT) delete next[column.header];
4555
+ else next[column.header] = chosen;
4556
+ return next;
4557
+ });
4558
+ },
4559
+ children: [
4560
+ /* @__PURE__ */ jsx16(
4561
+ SelectTrigger3,
4562
+ {
4563
+ size: "sm",
4564
+ "aria-label": `Where ${column.header} goes`,
4565
+ "data-goes-to": column.header,
4566
+ className: "w-full min-w-[14rem]",
4567
+ children: /* @__PURE__ */ jsx16(SelectValue3, {})
4568
+ }
4569
+ ),
4570
+ /* @__PURE__ */ jsxs12(SelectContent3, { children: [
4571
+ /* @__PURE__ */ jsx16(SelectItem3, { value: ABSENT, children: absent }),
4572
+ plan.fields.map((f) => /* @__PURE__ */ jsx16(SelectItem3, { value: f.key, children: f.label || f.key }, f.field_id))
4573
+ ] })
4574
+ ]
4575
+ }
4576
+ ),
4577
+ /* @__PURE__ */ jsxs12("div", { className: "pt-1", children: [
4578
+ /* @__PURE__ */ jsx16(Badge5, { variant: column.matched ? "secondary" : "outline", className: "text-[11px] font-normal", children: typeWord(column.type) }),
4579
+ column.unit ? /* @__PURE__ */ jsx16("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
4580
+ /* @__PURE__ */ jsx16("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
4581
+ column.ambiguous ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
4582
+ column.collides_with ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null
4583
+ ] }),
4584
+ /* @__PURE__ */ jsx16("span", { className: "truncate pt-1.5 text-muted-foreground", children: (column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ") })
4585
+ ]
4586
+ },
4587
+ column.header
4588
+ );
4589
+ })
4492
4590
  ] }) }),
4493
4591
  /* @__PURE__ */ jsx16(Separator2, {}),
4494
4592
  /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-3", children: [
@@ -4546,13 +4644,18 @@ function ImportWizard({ tableId, onDone, className }) {
4546
4644
  ] })
4547
4645
  ] }),
4548
4646
  /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
4549
- /* @__PURE__ */ jsx16(Button11, { 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"}` }),
4550
- /* @__PURE__ */ jsxs12("span", { className: "text-muted-foreground", children: [
4551
- mappedCount,
4552
- " of ",
4553
- plan.columns.length,
4554
- " columns go into this table",
4555
- fileHash ? "" : " \xB7 this browser cannot recognise the file again, so importing it twice would write it twice"
4647
+ /* @__PURE__ */ jsx16(
4648
+ Button11,
4649
+ {
4650
+ size: "sm",
4651
+ disabled: busy || !rights.write || rows.length === 0 || titleMissing,
4652
+ onClick: () => void run(),
4653
+ children: phase === "declaring" ? "Adding the columns\u2026" : phase === "writing" ? `Writing\u2026 ${progress} of ${rows.length}` : `Import ${rows.length} row${rows.length === 1 ? "" : "s"}`
4654
+ }
4655
+ ),
4656
+ /* @__PURE__ */ jsxs12("span", { className: cn12("text-muted-foreground", titleMissing && "text-destructive"), children: [
4657
+ 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`,
4658
+ fileHash || titleMissing ? "" : " \xB7 this browser cannot recognise the file again, so importing it twice would write it twice"
4556
4659
  ] })
4557
4660
  ] })
4558
4661
  ] }) : null,
@@ -4570,27 +4673,31 @@ function ImportWizard({ tableId, onDone, className }) {
4570
4673
  ] }) : null
4571
4674
  ] }) : null,
4572
4675
  alreadyRan ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: alreadyRan }) : null,
4573
- outcomes.length > 0 || summary ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
4676
+ outcomes.length > 0 || summary || ledger ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
4574
4677
  /* @__PURE__ */ jsx16(Separator2, {}),
4575
4678
  /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-2", children: [
4576
- /* @__PURE__ */ jsxs12("span", { children: [
4577
- counts.landed,
4578
- " landed \xB7 ",
4579
- counts.duplicate,
4580
- " already here \xB7 ",
4581
- counts.refused,
4582
- " refused"
4583
- ] }),
4584
- counts.refused + counts.duplicate > 0 ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
4679
+ /* @__PURE__ */ jsx16("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` }),
4680
+ ledger && !ledger.final && phase !== "writing" ? /* @__PURE__ */ jsx16("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,
4681
+ (ledger ? ledger.refused + ledger.duplicate : counts.refused + counts.duplicate) > 0 ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
4585
4682
  /* @__PURE__ */ jsx16("button", { type: "button", className: cn12("underline underline-offset-2", show === "all" && "font-medium"), onClick: () => setShow("all"), children: "everything that did not land" }),
4586
4683
  /* @__PURE__ */ jsx16("button", { type: "button", className: cn12("underline underline-offset-2", show === "refused" && "font-medium"), onClick: () => setShow("refused"), children: "refused" }),
4587
4684
  /* @__PURE__ */ jsx16("button", { type: "button", className: cn12("underline underline-offset-2", show === "duplicate" && "font-medium"), onClick: () => setShow("duplicate"), children: "already here" })
4588
4685
  ] }) : null
4589
4686
  ] }),
4590
4687
  summary ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: summary }) : null,
4688
+ ledger && show === "all" && ledger.refused + ledger.duplicate > interesting.length ? /* @__PURE__ */ jsxs12("p", { className: "text-muted-foreground", children: [
4689
+ interesting.length,
4690
+ " of the ",
4691
+ ledger.refused + ledger.duplicate,
4692
+ " row",
4693
+ ledger.refused + ledger.duplicate === 1 ? " that" : "s that",
4694
+ " did not land",
4695
+ interesting.length === 1 ? " is" : " are",
4696
+ " 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."
4697
+ ] }) : null,
4591
4698
  interesting.length > 0 ? /* @__PURE__ */ jsx16("div", { className: "max-h-64 overflow-auto rounded border", children: /* @__PURE__ */ jsxs12("table", { className: "w-full", children: [
4592
4699
  /* @__PURE__ */ jsx16("thead", { className: "sticky top-0 bg-muted", children: /* @__PURE__ */ jsxs12("tr", { children: [
4593
- /* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "Row" }),
4700
+ /* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "Line in your file" }),
4594
4701
  /* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "What happened" }),
4595
4702
  /* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "The row in your file" })
4596
4703
  ] }) }),
@@ -4803,13 +4910,13 @@ import {
4803
4910
  Button as Button13,
4804
4911
  Checkbox as Checkbox4,
4805
4912
  Label as Label4,
4806
- Select as Select3,
4807
- SelectContent as SelectContent3,
4913
+ Select as Select4,
4914
+ SelectContent as SelectContent4,
4808
4915
  SelectGroup,
4809
- SelectItem as SelectItem3,
4916
+ SelectItem as SelectItem4,
4810
4917
  SelectLabel,
4811
- SelectTrigger as SelectTrigger3,
4812
- SelectValue as SelectValue3,
4918
+ SelectTrigger as SelectTrigger4,
4919
+ SelectValue as SelectValue4,
4813
4920
  Separator as Separator4,
4814
4921
  cn as cn14
4815
4922
  } from "@ai-matrx/design-system";
@@ -4974,16 +5081,16 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
4974
5081
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
4975
5082
  /* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "What it holds" }),
4976
5083
  /* @__PURE__ */ jsxs14(
4977
- Select3,
5084
+ Select4,
4978
5085
  {
4979
5086
  value: type,
4980
5087
  disabled: editing,
4981
5088
  onValueChange: (next) => setType(next),
4982
5089
  children: [
4983
- /* @__PURE__ */ jsx18(SelectTrigger3, { size: "sm", "aria-label": "What this field holds", children: /* @__PURE__ */ jsx18(SelectValue3, {}) }),
4984
- /* @__PURE__ */ jsx18(SelectContent3, { children: FIELD_TYPE_GROUPS.map((group) => /* @__PURE__ */ jsxs14(SelectGroup, { children: [
5090
+ /* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": "What this field holds", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
5091
+ /* @__PURE__ */ jsx18(SelectContent4, { children: FIELD_TYPE_GROUPS.map((group) => /* @__PURE__ */ jsxs14(SelectGroup, { children: [
4985
5092
  /* @__PURE__ */ jsx18(SelectLabel, { children: group }),
4986
- FIELD_TYPE_CHOICES.filter((c) => c.group === group).map((c) => /* @__PURE__ */ jsx18(SelectItem3, { value: c.id, children: c.label }, c.id))
5093
+ FIELD_TYPE_CHOICES.filter((c) => c.group === group).map((c) => /* @__PURE__ */ jsx18(SelectItem4, { value: c.id, children: c.label }, c.id))
4987
5094
  ] }, group)) })
4988
5095
  ]
4989
5096
  }
@@ -5063,16 +5170,16 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
5063
5170
  /* @__PURE__ */ jsxs14("div", { className: "grid gap-3 sm:grid-cols-2", children: [
5064
5171
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
5065
5172
  /* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "Who can see what is in it" }),
5066
- /* @__PURE__ */ jsxs14(Select3, { value: sensitivity, onValueChange: setSensitivity, children: [
5067
- /* @__PURE__ */ jsx18(SelectTrigger3, { size: "sm", "aria-label": "Who can see what is in it", children: /* @__PURE__ */ jsx18(SelectValue3, {}) }),
5068
- /* @__PURE__ */ jsx18(SelectContent3, { children: SENSITIVITIES.map((row) => /* @__PURE__ */ jsx18(SelectItem3, { value: row.value, children: row.label }, row.value)) })
5173
+ /* @__PURE__ */ jsxs14(Select4, { value: sensitivity, onValueChange: setSensitivity, children: [
5174
+ /* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": "Who can see what is in it", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
5175
+ /* @__PURE__ */ jsx18(SelectContent4, { children: SENSITIVITIES.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.value, children: row.label }, row.value)) })
5069
5176
  ] })
5070
5177
  ] }),
5071
5178
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
5072
5179
  /* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "Can an assistant read it" }),
5073
- /* @__PURE__ */ jsxs14(Select3, { value: contextPolicy, onValueChange: setContextPolicy, children: [
5074
- /* @__PURE__ */ jsx18(SelectTrigger3, { size: "sm", "aria-label": "Can an assistant read it", children: /* @__PURE__ */ jsx18(SelectValue3, {}) }),
5075
- /* @__PURE__ */ jsx18(SelectContent3, { children: AGENT_VISIBILITY.map((row) => /* @__PURE__ */ jsx18(SelectItem3, { value: row.value, children: row.label }, row.value)) })
5180
+ /* @__PURE__ */ jsxs14(Select4, { value: contextPolicy, onValueChange: setContextPolicy, children: [
5181
+ /* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": "Can an assistant read it", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
5182
+ /* @__PURE__ */ jsx18(SelectContent4, { children: AGENT_VISIBILITY.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.value, children: row.label }, row.value)) })
5076
5183
  ] })
5077
5184
  ] })
5078
5185
  ] }),
@@ -5095,13 +5202,13 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
5095
5202
  rules.length === 0 ? /* @__PURE__ */ jsx18("p", { className: "text-muted-foreground", children: "No checks. Anything this kind of column can hold will be accepted." }) : null,
5096
5203
  rules.map((rule, index) => /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1", children: [
5097
5204
  /* @__PURE__ */ jsxs14(
5098
- Select3,
5205
+ Select4,
5099
5206
  {
5100
5207
  value: rule.kind,
5101
5208
  onValueChange: (kind) => setRules(rules.map((r, i) => i === index ? { ...r, kind } : r)),
5102
5209
  children: [
5103
- /* @__PURE__ */ jsx18(SelectTrigger3, { size: "sm", className: "w-52", "aria-label": `Check ${index + 1}`, children: /* @__PURE__ */ jsx18(SelectValue3, {}) }),
5104
- /* @__PURE__ */ jsx18(SelectContent3, { children: RULE_KINDS.map((row) => /* @__PURE__ */ jsx18(SelectItem3, { value: row.kind, children: row.label }, row.kind)) })
5210
+ /* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", className: "w-52", "aria-label": `Check ${index + 1}`, children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
5211
+ /* @__PURE__ */ jsx18(SelectContent4, { children: RULE_KINDS.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.kind, children: row.label }, row.kind)) })
5105
5212
  ]
5106
5213
  }
5107
5214
  ),
@@ -5277,9 +5384,9 @@ function TypeQuestions(props) {
5277
5384
  return /* @__PURE__ */ jsxs14("div", { className: "grid gap-3 sm:grid-cols-2", children: [
5278
5385
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
5279
5386
  /* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: type === "rollup" ? "Across the records in" : "Read it from" }),
5280
- /* @__PURE__ */ jsxs14(Select3, { value: props.via, onValueChange: props.setVia, children: [
5281
- /* @__PURE__ */ jsx18(SelectTrigger3, { size: "sm", "aria-label": type === "rollup" ? "Across the records in" : "Read it from", children: /* @__PURE__ */ jsx18(SelectValue3, { placeholder: "Choose a column" }) }),
5282
- /* @__PURE__ */ jsx18(SelectContent3, { children: usable.map((f) => /* @__PURE__ */ jsxs14(SelectItem3, { value: f.key, children: [
5387
+ /* @__PURE__ */ jsxs14(Select4, { value: props.via, onValueChange: props.setVia, children: [
5388
+ /* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": type === "rollup" ? "Across the records in" : "Read it from", children: /* @__PURE__ */ jsx18(SelectValue4, { placeholder: "Choose a column" }) }),
5389
+ /* @__PURE__ */ jsx18(SelectContent4, { children: usable.map((f) => /* @__PURE__ */ jsxs14(SelectItem4, { value: f.key, children: [
5283
5390
  fieldName(f),
5284
5391
  type === "rollup" && !f.multi ? " \u2014 holds one" : ""
5285
5392
  ] }, f.id)) })
@@ -5296,9 +5403,9 @@ function TypeQuestions(props) {
5296
5403
  }
5297
5404
  ) : /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
5298
5405
  /* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "What to work out" }),
5299
- /* @__PURE__ */ jsxs14(Select3, { value: props.agg, onValueChange: (v) => props.setAgg(v), children: [
5300
- /* @__PURE__ */ jsx18(SelectTrigger3, { size: "sm", "aria-label": "What to work out", children: /* @__PURE__ */ jsx18(SelectValue3, {}) }),
5301
- /* @__PURE__ */ jsx18(SelectContent3, { children: ROLLUP_OPERATIONS.map((row) => /* @__PURE__ */ jsx18(SelectItem3, { value: row.value, children: row.label }, row.value)) })
5406
+ /* @__PURE__ */ jsxs14(Select4, { value: props.agg, onValueChange: (v) => props.setAgg(v), children: [
5407
+ /* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": "What to work out", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
5408
+ /* @__PURE__ */ jsx18(SelectContent4, { children: ROLLUP_OPERATIONS.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.value, children: row.label }, row.value)) })
5302
5409
  ] }),
5303
5410
  props.agg !== "count" ? /* @__PURE__ */ jsx18(
5304
5411
  ValueOnTheOtherSide,
@@ -5321,9 +5428,9 @@ function TypeQuestions(props) {
5321
5428
  return /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-2", children: [
5322
5429
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
5323
5430
  /* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "What to do" }),
5324
- /* @__PURE__ */ jsxs14(Select3, { value: props.formulaOp, onValueChange: props.setFormulaOp, children: [
5325
- /* @__PURE__ */ jsx18(SelectTrigger3, { size: "sm", className: "max-w-[18rem]", "aria-label": "What to do", children: /* @__PURE__ */ jsx18(SelectValue3, {}) }),
5326
- /* @__PURE__ */ jsx18(SelectContent3, { children: FORMULA_OPERATIONS.map((row) => /* @__PURE__ */ jsx18(SelectItem3, { value: row.value, children: row.label }, row.value)) })
5431
+ /* @__PURE__ */ jsxs14(Select4, { value: props.formulaOp, onValueChange: props.setFormulaOp, children: [
5432
+ /* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", className: "max-w-[18rem]", "aria-label": "What to do", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
5433
+ /* @__PURE__ */ jsx18(SelectContent4, { children: FORMULA_OPERATIONS.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.value, children: row.label }, row.value)) })
5327
5434
  ] })
5328
5435
  ] }),
5329
5436
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
@@ -5368,9 +5475,9 @@ function ValueOnTheOtherSide({
5368
5475
  const body = !relation ? /* @__PURE__ */ jsx18("p", { className: "text-[11px] text-muted-foreground", children: "Choose the column above first." }) : stillAsking ? /* @__PURE__ */ jsx18("p", { className: "text-[11px] text-muted-foreground", children: "Reading that table's columns\u2026" }) : offered.length === 0 ? /* @__PURE__ */ jsxs14("p", { className: "text-[11px] text-muted-foreground", children: [
5369
5476
  fieldName(relation),
5370
5477
  " points at a table that has no columns to read yet. Add one there first."
5371
- ] }) : /* @__PURE__ */ jsxs14(Select3, { value, onValueChange: onChange, children: [
5372
- /* @__PURE__ */ jsx18(SelectTrigger3, { size: "sm", "aria-label": label, children: /* @__PURE__ */ jsx18(SelectValue3, { placeholder: "Choose a column" }) }),
5373
- /* @__PURE__ */ jsx18(SelectContent3, { children: offered.map((column) => /* @__PURE__ */ jsx18(SelectItem3, { value: column.key, children: column.name }, column.key)) })
5478
+ ] }) : /* @__PURE__ */ jsxs14(Select4, { value, onValueChange: onChange, children: [
5479
+ /* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": label, children: /* @__PURE__ */ jsx18(SelectValue4, { placeholder: "Choose a column" }) }),
5480
+ /* @__PURE__ */ jsx18(SelectContent4, { children: offered.map((column) => /* @__PURE__ */ jsx18(SelectItem4, { value: column.key, children: column.name }, column.key)) })
5374
5481
  ] });
5375
5482
  if (compact) return /* @__PURE__ */ jsx18("div", { className: "mt-1 flex flex-col gap-1", children: body });
5376
5483
  return /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
@@ -5977,8 +6084,8 @@ import { useState as useState18 } from "react";
5977
6084
  import {
5978
6085
  useFieldMutation as useFieldMutation2,
5979
6086
  useFields as useFields8,
5980
- useRecordMutation,
5981
- useTable as useTable6
6087
+ useTable as useTable6,
6088
+ useTableArchive
5982
6089
  } from "@ai-matrx/records/react";
5983
6090
  import { Badge as Badge6, Button as Button16, Separator as Separator6, cn as cn16 } from "@ai-matrx/design-system";
5984
6091
  import { Fragment as Fragment10, jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
@@ -5993,8 +6100,8 @@ function TableSettings({
5993
6100
  const table = useTable6(tableId);
5994
6101
  const fields = useFields8(tableId);
5995
6102
  const rights = useTableRights(table.data);
5996
- const mutation = useRecordMutation();
5997
6103
  const shape = useFieldMutation2();
6104
+ const archive = useTableArchive(tableId);
5998
6105
  const [editing, setEditing] = useState18(null);
5999
6106
  const [askingToDelete, setAskingToDelete] = useState18(false);
6000
6107
  const [askingToRemove, setAskingToRemove] = useState18(null);
@@ -6019,10 +6126,16 @@ function TableSettings({
6019
6126
  table.reload();
6020
6127
  }
6021
6128
  };
6022
- const deleteTable = async () => {
6023
- const gone = await mutation.remove({ record_id: tableId });
6024
- setAskingToDelete(false);
6025
- if (gone !== null) onDeleted?.();
6129
+ const startArchive = async () => {
6130
+ const finished = await archive.start();
6131
+ if (finished?.done && finished.table_archived) {
6132
+ setAskingToDelete(false);
6133
+ onDeleted?.();
6134
+ }
6135
+ };
6136
+ const askToArchive = async () => {
6137
+ setAskingToDelete(true);
6138
+ await archive.preview();
6026
6139
  };
6027
6140
  if (enriching) {
6028
6141
  return /* @__PURE__ */ jsxs17("div", { className, children: [
@@ -6058,7 +6171,6 @@ function TableSettings({
6058
6171
  /* @__PURE__ */ jsx21("span", { className: "text-muted-foreground", children: rows.length }),
6059
6172
  /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "outline", className: "ml-auto", onClick: () => setEditing("new"), children: "Add a field" })
6060
6173
  ] }),
6061
- mutation.error ? /* @__PURE__ */ jsx21(RefusalNotice, { error: mutation.error }) : null,
6062
6174
  shape.error ? /* @__PURE__ */ jsx21(RefusalNotice, { error: shape.error }) : null,
6063
6175
  askingToRemove ? /* @__PURE__ */ jsxs17("div", { className: "flex flex-wrap items-center gap-2 rounded border border-destructive/40 px-2 py-1.5", children: [
6064
6176
  /* @__PURE__ */ jsxs17("span", { className: "text-destructive", children: [
@@ -6149,21 +6261,32 @@ function TableSettings({
6149
6261
  /* @__PURE__ */ jsx21("span", { className: "font-medium", children: "This table" }),
6150
6262
  askingToDelete ? /* @__PURE__ */ jsxs17(Fragment10, { children: [
6151
6263
  /* @__PURE__ */ jsxs17("p", { className: "text-muted-foreground", children: [
6152
- "Deleting ",
6264
+ "Archiving ",
6153
6265
  table.data ? tableName(table.data) : "this table",
6154
6266
  " takes it out of everyone's list, with its ",
6155
6267
  rows.length,
6156
6268
  " field",
6157
6269
  rows.length === 1 ? "" : "s",
6158
- " and every record in it.",
6159
- 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."
6270
+ " and",
6271
+ " ",
6272
+ archive.progress ? `its ${archive.progress.remaining.toLocaleString()} record${archive.progress.remaining === 1 ? "" : "s"}` : "every record in it",
6273
+ ".",
6274
+ 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."
6160
6275
  ] }),
6161
- /* @__PURE__ */ jsxs17("div", { className: "flex gap-1", children: [
6162
- /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "destructive", disabled: mutation.saving, onClick: () => void deleteTable(), children: mutation.saving ? "Deleting\u2026" : "Delete this table" }),
6276
+ archive.progress && (archive.running || archive.passes > 0) ? /* @__PURE__ */ jsxs17("p", { className: archive.error ? "text-destructive" : "text-muted-foreground", children: [
6277
+ 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.`,
6278
+ archive.slowedDown ? " The store is busy, so it is taking smaller passes \u2014 it will still finish." : ""
6279
+ ] }) : null,
6280
+ archive.interrupted && archive.progress ? /* @__PURE__ */ jsx21("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,
6281
+ /* @__PURE__ */ jsx21("div", { className: "flex gap-1", children: archive.running ? /* @__PURE__ */ jsxs17(Fragment10, { children: [
6282
+ /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "destructive", disabled: true, children: "Archiving\u2026" }),
6283
+ /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "ghost", onClick: () => archive.stop(), children: "Stop" })
6284
+ ] }) : /* @__PURE__ */ jsxs17(Fragment10, { children: [
6285
+ /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "destructive", onClick: () => void startArchive(), children: archive.interrupted ? "Carry on archiving" : "Archive this table" }),
6163
6286
  /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "ghost", onClick: () => setAskingToDelete(false), children: "Keep it" })
6164
- ] })
6165
- ] }) : /* @__PURE__ */ jsx21("div", { children: /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "outline", onClick: () => setAskingToDelete(true), children: "Delete this table" }) }),
6166
- mutation.error ? /* @__PURE__ */ jsx21(RefusalNotice, { error: mutation.error }) : null
6287
+ ] }) })
6288
+ ] }) : /* @__PURE__ */ jsx21("div", { children: /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "outline", onClick: () => void askToArchive(), children: "Archive this table" }) }),
6289
+ archive.error ? /* @__PURE__ */ jsx21(RefusalNotice, { error: archive.error }) : null
6167
6290
  ] })
6168
6291
  ] });
6169
6292
  }
@@ -6305,7 +6428,7 @@ import { useEffect as useEffect11, useMemo as useMemo16, useState as useState21
6305
6428
  import {
6306
6429
  useFields as useFields9,
6307
6430
  useRecord,
6308
- useRecordMutation as useRecordMutation2
6431
+ useRecordMutation
6309
6432
  } from "@ai-matrx/records/react";
6310
6433
  import { predictWriteRefusals } from "@ai-matrx/records/core";
6311
6434
  import { Button as Button17, Separator as Separator7, cn as cn18 } from "@ai-matrx/design-system";
@@ -6494,7 +6617,7 @@ function RecordForm({
6494
6617
  const host = useRecordsUi();
6495
6618
  const fields = useFields9(tableId, recordType);
6496
6619
  const existing = useRecord(recordId ?? null);
6497
- const mutation = useRecordMutation2();
6620
+ const mutation = useRecordMutation();
6498
6621
  const [draft, setDraft] = useState21({});
6499
6622
  const [touched, setTouched] = useState21(false);
6500
6623
  const loaded = useRecordVersion(recordId ?? null);
@@ -7471,6 +7594,7 @@ function ViewSwitcher({
7471
7594
  )
7472
7595
  ] });
7473
7596
  }
7597
+ var UNUSUAL_FIELD_NOTE = "an unusual column to use here";
7474
7598
  function offerableFields(all, want) {
7475
7599
  return all.filter((field) => {
7476
7600
  const kind = editorKindFor(field);
@@ -7606,10 +7730,7 @@ function FieldPicker({
7606
7730
  children: [
7607
7731
  /* @__PURE__ */ jsx27("option", { value: "", children: "Choose a field" }),
7608
7732
  offerable.map((field) => /* @__PURE__ */ jsx27("option", { value: field.key, children: fieldName(field) }, field.key)),
7609
- unusual ? /* @__PURE__ */ jsxs23("option", { value: unusual.key, children: [
7610
- fieldName(unusual),
7611
- " \u2014 an unusual column to use here"
7612
- ] }, unusual.key) : null
7733
+ unusual ? /* @__PURE__ */ jsx27("option", { value: unusual.key, children: `${fieldName(unusual)} \u2014 ${UNUSUAL_FIELD_NOTE}` }, unusual.key) : null
7613
7734
  ]
7614
7735
  }
7615
7736
  ),
@@ -11450,8 +11571,35 @@ function SubscriptionsPanel({ tableId, className }) {
11450
11571
  }
11451
11572
 
11452
11573
  // src/FormBuilder.tsx
11453
- import { useCallback as useCallback25, useEffect as useEffect27, useMemo as useMemo26, useState as useState40 } from "react";
11454
- import { useFields as useFields18, useRecordsClient as useRecordsClient27, useTable as useTable14 } from "@ai-matrx/records/react";
11574
+ import { useCallback as useCallback25, useEffect as useEffect28, useMemo as useMemo26, useState as useState41 } from "react";
11575
+ import { useFields as useFields18, useRecordsClient as useRecordsClient28, useTable as useTable14 } from "@ai-matrx/records/react";
11576
+
11577
+ // src/publish-gate.ts
11578
+ import { useEffect as useEffect26, useState as useState39 } from "react";
11579
+ import { useRecordsClient as useRecordsClient27 } from "@ai-matrx/records/react";
11580
+ 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.";
11581
+ function usePublishGate() {
11582
+ const client = useRecordsClient27();
11583
+ const [storeOpen, setStoreOpen] = useState39(null);
11584
+ useEffect26(() => {
11585
+ let alive = true;
11586
+ void (async () => {
11587
+ const answered = await client.storeIsOpen();
11588
+ if (!alive) return;
11589
+ setStoreOpen(answered.ok ? answered.data : null);
11590
+ })();
11591
+ return () => {
11592
+ alive = false;
11593
+ };
11594
+ }, [client]);
11595
+ return {
11596
+ storeOpen,
11597
+ blocked: storeOpen === false,
11598
+ why: storeOpen === false ? PUBLISH_NEEDS_THE_STORE_ON : null
11599
+ };
11600
+ }
11601
+
11602
+ // src/FormBuilder.tsx
11455
11603
  import { publicFormPath } from "@ai-matrx/records";
11456
11604
  import {
11457
11605
  BasicInput as BasicInput13,
@@ -11464,7 +11612,7 @@ import {
11464
11612
  } from "@ai-matrx/design-system";
11465
11613
 
11466
11614
  // src/FormRunner.tsx
11467
- import { useCallback as useCallback24, useEffect as useEffect26, useMemo as useMemo25, useRef as useRef11, useState as useState39 } from "react";
11615
+ import { useCallback as useCallback24, useEffect as useEffect27, useMemo as useMemo25, useRef as useRef11, useState as useState40 } from "react";
11468
11616
  import { useFields as useFields17, useOptionalRecordsClient as useOptionalRecordsClient4 } from "@ai-matrx/records/react";
11469
11617
  import { Button as Button36, Progress, Skeleton as Skeleton20, cn as cn36 } from "@ai-matrx/design-system";
11470
11618
  import { Fragment as Fragment18, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
@@ -11535,13 +11683,13 @@ function FormStage({
11535
11683
  connected = false
11536
11684
  }) {
11537
11685
  const host = useRecordsUi();
11538
- const [answers, setAnswers] = useState39({});
11539
- const [at, setAt] = useState39(0);
11540
- const [error, setError] = useState39(null);
11541
- const [refusal, setRefusal] = useState39(null);
11542
- const [writing, setWriting] = useState39(false);
11543
- const [done, setDone] = useState39(null);
11544
- const [hidden, setHidden] = useState39({});
11686
+ const [answers, setAnswers] = useState40({});
11687
+ const [at, setAt] = useState40(0);
11688
+ const [error, setError] = useState40(null);
11689
+ const [refusal, setRefusal] = useState40(null);
11690
+ const [writing, setWriting] = useState40(false);
11691
+ const [done, setDone] = useState40(null);
11692
+ const [hidden, setHidden] = useState40({});
11545
11693
  const decoy = useRef11("");
11546
11694
  const stage = useRef11(null);
11547
11695
  const questions = useMemo25(() => {
@@ -11558,7 +11706,7 @@ function FormStage({
11558
11706
  };
11559
11707
  });
11560
11708
  }, [fields, form.questions]);
11561
- useEffect26(() => {
11709
+ useEffect27(() => {
11562
11710
  let cancelled = false;
11563
11711
  const conditional = questions.filter((q) => q.showIf);
11564
11712
  if (conditional.length === 0 || !evaluate) return;
@@ -11629,7 +11777,7 @@ function FormStage({
11629
11777
  if (event.shiftKey) retreat();
11630
11778
  else advance();
11631
11779
  }
11632
- useEffect26(() => {
11780
+ useEffect27(() => {
11633
11781
  const input = stage.current?.querySelector(
11634
11782
  "input:not([tabindex='-1']), textarea, select, [role='combobox']"
11635
11783
  );
@@ -11723,7 +11871,7 @@ function Question({
11723
11871
  onChange,
11724
11872
  upload
11725
11873
  }) {
11726
- const [uploadError, setUploadError] = useState39(null);
11874
+ const [uploadError, setUploadError] = useState40(null);
11727
11875
  const field = question.field;
11728
11876
  if (!field) return null;
11729
11877
  const id = `form-${field.key}`;
@@ -11780,21 +11928,22 @@ function whyNotAskable(fields, hidden = []) {
11780
11928
  // src/FormBuilder.tsx
11781
11929
  import { Fragment as Fragment19, jsx as jsx43, jsxs as jsxs39 } from "react/jsx-runtime";
11782
11930
  function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
11783
- const client = useRecordsClient27();
11931
+ const client = useRecordsClient28();
11932
+ const publishGate = usePublishGate();
11784
11933
  const host = useRecordsUi();
11785
11934
  const claimSeed = useSeedGuard();
11786
11935
  const table = useTable14(tableId);
11787
11936
  const rights = useTableRights(table.data);
11788
11937
  const fields = useFields18(tableId);
11789
- const [forms, setForms] = useState40(null);
11790
- const [error, setError] = useState40(null);
11791
- const [activeId, setActiveId] = useState40(activeFormId ?? null);
11792
- const [draft, setDraft] = useState40(null);
11793
- const [saving, setSaving] = useState40(false);
11794
- const [saved, setSaved] = useState40(null);
11795
- const [publishing, setPublishing] = useState40(false);
11796
- const [copied, setCopied] = useState40(false);
11797
- const [shownUrl, setShownUrl] = useState40(null);
11938
+ const [forms, setForms] = useState41(null);
11939
+ const [error, setError] = useState41(null);
11940
+ const [activeId, setActiveId] = useState41(activeFormId ?? null);
11941
+ const [draft, setDraft] = useState41(null);
11942
+ const [saving, setSaving] = useState41(false);
11943
+ const [saved, setSaved] = useState41(null);
11944
+ const [publishing, setPublishing] = useState41(false);
11945
+ const [copied, setCopied] = useState41(false);
11946
+ const [shownUrl, setShownUrl] = useState41(null);
11798
11947
  const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
11799
11948
  const load = useCallback25(async () => {
11800
11949
  const answered = await client.forms({ table_id: tableId });
@@ -11823,10 +11972,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
11823
11972
  setError(null);
11824
11973
  setForms(mine);
11825
11974
  }, [client, tableId, seed, claimSeed]);
11826
- useEffect27(() => {
11975
+ useEffect28(() => {
11827
11976
  void load();
11828
11977
  }, [load]);
11829
- useEffect27(() => {
11978
+ useEffect28(() => {
11830
11979
  if (!forms || forms.length === 0) return;
11831
11980
  const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
11832
11981
  if (chosen.id !== activeId) setActiveId(chosen.id);
@@ -11937,8 +12086,8 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
11937
12086
  {
11938
12087
  url: `${publicOrigin}${publicFormPath(draft.id)}`,
11939
12088
  state: draft.state,
11940
- mayPublish: rights.structure,
11941
- why: rights.why("structure"),
12089
+ mayPublish: rights.structure && !publishGate.blocked,
12090
+ why: publishGate.why ?? rights.why("structure"),
11942
12091
  busy: publishing,
11943
12092
  copied,
11944
12093
  shownUrl,
@@ -12237,17 +12386,17 @@ function groupLabel(groups) {
12237
12386
 
12238
12387
  // src/chartFrame.tsx
12239
12388
  import {
12240
- useEffect as useEffect28,
12389
+ useEffect as useEffect29,
12241
12390
  useId as useId2,
12242
12391
  useRef as useRef12,
12243
- useState as useState41
12392
+ useState as useState42
12244
12393
  } from "react";
12245
12394
  import { cn as cn38 } from "@ai-matrx/design-system";
12246
12395
  import { jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
12247
12396
  function useMeasuredWidth(fallback = 480) {
12248
12397
  const ref = useRef12(null);
12249
- const [width, setWidth] = useState41(fallback);
12250
- useEffect28(() => {
12398
+ const [width, setWidth] = useState42(fallback);
12399
+ useEffect29(() => {
12251
12400
  const node = ref.current;
12252
12401
  if (!node) return;
12253
12402
  const apply = () => {
@@ -12371,23 +12520,23 @@ function isSignatureField(field) {
12371
12520
  }
12372
12521
 
12373
12522
  // src/DocTemplate.tsx
12374
- import { useCallback as useCallback26, useEffect as useEffect29, useState as useState42 } from "react";
12375
- import { useFields as useFields19, useRecordsClient as useRecordsClient28, useTable as useTable15 } from "@ai-matrx/records/react";
12523
+ import { useCallback as useCallback26, useEffect as useEffect30, useState as useState43 } from "react";
12524
+ import { useFields as useFields19, useRecordsClient as useRecordsClient29, useTable as useTable15 } from "@ai-matrx/records/react";
12376
12525
  import { BasicInput as BasicInput14, BasicTextarea as BasicTextarea6, Button as Button38, Label as Label8, Skeleton as Skeleton22, cn as cn39 } from "@ai-matrx/design-system";
12377
12526
  import { jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
12378
12527
  function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
12379
- const client = useRecordsClient28();
12528
+ const client = useRecordsClient29();
12380
12529
  const claimSeed = useSeedGuard();
12381
12530
  const table = useTable15(tableId);
12382
12531
  const rights = useTableRights(table.data);
12383
12532
  const fields = useFields19(tableId);
12384
- const [templates, setTemplates] = useState42(null);
12385
- const [error, setError] = useState42(null);
12386
- const [activeId, setActiveId] = useState42(activeTemplateId ?? null);
12387
- const [draftName, setDraftName] = useState42("");
12388
- const [draftBody, setDraftBody] = useState42("");
12389
- const [unresolved, setUnresolved] = useState42([]);
12390
- const [saving, setSaving] = useState42(false);
12533
+ const [templates, setTemplates] = useState43(null);
12534
+ const [error, setError] = useState43(null);
12535
+ const [activeId, setActiveId] = useState43(activeTemplateId ?? null);
12536
+ const [draftName, setDraftName] = useState43("");
12537
+ const [draftBody, setDraftBody] = useState43("");
12538
+ const [unresolved, setUnresolved] = useState43([]);
12539
+ const [saving, setSaving] = useState43(false);
12391
12540
  const load = useCallback26(async () => {
12392
12541
  const held = await client.docTemplates({ table_id: tableId });
12393
12542
  if (!held.ok) {
@@ -12419,10 +12568,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
12419
12568
  setError(null);
12420
12569
  setTemplates(rows);
12421
12570
  }, [client, tableId, seed, fields.data]);
12422
- useEffect29(() => {
12571
+ useEffect30(() => {
12423
12572
  void load();
12424
12573
  }, [load]);
12425
- useEffect29(() => {
12574
+ useEffect30(() => {
12426
12575
  if (!templates || templates.length === 0) return;
12427
12576
  const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
12428
12577
  if (chosen.id !== activeId) setActiveId(chosen.id);
@@ -12430,7 +12579,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
12430
12579
  setDraftBody(chosen.body);
12431
12580
  onActiveTemplate?.(chosen);
12432
12581
  }, [templates, activeTemplateId]);
12433
- useEffect29(() => {
12582
+ useEffect30(() => {
12434
12583
  let cancelled = false;
12435
12584
  if (draftBody.trim() === "") {
12436
12585
  setUnresolved([]);
@@ -12550,17 +12699,17 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
12550
12699
  }
12551
12700
 
12552
12701
  // src/DocRender.tsx
12553
- import { useCallback as useCallback27, useEffect as useEffect30, useRef as useRef13, useState as useState43 } from "react";
12554
- import { useRecordsClient as useRecordsClient29 } from "@ai-matrx/records/react";
12702
+ import { useCallback as useCallback27, useEffect as useEffect31, useRef as useRef13, useState as useState44 } from "react";
12703
+ import { useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
12555
12704
  import { Button as Button39, Skeleton as Skeleton23, cn as cn40 } from "@ai-matrx/design-system";
12556
12705
  import { jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
12557
12706
  function DocRender({ templateId, recordId, filename, onRendered, className }) {
12558
- const client = useRecordsClient29();
12559
- const [preview, setPreview] = useState43(null);
12560
- const [renders, setRenders] = useState43(null);
12561
- const [showing, setShowing] = useState43(null);
12562
- const [error, setError] = useState43(null);
12563
- const [busy, setBusy] = useState43(null);
12707
+ const client = useRecordsClient30();
12708
+ const [preview, setPreview] = useState44(null);
12709
+ const [renders, setRenders] = useState44(null);
12710
+ const [showing, setShowing] = useState44(null);
12711
+ const [error, setError] = useState44(null);
12712
+ const [busy, setBusy] = useState44(null);
12564
12713
  const paper = useRef13(null);
12565
12714
  const load = useCallback27(async () => {
12566
12715
  const [body, held] = await Promise.all([
@@ -12579,7 +12728,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
12579
12728
  setPreview(body.data);
12580
12729
  setRenders(held.data.filter((r) => r.template_id === templateId));
12581
12730
  }, [client, templateId, recordId]);
12582
- useEffect30(() => {
12731
+ useEffect31(() => {
12583
12732
  void load();
12584
12733
  }, [load]);
12585
12734
  async function freeze() {
@@ -12663,21 +12812,21 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
12663
12812
  }
12664
12813
 
12665
12814
  // src/SignBlock.tsx
12666
- import { useCallback as useCallback28, useEffect as useEffect31, useState as useState44 } from "react";
12667
- import { useFields as useFields20, useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
12815
+ import { useCallback as useCallback28, useEffect as useEffect32, useState as useState45 } from "react";
12816
+ import { useFields as useFields20, useRecordsClient as useRecordsClient31 } from "@ai-matrx/records/react";
12668
12817
  import { BasicInput as BasicInput15, Button as Button40, Skeleton as Skeleton24, cn as cn41 } from "@ai-matrx/design-system";
12669
12818
  import { useTable as useTable16 } from "@ai-matrx/records/react";
12670
12819
  import { jsx as jsx47, jsxs as jsxs43 } from "react/jsx-runtime";
12671
12820
  function SignBlock({ tableId, recordId, render, className }) {
12672
- const client = useRecordsClient30();
12821
+ const client = useRecordsClient31();
12673
12822
  const table = useTable16(tableId);
12674
12823
  const rights = useTableRights(table.data);
12675
12824
  const fields = useFields20(tableId);
12676
- const [signatures, setSignatures] = useState44(null);
12677
- const [verdicts, setVerdicts] = useState44({});
12678
- const [error, setError] = useState44(null);
12679
- const [name, setName] = useState44("");
12680
- const [busy, setBusy] = useState44(false);
12825
+ const [signatures, setSignatures] = useState45(null);
12826
+ const [verdicts, setVerdicts] = useState45({});
12827
+ const [error, setError] = useState45(null);
12828
+ const [name, setName] = useState45("");
12829
+ const [busy, setBusy] = useState45(false);
12681
12830
  const load = useCallback28(async () => {
12682
12831
  const held = await client.docSignatures({ record_id: recordId });
12683
12832
  if (!held.ok) {
@@ -12693,7 +12842,7 @@ function SignBlock({ tableId, recordId, render, className }) {
12693
12842
  }
12694
12843
  setVerdicts(answers);
12695
12844
  }, [client, recordId]);
12696
- useEffect31(() => {
12845
+ useEffect32(() => {
12697
12846
  void load();
12698
12847
  }, [load]);
12699
12848
  async function sign(field) {
@@ -12800,22 +12949,22 @@ function SignBlock({ tableId, recordId, render, className }) {
12800
12949
  }
12801
12950
 
12802
12951
  // src/NotifyRuleEditor.tsx
12803
- import { useCallback as useCallback29, useEffect as useEffect32, useState as useState45 } from "react";
12804
- import { useRecordsClient as useRecordsClient31, useTable as useTable17 } from "@ai-matrx/records/react";
12952
+ import { useCallback as useCallback29, useEffect as useEffect33, useState as useState46 } from "react";
12953
+ import { useRecordsClient as useRecordsClient32, useTable as useTable17 } from "@ai-matrx/records/react";
12805
12954
  import { Button as Button41, Skeleton as Skeleton25, cn as cn42 } from "@ai-matrx/design-system";
12806
12955
  import { jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
12807
12956
  var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
12808
12957
  var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
12809
12958
  function NotifyRuleEditor({ tableId, seed, className }) {
12810
- const client = useRecordsClient31();
12959
+ const client = useRecordsClient32();
12811
12960
  const host = useRecordsUi();
12812
12961
  const table = useTable17(tableId);
12813
12962
  const rights = useTableRights(table.data);
12814
- const [subscriptions, setSubscriptions] = useState45(null);
12815
- const [cadences, setCadences] = useState45([]);
12816
- const [views, setViews] = useState45(null);
12817
- const [error, setError] = useState45(null);
12818
- const [busy, setBusy] = useState45(false);
12963
+ const [subscriptions, setSubscriptions] = useState46(null);
12964
+ const [cadences, setCadences] = useState46([]);
12965
+ const [views, setViews] = useState46(null);
12966
+ const [error, setError] = useState46(null);
12967
+ const [busy, setBusy] = useState46(false);
12819
12968
  const load = useCallback29(async () => {
12820
12969
  const [held, offered] = await Promise.all([
12821
12970
  // THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
@@ -12836,7 +12985,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
12836
12985
  if (host.savedViews) setViews(await host.savedViews());
12837
12986
  else setViews(null);
12838
12987
  }, [client, host, tableId]);
12839
- useEffect32(() => {
12988
+ useEffect33(() => {
12840
12989
  void load();
12841
12990
  }, [load]);
12842
12991
  const write = useCallback29(
@@ -12864,7 +13013,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
12864
13013
  },
12865
13014
  [client, tableId]
12866
13015
  );
12867
- useEffect32(() => {
13016
+ useEffect33(() => {
12868
13017
  if (!subscriptions || !seed || seed.length === 0) return;
12869
13018
  const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
12870
13019
  if (missing.length === 0) return;
@@ -13320,24 +13469,34 @@ function Drawing({
13320
13469
  }
13321
13470
 
13322
13471
  // src/DashboardCanvas.tsx
13323
- import { useCallback as useCallback30, useEffect as useEffect33, useMemo as useMemo28, useState as useState46 } from "react";
13324
- import { useFields as useFields21, useRecordsClient as useRecordsClient32, useTable as useTable18 } from "@ai-matrx/records/react";
13325
- import { BasicInput as BasicInput16, Button as Button42, Skeleton as Skeleton26, cn as cn44 } from "@ai-matrx/design-system";
13472
+ import { useCallback as useCallback30, useEffect as useEffect34, useMemo as useMemo28, useState as useState47 } from "react";
13473
+ import { useFields as useFields21, useRecordsClient as useRecordsClient33, useTable as useTable18 } from "@ai-matrx/records/react";
13474
+ import {
13475
+ BasicInput as BasicInput16,
13476
+ Button as Button42,
13477
+ Select as Select5,
13478
+ SelectContent as SelectContent5,
13479
+ SelectItem as SelectItem5,
13480
+ SelectTrigger as SelectTrigger5,
13481
+ SelectValue as SelectValue5,
13482
+ Skeleton as Skeleton26,
13483
+ cn as cn44
13484
+ } from "@ai-matrx/design-system";
13326
13485
  import { Fragment as Fragment21, jsx as jsx50, jsxs as jsxs46 } from "react/jsx-runtime";
13327
13486
  function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13328
- const client = useRecordsClient32();
13487
+ const client = useRecordsClient33();
13329
13488
  const host = useRecordsUi();
13330
13489
  const table = useTable18(tableId);
13331
13490
  const rights = useTableRights(table.data);
13332
13491
  const fields = useFields21(tableId);
13333
- const [boards, setBoards] = useState46(null);
13334
- const [error, setError] = useState46(null);
13335
- const [activeId, setActiveId] = useState46(activeDashboardId ?? null);
13336
- const [run, setRun] = useState46(null);
13337
- const [running, setRunning] = useState46(false);
13338
- const [question, setQuestion] = useState46("");
13339
- const [asking, setAsking] = useState46(false);
13340
- const [scheduling, setScheduling] = useState46(false);
13492
+ const [boards, setBoards] = useState47(null);
13493
+ const [error, setError] = useState47(null);
13494
+ const [activeId, setActiveId] = useState47(activeDashboardId ?? null);
13495
+ const [run, setRun] = useState47(null);
13496
+ const [running, setRunning] = useState47(false);
13497
+ const [question, setQuestion] = useState47("");
13498
+ const [asking, setAsking] = useState47(false);
13499
+ const [scheduling, setScheduling] = useState47(false);
13341
13500
  const load = useCallback30(async () => {
13342
13501
  const answered = await client.dashboards({ table_id: tableId });
13343
13502
  if (!answered.ok) {
@@ -13347,17 +13506,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13347
13506
  setError(null);
13348
13507
  setBoards(answered.data.map(dashboardFromSummary));
13349
13508
  }, [client, tableId]);
13350
- useEffect33(() => {
13509
+ useEffect34(() => {
13351
13510
  void load();
13352
13511
  }, [load]);
13353
- useEffect33(() => {
13512
+ useEffect34(() => {
13354
13513
  if (!boards || boards.length === 0) return;
13355
13514
  const chosen = boards.find((d) => d.id === (activeDashboardId ?? activeId)) ?? boards[0];
13356
13515
  if (chosen.id !== activeId) setActiveId(chosen.id);
13357
13516
  }, [boards, activeDashboardId]);
13358
13517
  const board = useMemo28(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
13359
13518
  const filterKey = JSON.stringify(filter ?? {});
13360
- useEffect33(() => {
13519
+ useEffect34(() => {
13361
13520
  if (!activeId) {
13362
13521
  setRun(null);
13363
13522
  return;
@@ -13396,8 +13555,8 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13396
13555
  [client, tableId, load, filter]
13397
13556
  );
13398
13557
  async function create() {
13399
- const groupable = (fields.data ?? []).filter((f) => f.type === "text" || f.type === "list");
13400
- const byField = groupable[0];
13558
+ const all = fields.data ?? [];
13559
+ const byField = offerableFields(all, "choice")[0] ?? all.find((f) => f.type === "text" || f.type === "list");
13401
13560
  const by = byField?.key;
13402
13561
  await declare2(
13403
13562
  {
@@ -13568,6 +13727,32 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13568
13727
  children: CHART_KINDS.map((kind) => /* @__PURE__ */ jsx50("option", { value: kind, children: CHART_KIND_LABEL[kind] }, kind))
13569
13728
  }
13570
13729
  ),
13730
+ (board.blocks[index].groupBy ?? []).length > 0 ? /* @__PURE__ */ jsx50(
13731
+ GroupingPicker,
13732
+ {
13733
+ block: board.blocks[index],
13734
+ fields: fields.data ?? [],
13735
+ onPick: (key) => void declare2(
13736
+ board,
13737
+ board.blocks.map((b, i) => {
13738
+ if (i !== index) return b;
13739
+ const picked = (fields.data ?? []).find((f) => f.key === key);
13740
+ const wasNamedAfterItsColumn = (fields.data ?? []).some(
13741
+ (f) => b.title === `By ${fieldName(f)}`
13742
+ );
13743
+ return {
13744
+ ...b,
13745
+ groupBy: [key],
13746
+ // A block titled after the column it grouped by
13747
+ // must not keep saying "By Job Number" once it
13748
+ // groups by Status. A title somebody wrote
13749
+ // themselves is left alone.
13750
+ ...wasNamedAfterItsColumn && picked ? { title: `By ${fieldName(picked)}` } : {}
13751
+ };
13752
+ })
13753
+ )
13754
+ }
13755
+ ) : null,
13571
13756
  /* @__PURE__ */ jsx50(
13572
13757
  Button42,
13573
13758
  {
@@ -13586,10 +13771,49 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
13586
13771
  ] })
13587
13772
  ] });
13588
13773
  }
13774
+ function groupingChoices(all) {
13775
+ const once = [];
13776
+ for (const field of all) if (!once.some((f) => f.key === field.key)) once.push(field);
13777
+ const sensible = offerableFields(once, "choice");
13778
+ const isSensible = (field) => sensible.some((s) => s.key === field.key);
13779
+ return [
13780
+ ...sensible.map((field) => ({ field, unusual: false })),
13781
+ ...once.filter((field) => !isSensible(field)).map((field) => ({ field, unusual: true }))
13782
+ ];
13783
+ }
13784
+ function GroupingPicker({
13785
+ block,
13786
+ fields,
13787
+ onPick
13788
+ }) {
13789
+ const choices = groupingChoices(fields);
13790
+ const current = block.groupBy?.[0] ?? "";
13791
+ if (choices.length === 0) {
13792
+ return /* @__PURE__ */ jsx50("span", { className: "text-muted-foreground", children: "This table has no columns yet, so there is nothing to group this block by." });
13793
+ }
13794
+ const known = choices.some((c) => c.field.key === current);
13795
+ return /* @__PURE__ */ jsxs46(Fragment21, { children: [
13796
+ /* @__PURE__ */ jsx50("label", { className: "text-muted-foreground", htmlFor: `block-group-${block.title}`, children: "Group by" }),
13797
+ /* @__PURE__ */ jsxs46(Select5, { value: known ? current : "", onValueChange: onPick, children: [
13798
+ /* @__PURE__ */ jsx50(
13799
+ SelectTrigger5,
13800
+ {
13801
+ size: "sm",
13802
+ id: `block-group-${block.title}`,
13803
+ "aria-label": `Which column ${block.title} is grouped by`,
13804
+ className: "h-7 min-w-[10rem] text-xs",
13805
+ children: /* @__PURE__ */ jsx50(SelectValue5, { placeholder: "Choose a column" })
13806
+ }
13807
+ ),
13808
+ /* @__PURE__ */ jsx50(SelectContent5, { children: choices.map(({ field, unusual }) => /* @__PURE__ */ jsx50(SelectItem5, { value: field.key, children: unusual ? `${fieldName(field)} \u2014 ${UNUSUAL_FIELD_NOTE}` : fieldName(field) }, field.key)) })
13809
+ ] }),
13810
+ known ? null : /* @__PURE__ */ jsx50("span", { className: "text-muted-foreground", children: "The column this block was grouped by is no longer on this table \u2014 choose another." })
13811
+ ] });
13812
+ }
13589
13813
 
13590
13814
  // src/FormsPanel.tsx
13591
- import { useCallback as useCallback31, useEffect as useEffect34, useState as useState47 } from "react";
13592
- import { useRecordsClient as useRecordsClient33, useTable as useTable19 } from "@ai-matrx/records/react";
13815
+ import { useCallback as useCallback31, useEffect as useEffect35, useState as useState48 } from "react";
13816
+ import { useRecordsClient as useRecordsClient34, useTable as useTable19 } from "@ai-matrx/records/react";
13593
13817
  import { publicFormPath as publicFormPath2 } from "@ai-matrx/records";
13594
13818
  import { Badge as Badge15, Button as Button43, Skeleton as Skeleton27, cn as cn45 } from "@ai-matrx/design-system";
13595
13819
  import { Fragment as Fragment22, jsx as jsx51, jsxs as jsxs47 } from "react/jsx-runtime";
@@ -13600,15 +13824,16 @@ function formSuggestion(tableName2) {
13600
13824
  return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
13601
13825
  }
13602
13826
  function FormsPanel({ tableId, className }) {
13603
- const client = useRecordsClient33();
13827
+ const client = useRecordsClient34();
13828
+ const publishGate = usePublishGate();
13604
13829
  const host = useRecordsUi();
13605
13830
  const table = useTable19(tableId);
13606
13831
  const rights = useTableRights(table.data);
13607
- const [forms, setForms] = useState47(null);
13608
- const [error, setError] = useState47(null);
13609
- const [busy, setBusy] = useState47(null);
13610
- const [copied, setCopied] = useState47(null);
13611
- const [building, setBuilding] = useState47(false);
13832
+ const [forms, setForms] = useState48(null);
13833
+ const [error, setError] = useState48(null);
13834
+ const [busy, setBusy] = useState48(null);
13835
+ const [copied, setCopied] = useState48(null);
13836
+ const [building, setBuilding] = useState48(false);
13612
13837
  const load = useCallback31(async () => {
13613
13838
  const answered = await client.forms({ table_id: tableId });
13614
13839
  if (!answered.ok) {
@@ -13619,7 +13844,7 @@ function FormsPanel({ tableId, className }) {
13619
13844
  setError(null);
13620
13845
  setForms(answered.data);
13621
13846
  }, [client, tableId]);
13622
- useEffect34(() => {
13847
+ useEffect35(() => {
13623
13848
  void load();
13624
13849
  }, [load]);
13625
13850
  const toggle = useCallback31(
@@ -13636,7 +13861,7 @@ function FormsPanel({ tableId, className }) {
13636
13861
  },
13637
13862
  [client, load]
13638
13863
  );
13639
- const [shown, setShown] = useState47(null);
13864
+ const [shown, setShown] = useState48(null);
13640
13865
  const copy = useCallback31(async (url, formId) => {
13641
13866
  try {
13642
13867
  await navigator.clipboard.writeText(url);
@@ -13712,7 +13937,7 @@ function FormsPanel({ tableId, className }) {
13712
13937
  ] }),
13713
13938
  /* @__PURE__ */ jsxs47("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
13714
13939
  form.published_at ? /* @__PURE__ */ jsx51(Button43, { size: "sm", variant: "outline", onClick: () => void copy(url, form.form_id), children: copied === form.form_id ? "Copied" : "Copy link" }) : null,
13715
- rights.structure ? /* @__PURE__ */ jsx51(
13940
+ rights.structure && !publishGate.blocked ? /* @__PURE__ */ jsx51(
13716
13941
  Button43,
13717
13942
  {
13718
13943
  size: "sm",
@@ -13723,6 +13948,7 @@ function FormsPanel({ tableId, className }) {
13723
13948
  }
13724
13949
  ) : null
13725
13950
  ] }),
13951
+ rights.structure && publishGate.why ? /* @__PURE__ */ jsx51("p", { className: "mt-1.5 text-xs text-muted-foreground", children: publishGate.why }) : null,
13726
13952
  shown && shown === url ? /* @__PURE__ */ jsxs47("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
13727
13953
  "This browser would not let the page copy for you, so here it is to copy by hand:",
13728
13954
  " ",
@@ -13735,8 +13961,8 @@ function FormsPanel({ tableId, className }) {
13735
13961
  }
13736
13962
 
13737
13963
  // src/BookingBuilder.tsx
13738
- import { useCallback as useCallback32, useEffect as useEffect35, useMemo as useMemo29, useState as useState48 } from "react";
13739
- import { useFields as useFields22, useRecordsClient as useRecordsClient34, useTable as useTable20 } from "@ai-matrx/records/react";
13964
+ import { useCallback as useCallback32, useEffect as useEffect36, useMemo as useMemo29, useState as useState49 } from "react";
13965
+ import { useFields as useFields22, useRecordsClient as useRecordsClient35, useTable as useTable20 } from "@ai-matrx/records/react";
13740
13966
  import {
13741
13967
  bookingPath
13742
13968
  } from "@ai-matrx/records";
@@ -13766,28 +13992,29 @@ function draftWindows(availability) {
13766
13992
  });
13767
13993
  }
13768
13994
  function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
13769
- const client = useRecordsClient34();
13995
+ const client = useRecordsClient35();
13996
+ const publishGate = usePublishGate();
13770
13997
  const host = useRecordsUi();
13771
13998
  const table = useTable20(tableId);
13772
13999
  const rights = useTableRights(table.data);
13773
14000
  const fields = useFields22(tableId);
13774
- const [existing, setExisting] = useState48(null);
13775
- const [loaded, setLoaded] = useState48(false);
13776
- const [error, setError] = useState48(null);
13777
- const [saving, setSaving] = useState48(false);
13778
- const [publishing, setPublishing] = useState48(false);
13779
- const [copied, setCopied] = useState48(false);
13780
- const [title, setTitle] = useState48("");
13781
- const [minutes, setMinutes] = useState48(30);
13782
- const [buffer, setBuffer] = useState48(0);
13783
- const [lead, setLead] = useState48(120);
13784
- const [perDay, setPerDay] = useState48(8);
13785
- const [days, setDays] = useState48(30);
13786
- const [windows, setWindows] = useState48(() => draftWindows(null));
13787
- const [asked, setAsked] = useState48([]);
13788
- const [confirmation, setConfirmation] = useState48("");
13789
- const [offer, setOffer] = useState48(null);
13790
- const [formId, setFormId] = useState48(bookingId ?? null);
14001
+ const [existing, setExisting] = useState49(null);
14002
+ const [loaded, setLoaded] = useState49(false);
14003
+ const [error, setError] = useState49(null);
14004
+ const [saving, setSaving] = useState49(false);
14005
+ const [publishing, setPublishing] = useState49(false);
14006
+ const [copied, setCopied] = useState49(false);
14007
+ const [title, setTitle] = useState49("");
14008
+ const [minutes, setMinutes] = useState49(30);
14009
+ const [buffer, setBuffer] = useState49(0);
14010
+ const [lead, setLead] = useState49(120);
14011
+ const [perDay, setPerDay] = useState49(8);
14012
+ const [days, setDays] = useState49(30);
14013
+ const [windows, setWindows] = useState49(() => draftWindows(null));
14014
+ const [asked, setAsked] = useState49([]);
14015
+ const [confirmation, setConfirmation] = useState49("");
14016
+ const [offer, setOffer] = useState49(null);
14017
+ const [formId, setFormId] = useState49(bookingId ?? null);
13791
14018
  const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
13792
14019
  const askable = useMemo29(
13793
14020
  () => askableFields(fields.data ?? [], STORE_ANSWERS_THESE),
@@ -13809,18 +14036,18 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
13809
14036
  setLoaded(true);
13810
14037
  if (mine) setFormId(mine.form_id);
13811
14038
  }, [client, tableId, bookingId]);
13812
- useEffect35(() => {
14039
+ useEffect36(() => {
13813
14040
  void load();
13814
14041
  }, [load]);
13815
- useEffect35(() => {
14042
+ useEffect36(() => {
13816
14043
  if (title !== "" || !table.data) return;
13817
14044
  setTitle(existing?.title ?? `Book a ${minutes}-minute ${table.data.name} appointment`);
13818
14045
  }, [table.data, existing]);
13819
- useEffect35(() => {
14046
+ useEffect36(() => {
13820
14047
  if (!existing) return;
13821
14048
  setMinutes(existing.slot_minutes);
13822
14049
  }, [existing]);
13823
- useEffect35(() => {
14050
+ useEffect36(() => {
13824
14051
  if (!offer) return;
13825
14052
  setWindows(draftWindows(offer));
13826
14053
  setMinutes(offer.slot_minutes);
@@ -14066,7 +14293,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
14066
14293
  children: copied ? "Copied" : "Copy link"
14067
14294
  }
14068
14295
  ),
14069
- /* @__PURE__ */ jsx52(
14296
+ publishGate.blocked ? null : /* @__PURE__ */ jsx52(
14070
14297
  Button44,
14071
14298
  {
14072
14299
  size: "sm",
@@ -14075,15 +14302,15 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
14075
14302
  children: publishing ? "\u2026" : existing?.published_at && !existing.closed_at ? "Unpublish" : "Publish"
14076
14303
  }
14077
14304
  ),
14078
- /* @__PURE__ */ jsx52("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." })
14305
+ /* @__PURE__ */ jsx52("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.") })
14079
14306
  ] }) : null
14080
14307
  ] })
14081
14308
  );
14082
14309
  }
14083
14310
 
14084
14311
  // src/BookingSlots.tsx
14085
- import { useCallback as useCallback33, useEffect as useEffect36, useMemo as useMemo30, useState as useState49 } from "react";
14086
- import { useRecordsClient as useRecordsClient35, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
14312
+ import { useCallback as useCallback33, useEffect as useEffect37, useMemo as useMemo30, useState as useState50 } from "react";
14313
+ import { useRecordsClient as useRecordsClient36, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
14087
14314
  import { bookingPath as bookingPath2 } from "@ai-matrx/records";
14088
14315
  import { Badge as Badge16, Button as Button45, Skeleton as Skeleton29, cn as cn47 } from "@ai-matrx/design-system";
14089
14316
  import { Fragment as Fragment24, jsx as jsx53, jsxs as jsxs49 } from "react/jsx-runtime";
@@ -14095,14 +14322,14 @@ function bookingSuggestion(tableName2) {
14095
14322
  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.";
14096
14323
  var STATE_WORDS = BOOKING_PAGE_STATE_LABEL;
14097
14324
  function BookingSlots({ tableId, className }) {
14098
- const client = useRecordsClient35();
14325
+ const client = useRecordsClient36();
14099
14326
  const host = useRecordsUi();
14100
- const [pages, setPages] = useState49(null);
14101
- const [error, setError] = useState49(null);
14102
- const [busy, setBusy] = useState49(null);
14103
- const [copied, setCopied] = useState49(null);
14104
- const [shown, setShown] = useState49(null);
14105
- const [building, setBuilding] = useState49(false);
14327
+ const [pages, setPages] = useState50(null);
14328
+ const [error, setError] = useState50(null);
14329
+ const [busy, setBusy] = useState50(null);
14330
+ const [copied, setCopied] = useState50(null);
14331
+ const [shown, setShown] = useState50(null);
14332
+ const [building, setBuilding] = useState50(false);
14106
14333
  const load = useCallback33(async () => {
14107
14334
  const answered = await client.bookings(tableId ? { table_id: tableId } : {});
14108
14335
  if (!answered.ok) {
@@ -14113,7 +14340,7 @@ function BookingSlots({ tableId, className }) {
14113
14340
  setError(null);
14114
14341
  setPages(answered.data);
14115
14342
  }, [client, tableId]);
14116
- useEffect36(() => {
14343
+ useEffect37(() => {
14117
14344
  void load();
14118
14345
  }, [load]);
14119
14346
  const subjectIds = useMemo30(
@@ -14282,18 +14509,18 @@ function nextInWords(page) {
14282
14509
  }
14283
14510
 
14284
14511
  // src/CaptureSheet.tsx
14285
- import { useCallback as useCallback35, useEffect as useEffect38, useRef as useRef15, useState as useState51 } from "react";
14286
- import { useFields as useFields23, useRecordsClient as useRecordsClient37, useTable as useTable21 } from "@ai-matrx/records/react";
14512
+ import { useCallback as useCallback35, useEffect as useEffect39, useRef as useRef15, useState as useState52 } from "react";
14513
+ import { useFields as useFields23, useRecordsClient as useRecordsClient38, useTable as useTable21 } from "@ai-matrx/records/react";
14287
14514
  import { Button as Button47, Input as Input4, Skeleton as Skeleton31, Textarea as Textarea3, cn as cn49 } from "@ai-matrx/design-system";
14288
14515
 
14289
14516
  // src/CaptureRun.tsx
14290
- import { useCallback as useCallback34, useEffect as useEffect37, useMemo as useMemo31, useRef as useRef14, useState as useState50 } from "react";
14517
+ import { useCallback as useCallback34, useEffect as useEffect38, useMemo as useMemo31, useRef as useRef14, useState as useState51 } from "react";
14291
14518
  import {
14292
14519
  coerceTypedAnswer as coerceTypedAnswer2,
14293
14520
  coercionLabel,
14294
14521
  openCaptureQueue
14295
14522
  } from "@ai-matrx/records";
14296
- import { useRecordsClient as useRecordsClient36 } from "@ai-matrx/records/react";
14523
+ import { useRecordsClient as useRecordsClient37 } from "@ai-matrx/records/react";
14297
14524
  import { Button as Button46, Input as Input3, Skeleton as Skeleton30, Textarea as Textarea2, cn as cn48 } from "@ai-matrx/design-system";
14298
14525
  import { jsx as jsx54, jsxs as jsxs50 } from "react/jsx-runtime";
14299
14526
  function controlFor(field) {
@@ -14332,21 +14559,21 @@ function whereWeAre(timeoutMs = 4e3) {
14332
14559
  });
14333
14560
  }
14334
14561
  function CaptureRun({ sheetId, face: given, className }) {
14335
- const client = useRecordsClient36();
14562
+ const client = useRecordsClient37();
14336
14563
  const host = useRecordsUi();
14337
- const [face, setFace] = useState50(given);
14338
- const [loadFailed, setLoadFailed] = useState50(null);
14339
- const [at, setAt] = useState50(0);
14340
- const [answers, setAnswers] = useState50({});
14341
- const [files, setFiles] = useState50({});
14342
- const [missing, setMissing] = useState50(null);
14343
- const [done, setDone] = useState50(null);
14344
- const [counts, setCounts] = useState50({ waiting: 0, sending: 0, refused: 0, landed: 0 });
14345
- const [items, setItems] = useState50([]);
14346
- const [lastSynced, setLastSynced] = useState50(null);
14347
- const [sending, setSending] = useState50(false);
14564
+ const [face, setFace] = useState51(given);
14565
+ const [loadFailed, setLoadFailed] = useState51(null);
14566
+ const [at, setAt] = useState51(0);
14567
+ const [answers, setAnswers] = useState51({});
14568
+ const [files, setFiles] = useState51({});
14569
+ const [missing, setMissing] = useState51(null);
14570
+ const [done, setDone] = useState51(null);
14571
+ const [counts, setCounts] = useState51({ waiting: 0, sending: 0, refused: 0, landed: 0 });
14572
+ const [items, setItems] = useState51([]);
14573
+ const [lastSynced, setLastSynced] = useState51(null);
14574
+ const [sending, setSending] = useState51(false);
14348
14575
  const queueRef = useRef14(null);
14349
- useEffect37(() => {
14576
+ useEffect38(() => {
14350
14577
  const q2 = openCaptureQueue({
14351
14578
  onChange: (c, all) => {
14352
14579
  setCounts(c);
@@ -14384,7 +14611,7 @@ function CaptureRun({ sheetId, face: given, className }) {
14384
14611
  void q2.sync().then(() => void q2.lastSyncedAt().then(setLastSynced));
14385
14612
  return () => q2.dispose();
14386
14613
  }, [client, host]);
14387
- useEffect37(() => {
14614
+ useEffect38(() => {
14388
14615
  if (given !== void 0) return;
14389
14616
  let cancelled = false;
14390
14617
  void client.captureOpen({ sheet_id: sheetId }).then((res) => {
@@ -14694,19 +14921,19 @@ function AdHocCaptureSheet({
14694
14921
  attachmentField,
14695
14922
  className
14696
14923
  }) {
14697
- const client = useRecordsClient37();
14924
+ const client = useRecordsClient38();
14698
14925
  const host = useRecordsUi();
14699
14926
  const table = useTable21(tableId);
14700
14927
  const fields = useFields23(tableId);
14701
- const [mode, setMode] = useState51("reading");
14702
- const [reading, setReading] = useState51("");
14703
- const [note, setNote] = useState51("");
14704
- const [fileId, setFileId] = useState51(null);
14705
- const [pending, setPending] = useState51(null);
14706
- const [saved, setSaved] = useState51([]);
14707
- const [error, setError] = useState51(null);
14708
- const [uploadError, setUploadError] = useState51(null);
14709
- const [flushing, setFlushing] = useState51(false);
14928
+ const [mode, setMode] = useState52("reading");
14929
+ const [reading, setReading] = useState52("");
14930
+ const [note, setNote] = useState52("");
14931
+ const [fileId, setFileId] = useState52(null);
14932
+ const [pending, setPending] = useState52(null);
14933
+ const [saved, setSaved] = useState52([]);
14934
+ const [error, setError] = useState52(null);
14935
+ const [uploadError, setUploadError] = useState52(null);
14936
+ const [flushing, setFlushing] = useState52(false);
14710
14937
  const queue = useRef15([]);
14711
14938
  const keep = useCallback35(
14712
14939
  async (next) => {
@@ -14716,7 +14943,7 @@ function AdHocCaptureSheet({
14716
14943
  },
14717
14944
  [host]
14718
14945
  );
14719
- useEffect38(() => {
14946
+ useEffect39(() => {
14720
14947
  let cancelled = false;
14721
14948
  void (async () => {
14722
14949
  const held = host.captureQueue ? await host.captureQueue.load() : [];
@@ -14895,17 +15122,17 @@ function AdHocCaptureSheet({
14895
15122
  }
14896
15123
 
14897
15124
  // src/PortalShell.tsx
14898
- import { useCallback as useCallback36, useEffect as useEffect39, useState as useState52 } from "react";
14899
- import { useRecordsClient as useRecordsClient38 } from "@ai-matrx/records/react";
15125
+ import { useCallback as useCallback36, useEffect as useEffect40, useState as useState53 } from "react";
15126
+ import { useRecordsClient as useRecordsClient39 } from "@ai-matrx/records/react";
14900
15127
  import { Button as Button48, Skeleton as Skeleton32, cn as cn50 } from "@ai-matrx/design-system";
14901
15128
  import { jsx as jsx56, jsxs as jsxs52 } from "react/jsx-runtime";
14902
15129
  function PortalShell({ tableId, form, resourceType = "record", className }) {
14903
- const client = useRecordsClient38();
14904
- const [card, setCard] = useState52(null);
14905
- const [reach, setReach] = useState52(null);
14906
- const [error, setError] = useState52(null);
14907
- const [open, setOpen] = useState52(null);
14908
- const [sending, setSending] = useState52(false);
15130
+ const client = useRecordsClient39();
15131
+ const [card, setCard] = useState53(null);
15132
+ const [reach, setReach] = useState53(null);
15133
+ const [error, setError] = useState53(null);
15134
+ const [open, setOpen] = useState53(null);
15135
+ const [sending, setSending] = useState53(false);
14909
15136
  const load = useCallback36(async () => {
14910
15137
  const who = await client.externalPrincipalCard();
14911
15138
  if (!who.ok) {
@@ -14921,7 +15148,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
14921
15148
  }
14922
15149
  setReach(reached.data.map((row) => row.resource_id));
14923
15150
  }, [client, resourceType]);
14924
- useEffect39(() => {
15151
+ useEffect40(() => {
14925
15152
  void load();
14926
15153
  }, [load]);
14927
15154
  if (error) {
@@ -14979,9 +15206,9 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
14979
15206
  ] });
14980
15207
  }
14981
15208
  function PortalRow({ tableId, recordId }) {
14982
- const client = useRecordsClient38();
14983
- const [title, setTitle] = useState52(null);
14984
- useEffect39(() => {
15209
+ const client = useRecordsClient39();
15210
+ const [title, setTitle] = useState53(null);
15211
+ useEffect40(() => {
14985
15212
  let cancelled = false;
14986
15213
  void client.recordRead({ record_id: recordId }).then((answered) => {
14987
15214
  if (cancelled) return;
@@ -15001,17 +15228,17 @@ function PortalRow({ tableId, recordId }) {
15001
15228
  }
15002
15229
 
15003
15230
  // src/PublicViewPage.tsx
15004
- import { useCallback as useCallback37, useEffect as useEffect40, useState as useState53 } from "react";
15005
- import { useRecordsClient as useRecordsClient39 } from "@ai-matrx/records/react";
15231
+ import { useCallback as useCallback37, useEffect as useEffect41, useState as useState54 } from "react";
15232
+ import { useRecordsClient as useRecordsClient40 } from "@ai-matrx/records/react";
15006
15233
  import { Skeleton as Skeleton33, cn as cn51 } from "@ai-matrx/design-system";
15007
15234
  import { jsx as jsx57, jsxs as jsxs53 } from "react/jsx-runtime";
15008
15235
  function PublicViewPage({ slug, className }) {
15009
- const client = useRecordsClient39();
15010
- const [binding, setBinding] = useState53(null);
15011
- const [rows, setRows] = useState53(null);
15012
- const [fields, setFields] = useState53(null);
15013
- const [error, setError] = useState53(null);
15014
- const [gap, setGap] = useState53(null);
15236
+ const client = useRecordsClient40();
15237
+ const [binding, setBinding] = useState54(null);
15238
+ const [rows, setRows] = useState54(null);
15239
+ const [fields, setFields] = useState54(null);
15240
+ const [error, setError] = useState54(null);
15241
+ const [gap, setGap] = useState54(null);
15015
15242
  const load = useCallback37(async () => {
15016
15243
  const notice = await client.worldPublishGapNotice();
15017
15244
  if (notice.ok) setGap(notice.data);
@@ -15045,7 +15272,7 @@ function PublicViewPage({ slug, className }) {
15045
15272
  }
15046
15273
  setRows([{ id: found.resource_id, document: read.data.document, level: "viewer", hidden: read.data.hidden }]);
15047
15274
  }, [client, slug]);
15048
- useEffect40(() => {
15275
+ useEffect41(() => {
15049
15276
  void load();
15050
15277
  }, [load]);
15051
15278
  if (error) return /* @__PURE__ */ jsx57(RefusalNotice, { error, className });
@@ -15082,8 +15309,8 @@ function PublicRow({ row, fields }) {
15082
15309
  }
15083
15310
 
15084
15311
  // src/EmbedFrame.tsx
15085
- import { useCallback as useCallback38, useEffect as useEffect41, useState as useState54 } from "react";
15086
- import { useRecordsClient as useRecordsClient40 } from "@ai-matrx/records/react";
15312
+ import { useCallback as useCallback38, useEffect as useEffect42, useState as useState55 } from "react";
15313
+ import { useRecordsClient as useRecordsClient41 } from "@ai-matrx/records/react";
15087
15314
  import { Button as Button49, Input as Input5, Skeleton as Skeleton34, Textarea as Textarea4, cn as cn52 } from "@ai-matrx/design-system";
15088
15315
  import { useTable as useTable22 } from "@ai-matrx/records/react";
15089
15316
  import { Fragment as Fragment26, jsx as jsx58, jsxs as jsxs54 } from "react/jsx-runtime";
@@ -15095,15 +15322,15 @@ function EmbedFrame({
15095
15322
  embedUrl,
15096
15323
  className
15097
15324
  }) {
15098
- const client = useRecordsClient40();
15325
+ const client = useRecordsClient41();
15099
15326
  const host = useRecordsUi();
15100
15327
  const table = useTable22(tableId);
15101
15328
  const rights = useTableRights(table.data);
15102
- const [origins, setOrigins] = useState54("");
15103
- const [secret, setSecret] = useState54(null);
15104
- const [tokenId, setTokenId] = useState54(null);
15105
- const [error, setError] = useState54(null);
15106
- const [busy, setBusy] = useState54(false);
15329
+ const [origins, setOrigins] = useState55("");
15330
+ const [secret, setSecret] = useState55(null);
15331
+ const [tokenId, setTokenId] = useState55(null);
15332
+ const [error, setError] = useState55(null);
15333
+ const [busy, setBusy] = useState55(false);
15107
15334
  const mode = formId ? "write" : "read";
15108
15335
  const parsed = origins.split(/[\s,]+/).map((o) => o.trim()).filter((o) => o.length > 0);
15109
15336
  const issue = useCallback38(async () => {
@@ -15189,13 +15416,13 @@ function EmbedFrame({
15189
15416
  ] });
15190
15417
  }
15191
15418
  function useEmbedHandshake(args) {
15192
- const client = useRecordsClient40();
15193
- const [binding, setBinding] = useState54(null);
15194
- const [error, setError] = useState54(null);
15195
- const [loading, setLoading] = useState54(true);
15419
+ const client = useRecordsClient41();
15420
+ const [binding, setBinding] = useState55(null);
15421
+ const [error, setError] = useState55(null);
15422
+ const [loading, setLoading] = useState55(true);
15196
15423
  const origin = args.origin ?? (typeof location === "undefined" ? "" : location.origin);
15197
15424
  const { secret, requiredMode } = args;
15198
- useEffect41(() => {
15425
+ useEffect42(() => {
15199
15426
  let cancelled = false;
15200
15427
  setLoading(true);
15201
15428
  setError(null);
@@ -15250,8 +15477,8 @@ function recordsDataSource(client, fallbackSchema = "custom") {
15250
15477
  }
15251
15478
 
15252
15479
  // src/TablesHome.tsx
15253
- import { useCallback as useCallback39, useEffect as useEffect42, useState as useState55 } from "react";
15254
- import { useRecordsClient as useRecordsClient41, useTables as useTables4 } from "@ai-matrx/records/react";
15480
+ import { useCallback as useCallback39, useEffect as useEffect43, useState as useState56 } from "react";
15481
+ import { useRecordsClient as useRecordsClient42, useTables as useTables4 } from "@ai-matrx/records/react";
15255
15482
  import { BasicInput as BasicInput18, Button as Button50, Skeleton as Skeleton35, cn as cn53 } from "@ai-matrx/design-system";
15256
15483
 
15257
15484
  // src/createTable.ts
@@ -15387,15 +15614,15 @@ function laneFor(table) {
15387
15614
  return "organization";
15388
15615
  }
15389
15616
  function TablesHome({ onOpenTable, className }) {
15390
- const client = useRecordsClient41();
15617
+ const client = useRecordsClient42();
15391
15618
  const tables = useTables4();
15392
- const [creating, setCreating] = useState55(false);
15393
- const [name, setName] = useState55("");
15394
- const [busy, setBusy] = useState55(false);
15395
- const [error, setError] = useState55(null);
15396
- const [importInto, setImportInto] = useState55(null);
15397
- const [boards, setBoards] = useState55(null);
15398
- useEffect42(() => {
15619
+ const [creating, setCreating] = useState56(false);
15620
+ const [name, setName] = useState56("");
15621
+ const [busy, setBusy] = useState56(false);
15622
+ const [error, setError] = useState56(null);
15623
+ const [importInto, setImportInto] = useState56(null);
15624
+ const [boards, setBoards] = useState56(null);
15625
+ useEffect43(() => {
15399
15626
  let cancelled = false;
15400
15627
  void client.dashboards({}).then((result) => {
15401
15628
  if (cancelled) return;
@@ -15529,8 +15756,8 @@ function TablesHome({ onOpenTable, className }) {
15529
15756
  }
15530
15757
 
15531
15758
  // src/TablePage.tsx
15532
- import { useCallback as useCallback40, useEffect as useEffect43, useState as useState56 } from "react";
15533
- import { useFields as useFields24, useRecordsClient as useRecordsClient42, useTable as useTable23 } from "@ai-matrx/records/react";
15759
+ import { useCallback as useCallback40, useEffect as useEffect44, useState as useState57 } from "react";
15760
+ import { useFields as useFields24, useRecordsClient as useRecordsClient43, useTable as useTable23 } from "@ai-matrx/records/react";
15534
15761
  import { Button as Button51, Separator as Separator12, Skeleton as Skeleton36, cn as cn54 } from "@ai-matrx/design-system";
15535
15762
  import { Fragment as Fragment28, jsx as jsx61, jsxs as jsxs56 } from "react/jsx-runtime";
15536
15763
  function filterInWords(filter, fields) {
@@ -15612,24 +15839,24 @@ function TablePage({
15612
15839
  filter,
15613
15840
  className
15614
15841
  }) {
15615
- const client = useRecordsClient42();
15842
+ const client = useRecordsClient43();
15616
15843
  const table = useTable23(tableId);
15617
15844
  const rights = useTableRights(table.data);
15618
15845
  const pageFields = useFields24(filter ? tableId : null);
15619
15846
  const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
15620
- const organizationId = useRecordsClient42().config.organizationId;
15621
- const [view, setView] = useState56(null);
15847
+ const organizationId = useRecordsClient43().config.organizationId;
15848
+ const [view, setView] = useState57(null);
15622
15849
  const opening = openingRail(activeRecordId);
15623
15850
  const opened = openingView(activeView, activeDashboardId);
15624
- const [asking, setAsking] = useState56(null);
15625
- const [layoutFromLink, setLayoutFromLink] = useState56(opened.layout);
15626
- const [surface, setSurface] = useState56({
15851
+ const [asking, setAsking] = useState57(null);
15852
+ const [layoutFromLink, setLayoutFromLink] = useState57(opened.layout);
15853
+ const [surface, setSurface] = useState57({
15627
15854
  main: opened.main,
15628
15855
  rail: opening.rail
15629
15856
  });
15630
15857
  const { main, rail } = surface;
15631
15858
  const setRail = (next) => setSurface((now) => ({ ...now, rail: next }));
15632
- const [openRecord, setOpenRecord] = useState56(opening.record);
15859
+ const [openRecord, setOpenRecord] = useState57(opening.record);
15633
15860
  const viewVersion = useRecordVersion(view?.id ?? null);
15634
15861
  const shownLayout = layoutFromLink ?? view?.layout ?? "grid";
15635
15862
  const press = (pressed) => {
@@ -15640,12 +15867,12 @@ function TablePage({
15640
15867
  }
15641
15868
  };
15642
15869
  const show = (next) => press({ rail: next });
15643
- useEffect43(() => {
15870
+ useEffect44(() => {
15644
15871
  if (!activeRecordId) return;
15645
15872
  setOpenRecord(activeRecordId);
15646
15873
  setSurface((now) => ({ ...now, rail: "record" }));
15647
15874
  }, [activeRecordId]);
15648
- useEffect43(() => {
15875
+ useEffect44(() => {
15649
15876
  const named = pageViewFromParam(activeView);
15650
15877
  if (named === null) return;
15651
15878
  const isLayout = named !== "dashboards" && named !== "archived";