@ai-matrx/records-ui 0.77.0 → 0.79.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 +54 -0
- package/dist/index.cjs +311 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +367 -130
- 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";
|
|
@@ -4822,6 +4929,9 @@ var ROLLUP_OPERATIONS = ROLLUP_AGG_VALUES.map((value) => ({
|
|
|
4822
4929
|
value,
|
|
4823
4930
|
label: ROLLUP_AGG_LABEL[value]
|
|
4824
4931
|
}));
|
|
4932
|
+
var FORMULA_SEPARATOR_LABEL = "Joined by";
|
|
4933
|
+
var EDITING_A_WORKED_OUT_COLUMN = "This column works out its answer from other columns. Which columns it uses, and what joins them, cannot be changed on this screen yet \u2014 remove it and add it again to change them.";
|
|
4934
|
+
var FORMULA_DEFAULT_SEPARATOR = " \u2014 ";
|
|
4825
4935
|
var FORMULA_OPERATIONS = FORMULA_OP_VALUES.map((value) => ({
|
|
4826
4936
|
value,
|
|
4827
4937
|
label: FORMULA_OP_LABEL[value],
|
|
@@ -4858,8 +4968,9 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
4858
4968
|
const [of, setOf] = useState15(String(field?.config?.["of"] ?? ""));
|
|
4859
4969
|
const [relationTarget, setRelationTarget] = useState15(String(field?.relation_target ?? ""));
|
|
4860
4970
|
const [display, setDisplay] = useState15(field?.display ?? null);
|
|
4861
|
-
const [formulaOp, setFormulaOp] = useState15(
|
|
4862
|
-
const [formulaFields, setFormulaFields] = useState15(
|
|
4971
|
+
const [formulaOp, setFormulaOp] = useState15(() => formulaOpOf(field));
|
|
4972
|
+
const [formulaFields, setFormulaFields] = useState15(() => formulaFieldIdsOf(field));
|
|
4973
|
+
const [formulaSeparator, setFormulaSeparator] = useState15(() => formulaSeparatorOf(field));
|
|
4863
4974
|
const [askingToRemove, setAskingToRemove] = useState15(false);
|
|
4864
4975
|
const [notLanded, setNotLanded] = useState15(false);
|
|
4865
4976
|
const others = useMemo12(
|
|
@@ -4933,7 +5044,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
4933
5044
|
...type === "datetime" ? { kind: withTime ? "datetime" : "date" } : {},
|
|
4934
5045
|
...type === "lookup" ? { via, pick } : {},
|
|
4935
5046
|
...type === "rollup" ? { via, agg, ...agg === "count" ? {} : { of } } : {},
|
|
4936
|
-
...type === "formula" ? { expr: formulaExpression(formulaOp, formulaFields) } : {}
|
|
5047
|
+
...type === "formula" ? { expr: formulaExpression(formulaOp, formulaFields, formulaSeparator) } : {}
|
|
4937
5048
|
};
|
|
4938
5049
|
const written = await mutation.declare({ table_id: tableId, spec });
|
|
4939
5050
|
if (written === null) return;
|
|
@@ -4974,16 +5085,16 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
4974
5085
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
4975
5086
|
/* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "What it holds" }),
|
|
4976
5087
|
/* @__PURE__ */ jsxs14(
|
|
4977
|
-
|
|
5088
|
+
Select4,
|
|
4978
5089
|
{
|
|
4979
5090
|
value: type,
|
|
4980
5091
|
disabled: editing,
|
|
4981
5092
|
onValueChange: (next) => setType(next),
|
|
4982
5093
|
children: [
|
|
4983
|
-
/* @__PURE__ */ jsx18(
|
|
4984
|
-
/* @__PURE__ */ jsx18(
|
|
5094
|
+
/* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": "What this field holds", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
|
|
5095
|
+
/* @__PURE__ */ jsx18(SelectContent4, { children: FIELD_TYPE_GROUPS.map((group) => /* @__PURE__ */ jsxs14(SelectGroup, { children: [
|
|
4985
5096
|
/* @__PURE__ */ jsx18(SelectLabel, { children: group }),
|
|
4986
|
-
FIELD_TYPE_CHOICES.filter((c) => c.group === group).map((c) => /* @__PURE__ */ jsx18(
|
|
5097
|
+
FIELD_TYPE_CHOICES.filter((c) => c.group === group).map((c) => /* @__PURE__ */ jsx18(SelectItem4, { value: c.id, children: c.label }, c.id))
|
|
4987
5098
|
] }, group)) })
|
|
4988
5099
|
]
|
|
4989
5100
|
}
|
|
@@ -4991,6 +5102,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
4991
5102
|
/* @__PURE__ */ jsx18("p", { className: "text-[11px] text-muted-foreground", children: editing ? "What a column holds cannot be changed here \u2014 every value already saved would have to be converted. Add a new column and move the values across." : choice?.explanation ?? "" })
|
|
4992
5103
|
] })
|
|
4993
5104
|
] }),
|
|
5105
|
+
editing && type === "formula" ? /* @__PURE__ */ jsx18("p", { className: "text-[11px] text-muted-foreground", children: EDITING_A_WORKED_OUT_COLUMN }) : null,
|
|
4994
5106
|
!editing || type === "select" || type === "multi_select" || type === "relation" ? /* @__PURE__ */ jsx18(
|
|
4995
5107
|
TypeQuestions,
|
|
4996
5108
|
{
|
|
@@ -5019,6 +5131,8 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5019
5131
|
setFormulaOp,
|
|
5020
5132
|
formulaFields,
|
|
5021
5133
|
setFormulaFields,
|
|
5134
|
+
formulaSeparator,
|
|
5135
|
+
setFormulaSeparator,
|
|
5022
5136
|
relations,
|
|
5023
5137
|
others,
|
|
5024
5138
|
existingOptionsTable: Boolean(field?.options_table_id ?? field?.config?.["options_table_id"])
|
|
@@ -5063,16 +5177,16 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5063
5177
|
/* @__PURE__ */ jsxs14("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
5064
5178
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
5065
5179
|
/* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "Who can see what is in it" }),
|
|
5066
|
-
/* @__PURE__ */ jsxs14(
|
|
5067
|
-
/* @__PURE__ */ jsx18(
|
|
5068
|
-
/* @__PURE__ */ jsx18(
|
|
5180
|
+
/* @__PURE__ */ jsxs14(Select4, { value: sensitivity, onValueChange: setSensitivity, children: [
|
|
5181
|
+
/* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": "Who can see what is in it", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
|
|
5182
|
+
/* @__PURE__ */ jsx18(SelectContent4, { children: SENSITIVITIES.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.value, children: row.label }, row.value)) })
|
|
5069
5183
|
] })
|
|
5070
5184
|
] }),
|
|
5071
5185
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
5072
5186
|
/* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "Can an assistant read it" }),
|
|
5073
|
-
/* @__PURE__ */ jsxs14(
|
|
5074
|
-
/* @__PURE__ */ jsx18(
|
|
5075
|
-
/* @__PURE__ */ jsx18(
|
|
5187
|
+
/* @__PURE__ */ jsxs14(Select4, { value: contextPolicy, onValueChange: setContextPolicy, children: [
|
|
5188
|
+
/* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": "Can an assistant read it", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
|
|
5189
|
+
/* @__PURE__ */ jsx18(SelectContent4, { children: AGENT_VISIBILITY.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.value, children: row.label }, row.value)) })
|
|
5076
5190
|
] })
|
|
5077
5191
|
] })
|
|
5078
5192
|
] }),
|
|
@@ -5095,13 +5209,13 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5095
5209
|
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
5210
|
rules.map((rule, index) => /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1", children: [
|
|
5097
5211
|
/* @__PURE__ */ jsxs14(
|
|
5098
|
-
|
|
5212
|
+
Select4,
|
|
5099
5213
|
{
|
|
5100
5214
|
value: rule.kind,
|
|
5101
5215
|
onValueChange: (kind) => setRules(rules.map((r, i) => i === index ? { ...r, kind } : r)),
|
|
5102
5216
|
children: [
|
|
5103
|
-
/* @__PURE__ */ jsx18(
|
|
5104
|
-
/* @__PURE__ */ jsx18(
|
|
5217
|
+
/* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", className: "w-52", "aria-label": `Check ${index + 1}`, children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
|
|
5218
|
+
/* @__PURE__ */ jsx18(SelectContent4, { children: RULE_KINDS.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.kind, children: row.label }, row.kind)) })
|
|
5105
5219
|
]
|
|
5106
5220
|
}
|
|
5107
5221
|
),
|
|
@@ -5203,10 +5317,30 @@ function startingType(field) {
|
|
|
5203
5317
|
if (fieldTypeChoice(kind)) return kind;
|
|
5204
5318
|
return "text";
|
|
5205
5319
|
}
|
|
5206
|
-
function formulaExpression(op, fieldIds) {
|
|
5320
|
+
function formulaExpression(op, fieldIds, separator) {
|
|
5207
5321
|
const args = fieldIds.filter((id) => id !== "").map((id) => ({ field: id }));
|
|
5208
|
-
|
|
5209
|
-
return { op, args };
|
|
5322
|
+
const joined = op === "concat" && separator !== "" ? { separator } : {};
|
|
5323
|
+
if (args.length === 0) return { op, ...joined, args: [{ const: "" }] };
|
|
5324
|
+
return { op, ...joined, args };
|
|
5325
|
+
}
|
|
5326
|
+
function formulaExprOf(field) {
|
|
5327
|
+
const expr = field?.config?.["expr"];
|
|
5328
|
+
return expr !== null && typeof expr === "object" ? expr : null;
|
|
5329
|
+
}
|
|
5330
|
+
function formulaOpOf(field) {
|
|
5331
|
+
const op = formulaExprOf(field)?.["op"];
|
|
5332
|
+
return typeof op === "string" && op !== "" ? op : "concat";
|
|
5333
|
+
}
|
|
5334
|
+
function formulaFieldIdsOf(field) {
|
|
5335
|
+
const args = formulaExprOf(field)?.["args"];
|
|
5336
|
+
if (!Array.isArray(args)) return [];
|
|
5337
|
+
return args.map((a) => a !== null && typeof a === "object" ? a["field"] : void 0).filter((id) => typeof id === "string" && id !== "");
|
|
5338
|
+
}
|
|
5339
|
+
function formulaSeparatorOf(field) {
|
|
5340
|
+
const expr = formulaExprOf(field);
|
|
5341
|
+
if (expr === null) return FORMULA_DEFAULT_SEPARATOR;
|
|
5342
|
+
const sep = expr["separator"];
|
|
5343
|
+
return typeof sep === "string" ? sep : "";
|
|
5210
5344
|
}
|
|
5211
5345
|
function TypeQuestions(props) {
|
|
5212
5346
|
const { type } = props;
|
|
@@ -5277,9 +5411,9 @@ function TypeQuestions(props) {
|
|
|
5277
5411
|
return /* @__PURE__ */ jsxs14("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
5278
5412
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
5279
5413
|
/* @__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(
|
|
5414
|
+
/* @__PURE__ */ jsxs14(Select4, { value: props.via, onValueChange: props.setVia, children: [
|
|
5415
|
+
/* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": type === "rollup" ? "Across the records in" : "Read it from", children: /* @__PURE__ */ jsx18(SelectValue4, { placeholder: "Choose a column" }) }),
|
|
5416
|
+
/* @__PURE__ */ jsx18(SelectContent4, { children: usable.map((f) => /* @__PURE__ */ jsxs14(SelectItem4, { value: f.key, children: [
|
|
5283
5417
|
fieldName(f),
|
|
5284
5418
|
type === "rollup" && !f.multi ? " \u2014 holds one" : ""
|
|
5285
5419
|
] }, f.id)) })
|
|
@@ -5296,9 +5430,9 @@ function TypeQuestions(props) {
|
|
|
5296
5430
|
}
|
|
5297
5431
|
) : /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
5298
5432
|
/* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "What to work out" }),
|
|
5299
|
-
/* @__PURE__ */ jsxs14(
|
|
5300
|
-
/* @__PURE__ */ jsx18(
|
|
5301
|
-
/* @__PURE__ */ jsx18(
|
|
5433
|
+
/* @__PURE__ */ jsxs14(Select4, { value: props.agg, onValueChange: (v) => props.setAgg(v), children: [
|
|
5434
|
+
/* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": "What to work out", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
|
|
5435
|
+
/* @__PURE__ */ jsx18(SelectContent4, { children: ROLLUP_OPERATIONS.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.value, children: row.label }, row.value)) })
|
|
5302
5436
|
] }),
|
|
5303
5437
|
props.agg !== "count" ? /* @__PURE__ */ jsx18(
|
|
5304
5438
|
ValueOnTheOtherSide,
|
|
@@ -5321,9 +5455,9 @@ function TypeQuestions(props) {
|
|
|
5321
5455
|
return /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-2", children: [
|
|
5322
5456
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
5323
5457
|
/* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: "What to do" }),
|
|
5324
|
-
/* @__PURE__ */ jsxs14(
|
|
5325
|
-
/* @__PURE__ */ jsx18(
|
|
5326
|
-
/* @__PURE__ */ jsx18(
|
|
5458
|
+
/* @__PURE__ */ jsxs14(Select4, { value: props.formulaOp, onValueChange: props.setFormulaOp, children: [
|
|
5459
|
+
/* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", className: "max-w-[18rem]", "aria-label": "What to do", children: /* @__PURE__ */ jsx18(SelectValue4, {}) }),
|
|
5460
|
+
/* @__PURE__ */ jsx18(SelectContent4, { children: FORMULA_OPERATIONS.map((row) => /* @__PURE__ */ jsx18(SelectItem4, { value: row.value, children: row.label }, row.value)) })
|
|
5327
5461
|
] })
|
|
5328
5462
|
] }),
|
|
5329
5463
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
@@ -5345,7 +5479,21 @@ function TypeQuestions(props) {
|
|
|
5345
5479
|
);
|
|
5346
5480
|
}) }),
|
|
5347
5481
|
/* @__PURE__ */ jsx18("p", { className: "text-[11px] text-muted-foreground", children: "In the order you pick them. Columns are remembered by which column they are, so renaming one never breaks this." })
|
|
5348
|
-
] })
|
|
5482
|
+
] }),
|
|
5483
|
+
props.formulaOp === "concat" ? /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
5484
|
+
/* @__PURE__ */ jsx18(Label4, { className: "text-xs", children: FORMULA_SEPARATOR_LABEL }),
|
|
5485
|
+
/* @__PURE__ */ jsx18(
|
|
5486
|
+
BasicInput5,
|
|
5487
|
+
{
|
|
5488
|
+
value: props.formulaSeparator,
|
|
5489
|
+
"aria-label": FORMULA_SEPARATOR_LABEL,
|
|
5490
|
+
placeholder: "\u2014",
|
|
5491
|
+
className: "max-w-[10rem]",
|
|
5492
|
+
onChange: (e) => props.setFormulaSeparator(e.target.value)
|
|
5493
|
+
}
|
|
5494
|
+
),
|
|
5495
|
+
/* @__PURE__ */ jsx18("p", { className: "text-[11px] text-muted-foreground", children: "What goes between the columns. Leave it empty to run them straight together." })
|
|
5496
|
+
] }) : null
|
|
5349
5497
|
] });
|
|
5350
5498
|
}
|
|
5351
5499
|
return null;
|
|
@@ -5368,9 +5516,9 @@ function ValueOnTheOtherSide({
|
|
|
5368
5516
|
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
5517
|
fieldName(relation),
|
|
5370
5518
|
" points at a table that has no columns to read yet. Add one there first."
|
|
5371
|
-
] }) : /* @__PURE__ */ jsxs14(
|
|
5372
|
-
/* @__PURE__ */ jsx18(
|
|
5373
|
-
/* @__PURE__ */ jsx18(
|
|
5519
|
+
] }) : /* @__PURE__ */ jsxs14(Select4, { value, onValueChange: onChange, children: [
|
|
5520
|
+
/* @__PURE__ */ jsx18(SelectTrigger4, { size: "sm", "aria-label": label, children: /* @__PURE__ */ jsx18(SelectValue4, { placeholder: "Choose a column" }) }),
|
|
5521
|
+
/* @__PURE__ */ jsx18(SelectContent4, { children: offered.map((column) => /* @__PURE__ */ jsx18(SelectItem4, { value: column.key, children: column.name }, column.key)) })
|
|
5374
5522
|
] });
|
|
5375
5523
|
if (compact) return /* @__PURE__ */ jsx18("div", { className: "mt-1 flex flex-col gap-1", children: body });
|
|
5376
5524
|
return /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
@@ -5977,8 +6125,8 @@ import { useState as useState18 } from "react";
|
|
|
5977
6125
|
import {
|
|
5978
6126
|
useFieldMutation as useFieldMutation2,
|
|
5979
6127
|
useFields as useFields8,
|
|
5980
|
-
|
|
5981
|
-
|
|
6128
|
+
useTable as useTable6,
|
|
6129
|
+
useTableArchive
|
|
5982
6130
|
} from "@ai-matrx/records/react";
|
|
5983
6131
|
import { Badge as Badge6, Button as Button16, Separator as Separator6, cn as cn16 } from "@ai-matrx/design-system";
|
|
5984
6132
|
import { Fragment as Fragment10, jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
@@ -5993,8 +6141,8 @@ function TableSettings({
|
|
|
5993
6141
|
const table = useTable6(tableId);
|
|
5994
6142
|
const fields = useFields8(tableId);
|
|
5995
6143
|
const rights = useTableRights(table.data);
|
|
5996
|
-
const mutation = useRecordMutation();
|
|
5997
6144
|
const shape = useFieldMutation2();
|
|
6145
|
+
const archive = useTableArchive(tableId);
|
|
5998
6146
|
const [editing, setEditing] = useState18(null);
|
|
5999
6147
|
const [askingToDelete, setAskingToDelete] = useState18(false);
|
|
6000
6148
|
const [askingToRemove, setAskingToRemove] = useState18(null);
|
|
@@ -6019,10 +6167,16 @@ function TableSettings({
|
|
|
6019
6167
|
table.reload();
|
|
6020
6168
|
}
|
|
6021
6169
|
};
|
|
6022
|
-
const
|
|
6023
|
-
const
|
|
6024
|
-
|
|
6025
|
-
|
|
6170
|
+
const startArchive = async () => {
|
|
6171
|
+
const finished = await archive.start();
|
|
6172
|
+
if (finished?.done && finished.table_archived) {
|
|
6173
|
+
setAskingToDelete(false);
|
|
6174
|
+
onDeleted?.();
|
|
6175
|
+
}
|
|
6176
|
+
};
|
|
6177
|
+
const askToArchive = async () => {
|
|
6178
|
+
setAskingToDelete(true);
|
|
6179
|
+
await archive.preview();
|
|
6026
6180
|
};
|
|
6027
6181
|
if (enriching) {
|
|
6028
6182
|
return /* @__PURE__ */ jsxs17("div", { className, children: [
|
|
@@ -6058,7 +6212,6 @@ function TableSettings({
|
|
|
6058
6212
|
/* @__PURE__ */ jsx21("span", { className: "text-muted-foreground", children: rows.length }),
|
|
6059
6213
|
/* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "outline", className: "ml-auto", onClick: () => setEditing("new"), children: "Add a field" })
|
|
6060
6214
|
] }),
|
|
6061
|
-
mutation.error ? /* @__PURE__ */ jsx21(RefusalNotice, { error: mutation.error }) : null,
|
|
6062
6215
|
shape.error ? /* @__PURE__ */ jsx21(RefusalNotice, { error: shape.error }) : null,
|
|
6063
6216
|
askingToRemove ? /* @__PURE__ */ jsxs17("div", { className: "flex flex-wrap items-center gap-2 rounded border border-destructive/40 px-2 py-1.5", children: [
|
|
6064
6217
|
/* @__PURE__ */ jsxs17("span", { className: "text-destructive", children: [
|
|
@@ -6149,21 +6302,32 @@ function TableSettings({
|
|
|
6149
6302
|
/* @__PURE__ */ jsx21("span", { className: "font-medium", children: "This table" }),
|
|
6150
6303
|
askingToDelete ? /* @__PURE__ */ jsxs17(Fragment10, { children: [
|
|
6151
6304
|
/* @__PURE__ */ jsxs17("p", { className: "text-muted-foreground", children: [
|
|
6152
|
-
"
|
|
6305
|
+
"Archiving ",
|
|
6153
6306
|
table.data ? tableName(table.data) : "this table",
|
|
6154
6307
|
" takes it out of everyone's list, with its ",
|
|
6155
6308
|
rows.length,
|
|
6156
6309
|
" field",
|
|
6157
6310
|
rows.length === 1 ? "" : "s",
|
|
6158
|
-
" and
|
|
6159
|
-
|
|
6311
|
+
" and",
|
|
6312
|
+
" ",
|
|
6313
|
+
archive.progress ? `its ${archive.progress.remaining.toLocaleString()} record${archive.progress.remaining === 1 ? "" : "s"}` : "every record in it",
|
|
6314
|
+
".",
|
|
6315
|
+
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
6316
|
] }),
|
|
6161
|
-
/* @__PURE__ */ jsxs17("
|
|
6162
|
-
|
|
6317
|
+
archive.progress && (archive.running || archive.passes > 0) ? /* @__PURE__ */ jsxs17("p", { className: archive.error ? "text-destructive" : "text-muted-foreground", children: [
|
|
6318
|
+
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.`,
|
|
6319
|
+
archive.slowedDown ? " The store is busy, so it is taking smaller passes \u2014 it will still finish." : ""
|
|
6320
|
+
] }) : null,
|
|
6321
|
+
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,
|
|
6322
|
+
/* @__PURE__ */ jsx21("div", { className: "flex gap-1", children: archive.running ? /* @__PURE__ */ jsxs17(Fragment10, { children: [
|
|
6323
|
+
/* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "destructive", disabled: true, children: "Archiving\u2026" }),
|
|
6324
|
+
/* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "ghost", onClick: () => archive.stop(), children: "Stop" })
|
|
6325
|
+
] }) : /* @__PURE__ */ jsxs17(Fragment10, { children: [
|
|
6326
|
+
/* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "destructive", onClick: () => void startArchive(), children: archive.interrupted ? "Carry on archiving" : "Archive this table" }),
|
|
6163
6327
|
/* @__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
|
-
|
|
6328
|
+
] }) })
|
|
6329
|
+
] }) : /* @__PURE__ */ jsx21("div", { children: /* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "outline", onClick: () => void askToArchive(), children: "Archive this table" }) }),
|
|
6330
|
+
archive.error ? /* @__PURE__ */ jsx21(RefusalNotice, { error: archive.error }) : null
|
|
6167
6331
|
] })
|
|
6168
6332
|
] });
|
|
6169
6333
|
}
|
|
@@ -6305,7 +6469,7 @@ import { useEffect as useEffect11, useMemo as useMemo16, useState as useState21
|
|
|
6305
6469
|
import {
|
|
6306
6470
|
useFields as useFields9,
|
|
6307
6471
|
useRecord,
|
|
6308
|
-
useRecordMutation
|
|
6472
|
+
useRecordMutation
|
|
6309
6473
|
} from "@ai-matrx/records/react";
|
|
6310
6474
|
import { predictWriteRefusals } from "@ai-matrx/records/core";
|
|
6311
6475
|
import { Button as Button17, Separator as Separator7, cn as cn18 } from "@ai-matrx/design-system";
|
|
@@ -6494,7 +6658,7 @@ function RecordForm({
|
|
|
6494
6658
|
const host = useRecordsUi();
|
|
6495
6659
|
const fields = useFields9(tableId, recordType);
|
|
6496
6660
|
const existing = useRecord(recordId ?? null);
|
|
6497
|
-
const mutation =
|
|
6661
|
+
const mutation = useRecordMutation();
|
|
6498
6662
|
const [draft, setDraft] = useState21({});
|
|
6499
6663
|
const [touched, setTouched] = useState21(false);
|
|
6500
6664
|
const loaded = useRecordVersion(recordId ?? null);
|
|
@@ -7471,6 +7635,7 @@ function ViewSwitcher({
|
|
|
7471
7635
|
)
|
|
7472
7636
|
] });
|
|
7473
7637
|
}
|
|
7638
|
+
var UNUSUAL_FIELD_NOTE = "an unusual column to use here";
|
|
7474
7639
|
function offerableFields(all, want) {
|
|
7475
7640
|
return all.filter((field) => {
|
|
7476
7641
|
const kind = editorKindFor(field);
|
|
@@ -7606,10 +7771,7 @@ function FieldPicker({
|
|
|
7606
7771
|
children: [
|
|
7607
7772
|
/* @__PURE__ */ jsx27("option", { value: "", children: "Choose a field" }),
|
|
7608
7773
|
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
|
|
7774
|
+
unusual ? /* @__PURE__ */ jsx27("option", { value: unusual.key, children: `${fieldName(unusual)} \u2014 ${UNUSUAL_FIELD_NOTE}` }, unusual.key) : null
|
|
7613
7775
|
]
|
|
7614
7776
|
}
|
|
7615
7777
|
),
|
|
@@ -13350,7 +13512,17 @@ function Drawing({
|
|
|
13350
13512
|
// src/DashboardCanvas.tsx
|
|
13351
13513
|
import { useCallback as useCallback30, useEffect as useEffect34, useMemo as useMemo28, useState as useState47 } from "react";
|
|
13352
13514
|
import { useFields as useFields21, useRecordsClient as useRecordsClient33, useTable as useTable18 } from "@ai-matrx/records/react";
|
|
13353
|
-
import {
|
|
13515
|
+
import {
|
|
13516
|
+
BasicInput as BasicInput16,
|
|
13517
|
+
Button as Button42,
|
|
13518
|
+
Select as Select5,
|
|
13519
|
+
SelectContent as SelectContent5,
|
|
13520
|
+
SelectItem as SelectItem5,
|
|
13521
|
+
SelectTrigger as SelectTrigger5,
|
|
13522
|
+
SelectValue as SelectValue5,
|
|
13523
|
+
Skeleton as Skeleton26,
|
|
13524
|
+
cn as cn44
|
|
13525
|
+
} from "@ai-matrx/design-system";
|
|
13354
13526
|
import { Fragment as Fragment21, jsx as jsx50, jsxs as jsxs46 } from "react/jsx-runtime";
|
|
13355
13527
|
function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
13356
13528
|
const client = useRecordsClient33();
|
|
@@ -13424,8 +13596,8 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13424
13596
|
[client, tableId, load, filter]
|
|
13425
13597
|
);
|
|
13426
13598
|
async function create() {
|
|
13427
|
-
const
|
|
13428
|
-
const byField =
|
|
13599
|
+
const all = fields.data ?? [];
|
|
13600
|
+
const byField = offerableFields(all, "choice")[0] ?? all.find((f) => f.type === "text" || f.type === "list");
|
|
13429
13601
|
const by = byField?.key;
|
|
13430
13602
|
await declare2(
|
|
13431
13603
|
{
|
|
@@ -13596,6 +13768,32 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13596
13768
|
children: CHART_KINDS.map((kind) => /* @__PURE__ */ jsx50("option", { value: kind, children: CHART_KIND_LABEL[kind] }, kind))
|
|
13597
13769
|
}
|
|
13598
13770
|
),
|
|
13771
|
+
(board.blocks[index].groupBy ?? []).length > 0 ? /* @__PURE__ */ jsx50(
|
|
13772
|
+
GroupingPicker,
|
|
13773
|
+
{
|
|
13774
|
+
block: board.blocks[index],
|
|
13775
|
+
fields: fields.data ?? [],
|
|
13776
|
+
onPick: (key) => void declare2(
|
|
13777
|
+
board,
|
|
13778
|
+
board.blocks.map((b, i) => {
|
|
13779
|
+
if (i !== index) return b;
|
|
13780
|
+
const picked = (fields.data ?? []).find((f) => f.key === key);
|
|
13781
|
+
const wasNamedAfterItsColumn = (fields.data ?? []).some(
|
|
13782
|
+
(f) => b.title === `By ${fieldName(f)}`
|
|
13783
|
+
);
|
|
13784
|
+
return {
|
|
13785
|
+
...b,
|
|
13786
|
+
groupBy: [key],
|
|
13787
|
+
// A block titled after the column it grouped by
|
|
13788
|
+
// must not keep saying "By Job Number" once it
|
|
13789
|
+
// groups by Status. A title somebody wrote
|
|
13790
|
+
// themselves is left alone.
|
|
13791
|
+
...wasNamedAfterItsColumn && picked ? { title: `By ${fieldName(picked)}` } : {}
|
|
13792
|
+
};
|
|
13793
|
+
})
|
|
13794
|
+
)
|
|
13795
|
+
}
|
|
13796
|
+
) : null,
|
|
13599
13797
|
/* @__PURE__ */ jsx50(
|
|
13600
13798
|
Button42,
|
|
13601
13799
|
{
|
|
@@ -13614,6 +13812,45 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13614
13812
|
] })
|
|
13615
13813
|
] });
|
|
13616
13814
|
}
|
|
13815
|
+
function groupingChoices(all) {
|
|
13816
|
+
const once = [];
|
|
13817
|
+
for (const field of all) if (!once.some((f) => f.key === field.key)) once.push(field);
|
|
13818
|
+
const sensible = offerableFields(once, "choice");
|
|
13819
|
+
const isSensible = (field) => sensible.some((s) => s.key === field.key);
|
|
13820
|
+
return [
|
|
13821
|
+
...sensible.map((field) => ({ field, unusual: false })),
|
|
13822
|
+
...once.filter((field) => !isSensible(field)).map((field) => ({ field, unusual: true }))
|
|
13823
|
+
];
|
|
13824
|
+
}
|
|
13825
|
+
function GroupingPicker({
|
|
13826
|
+
block,
|
|
13827
|
+
fields,
|
|
13828
|
+
onPick
|
|
13829
|
+
}) {
|
|
13830
|
+
const choices = groupingChoices(fields);
|
|
13831
|
+
const current = block.groupBy?.[0] ?? "";
|
|
13832
|
+
if (choices.length === 0) {
|
|
13833
|
+
return /* @__PURE__ */ jsx50("span", { className: "text-muted-foreground", children: "This table has no columns yet, so there is nothing to group this block by." });
|
|
13834
|
+
}
|
|
13835
|
+
const known = choices.some((c) => c.field.key === current);
|
|
13836
|
+
return /* @__PURE__ */ jsxs46(Fragment21, { children: [
|
|
13837
|
+
/* @__PURE__ */ jsx50("label", { className: "text-muted-foreground", htmlFor: `block-group-${block.title}`, children: "Group by" }),
|
|
13838
|
+
/* @__PURE__ */ jsxs46(Select5, { value: known ? current : "", onValueChange: onPick, children: [
|
|
13839
|
+
/* @__PURE__ */ jsx50(
|
|
13840
|
+
SelectTrigger5,
|
|
13841
|
+
{
|
|
13842
|
+
size: "sm",
|
|
13843
|
+
id: `block-group-${block.title}`,
|
|
13844
|
+
"aria-label": `Which column ${block.title} is grouped by`,
|
|
13845
|
+
className: "h-7 min-w-[10rem] text-xs",
|
|
13846
|
+
children: /* @__PURE__ */ jsx50(SelectValue5, { placeholder: "Choose a column" })
|
|
13847
|
+
}
|
|
13848
|
+
),
|
|
13849
|
+
/* @__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)) })
|
|
13850
|
+
] }),
|
|
13851
|
+
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." })
|
|
13852
|
+
] });
|
|
13853
|
+
}
|
|
13617
13854
|
|
|
13618
13855
|
// src/FormsPanel.tsx
|
|
13619
13856
|
import { useCallback as useCallback31, useEffect as useEffect35, useState as useState48 } from "react";
|