@ai-matrx/records-ui 0.77.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/CHANGELOG.md +25 -0
- package/dist/index.cjs +263 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +319 -123
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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 {
|
|
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__ */
|
|
4444
|
-
/* @__PURE__ */
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
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
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
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(
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
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__ */
|
|
4577
|
-
|
|
4578
|
-
|
|
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: "
|
|
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
|
|
4807
|
-
SelectContent as
|
|
4913
|
+
Select as Select4,
|
|
4914
|
+
SelectContent as SelectContent4,
|
|
4808
4915
|
SelectGroup,
|
|
4809
|
-
SelectItem as
|
|
4916
|
+
SelectItem as SelectItem4,
|
|
4810
4917
|
SelectLabel,
|
|
4811
|
-
SelectTrigger as
|
|
4812
|
-
SelectValue as
|
|
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
|
-
|
|
5084
|
+
Select4,
|
|
4978
5085
|
{
|
|
4979
5086
|
value: type,
|
|
4980
5087
|
disabled: editing,
|
|
4981
5088
|
onValueChange: (next) => setType(next),
|
|
4982
5089
|
children: [
|
|
4983
|
-
/* @__PURE__ */ jsx18(
|
|
4984
|
-
/* @__PURE__ */ jsx18(
|
|
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(
|
|
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(
|
|
5067
|
-
/* @__PURE__ */ jsx18(
|
|
5068
|
-
/* @__PURE__ */ jsx18(
|
|
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(
|
|
5074
|
-
/* @__PURE__ */ jsx18(
|
|
5075
|
-
/* @__PURE__ */ jsx18(
|
|
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
|
-
|
|
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(
|
|
5104
|
-
/* @__PURE__ */ jsx18(
|
|
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(
|
|
5281
|
-
/* @__PURE__ */ jsx18(
|
|
5282
|
-
/* @__PURE__ */ jsx18(
|
|
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(
|
|
5300
|
-
/* @__PURE__ */ jsx18(
|
|
5301
|
-
/* @__PURE__ */ jsx18(
|
|
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(
|
|
5325
|
-
/* @__PURE__ */ jsx18(
|
|
5326
|
-
/* @__PURE__ */ jsx18(
|
|
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(
|
|
5372
|
-
/* @__PURE__ */ jsx18(
|
|
5373
|
-
/* @__PURE__ */ jsx18(
|
|
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
|
-
|
|
5981
|
-
|
|
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
|
|
6023
|
-
const
|
|
6024
|
-
|
|
6025
|
-
|
|
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
|
-
"
|
|
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
|
|
6159
|
-
|
|
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("
|
|
6162
|
-
|
|
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: () =>
|
|
6166
|
-
|
|
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
|
|
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 =
|
|
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__ */
|
|
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
|
),
|
|
@@ -13350,7 +13471,17 @@ function Drawing({
|
|
|
13350
13471
|
// src/DashboardCanvas.tsx
|
|
13351
13472
|
import { useCallback as useCallback30, useEffect as useEffect34, useMemo as useMemo28, useState as useState47 } from "react";
|
|
13352
13473
|
import { useFields as useFields21, useRecordsClient as useRecordsClient33, useTable as useTable18 } from "@ai-matrx/records/react";
|
|
13353
|
-
import {
|
|
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";
|
|
13354
13485
|
import { Fragment as Fragment21, jsx as jsx50, jsxs as jsxs46 } from "react/jsx-runtime";
|
|
13355
13486
|
function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
13356
13487
|
const client = useRecordsClient33();
|
|
@@ -13424,8 +13555,8 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13424
13555
|
[client, tableId, load, filter]
|
|
13425
13556
|
);
|
|
13426
13557
|
async function create() {
|
|
13427
|
-
const
|
|
13428
|
-
const byField =
|
|
13558
|
+
const all = fields.data ?? [];
|
|
13559
|
+
const byField = offerableFields(all, "choice")[0] ?? all.find((f) => f.type === "text" || f.type === "list");
|
|
13429
13560
|
const by = byField?.key;
|
|
13430
13561
|
await declare2(
|
|
13431
13562
|
{
|
|
@@ -13596,6 +13727,32 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13596
13727
|
children: CHART_KINDS.map((kind) => /* @__PURE__ */ jsx50("option", { value: kind, children: CHART_KIND_LABEL[kind] }, kind))
|
|
13597
13728
|
}
|
|
13598
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,
|
|
13599
13756
|
/* @__PURE__ */ jsx50(
|
|
13600
13757
|
Button42,
|
|
13601
13758
|
{
|
|
@@ -13614,6 +13771,45 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13614
13771
|
] })
|
|
13615
13772
|
] });
|
|
13616
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
|
+
}
|
|
13617
13813
|
|
|
13618
13814
|
// src/FormsPanel.tsx
|
|
13619
13815
|
import { useCallback as useCallback31, useEffect as useEffect35, useState as useState48 } from "react";
|