@ai-matrx/records-ui 0.51.0 → 0.52.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 +45 -0
- package/dist/index.cjs +1366 -1017
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +1334 -985
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -9398,28 +9398,325 @@ function Invite({ card, onInvited }) {
|
|
|
9398
9398
|
] });
|
|
9399
9399
|
}
|
|
9400
9400
|
|
|
9401
|
-
// src/
|
|
9402
|
-
import { useCallback as useCallback19, useEffect as useEffect22, useState as useState32 } from "react";
|
|
9401
|
+
// src/DigestScheduler.tsx
|
|
9402
|
+
import { useCallback as useCallback19, useEffect as useEffect22, useMemo as useMemo23, useState as useState32 } from "react";
|
|
9403
9403
|
import { useRecordsClient as useRecordsClient26 } from "@ai-matrx/records/react";
|
|
9404
|
-
import {
|
|
9405
|
-
import { jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
9404
|
+
import { BasicInput as BasicInput10, Button as Button29, Checkbox as Checkbox5, Label as Label5, Separator as Separator11, Skeleton as Skeleton16, cn as cn30 } from "@ai-matrx/design-system";
|
|
9405
|
+
import { Fragment as Fragment16, jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
9406
|
+
var WEEKDAYS = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
|
9407
|
+
var CADENCE_WORDS = {
|
|
9408
|
+
instant: "the moment something arrives",
|
|
9409
|
+
hourly: "every hour",
|
|
9410
|
+
daily: "every day",
|
|
9411
|
+
weekly: "every week"
|
|
9412
|
+
};
|
|
9413
|
+
var CHANNEL_WORDS = {
|
|
9414
|
+
in_app: "in the app",
|
|
9415
|
+
email: "by email \u2014 only if an address is on their account",
|
|
9416
|
+
sms: "by text \u2014 only if a number is on their account"
|
|
9417
|
+
};
|
|
9418
|
+
function DigestScheduler({
|
|
9419
|
+
tableId,
|
|
9420
|
+
savedViewId,
|
|
9421
|
+
subjectName,
|
|
9422
|
+
filters,
|
|
9423
|
+
onScheduled,
|
|
9424
|
+
onClose,
|
|
9425
|
+
className
|
|
9426
|
+
}) {
|
|
9427
|
+
const client = useRecordsClient26();
|
|
9428
|
+
const host = useRecordsUi();
|
|
9429
|
+
const [views, setViews] = useState32(null);
|
|
9430
|
+
const [cadences, setCadences] = useState32([]);
|
|
9431
|
+
const [members, setMembers] = useState32(null);
|
|
9432
|
+
const [error, setError] = useState32(null);
|
|
9433
|
+
const [viewId, setViewId] = useState32(savedViewId ?? "");
|
|
9434
|
+
const [name, setName] = useState32(subjectName?.trim() ? `${subjectName.trim()} summary` : "");
|
|
9435
|
+
const [cadence, setCadence] = useState32("weekly");
|
|
9436
|
+
const [weekday, setWeekday] = useState32("monday");
|
|
9437
|
+
const [time, setTime] = useState32("08:00");
|
|
9438
|
+
const [channel, setChannel] = useState32("in_app");
|
|
9439
|
+
const [quiet, setQuiet] = useState32(false);
|
|
9440
|
+
const [quietFrom, setQuietFrom] = useState32("22:00");
|
|
9441
|
+
const [quietTo, setQuietTo] = useState32("07:00");
|
|
9442
|
+
const [recipients, setRecipients] = useState32([]);
|
|
9443
|
+
const [saving, setSaving] = useState32(false);
|
|
9444
|
+
const [outcomes, setOutcomes] = useState32(null);
|
|
9445
|
+
const [preview, setPreview] = useState32(null);
|
|
9446
|
+
const [previewing, setPreviewing] = useState32(false);
|
|
9447
|
+
const load = useCallback19(async () => {
|
|
9448
|
+
const [saved, offered] = await Promise.all([
|
|
9449
|
+
// The store's own door onto `platform.saved_view`, narrowed in SQL to
|
|
9450
|
+
// Tables this person can already open — never a grant on the table behind it.
|
|
9451
|
+
client.views({ table_id: tableId }),
|
|
9452
|
+
// The cadences are ASKED, never typed into this file, so a picker can
|
|
9453
|
+
// never offer a cadence the digest runner does not honour.
|
|
9454
|
+
client.subscriptionCadences()
|
|
9455
|
+
]);
|
|
9456
|
+
if (!saved.ok) setError(saved.error);
|
|
9457
|
+
else setViews(saved.data);
|
|
9458
|
+
if (offered.ok) setCadences(offered.data.filter((c) => c !== "instant"));
|
|
9459
|
+
if (host.members) {
|
|
9460
|
+
const who = await host.members();
|
|
9461
|
+
setMembers(who.map((m) => ({ userId: m.userId, name: m.name ?? m.email ?? m.userId })));
|
|
9462
|
+
} else {
|
|
9463
|
+
setMembers([]);
|
|
9464
|
+
}
|
|
9465
|
+
}, [client, tableId, host]);
|
|
9466
|
+
useEffect22(() => {
|
|
9467
|
+
void load();
|
|
9468
|
+
}, [load]);
|
|
9469
|
+
const schedule = useMemo23(() => {
|
|
9470
|
+
if (cadence === "weekly") return `${weekday} ${time}`;
|
|
9471
|
+
if (cadence === "daily") return time;
|
|
9472
|
+
return null;
|
|
9473
|
+
}, [cadence, weekday, time]);
|
|
9474
|
+
const quietHours = useMemo23(
|
|
9475
|
+
() => quiet ? { start: quietFrom, end: quietTo } : null,
|
|
9476
|
+
[quiet, quietFrom, quietTo]
|
|
9477
|
+
);
|
|
9478
|
+
async function scheduleIt() {
|
|
9479
|
+
setSaving(true);
|
|
9480
|
+
setError(null);
|
|
9481
|
+
setOutcomes(null);
|
|
9482
|
+
let subject = viewId;
|
|
9483
|
+
if (subject === "" || subject === "new") {
|
|
9484
|
+
const declared = await client.viewDeclare({
|
|
9485
|
+
table_id: tableId,
|
|
9486
|
+
spec: {
|
|
9487
|
+
name: subjectName?.trim() ? subjectName.trim() : name.trim() || "Weekly summary",
|
|
9488
|
+
filters: filters ?? {}
|
|
9489
|
+
}
|
|
9490
|
+
});
|
|
9491
|
+
if (!declared.ok) {
|
|
9492
|
+
setSaving(false);
|
|
9493
|
+
setError(declared.error);
|
|
9494
|
+
return;
|
|
9495
|
+
}
|
|
9496
|
+
subject = declared.data;
|
|
9497
|
+
setViewId(declared.data);
|
|
9498
|
+
}
|
|
9499
|
+
const who = recipients.length > 0 ? recipients : [""];
|
|
9500
|
+
const done = [];
|
|
9501
|
+
for (const userId of who) {
|
|
9502
|
+
const label = members?.find((m) => m.userId === userId)?.name ?? (userId === "" ? "you" : userId);
|
|
9503
|
+
const written = await client.subscriptionDeclare({
|
|
9504
|
+
table_id: tableId,
|
|
9505
|
+
spec: {
|
|
9506
|
+
name: name.trim() === "" ? "Weekly summary" : name.trim(),
|
|
9507
|
+
saved_view_id: subject,
|
|
9508
|
+
cadence,
|
|
9509
|
+
schedule,
|
|
9510
|
+
channel,
|
|
9511
|
+
quiet_hours: quietHours,
|
|
9512
|
+
...userId === "" ? {} : { recipient_user_id: userId }
|
|
9513
|
+
}
|
|
9514
|
+
});
|
|
9515
|
+
if (written.ok) done.push({ userId, who: label, ruleId: written.data });
|
|
9516
|
+
else done.push({ userId, who: label, refused: written.error });
|
|
9517
|
+
}
|
|
9518
|
+
setSaving(false);
|
|
9519
|
+
setOutcomes(done);
|
|
9520
|
+
if (done.some((d) => d.ruleId)) onScheduled?.();
|
|
9521
|
+
}
|
|
9522
|
+
async function showOne(ruleId) {
|
|
9523
|
+
setPreviewing(true);
|
|
9524
|
+
const answered = await client.subscriptionPreview({ rule_id: ruleId });
|
|
9525
|
+
setPreviewing(false);
|
|
9526
|
+
if (!answered.ok) {
|
|
9527
|
+
setError(answered.error);
|
|
9528
|
+
return;
|
|
9529
|
+
}
|
|
9530
|
+
setPreview(answered.data);
|
|
9531
|
+
}
|
|
9532
|
+
if (views === null && !error) return /* @__PURE__ */ jsx36(Skeleton16, { className: cn30("h-56 w-full", className) });
|
|
9533
|
+
return /* @__PURE__ */ jsxs32("section", { className: cn30("flex flex-col gap-3", className), children: [
|
|
9534
|
+
/* @__PURE__ */ jsxs32("header", { className: "flex items-center gap-2", children: [
|
|
9535
|
+
/* @__PURE__ */ jsx36("h3", { className: "text-sm font-medium", children: "Send this on a schedule" }),
|
|
9536
|
+
/* @__PURE__ */ jsx36("div", { className: "flex-1" }),
|
|
9537
|
+
/* @__PURE__ */ jsx36(Button29, { size: "sm", disabled: saving, onClick: () => void scheduleIt(), children: saving ? "Scheduling\u2026" : "Schedule it" }),
|
|
9538
|
+
onClose ? /* @__PURE__ */ jsx36(Button29, { size: "sm", variant: "ghost", onClick: onClose, children: "Close" }) : null
|
|
9539
|
+
] }),
|
|
9540
|
+
error ? /* @__PURE__ */ jsx36(RefusalNotice, { error }) : null,
|
|
9541
|
+
/* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
|
|
9542
|
+
/* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "What to call it" }),
|
|
9543
|
+
/* @__PURE__ */ jsx36(
|
|
9544
|
+
BasicInput10,
|
|
9545
|
+
{
|
|
9546
|
+
value: name,
|
|
9547
|
+
placeholder: "Monday donor summary",
|
|
9548
|
+
onChange: (e) => setName(e.target.value)
|
|
9549
|
+
}
|
|
9550
|
+
)
|
|
9551
|
+
] }),
|
|
9552
|
+
savedViewId ? null : /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
|
|
9553
|
+
/* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "What it summarises" }),
|
|
9554
|
+
/* @__PURE__ */ jsxs32(
|
|
9555
|
+
"select",
|
|
9556
|
+
{
|
|
9557
|
+
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
9558
|
+
value: viewId,
|
|
9559
|
+
onChange: (e) => setViewId(e.target.value),
|
|
9560
|
+
children: [
|
|
9561
|
+
/* @__PURE__ */ jsx36("option", { value: "new", children: subjectName?.trim() ? `What I am looking at now (${subjectName.trim()})` : "What I am looking at now" }),
|
|
9562
|
+
(views ?? []).map((v) => /* @__PURE__ */ jsx36("option", { value: v.view_id, children: v.name }, v.view_id))
|
|
9563
|
+
]
|
|
9564
|
+
}
|
|
9565
|
+
),
|
|
9566
|
+
/* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children: "The first choice saves what is on screen as a view, so the summary and the screen stay the same thing." })
|
|
9567
|
+
] }),
|
|
9568
|
+
/* @__PURE__ */ jsxs32("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
9569
|
+
/* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
|
|
9570
|
+
/* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "How often" }),
|
|
9571
|
+
/* @__PURE__ */ jsx36(
|
|
9572
|
+
"select",
|
|
9573
|
+
{
|
|
9574
|
+
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
9575
|
+
value: cadence,
|
|
9576
|
+
onChange: (e) => setCadence(e.target.value),
|
|
9577
|
+
children: (cadences.length > 0 ? cadences : ["hourly", "daily", "weekly"]).map((c) => /* @__PURE__ */ jsx36("option", { value: c, children: CADENCE_WORDS[c] ?? c }, c))
|
|
9578
|
+
}
|
|
9579
|
+
)
|
|
9580
|
+
] }),
|
|
9581
|
+
cadence === "weekly" ? /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
|
|
9582
|
+
/* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "Which day" }),
|
|
9583
|
+
/* @__PURE__ */ jsx36(
|
|
9584
|
+
"select",
|
|
9585
|
+
{
|
|
9586
|
+
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
9587
|
+
value: weekday,
|
|
9588
|
+
onChange: (e) => setWeekday(e.target.value),
|
|
9589
|
+
children: WEEKDAYS.map((d) => /* @__PURE__ */ jsx36("option", { value: d, children: d[0].toUpperCase() + d.slice(1) }, d))
|
|
9590
|
+
}
|
|
9591
|
+
)
|
|
9592
|
+
] }) : null,
|
|
9593
|
+
cadence === "weekly" || cadence === "daily" ? /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
|
|
9594
|
+
/* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "At" }),
|
|
9595
|
+
/* @__PURE__ */ jsx36(BasicInput10, { type: "time", value: time, onChange: (e) => setTime(e.target.value) })
|
|
9596
|
+
] }) : null,
|
|
9597
|
+
/* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
|
|
9598
|
+
/* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "Where it arrives" }),
|
|
9599
|
+
/* @__PURE__ */ jsx36(
|
|
9600
|
+
"select",
|
|
9601
|
+
{
|
|
9602
|
+
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
9603
|
+
value: channel,
|
|
9604
|
+
onChange: (e) => setChannel(e.target.value),
|
|
9605
|
+
children: Object.entries(CHANNEL_WORDS).map(([key, words]) => /* @__PURE__ */ jsx36("option", { value: key, children: words }, key))
|
|
9606
|
+
}
|
|
9607
|
+
)
|
|
9608
|
+
] })
|
|
9609
|
+
] }),
|
|
9610
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex flex-col gap-1.5", children: [
|
|
9611
|
+
/* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "Who to send it to" }),
|
|
9612
|
+
members === null ? /* @__PURE__ */ jsx36(Skeleton16, { className: "h-8 w-full" }) : members.length === 0 ? /* @__PURE__ */ jsx36("p", { className: "text-xs text-muted-foreground", children: "Nobody else is offered here, so this will be addressed to you. Who the people are is the platform's answer and this screen reaches it through its host's `members` port, which is not bound here." }) : /* @__PURE__ */ jsxs32(Fragment16, { children: [
|
|
9613
|
+
/* @__PURE__ */ jsx36("div", { className: "flex flex-wrap gap-x-4 gap-y-1.5", children: members.map((m) => /* @__PURE__ */ jsxs32("label", { className: "flex items-center gap-1.5 text-xs", children: [
|
|
9614
|
+
/* @__PURE__ */ jsx36(
|
|
9615
|
+
Checkbox5,
|
|
9616
|
+
{
|
|
9617
|
+
checked: recipients.includes(m.userId),
|
|
9618
|
+
onCheckedChange: (on) => setRecipients(
|
|
9619
|
+
(prev) => on ? [...prev, m.userId] : prev.filter((u) => u !== m.userId)
|
|
9620
|
+
)
|
|
9621
|
+
}
|
|
9622
|
+
),
|
|
9623
|
+
m.name
|
|
9624
|
+
] }, m.userId)) }),
|
|
9625
|
+
/* @__PURE__ */ jsx36("p", { className: "text-xs text-muted-foreground", children: recipients.length === 0 ? "Nobody picked \u2014 it will be addressed to you." : `${recipients.length} ${recipients.length === 1 ? "person" : "people"}, and each one's summary holds only what that person may see.` })
|
|
9626
|
+
] })
|
|
9627
|
+
] }),
|
|
9628
|
+
/* @__PURE__ */ jsxs32("label", { className: "flex items-center gap-1.5 text-xs", children: [
|
|
9629
|
+
/* @__PURE__ */ jsx36(Checkbox5, { checked: quiet, onCheckedChange: (on) => setQuiet(Boolean(on)) }),
|
|
9630
|
+
"Not between certain hours"
|
|
9631
|
+
] }),
|
|
9632
|
+
quiet ? /* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-2", children: [
|
|
9633
|
+
/* @__PURE__ */ jsx36(
|
|
9634
|
+
BasicInput10,
|
|
9635
|
+
{
|
|
9636
|
+
type: "time",
|
|
9637
|
+
className: "h-8 w-28",
|
|
9638
|
+
value: quietFrom,
|
|
9639
|
+
onChange: (e) => setQuietFrom(e.target.value)
|
|
9640
|
+
}
|
|
9641
|
+
),
|
|
9642
|
+
/* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
9643
|
+
/* @__PURE__ */ jsx36(
|
|
9644
|
+
BasicInput10,
|
|
9645
|
+
{
|
|
9646
|
+
type: "time",
|
|
9647
|
+
className: "h-8 w-28",
|
|
9648
|
+
value: quietTo,
|
|
9649
|
+
onChange: (e) => setQuietTo(e.target.value)
|
|
9650
|
+
}
|
|
9651
|
+
),
|
|
9652
|
+
/* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children: "A send inside these hours waits until they end \u2014 it is never dropped." })
|
|
9653
|
+
] }) : null,
|
|
9654
|
+
outcomes ? /* @__PURE__ */ jsxs32(Fragment16, { children: [
|
|
9655
|
+
/* @__PURE__ */ jsx36(Separator11, {}),
|
|
9656
|
+
/* @__PURE__ */ jsx36("div", { className: "flex flex-col gap-1.5", children: outcomes.map((o) => /* @__PURE__ */ jsx36("div", { className: "text-xs", children: o.ruleId ? /* @__PURE__ */ jsxs32("span", { children: [
|
|
9657
|
+
o.who,
|
|
9658
|
+
" will get it ",
|
|
9659
|
+
CADENCE_WORDS[cadence] ?? cadence,
|
|
9660
|
+
schedule ? `, ${schedule}` : "",
|
|
9661
|
+
".",
|
|
9662
|
+
" ",
|
|
9663
|
+
/* @__PURE__ */ jsx36(
|
|
9664
|
+
Button29,
|
|
9665
|
+
{
|
|
9666
|
+
size: "sm",
|
|
9667
|
+
variant: "ghost",
|
|
9668
|
+
disabled: previewing,
|
|
9669
|
+
onClick: () => void showOne(o.ruleId),
|
|
9670
|
+
children: "Send me one now"
|
|
9671
|
+
}
|
|
9672
|
+
)
|
|
9673
|
+
] }) : o.refused ? /* @__PURE__ */ jsx36(RefusalNotice, { error: o.refused }) : null }, o.userId || "me")) })
|
|
9674
|
+
] }) : null,
|
|
9675
|
+
preview ? /* @__PURE__ */ jsxs32("div", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
9676
|
+
/* @__PURE__ */ jsx36("p", { className: "font-medium", children: preview.subject }),
|
|
9677
|
+
/* @__PURE__ */ jsx36("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
9678
|
+
preview.incomplete ? /* @__PURE__ */ jsx36("p", { className: "mt-1 text-destructive", children: preview.incomplete }) : null,
|
|
9679
|
+
preview.entered.length > 0 ? /* @__PURE__ */ jsxs32("p", { className: "mt-1", children: [
|
|
9680
|
+
/* @__PURE__ */ jsx36("span", { className: "font-medium", children: "Arrived:" }),
|
|
9681
|
+
" ",
|
|
9682
|
+
preview.entered.map((e) => e.name).join(", ")
|
|
9683
|
+
] }) : null,
|
|
9684
|
+
preview.changed.length > 0 ? /* @__PURE__ */ jsxs32("p", { className: "mt-1", children: [
|
|
9685
|
+
/* @__PURE__ */ jsx36("span", { className: "font-medium", children: "Changed:" }),
|
|
9686
|
+
" ",
|
|
9687
|
+
preview.changed.map((e) => e.name).join(", ")
|
|
9688
|
+
] }) : null,
|
|
9689
|
+
/* @__PURE__ */ jsx36("p", { className: "mt-1.5 text-muted-foreground", children: "Nothing was sent and nothing was recorded \u2014 this is what the next one would say." }),
|
|
9690
|
+
/* @__PURE__ */ jsx36(Button29, { size: "sm", variant: "ghost", className: "mt-1", onClick: () => setPreview(null), children: "Close" })
|
|
9691
|
+
] }) : null
|
|
9692
|
+
] });
|
|
9693
|
+
}
|
|
9694
|
+
|
|
9695
|
+
// src/SubscriptionsPanel.tsx
|
|
9696
|
+
import { useCallback as useCallback20, useEffect as useEffect23, useState as useState33 } from "react";
|
|
9697
|
+
import { useRecordsClient as useRecordsClient27 } from "@ai-matrx/records/react";
|
|
9698
|
+
import { Badge as Badge14, Button as Button30, Skeleton as Skeleton17, Switch as Switch2, cn as cn31 } from "@ai-matrx/design-system";
|
|
9699
|
+
import { jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
9406
9700
|
function whenItFires(subscription) {
|
|
9407
9701
|
if (subscription.cadence === "instant") return "as it happens";
|
|
9408
9702
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
9409
9703
|
return subscription.schedule ? `${every}, ${subscription.schedule}` : every;
|
|
9410
9704
|
}
|
|
9411
|
-
var
|
|
9705
|
+
var CHANNEL_WORDS2 = {
|
|
9412
9706
|
in_app: "in the app",
|
|
9413
9707
|
email: "by email \u2014 only if an address is on the account",
|
|
9414
9708
|
sms: "by text \u2014 only if a number is on the account"
|
|
9415
9709
|
};
|
|
9710
|
+
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
9416
9711
|
function SubscriptionsPanel({ tableId, className }) {
|
|
9417
|
-
const client =
|
|
9418
|
-
const [rows, setRows] =
|
|
9419
|
-
const [error, setError] =
|
|
9420
|
-
const [busy, setBusy] =
|
|
9421
|
-
const [preview, setPreview] =
|
|
9422
|
-
const
|
|
9712
|
+
const client = useRecordsClient27();
|
|
9713
|
+
const [rows, setRows] = useState33(null);
|
|
9714
|
+
const [error, setError] = useState33(null);
|
|
9715
|
+
const [busy, setBusy] = useState33(null);
|
|
9716
|
+
const [preview, setPreview] = useState33(null);
|
|
9717
|
+
const [scheduling, setScheduling] = useState33(false);
|
|
9718
|
+
const host = useRecordsUi();
|
|
9719
|
+
const load = useCallback20(async () => {
|
|
9423
9720
|
const answered = await client.subscriptions({ table_id: tableId });
|
|
9424
9721
|
if (!answered.ok) {
|
|
9425
9722
|
setError(answered.error);
|
|
@@ -9429,10 +9726,10 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
9429
9726
|
setError(null);
|
|
9430
9727
|
setRows(answered.data);
|
|
9431
9728
|
}, [client, tableId]);
|
|
9432
|
-
|
|
9729
|
+
useEffect23(() => {
|
|
9433
9730
|
void load();
|
|
9434
9731
|
}, [load]);
|
|
9435
|
-
const flip =
|
|
9732
|
+
const flip = useCallback20(
|
|
9436
9733
|
async (subscription, on) => {
|
|
9437
9734
|
setBusy(subscription.rule_id);
|
|
9438
9735
|
const answered = await client.subscriptionMute({
|
|
@@ -9448,7 +9745,7 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
9448
9745
|
},
|
|
9449
9746
|
[client, load]
|
|
9450
9747
|
);
|
|
9451
|
-
const showOne =
|
|
9748
|
+
const showOne = useCallback20(
|
|
9452
9749
|
async (subscription) => {
|
|
9453
9750
|
setBusy(subscription.rule_id);
|
|
9454
9751
|
const answered = await client.subscriptionPreview({ rule_id: subscription.rule_id });
|
|
@@ -9462,25 +9759,57 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
9462
9759
|
},
|
|
9463
9760
|
[client]
|
|
9464
9761
|
);
|
|
9465
|
-
if (rows === null) return /* @__PURE__ */
|
|
9466
|
-
return /* @__PURE__ */
|
|
9467
|
-
/* @__PURE__ */
|
|
9468
|
-
/* @__PURE__ */
|
|
9469
|
-
/* @__PURE__ */
|
|
9762
|
+
if (rows === null) return /* @__PURE__ */ jsx37(Skeleton17, { className: cn31("h-32 w-full", className) });
|
|
9763
|
+
return /* @__PURE__ */ jsxs33("section", { className: cn31("flex flex-col gap-3", className), children: [
|
|
9764
|
+
/* @__PURE__ */ jsxs33("header", { className: "flex items-center gap-2", children: [
|
|
9765
|
+
/* @__PURE__ */ jsx37("h3", { className: "text-sm font-medium", children: "Notifications" }),
|
|
9766
|
+
/* @__PURE__ */ jsx37("span", { className: "text-xs text-muted-foreground", children: rows.length === 0 ? "none" : `${rows.filter((r) => !r.muted).length} on` }),
|
|
9767
|
+
/* @__PURE__ */ jsx37("div", { className: "flex-1" }),
|
|
9768
|
+
rows.length > 0 ? /* @__PURE__ */ jsx37(
|
|
9769
|
+
Button30,
|
|
9770
|
+
{
|
|
9771
|
+
size: "sm",
|
|
9772
|
+
variant: scheduling ? "secondary" : "ghost",
|
|
9773
|
+
onClick: () => setScheduling((s) => !s),
|
|
9774
|
+
children: scheduling ? "Done" : "Schedule a summary"
|
|
9775
|
+
}
|
|
9776
|
+
) : null
|
|
9470
9777
|
] }),
|
|
9471
|
-
error ? /* @__PURE__ */
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9778
|
+
error ? /* @__PURE__ */ jsx37(RefusalNotice, { error }) : null,
|
|
9779
|
+
scheduling ? /* @__PURE__ */ jsx37(
|
|
9780
|
+
DigestScheduler,
|
|
9781
|
+
{
|
|
9782
|
+
tableId,
|
|
9783
|
+
onScheduled: () => {
|
|
9784
|
+
void load();
|
|
9785
|
+
},
|
|
9786
|
+
onClose: () => setScheduling(false)
|
|
9787
|
+
}
|
|
9788
|
+
) : null,
|
|
9789
|
+
rows.length === 0 && !scheduling ? /* @__PURE__ */ jsx37(
|
|
9790
|
+
BuildOrAsk,
|
|
9791
|
+
{
|
|
9792
|
+
mayBuild: true,
|
|
9793
|
+
onBuild: () => setScheduling(true),
|
|
9794
|
+
...host.onAskForOne ? {
|
|
9795
|
+
onAsk: () => host.onAskForOne?.({ kind: "digest", tableId, suggestion: DIGEST_SUGGESTION })
|
|
9796
|
+
} : {},
|
|
9797
|
+
suggestion: DIGEST_SUGGESTION,
|
|
9798
|
+
buildLabel: "Schedule a summary",
|
|
9799
|
+
children: "Nothing is telling you about this table. A summary arrives on a schedule you set and names what arrived, what left and what changed since the last one."
|
|
9800
|
+
}
|
|
9801
|
+
) : null,
|
|
9802
|
+
/* @__PURE__ */ jsx37("ul", { className: "flex flex-col gap-2", children: rows.map((subscription) => /* @__PURE__ */ jsxs33("li", { className: "rounded-md border p-2.5", children: [
|
|
9803
|
+
/* @__PURE__ */ jsxs33("div", { className: "flex items-start gap-2", children: [
|
|
9804
|
+
/* @__PURE__ */ jsxs33("div", { className: "min-w-0 flex-1", children: [
|
|
9805
|
+
/* @__PURE__ */ jsx37("p", { className: "truncate text-sm font-medium", children: subscription.name }),
|
|
9806
|
+
/* @__PURE__ */ jsxs33("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
9807
|
+
CHANNEL_WORDS2[subscription.channel] ?? `on the ${subscription.channel} channel`,
|
|
9479
9808
|
" \xB7 ",
|
|
9480
9809
|
whenItFires(subscription)
|
|
9481
9810
|
] })
|
|
9482
9811
|
] }),
|
|
9483
|
-
subscription.i_may_mute ? /* @__PURE__ */
|
|
9812
|
+
subscription.i_may_mute ? /* @__PURE__ */ jsx37(
|
|
9484
9813
|
Switch2,
|
|
9485
9814
|
{
|
|
9486
9815
|
checked: !subscription.muted,
|
|
@@ -9488,27 +9817,27 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
9488
9817
|
"aria-label": `Tell me about ${subscription.name}`,
|
|
9489
9818
|
onCheckedChange: (on) => void flip(subscription, on)
|
|
9490
9819
|
}
|
|
9491
|
-
) : /* @__PURE__ */
|
|
9820
|
+
) : /* @__PURE__ */ jsx37(Badge14, { variant: "secondary", children: "someone else's" })
|
|
9492
9821
|
] }),
|
|
9493
|
-
/* @__PURE__ */
|
|
9494
|
-
/* @__PURE__ */
|
|
9495
|
-
subscription.next_digest_at ? /* @__PURE__ */
|
|
9822
|
+
/* @__PURE__ */ jsx37("p", { className: "mt-1.5 text-xs text-muted-foreground", children: subscription.muted ? "Off \u2014 it stays here and tells nobody until you switch it back on." : subscription.mine ? "On, and addressed to you." : "On, and addressed to somebody else in this organization." }),
|
|
9823
|
+
/* @__PURE__ */ jsxs33("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
9824
|
+
subscription.next_digest_at ? /* @__PURE__ */ jsxs33("span", { children: [
|
|
9496
9825
|
"Next summary ",
|
|
9497
9826
|
new Date(subscription.next_digest_at).toLocaleString()
|
|
9498
|
-
] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */
|
|
9499
|
-
subscription.last_sent_at ? /* @__PURE__ */
|
|
9827
|
+
] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */ jsx37("span", { children: "No next summary while it is off." }) : null,
|
|
9828
|
+
subscription.last_sent_at ? /* @__PURE__ */ jsxs33("span", { children: [
|
|
9500
9829
|
"Last told you ",
|
|
9501
9830
|
new Date(subscription.last_sent_at).toLocaleString()
|
|
9502
|
-
] }) : /* @__PURE__ */
|
|
9503
|
-
subscription.quiet_hours ? /* @__PURE__ */
|
|
9831
|
+
] }) : /* @__PURE__ */ jsx37("span", { children: "It has not told you anything yet." }),
|
|
9832
|
+
subscription.quiet_hours ? /* @__PURE__ */ jsxs33("span", { children: [
|
|
9504
9833
|
"Not between ",
|
|
9505
9834
|
subscription.quiet_hours.start,
|
|
9506
9835
|
" and ",
|
|
9507
9836
|
subscription.quiet_hours.end,
|
|
9508
9837
|
" \u2014 a send inside those hours waits until they end."
|
|
9509
9838
|
] }) : null,
|
|
9510
|
-
subscription.cadence === "instant" ? null : /* @__PURE__ */
|
|
9511
|
-
|
|
9839
|
+
subscription.cadence === "instant" ? null : /* @__PURE__ */ jsx37(
|
|
9840
|
+
Button30,
|
|
9512
9841
|
{
|
|
9513
9842
|
size: "sm",
|
|
9514
9843
|
variant: "ghost",
|
|
@@ -9518,66 +9847,66 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
9518
9847
|
}
|
|
9519
9848
|
)
|
|
9520
9849
|
] }),
|
|
9521
|
-
preview && preview.rule_id === subscription.rule_id ? /* @__PURE__ */
|
|
9522
|
-
/* @__PURE__ */
|
|
9523
|
-
/* @__PURE__ */
|
|
9850
|
+
preview && preview.rule_id === subscription.rule_id ? /* @__PURE__ */ jsxs33("div", { className: "mt-2 rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
9851
|
+
/* @__PURE__ */ jsx37("p", { className: "font-medium", children: preview.subject }),
|
|
9852
|
+
/* @__PURE__ */ jsx37("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
9524
9853
|
preview.incomplete ? (
|
|
9525
9854
|
// Absent or honest: a subscription that cannot produce a summary
|
|
9526
9855
|
// says which piece is missing instead of showing an empty one.
|
|
9527
|
-
/* @__PURE__ */
|
|
9856
|
+
/* @__PURE__ */ jsx37("p", { className: "mt-1 text-destructive", children: preview.incomplete })
|
|
9528
9857
|
) : null,
|
|
9529
|
-
preview.entered.length > 0 ? /* @__PURE__ */
|
|
9530
|
-
/* @__PURE__ */
|
|
9858
|
+
preview.entered.length > 0 ? /* @__PURE__ */ jsxs33("p", { className: "mt-1", children: [
|
|
9859
|
+
/* @__PURE__ */ jsx37("span", { className: "font-medium", children: "Arrived:" }),
|
|
9531
9860
|
" ",
|
|
9532
9861
|
preview.entered.map((e) => e.name).join(", ")
|
|
9533
9862
|
] }) : null,
|
|
9534
|
-
preview.left.length > 0 ? /* @__PURE__ */
|
|
9535
|
-
/* @__PURE__ */
|
|
9863
|
+
preview.left.length > 0 ? /* @__PURE__ */ jsxs33("p", { className: "mt-1", children: [
|
|
9864
|
+
/* @__PURE__ */ jsx37("span", { className: "font-medium", children: "Left:" }),
|
|
9536
9865
|
" ",
|
|
9537
9866
|
preview.left.map((e) => e.name).join(", ")
|
|
9538
9867
|
] }) : null,
|
|
9539
|
-
preview.changed.length > 0 ? /* @__PURE__ */
|
|
9540
|
-
/* @__PURE__ */
|
|
9868
|
+
preview.changed.length > 0 ? /* @__PURE__ */ jsxs33("p", { className: "mt-1", children: [
|
|
9869
|
+
/* @__PURE__ */ jsx37("span", { className: "font-medium", children: "Changed:" }),
|
|
9541
9870
|
" ",
|
|
9542
9871
|
preview.changed.map((e) => e.name).join(", ")
|
|
9543
9872
|
] }) : null,
|
|
9544
|
-
/* @__PURE__ */
|
|
9545
|
-
/* @__PURE__ */
|
|
9873
|
+
/* @__PURE__ */ jsx37("p", { className: "mt-1.5 text-muted-foreground", children: "Nothing was sent and nothing was recorded \u2014 this is what the next one would say." }),
|
|
9874
|
+
/* @__PURE__ */ jsx37(Button30, { size: "sm", variant: "ghost", className: "mt-1", onClick: () => setPreview(null), children: "Close" })
|
|
9546
9875
|
] }) : null
|
|
9547
9876
|
] }, subscription.rule_id)) })
|
|
9548
9877
|
] });
|
|
9549
9878
|
}
|
|
9550
9879
|
|
|
9551
9880
|
// src/FormBuilder.tsx
|
|
9552
|
-
import { useCallback as
|
|
9553
|
-
import { useFields as useFields16, useRecordsClient as
|
|
9881
|
+
import { useCallback as useCallback22, useEffect as useEffect25, useMemo as useMemo25, useState as useState35 } from "react";
|
|
9882
|
+
import { useFields as useFields16, useRecordsClient as useRecordsClient28, useTable as useTable13 } from "@ai-matrx/records/react";
|
|
9554
9883
|
import { publicFormPath } from "@ai-matrx/records";
|
|
9555
9884
|
import {
|
|
9556
|
-
BasicInput as
|
|
9885
|
+
BasicInput as BasicInput11,
|
|
9557
9886
|
BasicTextarea as BasicTextarea5,
|
|
9558
|
-
Button as
|
|
9559
|
-
Checkbox as
|
|
9560
|
-
Label as
|
|
9561
|
-
Skeleton as
|
|
9562
|
-
cn as
|
|
9887
|
+
Button as Button32,
|
|
9888
|
+
Checkbox as Checkbox6,
|
|
9889
|
+
Label as Label6,
|
|
9890
|
+
Skeleton as Skeleton19,
|
|
9891
|
+
cn as cn33
|
|
9563
9892
|
} from "@ai-matrx/design-system";
|
|
9564
9893
|
|
|
9565
9894
|
// src/FormRunner.tsx
|
|
9566
|
-
import { useCallback as
|
|
9895
|
+
import { useCallback as useCallback21, useEffect as useEffect24, useMemo as useMemo24, useRef as useRef9, useState as useState34 } from "react";
|
|
9567
9896
|
import { useFields as useFields15, useOptionalRecordsClient } from "@ai-matrx/records/react";
|
|
9568
|
-
import { Button as
|
|
9569
|
-
import { Fragment as
|
|
9897
|
+
import { Button as Button31, Progress, Skeleton as Skeleton18, cn as cn32 } from "@ai-matrx/design-system";
|
|
9898
|
+
import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
9570
9899
|
function FormRunner(props) {
|
|
9571
9900
|
if (props.fields && props.onSubmit) {
|
|
9572
|
-
return /* @__PURE__ */
|
|
9901
|
+
return /* @__PURE__ */ jsx38(FormStage, { ...props, fields: props.fields, onSubmit: props.onSubmit });
|
|
9573
9902
|
}
|
|
9574
|
-
return /* @__PURE__ */
|
|
9903
|
+
return /* @__PURE__ */ jsx38(ConnectedFormRunner, { ...props });
|
|
9575
9904
|
}
|
|
9576
9905
|
function ConnectedFormRunner(props) {
|
|
9577
9906
|
const client = useOptionalRecordsClient();
|
|
9578
9907
|
const fields = useFields15(props.form.subject);
|
|
9579
9908
|
const { form, className } = props;
|
|
9580
|
-
const submit =
|
|
9909
|
+
const submit = useCallback21(
|
|
9581
9910
|
async (values) => {
|
|
9582
9911
|
if (!client) {
|
|
9583
9912
|
return {
|
|
@@ -9600,7 +9929,7 @@ function ConnectedFormRunner(props) {
|
|
|
9600
9929
|
},
|
|
9601
9930
|
[client, form]
|
|
9602
9931
|
);
|
|
9603
|
-
const evaluate =
|
|
9932
|
+
const evaluate = useCallback21(
|
|
9604
9933
|
async (expr, values) => {
|
|
9605
9934
|
if (!client) return null;
|
|
9606
9935
|
const answered = await client.ruleEval({ expr, values });
|
|
@@ -9609,9 +9938,9 @@ function ConnectedFormRunner(props) {
|
|
|
9609
9938
|
},
|
|
9610
9939
|
[client]
|
|
9611
9940
|
);
|
|
9612
|
-
if (fields.error) return /* @__PURE__ */
|
|
9613
|
-
if (fields.loading) return /* @__PURE__ */
|
|
9614
|
-
return /* @__PURE__ */
|
|
9941
|
+
if (fields.error) return /* @__PURE__ */ jsx38(RefusalNotice, { error: fields.error, className });
|
|
9942
|
+
if (fields.loading) return /* @__PURE__ */ jsx38(Skeleton18, { className: cn32("h-40 w-full", className) });
|
|
9943
|
+
return /* @__PURE__ */ jsx38(
|
|
9615
9944
|
FormStage,
|
|
9616
9945
|
{
|
|
9617
9946
|
...props,
|
|
@@ -9634,16 +9963,16 @@ function FormStage({
|
|
|
9634
9963
|
connected = false
|
|
9635
9964
|
}) {
|
|
9636
9965
|
const host = useRecordsUi();
|
|
9637
|
-
const [answers, setAnswers] =
|
|
9638
|
-
const [at, setAt] =
|
|
9639
|
-
const [error, setError] =
|
|
9640
|
-
const [refusal, setRefusal] =
|
|
9641
|
-
const [writing, setWriting] =
|
|
9642
|
-
const [done, setDone] =
|
|
9643
|
-
const [hidden, setHidden] =
|
|
9966
|
+
const [answers, setAnswers] = useState34({});
|
|
9967
|
+
const [at, setAt] = useState34(0);
|
|
9968
|
+
const [error, setError] = useState34(null);
|
|
9969
|
+
const [refusal, setRefusal] = useState34(null);
|
|
9970
|
+
const [writing, setWriting] = useState34(false);
|
|
9971
|
+
const [done, setDone] = useState34(null);
|
|
9972
|
+
const [hidden, setHidden] = useState34({});
|
|
9644
9973
|
const decoy = useRef9("");
|
|
9645
9974
|
const stage = useRef9(null);
|
|
9646
|
-
const questions =
|
|
9975
|
+
const questions = useMemo24(() => {
|
|
9647
9976
|
const byKey = new Map((fields ?? []).map((f) => [f.key, f]));
|
|
9648
9977
|
return (form.questions ?? []).map((q) => {
|
|
9649
9978
|
const field = byKey.get(q.field) ?? null;
|
|
@@ -9657,7 +9986,7 @@ function FormStage({
|
|
|
9657
9986
|
};
|
|
9658
9987
|
});
|
|
9659
9988
|
}, [fields, form.questions]);
|
|
9660
|
-
|
|
9989
|
+
useEffect24(() => {
|
|
9661
9990
|
let cancelled = false;
|
|
9662
9991
|
const conditional = questions.filter((q) => q.showIf);
|
|
9663
9992
|
if (conditional.length === 0 || !evaluate) return;
|
|
@@ -9691,7 +10020,7 @@ function FormStage({
|
|
|
9691
10020
|
const v = answers[q.key];
|
|
9692
10021
|
return q.required && (v === void 0 || v === null || v === "");
|
|
9693
10022
|
});
|
|
9694
|
-
const submit =
|
|
10023
|
+
const submit = useCallback21(async () => {
|
|
9695
10024
|
if (preview) {
|
|
9696
10025
|
setDone("preview");
|
|
9697
10026
|
return;
|
|
@@ -9728,51 +10057,51 @@ function FormStage({
|
|
|
9728
10057
|
if (event.shiftKey) retreat();
|
|
9729
10058
|
else advance();
|
|
9730
10059
|
}
|
|
9731
|
-
|
|
10060
|
+
useEffect24(() => {
|
|
9732
10061
|
const input = stage.current?.querySelector(
|
|
9733
10062
|
"input:not([tabindex='-1']), textarea, select, [role='combobox']"
|
|
9734
10063
|
);
|
|
9735
10064
|
input?.focus();
|
|
9736
10065
|
}, [index, done]);
|
|
9737
10066
|
if (done) {
|
|
9738
|
-
return /* @__PURE__ */
|
|
9739
|
-
/* @__PURE__ */
|
|
9740
|
-
form.thankYou?.body ? /* @__PURE__ */
|
|
9741
|
-
/* @__PURE__ */
|
|
10067
|
+
return /* @__PURE__ */ jsxs34("section", { className: cn32("mx-auto max-w-xl py-10", centred && "text-center", className), children: [
|
|
10068
|
+
/* @__PURE__ */ jsx38("h2", { className: "text-lg font-medium", children: form.thankYou?.title ?? "Thank you" }),
|
|
10069
|
+
form.thankYou?.body ? /* @__PURE__ */ jsx38("p", { className: "mt-1 text-sm text-muted-foreground", children: form.thankYou.body }) : null,
|
|
10070
|
+
/* @__PURE__ */ jsx38("p", { className: "mt-3 text-xs text-muted-foreground", children: done === "preview" ? "This was a preview, so nothing was written." : done === "sent" ? `Saved to ${form.name}, with this form stamped on it.` : done })
|
|
9742
10071
|
] });
|
|
9743
10072
|
}
|
|
9744
10073
|
const unresolved = questions.filter((q) => !q.field);
|
|
9745
10074
|
const unanswerable = !evaluate && questions.some((q) => q.showIf);
|
|
9746
10075
|
const signedInPublic = connected && form.isPublic === true;
|
|
9747
|
-
return /* @__PURE__ */
|
|
10076
|
+
return /* @__PURE__ */ jsxs34(
|
|
9748
10077
|
"section",
|
|
9749
10078
|
{
|
|
9750
|
-
className:
|
|
10079
|
+
className: cn32("mx-auto flex max-w-xl flex-col gap-3 py-4", centred && "text-center", className),
|
|
9751
10080
|
onKeyDown,
|
|
9752
10081
|
children: [
|
|
9753
|
-
/* @__PURE__ */
|
|
9754
|
-
/* @__PURE__ */
|
|
10082
|
+
/* @__PURE__ */ jsxs34("header", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
10083
|
+
/* @__PURE__ */ jsx38(
|
|
9755
10084
|
Progress,
|
|
9756
10085
|
{
|
|
9757
10086
|
value: live.length === 0 ? 0 : (oneAtATime ? index + 1 : answered) / live.length * 100,
|
|
9758
10087
|
className: "h-1 flex-1"
|
|
9759
10088
|
}
|
|
9760
10089
|
),
|
|
9761
|
-
/* @__PURE__ */
|
|
10090
|
+
/* @__PURE__ */ jsx38("span", { className: "tabular-nums", children: oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` })
|
|
9762
10091
|
] }),
|
|
9763
|
-
signedInPublic ? /* @__PURE__ */
|
|
9764
|
-
unanswerable ? /* @__PURE__ */
|
|
9765
|
-
unresolved.length > 0 ? /* @__PURE__ */
|
|
10092
|
+
signedInPublic ? /* @__PURE__ */ jsx38("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-muted-foreground", children: "You are answering this signed in, so the record will carry your name. A stranger answers the same form at its public link, where the answer arrives through the anonymous door instead." }) : null,
|
|
10093
|
+
unanswerable ? /* @__PURE__ */ jsx38("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-muted-foreground", children: "This form has questions that only appear in certain cases. Nothing here can work out which ones, so all of them are being shown rather than any being skipped." }) : null,
|
|
10094
|
+
unresolved.length > 0 ? /* @__PURE__ */ jsxs34("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-destructive", children: [
|
|
9766
10095
|
unresolved.map((q) => q.key).join(", "),
|
|
9767
10096
|
" ",
|
|
9768
10097
|
unresolved.length === 1 ? "is a question" : "are questions",
|
|
9769
10098
|
" this form asks for and this table has no such Field. Add the Field, or take the question out \u2014 it is shown here rather than quietly dropped, because a dropped question is an answer nobody gave."
|
|
9770
10099
|
] }) : null,
|
|
9771
|
-
form.intro && index === 0 ? /* @__PURE__ */
|
|
9772
|
-
error ? /* @__PURE__ */
|
|
9773
|
-
refusal ? /* @__PURE__ */
|
|
9774
|
-
/* @__PURE__ */
|
|
9775
|
-
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */
|
|
10100
|
+
form.intro && index === 0 ? /* @__PURE__ */ jsx38("p", { className: "text-sm text-muted-foreground", children: form.intro }) : null,
|
|
10101
|
+
error ? /* @__PURE__ */ jsx38(RefusalNotice, { error, className: "text-left" }) : null,
|
|
10102
|
+
refusal ? /* @__PURE__ */ jsx38("p", { role: "alert", className: "rounded border border-destructive/40 px-2 py-1 text-left text-xs text-destructive", children: refusal }) : null,
|
|
10103
|
+
/* @__PURE__ */ jsxs34("div", { ref: stage, className: "flex flex-col gap-4 text-left", children: [
|
|
10104
|
+
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ jsx38(
|
|
9776
10105
|
Question,
|
|
9777
10106
|
{
|
|
9778
10107
|
question: q,
|
|
@@ -9782,11 +10111,11 @@ function FormStage({
|
|
|
9782
10111
|
},
|
|
9783
10112
|
q.key
|
|
9784
10113
|
)),
|
|
9785
|
-
live.length === 0 ? /* @__PURE__ */
|
|
10114
|
+
live.length === 0 ? /* @__PURE__ */ jsx38("p", { className: "text-xs text-muted-foreground", children: "Every question in this form is hidden by its own condition right now, so there is nothing to answer yet." }) : null
|
|
9786
10115
|
] }),
|
|
9787
|
-
honeypotKey ? /* @__PURE__ */
|
|
9788
|
-
/* @__PURE__ */
|
|
9789
|
-
/* @__PURE__ */
|
|
10116
|
+
honeypotKey ? /* @__PURE__ */ jsxs34("div", { "aria-hidden": true, className: "pointer-events-none absolute left-[-9999px] h-px w-px overflow-hidden", children: [
|
|
10117
|
+
/* @__PURE__ */ jsx38("label", { htmlFor: `hp-${honeypotKey}`, children: "Leave this field empty" }),
|
|
10118
|
+
/* @__PURE__ */ jsx38(
|
|
9790
10119
|
"input",
|
|
9791
10120
|
{
|
|
9792
10121
|
id: `hp-${honeypotKey}`,
|
|
@@ -9801,16 +10130,16 @@ function FormStage({
|
|
|
9801
10130
|
}
|
|
9802
10131
|
)
|
|
9803
10132
|
] }) : null,
|
|
9804
|
-
/* @__PURE__ */
|
|
9805
|
-
oneAtATime && index > 0 ? /* @__PURE__ */
|
|
9806
|
-
oneAtATime && index < live.length - 1 ? /* @__PURE__ */
|
|
9807
|
-
missing.length > 0 && (!oneAtATime || index === live.length - 1) ? /* @__PURE__ */
|
|
10133
|
+
/* @__PURE__ */ jsxs34("footer", { className: cn32("flex flex-wrap items-center gap-2", centred && "justify-center"), children: [
|
|
10134
|
+
oneAtATime && index > 0 ? /* @__PURE__ */ jsx38(Button31, { size: "sm", variant: "ghost", onClick: retreat, children: "Back" }) : null,
|
|
10135
|
+
oneAtATime && index < live.length - 1 ? /* @__PURE__ */ jsx38(Button31, { size: "sm", onClick: advance, children: "Next" }) : /* @__PURE__ */ jsx38(Button31, { size: "sm", disabled: writing || missing.length > 0, onClick: () => void submit(), children: writing ? "Sending\u2026" : form.submitLabel ?? "Submit" }),
|
|
10136
|
+
missing.length > 0 && (!oneAtATime || index === live.length - 1) ? /* @__PURE__ */ jsxs34("span", { className: "text-xs text-muted-foreground", children: [
|
|
9808
10137
|
missing.map((q) => q.ask).join(", "),
|
|
9809
10138
|
" still ",
|
|
9810
10139
|
missing.length === 1 ? "needs" : "need",
|
|
9811
10140
|
" an answer."
|
|
9812
10141
|
] }) : null,
|
|
9813
|
-
preview ? /* @__PURE__ */
|
|
10142
|
+
preview ? /* @__PURE__ */ jsx38("span", { className: "text-xs text-muted-foreground", children: "Preview \u2014 nothing is written." }) : null
|
|
9814
10143
|
] })
|
|
9815
10144
|
]
|
|
9816
10145
|
}
|
|
@@ -9822,16 +10151,16 @@ function Question({
|
|
|
9822
10151
|
onChange,
|
|
9823
10152
|
upload
|
|
9824
10153
|
}) {
|
|
9825
|
-
const [uploadError, setUploadError] =
|
|
10154
|
+
const [uploadError, setUploadError] = useState34(null);
|
|
9826
10155
|
const field = question.field;
|
|
9827
10156
|
if (!field) return null;
|
|
9828
10157
|
const id = `form-${field.key}`;
|
|
9829
10158
|
const isAttachment = editorKindFor(field) === "attachment";
|
|
9830
|
-
return /* @__PURE__ */
|
|
9831
|
-
/* @__PURE__ */
|
|
9832
|
-
question.help ? /* @__PURE__ */
|
|
9833
|
-
isAttachment && !upload ? /* @__PURE__ */
|
|
9834
|
-
/* @__PURE__ */
|
|
10159
|
+
return /* @__PURE__ */ jsxs34("div", { className: "flex flex-col gap-1.5", children: [
|
|
10160
|
+
/* @__PURE__ */ jsx38(FieldLabel, { field: { ...field, label: question.ask, required: question.required }, htmlFor: id }),
|
|
10161
|
+
question.help ? /* @__PURE__ */ jsx38("p", { className: "text-xs text-muted-foreground", children: question.help }) : null,
|
|
10162
|
+
isAttachment && !upload ? /* @__PURE__ */ jsx38("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : isAttachment ? /* @__PURE__ */ jsxs34(Fragment17, { children: [
|
|
10163
|
+
/* @__PURE__ */ jsx38(
|
|
9835
10164
|
"input",
|
|
9836
10165
|
{
|
|
9837
10166
|
id,
|
|
@@ -9849,31 +10178,31 @@ function Question({
|
|
|
9849
10178
|
}
|
|
9850
10179
|
}
|
|
9851
10180
|
),
|
|
9852
|
-
uploadError ? /* @__PURE__ */
|
|
9853
|
-
] }) : /* @__PURE__ */
|
|
10181
|
+
uploadError ? /* @__PURE__ */ jsx38("p", { className: "text-xs text-destructive", children: uploadError }) : null
|
|
10182
|
+
] }) : /* @__PURE__ */ jsx38(FieldControl, { field, value, onChange, id })
|
|
9854
10183
|
] });
|
|
9855
10184
|
}
|
|
9856
10185
|
|
|
9857
10186
|
// src/FormBuilder.tsx
|
|
9858
|
-
import { Fragment as
|
|
10187
|
+
import { Fragment as Fragment18, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
9859
10188
|
function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
9860
|
-
const client =
|
|
10189
|
+
const client = useRecordsClient28();
|
|
9861
10190
|
const host = useRecordsUi();
|
|
9862
10191
|
const claimSeed = useSeedGuard();
|
|
9863
10192
|
const table = useTable13(tableId);
|
|
9864
10193
|
const rights = useTableRights(table.data);
|
|
9865
10194
|
const fields = useFields16(tableId);
|
|
9866
|
-
const [forms, setForms] =
|
|
9867
|
-
const [error, setError] =
|
|
9868
|
-
const [activeId, setActiveId] =
|
|
9869
|
-
const [draft, setDraft] =
|
|
9870
|
-
const [saving, setSaving] =
|
|
9871
|
-
const [saved, setSaved] =
|
|
9872
|
-
const [publishing, setPublishing] =
|
|
9873
|
-
const [copied, setCopied] =
|
|
9874
|
-
const [shownUrl, setShownUrl] =
|
|
10195
|
+
const [forms, setForms] = useState35(null);
|
|
10196
|
+
const [error, setError] = useState35(null);
|
|
10197
|
+
const [activeId, setActiveId] = useState35(activeFormId ?? null);
|
|
10198
|
+
const [draft, setDraft] = useState35(null);
|
|
10199
|
+
const [saving, setSaving] = useState35(false);
|
|
10200
|
+
const [saved, setSaved] = useState35(null);
|
|
10201
|
+
const [publishing, setPublishing] = useState35(false);
|
|
10202
|
+
const [copied, setCopied] = useState35(false);
|
|
10203
|
+
const [shownUrl, setShownUrl] = useState35(null);
|
|
9875
10204
|
const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
9876
|
-
const load =
|
|
10205
|
+
const load = useCallback22(async () => {
|
|
9877
10206
|
const answered = await client.forms({ table_id: tableId });
|
|
9878
10207
|
if (!answered.ok) {
|
|
9879
10208
|
setError(answered.error);
|
|
@@ -9900,17 +10229,17 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
9900
10229
|
setError(null);
|
|
9901
10230
|
setForms(mine);
|
|
9902
10231
|
}, [client, tableId, seed, claimSeed]);
|
|
9903
|
-
|
|
10232
|
+
useEffect25(() => {
|
|
9904
10233
|
void load();
|
|
9905
10234
|
}, [load]);
|
|
9906
|
-
|
|
10235
|
+
useEffect25(() => {
|
|
9907
10236
|
if (!forms || forms.length === 0) return;
|
|
9908
10237
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
9909
10238
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
9910
10239
|
setDraft(chosen);
|
|
9911
10240
|
onActiveForm?.(chosen);
|
|
9912
10241
|
}, [forms, activeFormId]);
|
|
9913
|
-
const byKey =
|
|
10242
|
+
const byKey = useMemo25(() => new Map((fields.data ?? []).map((f) => [f.key, f])), [fields.data]);
|
|
9914
10243
|
function patch(change) {
|
|
9915
10244
|
setDraft((prev) => prev ? { ...prev, ...change } : prev);
|
|
9916
10245
|
setSaved(null);
|
|
@@ -9970,26 +10299,26 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
9970
10299
|
setActiveId(written.data);
|
|
9971
10300
|
await load();
|
|
9972
10301
|
}
|
|
9973
|
-
if (fields.error) return /* @__PURE__ */
|
|
10302
|
+
if (fields.error) return /* @__PURE__ */ jsx39(RefusalNotice, { error: fields.error, className });
|
|
9974
10303
|
if (error) {
|
|
9975
|
-
return /* @__PURE__ */
|
|
10304
|
+
return /* @__PURE__ */ jsx39(
|
|
9976
10305
|
RefusalNotice,
|
|
9977
10306
|
{
|
|
9978
10307
|
error,
|
|
9979
10308
|
className,
|
|
9980
|
-
actions: /* @__PURE__ */
|
|
10309
|
+
actions: /* @__PURE__ */ jsx39(Button32, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
9981
10310
|
}
|
|
9982
10311
|
);
|
|
9983
10312
|
}
|
|
9984
|
-
if (fields.loading || forms === null) return /* @__PURE__ */
|
|
10313
|
+
if (fields.loading || forms === null) return /* @__PURE__ */ jsx39(Skeleton19, { className: cn33("h-64 w-full", className) });
|
|
9985
10314
|
if (!rights.admin) {
|
|
9986
|
-
return /* @__PURE__ */
|
|
10315
|
+
return /* @__PURE__ */ jsx39("p", { className: cn33("text-xs text-muted-foreground", className), children: rights.why("structure") });
|
|
9987
10316
|
}
|
|
9988
|
-
return /* @__PURE__ */
|
|
9989
|
-
/* @__PURE__ */
|
|
9990
|
-
/* @__PURE__ */
|
|
9991
|
-
forms.map((form) => /* @__PURE__ */
|
|
9992
|
-
|
|
10317
|
+
return /* @__PURE__ */ jsxs35("div", { className: cn33("flex min-h-0 gap-3", className), children: [
|
|
10318
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex min-w-0 flex-1 flex-col gap-2 overflow-auto", children: [
|
|
10319
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Forms", children: [
|
|
10320
|
+
forms.map((form) => /* @__PURE__ */ jsx39(
|
|
10321
|
+
Button32,
|
|
9993
10322
|
{
|
|
9994
10323
|
size: "sm",
|
|
9995
10324
|
variant: form.id === activeId ? "secondary" : "ghost",
|
|
@@ -10002,14 +10331,14 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10002
10331
|
},
|
|
10003
10332
|
form.id
|
|
10004
10333
|
)),
|
|
10005
|
-
(fields.data ?? []).length > 0 ? /* @__PURE__ */
|
|
10006
|
-
/* @__PURE__ */
|
|
10007
|
-
saved ? /* @__PURE__ */
|
|
10008
|
-
/* @__PURE__ */
|
|
10334
|
+
(fields.data ?? []).length > 0 ? /* @__PURE__ */ jsx39(Button32, { size: "sm", variant: "ghost", onClick: () => void create(`Form ${forms.length + 1}`), children: "New form" }) : null,
|
|
10335
|
+
/* @__PURE__ */ jsxs35("span", { className: "ml-auto flex items-center gap-2", children: [
|
|
10336
|
+
saved ? /* @__PURE__ */ jsx39("span", { className: "text-xs text-muted-foreground", children: saved }) : null,
|
|
10337
|
+
/* @__PURE__ */ jsx39(Button32, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
10009
10338
|
] })
|
|
10010
10339
|
] }),
|
|
10011
|
-
!draft ? /* @__PURE__ */
|
|
10012
|
-
/* @__PURE__ */
|
|
10340
|
+
!draft ? /* @__PURE__ */ jsx39("p", { className: "text-xs text-muted-foreground", children: (fields.data ?? []).length === 0 ? "This table has no fields yet, so there is nothing a form could ask for. Add a field first \u2014 every question is one of this table's own fields." : 'No form collects into this table yet. "New form" makes one, or you ask an agent for the whole thing in a sentence.' }) : /* @__PURE__ */ jsxs35(Fragment18, { children: [
|
|
10341
|
+
/* @__PURE__ */ jsx39(
|
|
10013
10342
|
PublishRow,
|
|
10014
10343
|
{
|
|
10015
10344
|
url: `${publicOrigin}${publicFormPath(draft.id)}`,
|
|
@@ -10023,26 +10352,26 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10023
10352
|
onCopy: (url) => void copyLink(url)
|
|
10024
10353
|
}
|
|
10025
10354
|
),
|
|
10026
|
-
/* @__PURE__ */
|
|
10027
|
-
/* @__PURE__ */
|
|
10028
|
-
/* @__PURE__ */
|
|
10029
|
-
/* @__PURE__ */
|
|
10355
|
+
/* @__PURE__ */ jsxs35("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
10356
|
+
/* @__PURE__ */ jsxs35("label", { className: "flex flex-col gap-1", children: [
|
|
10357
|
+
/* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Name" }),
|
|
10358
|
+
/* @__PURE__ */ jsx39(BasicInput11, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
|
|
10030
10359
|
] }),
|
|
10031
|
-
/* @__PURE__ */
|
|
10032
|
-
/* @__PURE__ */
|
|
10033
|
-
/* @__PURE__ */
|
|
10360
|
+
/* @__PURE__ */ jsxs35("label", { className: "flex flex-col gap-1", children: [
|
|
10361
|
+
/* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Flow" }),
|
|
10362
|
+
/* @__PURE__ */ jsx39(
|
|
10034
10363
|
"select",
|
|
10035
10364
|
{
|
|
10036
10365
|
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
10037
10366
|
value: draft.flow ?? "one-at-a-time",
|
|
10038
10367
|
onChange: (e) => patch({ flow: e.target.value }),
|
|
10039
|
-
children: FORM_FLOWS.map((flow) => /* @__PURE__ */
|
|
10368
|
+
children: FORM_FLOWS.map((flow) => /* @__PURE__ */ jsx39("option", { value: flow, children: flow === "one-at-a-time" ? "One question at a time" : "All on one page" }, flow))
|
|
10040
10369
|
}
|
|
10041
10370
|
)
|
|
10042
10371
|
] }),
|
|
10043
|
-
/* @__PURE__ */
|
|
10044
|
-
/* @__PURE__ */
|
|
10045
|
-
/* @__PURE__ */
|
|
10372
|
+
/* @__PURE__ */ jsxs35("label", { className: "col-span-2 flex flex-col gap-1", children: [
|
|
10373
|
+
/* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Intro" }),
|
|
10374
|
+
/* @__PURE__ */ jsx39(
|
|
10046
10375
|
BasicTextarea5,
|
|
10047
10376
|
{
|
|
10048
10377
|
rows: 2,
|
|
@@ -10051,10 +10380,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10051
10380
|
}
|
|
10052
10381
|
)
|
|
10053
10382
|
] }),
|
|
10054
|
-
/* @__PURE__ */
|
|
10055
|
-
/* @__PURE__ */
|
|
10056
|
-
/* @__PURE__ */
|
|
10057
|
-
|
|
10383
|
+
/* @__PURE__ */ jsxs35("label", { className: "flex flex-col gap-1", children: [
|
|
10384
|
+
/* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Submit button" }),
|
|
10385
|
+
/* @__PURE__ */ jsx39(
|
|
10386
|
+
BasicInput11,
|
|
10058
10387
|
{
|
|
10059
10388
|
value: draft.submitLabel ?? "",
|
|
10060
10389
|
placeholder: "Submit",
|
|
@@ -10062,10 +10391,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10062
10391
|
}
|
|
10063
10392
|
)
|
|
10064
10393
|
] }),
|
|
10065
|
-
/* @__PURE__ */
|
|
10066
|
-
/* @__PURE__ */
|
|
10067
|
-
/* @__PURE__ */
|
|
10068
|
-
|
|
10394
|
+
/* @__PURE__ */ jsxs35("label", { className: "flex flex-col gap-1", children: [
|
|
10395
|
+
/* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Thank-you title" }),
|
|
10396
|
+
/* @__PURE__ */ jsx39(
|
|
10397
|
+
BasicInput11,
|
|
10069
10398
|
{
|
|
10070
10399
|
value: draft.thankYou?.title ?? "",
|
|
10071
10400
|
placeholder: "Thank you",
|
|
@@ -10076,10 +10405,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10076
10405
|
)
|
|
10077
10406
|
] })
|
|
10078
10407
|
] }),
|
|
10079
|
-
/* @__PURE__ */
|
|
10080
|
-
/* @__PURE__ */
|
|
10081
|
-
/* @__PURE__ */
|
|
10082
|
-
/* @__PURE__ */
|
|
10408
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex flex-col gap-1", children: [
|
|
10409
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
10410
|
+
/* @__PURE__ */ jsx39("span", { className: "font-medium text-foreground", children: "Questions" }),
|
|
10411
|
+
/* @__PURE__ */ jsxs35("span", { children: [
|
|
10083
10412
|
draft.questions.length,
|
|
10084
10413
|
" of this table's ",
|
|
10085
10414
|
fields.data?.length ?? 0,
|
|
@@ -10090,10 +10419,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10090
10419
|
const index = draft.questions.findIndex((q) => q.field === field.key);
|
|
10091
10420
|
const asked = index >= 0;
|
|
10092
10421
|
const question = asked ? draft.questions[index] : null;
|
|
10093
|
-
return /* @__PURE__ */
|
|
10094
|
-
/* @__PURE__ */
|
|
10095
|
-
/* @__PURE__ */
|
|
10096
|
-
|
|
10422
|
+
return /* @__PURE__ */ jsxs35("div", { className: "rounded border px-2 py-1.5", children: [
|
|
10423
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-2", children: [
|
|
10424
|
+
/* @__PURE__ */ jsx39(
|
|
10425
|
+
Checkbox6,
|
|
10097
10426
|
{
|
|
10098
10427
|
checked: asked,
|
|
10099
10428
|
"aria-label": `Ask for ${fieldName(field)}`,
|
|
@@ -10102,13 +10431,13 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10102
10431
|
})
|
|
10103
10432
|
}
|
|
10104
10433
|
),
|
|
10105
|
-
/* @__PURE__ */
|
|
10106
|
-
/* @__PURE__ */
|
|
10107
|
-
field.required ? /* @__PURE__ */
|
|
10434
|
+
/* @__PURE__ */ jsx39("span", { className: "text-xs font-medium", children: fieldName(field) }),
|
|
10435
|
+
/* @__PURE__ */ jsx39("span", { className: "text-xs text-muted-foreground", children: field.key }),
|
|
10436
|
+
field.required ? /* @__PURE__ */ jsx39("span", { className: "text-xs text-destructive", children: "required" }) : null
|
|
10108
10437
|
] }),
|
|
10109
|
-
asked && question ? /* @__PURE__ */
|
|
10110
|
-
/* @__PURE__ */
|
|
10111
|
-
|
|
10438
|
+
asked && question ? /* @__PURE__ */ jsxs35("div", { className: "mt-1.5 grid grid-cols-2 gap-2 pl-6", children: [
|
|
10439
|
+
/* @__PURE__ */ jsx39(
|
|
10440
|
+
BasicInput11,
|
|
10112
10441
|
{
|
|
10113
10442
|
className: "h-8 text-xs",
|
|
10114
10443
|
placeholder: fieldName(field),
|
|
@@ -10117,8 +10446,8 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10117
10446
|
onChange: (e) => patchQuestion(index, { ask: e.target.value || null })
|
|
10118
10447
|
}
|
|
10119
10448
|
),
|
|
10120
|
-
/* @__PURE__ */
|
|
10121
|
-
|
|
10449
|
+
/* @__PURE__ */ jsx39(
|
|
10450
|
+
BasicInput11,
|
|
10122
10451
|
{
|
|
10123
10452
|
className: "h-8 text-xs",
|
|
10124
10453
|
placeholder: "Help text",
|
|
@@ -10127,7 +10456,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10127
10456
|
onChange: (e) => patchQuestion(index, { help: e.target.value || null })
|
|
10128
10457
|
}
|
|
10129
10458
|
),
|
|
10130
|
-
/* @__PURE__ */
|
|
10459
|
+
/* @__PURE__ */ jsx39(
|
|
10131
10460
|
Condition,
|
|
10132
10461
|
{
|
|
10133
10462
|
question,
|
|
@@ -10138,7 +10467,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10138
10467
|
] }) : null
|
|
10139
10468
|
] }, field.id);
|
|
10140
10469
|
}),
|
|
10141
|
-
draft.questions.filter((q) => !byKey.has(q.field)).map((q) => /* @__PURE__ */
|
|
10470
|
+
draft.questions.filter((q) => !byKey.has(q.field)).map((q) => /* @__PURE__ */ jsxs35("p", { className: "text-xs text-destructive", children: [
|
|
10142
10471
|
'This form asks for "',
|
|
10143
10472
|
q.field,
|
|
10144
10473
|
'" and this table has no such Field. It is shown here rather than quietly dropped.'
|
|
@@ -10146,9 +10475,9 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
10146
10475
|
] })
|
|
10147
10476
|
] })
|
|
10148
10477
|
] }),
|
|
10149
|
-
draft ? /* @__PURE__ */
|
|
10150
|
-
/* @__PURE__ */
|
|
10151
|
-
/* @__PURE__ */
|
|
10478
|
+
draft ? /* @__PURE__ */ jsxs35("aside", { className: "w-96 shrink-0 overflow-auto rounded border p-2", children: [
|
|
10479
|
+
/* @__PURE__ */ jsx39("h4", { className: "px-1 text-sm font-medium", children: draft.name }),
|
|
10480
|
+
/* @__PURE__ */ jsx39(FormRunner, { form: draft, preview: true })
|
|
10152
10481
|
] }) : null
|
|
10153
10482
|
] });
|
|
10154
10483
|
}
|
|
@@ -10164,10 +10493,10 @@ function PublishRow({
|
|
|
10164
10493
|
onCopy
|
|
10165
10494
|
}) {
|
|
10166
10495
|
const open = state === "open";
|
|
10167
|
-
return /* @__PURE__ */
|
|
10168
|
-
/* @__PURE__ */
|
|
10169
|
-
mayPublish ? /* @__PURE__ */
|
|
10170
|
-
|
|
10496
|
+
return /* @__PURE__ */ jsxs35("div", { className: "flex flex-col gap-1.5 rounded border px-2.5 py-2", children: [
|
|
10497
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
10498
|
+
mayPublish ? /* @__PURE__ */ jsx39(
|
|
10499
|
+
Button32,
|
|
10171
10500
|
{
|
|
10172
10501
|
size: "sm",
|
|
10173
10502
|
variant: open ? "ghost" : "default",
|
|
@@ -10176,8 +10505,8 @@ function PublishRow({
|
|
|
10176
10505
|
children: busy ? "\u2026" : open ? "Unpublish" : "Publish"
|
|
10177
10506
|
}
|
|
10178
10507
|
) : null,
|
|
10179
|
-
state !== "draft" ? /* @__PURE__ */
|
|
10180
|
-
/* @__PURE__ */
|
|
10508
|
+
state !== "draft" ? /* @__PURE__ */ jsxs35(Fragment18, { children: [
|
|
10509
|
+
/* @__PURE__ */ jsx39(
|
|
10181
10510
|
"a",
|
|
10182
10511
|
{
|
|
10183
10512
|
href: url,
|
|
@@ -10187,12 +10516,12 @@ function PublishRow({
|
|
|
10187
10516
|
children: url
|
|
10188
10517
|
}
|
|
10189
10518
|
),
|
|
10190
|
-
/* @__PURE__ */
|
|
10519
|
+
/* @__PURE__ */ jsx39(Button32, { size: "sm", variant: "outline", onClick: () => onCopy(url), children: copied ? "Copied" : "Copy link" })
|
|
10191
10520
|
] }) : null
|
|
10192
10521
|
] }),
|
|
10193
|
-
/* @__PURE__ */
|
|
10194
|
-
!mayPublish ? /* @__PURE__ */
|
|
10195
|
-
shownUrl ? /* @__PURE__ */
|
|
10522
|
+
/* @__PURE__ */ jsx39("p", { className: "text-xs text-muted-foreground", children: FORM_STATE_WORDS[state] }),
|
|
10523
|
+
!mayPublish ? /* @__PURE__ */ jsx39("p", { className: "text-xs text-muted-foreground", children: why }) : null,
|
|
10524
|
+
shownUrl ? /* @__PURE__ */ jsxs35("p", { className: "break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
10196
10525
|
"This browser would not let the page copy for you, so here it is to copy by hand: ",
|
|
10197
10526
|
shownUrl
|
|
10198
10527
|
] }) : null
|
|
@@ -10203,7 +10532,7 @@ function Condition({
|
|
|
10203
10532
|
fieldKeys,
|
|
10204
10533
|
onChange
|
|
10205
10534
|
}) {
|
|
10206
|
-
return /* @__PURE__ */
|
|
10535
|
+
return /* @__PURE__ */ jsx39(
|
|
10207
10536
|
ConditionRow,
|
|
10208
10537
|
{
|
|
10209
10538
|
lead: "Ask only when",
|
|
@@ -10301,17 +10630,17 @@ function groupLabel(groups) {
|
|
|
10301
10630
|
|
|
10302
10631
|
// src/chartFrame.tsx
|
|
10303
10632
|
import {
|
|
10304
|
-
useEffect as
|
|
10633
|
+
useEffect as useEffect26,
|
|
10305
10634
|
useId,
|
|
10306
10635
|
useRef as useRef10,
|
|
10307
|
-
useState as
|
|
10636
|
+
useState as useState36
|
|
10308
10637
|
} from "react";
|
|
10309
|
-
import { cn as
|
|
10310
|
-
import { jsx as
|
|
10638
|
+
import { cn as cn34 } from "@ai-matrx/design-system";
|
|
10639
|
+
import { jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
10311
10640
|
function useMeasuredWidth(fallback = 480) {
|
|
10312
10641
|
const ref = useRef10(null);
|
|
10313
|
-
const [width, setWidth] =
|
|
10314
|
-
|
|
10642
|
+
const [width, setWidth] = useState36(fallback);
|
|
10643
|
+
useEffect26(() => {
|
|
10315
10644
|
const node = ref.current;
|
|
10316
10645
|
if (!node) return;
|
|
10317
10646
|
const apply = () => {
|
|
@@ -10342,13 +10671,13 @@ function ChartFrame({
|
|
|
10342
10671
|
}) {
|
|
10343
10672
|
const [ref, width] = useMeasuredWidth();
|
|
10344
10673
|
const chartId = `records-chart-${useId().replace(/:/g, "")}`;
|
|
10345
|
-
return /* @__PURE__ */
|
|
10674
|
+
return /* @__PURE__ */ jsx40(
|
|
10346
10675
|
"div",
|
|
10347
10676
|
{
|
|
10348
10677
|
ref,
|
|
10349
10678
|
"data-chart": chartId,
|
|
10350
10679
|
style: { height, ...chartVariables(config) },
|
|
10351
|
-
className:
|
|
10680
|
+
className: cn34(
|
|
10352
10681
|
"w-full min-w-0 overflow-hidden text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-layer]:outline-none [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
|
10353
10682
|
className
|
|
10354
10683
|
),
|
|
@@ -10364,27 +10693,27 @@ function ChartTooltipContent({
|
|
|
10364
10693
|
className
|
|
10365
10694
|
}) {
|
|
10366
10695
|
if (!active || !payload || payload.length === 0) return null;
|
|
10367
|
-
return /* @__PURE__ */
|
|
10696
|
+
return /* @__PURE__ */ jsxs36(
|
|
10368
10697
|
"div",
|
|
10369
10698
|
{
|
|
10370
|
-
className:
|
|
10699
|
+
className: cn34(
|
|
10371
10700
|
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
|
|
10372
10701
|
className
|
|
10373
10702
|
),
|
|
10374
10703
|
children: [
|
|
10375
|
-
label ? /* @__PURE__ */
|
|
10376
|
-
/* @__PURE__ */
|
|
10704
|
+
label ? /* @__PURE__ */ jsx40("div", { className: "font-medium", children: label }) : null,
|
|
10705
|
+
/* @__PURE__ */ jsx40("div", { className: "grid gap-1.5", children: payload.map((entry, index) => {
|
|
10377
10706
|
const key = String(entry.dataKey ?? entry.name ?? index);
|
|
10378
|
-
return /* @__PURE__ */
|
|
10379
|
-
/* @__PURE__ */
|
|
10707
|
+
return /* @__PURE__ */ jsxs36("div", { className: "flex w-full items-center gap-2", children: [
|
|
10708
|
+
/* @__PURE__ */ jsx40(
|
|
10380
10709
|
"span",
|
|
10381
10710
|
{
|
|
10382
10711
|
className: "h-2.5 w-2.5 shrink-0 rounded-[2px] bg-[--color-swatch]",
|
|
10383
10712
|
style: { "--color-swatch": entry.color }
|
|
10384
10713
|
}
|
|
10385
10714
|
),
|
|
10386
|
-
/* @__PURE__ */
|
|
10387
|
-
/* @__PURE__ */
|
|
10715
|
+
/* @__PURE__ */ jsx40("span", { className: "text-muted-foreground", children: config[key]?.label ?? key }),
|
|
10716
|
+
/* @__PURE__ */ jsx40("span", { className: "ml-auto font-mono font-medium tabular-nums text-foreground", children: typeof entry.value === "number" ? entry.value.toLocaleString() : entry.value })
|
|
10388
10717
|
] }, key);
|
|
10389
10718
|
}) })
|
|
10390
10719
|
]
|
|
@@ -10397,10 +10726,10 @@ function ChartLegendContent({
|
|
|
10397
10726
|
className
|
|
10398
10727
|
}) {
|
|
10399
10728
|
if (!payload || payload.length === 0) return null;
|
|
10400
|
-
return /* @__PURE__ */
|
|
10729
|
+
return /* @__PURE__ */ jsx40("div", { className: cn34("flex flex-wrap items-center justify-center gap-3 pt-2", className), children: payload.map((entry, index) => {
|
|
10401
10730
|
const key = String(entry.dataKey ?? entry.value ?? index);
|
|
10402
|
-
return /* @__PURE__ */
|
|
10403
|
-
/* @__PURE__ */
|
|
10731
|
+
return /* @__PURE__ */ jsxs36("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
10732
|
+
/* @__PURE__ */ jsx40(
|
|
10404
10733
|
"span",
|
|
10405
10734
|
{
|
|
10406
10735
|
className: "h-2 w-2 shrink-0 rounded-[2px] bg-[--color-swatch]",
|
|
@@ -10435,24 +10764,24 @@ function isSignatureField(field) {
|
|
|
10435
10764
|
}
|
|
10436
10765
|
|
|
10437
10766
|
// src/DocTemplate.tsx
|
|
10438
|
-
import { useCallback as
|
|
10439
|
-
import { useFields as useFields17, useRecordsClient as
|
|
10440
|
-
import { BasicInput as
|
|
10441
|
-
import { jsx as
|
|
10767
|
+
import { useCallback as useCallback23, useEffect as useEffect27, useState as useState37 } from "react";
|
|
10768
|
+
import { useFields as useFields17, useRecordsClient as useRecordsClient29, useTable as useTable14 } from "@ai-matrx/records/react";
|
|
10769
|
+
import { BasicInput as BasicInput12, BasicTextarea as BasicTextarea6, Button as Button33, Label as Label7, Skeleton as Skeleton20, cn as cn35 } from "@ai-matrx/design-system";
|
|
10770
|
+
import { jsx as jsx41, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
10442
10771
|
function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
|
|
10443
|
-
const client =
|
|
10772
|
+
const client = useRecordsClient29();
|
|
10444
10773
|
const claimSeed = useSeedGuard();
|
|
10445
10774
|
const table = useTable14(tableId);
|
|
10446
10775
|
const rights = useTableRights(table.data);
|
|
10447
10776
|
const fields = useFields17(tableId);
|
|
10448
|
-
const [templates, setTemplates] =
|
|
10449
|
-
const [error, setError] =
|
|
10450
|
-
const [activeId, setActiveId] =
|
|
10451
|
-
const [draftName, setDraftName] =
|
|
10452
|
-
const [draftBody, setDraftBody] =
|
|
10453
|
-
const [unresolved, setUnresolved] =
|
|
10454
|
-
const [saving, setSaving] =
|
|
10455
|
-
const load =
|
|
10777
|
+
const [templates, setTemplates] = useState37(null);
|
|
10778
|
+
const [error, setError] = useState37(null);
|
|
10779
|
+
const [activeId, setActiveId] = useState37(activeTemplateId ?? null);
|
|
10780
|
+
const [draftName, setDraftName] = useState37("");
|
|
10781
|
+
const [draftBody, setDraftBody] = useState37("");
|
|
10782
|
+
const [unresolved, setUnresolved] = useState37([]);
|
|
10783
|
+
const [saving, setSaving] = useState37(false);
|
|
10784
|
+
const load = useCallback23(async () => {
|
|
10456
10785
|
const held = await client.docTemplates({ table_id: tableId });
|
|
10457
10786
|
if (!held.ok) {
|
|
10458
10787
|
setError(held.error);
|
|
@@ -10483,10 +10812,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
10483
10812
|
setError(null);
|
|
10484
10813
|
setTemplates(rows);
|
|
10485
10814
|
}, [client, tableId, seed, fields.data]);
|
|
10486
|
-
|
|
10815
|
+
useEffect27(() => {
|
|
10487
10816
|
void load();
|
|
10488
10817
|
}, [load]);
|
|
10489
|
-
|
|
10818
|
+
useEffect27(() => {
|
|
10490
10819
|
if (!templates || templates.length === 0) return;
|
|
10491
10820
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
10492
10821
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -10494,7 +10823,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
10494
10823
|
setDraftBody(chosen.body);
|
|
10495
10824
|
onActiveTemplate?.(chosen);
|
|
10496
10825
|
}, [templates, activeTemplateId]);
|
|
10497
|
-
|
|
10826
|
+
useEffect27(() => {
|
|
10498
10827
|
let cancelled = false;
|
|
10499
10828
|
if (draftBody.trim() === "") {
|
|
10500
10829
|
setUnresolved([]);
|
|
@@ -10524,25 +10853,25 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
10524
10853
|
setActiveId(saved.data);
|
|
10525
10854
|
await load();
|
|
10526
10855
|
}
|
|
10527
|
-
if (fields.error) return /* @__PURE__ */
|
|
10856
|
+
if (fields.error) return /* @__PURE__ */ jsx41(RefusalNotice, { error: fields.error, className });
|
|
10528
10857
|
if (error) {
|
|
10529
|
-
return /* @__PURE__ */
|
|
10858
|
+
return /* @__PURE__ */ jsx41(
|
|
10530
10859
|
RefusalNotice,
|
|
10531
10860
|
{
|
|
10532
10861
|
error,
|
|
10533
10862
|
className,
|
|
10534
|
-
actions: /* @__PURE__ */
|
|
10863
|
+
actions: /* @__PURE__ */ jsx41(Button33, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
10535
10864
|
}
|
|
10536
10865
|
);
|
|
10537
10866
|
}
|
|
10538
|
-
if (templates === null || fields.loading) return /* @__PURE__ */
|
|
10867
|
+
if (templates === null || fields.loading) return /* @__PURE__ */ jsx41(Skeleton20, { className: cn35("h-64 w-full", className) });
|
|
10539
10868
|
if (!rights.admin) {
|
|
10540
|
-
return /* @__PURE__ */
|
|
10869
|
+
return /* @__PURE__ */ jsx41("p", { className: cn35("text-xs text-muted-foreground", className), children: rights.why("structure") });
|
|
10541
10870
|
}
|
|
10542
|
-
return /* @__PURE__ */
|
|
10543
|
-
/* @__PURE__ */
|
|
10544
|
-
templates.map((t) => /* @__PURE__ */
|
|
10545
|
-
|
|
10871
|
+
return /* @__PURE__ */ jsxs37("div", { className: cn35("flex flex-col gap-2", className), children: [
|
|
10872
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Templates", children: [
|
|
10873
|
+
templates.map((t) => /* @__PURE__ */ jsxs37(
|
|
10874
|
+
Button33,
|
|
10546
10875
|
{
|
|
10547
10876
|
size: "sm",
|
|
10548
10877
|
variant: t.id === activeId ? "secondary" : "ghost",
|
|
@@ -10554,7 +10883,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
10554
10883
|
},
|
|
10555
10884
|
children: [
|
|
10556
10885
|
t.name,
|
|
10557
|
-
/* @__PURE__ */
|
|
10886
|
+
/* @__PURE__ */ jsxs37("span", { className: "ml-1 text-[10px] text-muted-foreground", children: [
|
|
10558
10887
|
"v",
|
|
10559
10888
|
t.template_version
|
|
10560
10889
|
] })
|
|
@@ -10562,8 +10891,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
10562
10891
|
},
|
|
10563
10892
|
t.id
|
|
10564
10893
|
)),
|
|
10565
|
-
/* @__PURE__ */
|
|
10566
|
-
|
|
10894
|
+
/* @__PURE__ */ jsx41(
|
|
10895
|
+
Button33,
|
|
10567
10896
|
{
|
|
10568
10897
|
size: "sm",
|
|
10569
10898
|
variant: "ghost",
|
|
@@ -10575,16 +10904,16 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
10575
10904
|
children: "New template"
|
|
10576
10905
|
}
|
|
10577
10906
|
),
|
|
10578
|
-
/* @__PURE__ */
|
|
10907
|
+
/* @__PURE__ */ jsx41(Button33, { size: "sm", className: "ml-auto", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
|
|
10579
10908
|
] }),
|
|
10580
|
-
/* @__PURE__ */
|
|
10581
|
-
/* @__PURE__ */
|
|
10582
|
-
/* @__PURE__ */
|
|
10909
|
+
/* @__PURE__ */ jsxs37("label", { className: "flex flex-col gap-1", children: [
|
|
10910
|
+
/* @__PURE__ */ jsx41(Label7, { className: "text-xs font-medium", children: "Name" }),
|
|
10911
|
+
/* @__PURE__ */ jsx41(BasicInput12, { value: draftName, onChange: (e) => setDraftName(e.target.value), placeholder: "Agreement" })
|
|
10583
10912
|
] }),
|
|
10584
|
-
/* @__PURE__ */
|
|
10585
|
-
/* @__PURE__ */
|
|
10586
|
-
(fields.data ?? []).map((field) => /* @__PURE__ */
|
|
10587
|
-
|
|
10913
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
10914
|
+
/* @__PURE__ */ jsx41("span", { className: "text-xs text-muted-foreground", children: "Insert" }),
|
|
10915
|
+
(fields.data ?? []).map((field) => /* @__PURE__ */ jsx41(
|
|
10916
|
+
Button33,
|
|
10588
10917
|
{
|
|
10589
10918
|
size: "sm",
|
|
10590
10919
|
variant: "ghost",
|
|
@@ -10595,7 +10924,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
10595
10924
|
field.id
|
|
10596
10925
|
))
|
|
10597
10926
|
] }),
|
|
10598
|
-
/* @__PURE__ */
|
|
10927
|
+
/* @__PURE__ */ jsx41(
|
|
10599
10928
|
BasicTextarea6,
|
|
10600
10929
|
{
|
|
10601
10930
|
rows: 10,
|
|
@@ -10605,28 +10934,28 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
10605
10934
|
onChange: (e) => setDraftBody(e.target.value)
|
|
10606
10935
|
}
|
|
10607
10936
|
),
|
|
10608
|
-
unresolved.length > 0 ? /* @__PURE__ */
|
|
10609
|
-
/* @__PURE__ */
|
|
10937
|
+
unresolved.length > 0 ? /* @__PURE__ */ jsx41("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ jsxs37("li", { className: "text-destructive", children: [
|
|
10938
|
+
/* @__PURE__ */ jsx41("code", { children: token.raw }),
|
|
10610
10939
|
" \u2014 ",
|
|
10611
10940
|
token.why
|
|
10612
|
-
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */
|
|
10941
|
+
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ jsx41("p", { className: "text-xs text-muted-foreground", children: "Every token in this body points at a Field this table can answer." }) : null
|
|
10613
10942
|
] });
|
|
10614
10943
|
}
|
|
10615
10944
|
|
|
10616
10945
|
// src/DocRender.tsx
|
|
10617
|
-
import { useCallback as
|
|
10618
|
-
import { useRecordsClient as
|
|
10619
|
-
import { Button as
|
|
10620
|
-
import { jsx as
|
|
10946
|
+
import { useCallback as useCallback24, useEffect as useEffect28, useRef as useRef11, useState as useState38 } from "react";
|
|
10947
|
+
import { useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
|
|
10948
|
+
import { Button as Button34, Skeleton as Skeleton21, cn as cn36 } from "@ai-matrx/design-system";
|
|
10949
|
+
import { jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
10621
10950
|
function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
10622
|
-
const client =
|
|
10623
|
-
const [preview, setPreview] =
|
|
10624
|
-
const [renders, setRenders] =
|
|
10625
|
-
const [showing, setShowing] =
|
|
10626
|
-
const [error, setError] =
|
|
10627
|
-
const [busy, setBusy] =
|
|
10951
|
+
const client = useRecordsClient30();
|
|
10952
|
+
const [preview, setPreview] = useState38(null);
|
|
10953
|
+
const [renders, setRenders] = useState38(null);
|
|
10954
|
+
const [showing, setShowing] = useState38(null);
|
|
10955
|
+
const [error, setError] = useState38(null);
|
|
10956
|
+
const [busy, setBusy] = useState38(null);
|
|
10628
10957
|
const paper = useRef11(null);
|
|
10629
|
-
const load =
|
|
10958
|
+
const load = useCallback24(async () => {
|
|
10630
10959
|
const [body, held] = await Promise.all([
|
|
10631
10960
|
client.docRenderBody({ template_id: templateId, record_id: recordId }),
|
|
10632
10961
|
client.docRenders({ record_id: recordId })
|
|
@@ -10643,7 +10972,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
10643
10972
|
setPreview(body.data);
|
|
10644
10973
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
10645
10974
|
}, [client, templateId, recordId]);
|
|
10646
|
-
|
|
10975
|
+
useEffect28(() => {
|
|
10647
10976
|
void load();
|
|
10648
10977
|
}, [load]);
|
|
10649
10978
|
async function freeze() {
|
|
@@ -10680,22 +11009,22 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
10680
11009
|
}
|
|
10681
11010
|
}
|
|
10682
11011
|
if (error) {
|
|
10683
|
-
return /* @__PURE__ */
|
|
11012
|
+
return /* @__PURE__ */ jsx42(
|
|
10684
11013
|
RefusalNotice,
|
|
10685
11014
|
{
|
|
10686
11015
|
error,
|
|
10687
11016
|
className,
|
|
10688
|
-
actions: /* @__PURE__ */
|
|
11017
|
+
actions: /* @__PURE__ */ jsx42(Button34, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
10689
11018
|
}
|
|
10690
11019
|
);
|
|
10691
11020
|
}
|
|
10692
|
-
if (preview === null || renders === null) return /* @__PURE__ */
|
|
11021
|
+
if (preview === null || renders === null) return /* @__PURE__ */ jsx42(Skeleton21, { className: cn36("h-64 w-full", className) });
|
|
10693
11022
|
const frozen = showing ? renders.find((r) => r.id === showing) ?? null : null;
|
|
10694
11023
|
const text = frozen ? frozen.body : preview;
|
|
10695
|
-
return /* @__PURE__ */
|
|
10696
|
-
/* @__PURE__ */
|
|
10697
|
-
/* @__PURE__ */
|
|
10698
|
-
|
|
11024
|
+
return /* @__PURE__ */ jsxs38("div", { className: cn36("flex flex-col gap-2", className), children: [
|
|
11025
|
+
/* @__PURE__ */ jsxs38("div", { className: "flex items-center gap-1 text-xs", children: [
|
|
11026
|
+
/* @__PURE__ */ jsx42(
|
|
11027
|
+
Button34,
|
|
10699
11028
|
{
|
|
10700
11029
|
size: "sm",
|
|
10701
11030
|
variant: frozen ? "ghost" : "secondary",
|
|
@@ -10704,8 +11033,8 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
10704
11033
|
children: "As it reads now"
|
|
10705
11034
|
}
|
|
10706
11035
|
),
|
|
10707
|
-
renders.map((render) => /* @__PURE__ */
|
|
10708
|
-
|
|
11036
|
+
renders.map((render) => /* @__PURE__ */ jsx42(
|
|
11037
|
+
Button34,
|
|
10709
11038
|
{
|
|
10710
11039
|
size: "sm",
|
|
10711
11040
|
variant: render.id === showing ? "secondary" : "ghost",
|
|
@@ -10716,33 +11045,33 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
10716
11045
|
},
|
|
10717
11046
|
render.id
|
|
10718
11047
|
)),
|
|
10719
|
-
/* @__PURE__ */
|
|
10720
|
-
/* @__PURE__ */
|
|
10721
|
-
/* @__PURE__ */
|
|
11048
|
+
/* @__PURE__ */ jsxs38("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
11049
|
+
/* @__PURE__ */ jsx42(Button34, { size: "sm", disabled: busy !== null, onClick: () => void freeze(), children: busy === "freeze" ? "Freezing\u2026" : "Freeze this version" }),
|
|
11050
|
+
/* @__PURE__ */ jsx42(Button34, { size: "sm", variant: "ghost", disabled: busy !== null, onClick: () => void toPdf(), children: busy === "pdf" ? "Making the PDF\u2026" : "PDF" })
|
|
10722
11051
|
] })
|
|
10723
11052
|
] }),
|
|
10724
|
-
/* @__PURE__ */
|
|
10725
|
-
/* @__PURE__ */
|
|
11053
|
+
/* @__PURE__ */ jsx42("div", { ref: paper, className: "whitespace-pre-wrap rounded border bg-background p-4 text-sm text-foreground", children: text }),
|
|
11054
|
+
/* @__PURE__ */ jsx42("p", { className: "text-xs text-muted-foreground", children: frozen ? `Frozen ${new Date(frozen.rendered_at).toLocaleString()} from template v${frozen.template_version}. Its text and its SHA-256 (${frozen.content_hash.slice(0, 12)}\u2026) are what any signature seals.` : "This is how the template reads against this record right now. Nothing has been frozen, so there is no version for anyone to sign yet." })
|
|
10726
11055
|
] });
|
|
10727
11056
|
}
|
|
10728
11057
|
|
|
10729
11058
|
// src/SignBlock.tsx
|
|
10730
|
-
import { useCallback as
|
|
10731
|
-
import { useFields as useFields18, useRecordsClient as
|
|
10732
|
-
import { BasicInput as
|
|
11059
|
+
import { useCallback as useCallback25, useEffect as useEffect29, useState as useState39 } from "react";
|
|
11060
|
+
import { useFields as useFields18, useRecordsClient as useRecordsClient31 } from "@ai-matrx/records/react";
|
|
11061
|
+
import { BasicInput as BasicInput13, Button as Button35, Skeleton as Skeleton22, cn as cn37 } from "@ai-matrx/design-system";
|
|
10733
11062
|
import { useTable as useTable15 } from "@ai-matrx/records/react";
|
|
10734
|
-
import { jsx as
|
|
11063
|
+
import { jsx as jsx43, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
10735
11064
|
function SignBlock({ tableId, recordId, render, className }) {
|
|
10736
|
-
const client =
|
|
11065
|
+
const client = useRecordsClient31();
|
|
10737
11066
|
const table = useTable15(tableId);
|
|
10738
11067
|
const rights = useTableRights(table.data);
|
|
10739
11068
|
const fields = useFields18(tableId);
|
|
10740
|
-
const [signatures, setSignatures] =
|
|
10741
|
-
const [verdicts, setVerdicts] =
|
|
10742
|
-
const [error, setError] =
|
|
10743
|
-
const [name, setName] =
|
|
10744
|
-
const [busy, setBusy] =
|
|
10745
|
-
const load =
|
|
11069
|
+
const [signatures, setSignatures] = useState39(null);
|
|
11070
|
+
const [verdicts, setVerdicts] = useState39({});
|
|
11071
|
+
const [error, setError] = useState39(null);
|
|
11072
|
+
const [name, setName] = useState39("");
|
|
11073
|
+
const [busy, setBusy] = useState39(false);
|
|
11074
|
+
const load = useCallback25(async () => {
|
|
10746
11075
|
const held = await client.docSignatures({ record_id: recordId });
|
|
10747
11076
|
if (!held.ok) {
|
|
10748
11077
|
setError(held.error);
|
|
@@ -10757,7 +11086,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
10757
11086
|
}
|
|
10758
11087
|
setVerdicts(answers);
|
|
10759
11088
|
}, [client, recordId]);
|
|
10760
|
-
|
|
11089
|
+
useEffect29(() => {
|
|
10761
11090
|
void load();
|
|
10762
11091
|
}, [load]);
|
|
10763
11092
|
async function sign(field) {
|
|
@@ -10777,58 +11106,58 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
10777
11106
|
setName("");
|
|
10778
11107
|
await load();
|
|
10779
11108
|
}
|
|
10780
|
-
if (fields.error) return /* @__PURE__ */
|
|
11109
|
+
if (fields.error) return /* @__PURE__ */ jsx43(RefusalNotice, { error: fields.error, className });
|
|
10781
11110
|
if (error) {
|
|
10782
|
-
return /* @__PURE__ */
|
|
11111
|
+
return /* @__PURE__ */ jsx43(
|
|
10783
11112
|
RefusalNotice,
|
|
10784
11113
|
{
|
|
10785
11114
|
error,
|
|
10786
11115
|
className,
|
|
10787
|
-
actions: /* @__PURE__ */
|
|
11116
|
+
actions: /* @__PURE__ */ jsx43(Button35, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
10788
11117
|
}
|
|
10789
11118
|
);
|
|
10790
11119
|
}
|
|
10791
|
-
if (fields.loading || signatures === null) return /* @__PURE__ */
|
|
11120
|
+
if (fields.loading || signatures === null) return /* @__PURE__ */ jsx43(Skeleton22, { className: cn37("h-32 w-full", className) });
|
|
10792
11121
|
const signable = (fields.data ?? []).filter(isSignatureField);
|
|
10793
|
-
return /* @__PURE__ */
|
|
10794
|
-
/* @__PURE__ */
|
|
10795
|
-
/* @__PURE__ */
|
|
10796
|
-
/* @__PURE__ */
|
|
11122
|
+
return /* @__PURE__ */ jsxs39("div", { className: cn37("flex flex-col gap-2", className), children: [
|
|
11123
|
+
/* @__PURE__ */ jsxs39("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
11124
|
+
/* @__PURE__ */ jsx43("span", { className: "font-medium text-foreground", children: "Signatures" }),
|
|
11125
|
+
/* @__PURE__ */ jsxs39("span", { children: [
|
|
10797
11126
|
signatures.length,
|
|
10798
11127
|
" on this record"
|
|
10799
11128
|
] })
|
|
10800
11129
|
] }),
|
|
10801
|
-
signatures.length > 0 ? /* @__PURE__ */
|
|
11130
|
+
signatures.length > 0 ? /* @__PURE__ */ jsx43("ul", { className: "flex flex-col gap-1", children: signatures.map((signature) => {
|
|
10802
11131
|
const verdict = verdicts[signature.id];
|
|
10803
11132
|
const intact = typeof verdict === "object" && verdict !== null ? verdict.intact : void 0;
|
|
10804
|
-
return /* @__PURE__ */
|
|
10805
|
-
/* @__PURE__ */
|
|
10806
|
-
/* @__PURE__ */
|
|
10807
|
-
/* @__PURE__ */
|
|
10808
|
-
/* @__PURE__ */
|
|
10809
|
-
/* @__PURE__ */
|
|
11133
|
+
return /* @__PURE__ */ jsxs39("li", { className: "rounded border px-2 py-1.5 text-xs", children: [
|
|
11134
|
+
/* @__PURE__ */ jsxs39("div", { className: "flex items-center gap-2", children: [
|
|
11135
|
+
/* @__PURE__ */ jsx43("span", { className: "font-medium", children: signature.signer_name }),
|
|
11136
|
+
/* @__PURE__ */ jsx43("span", { className: "text-muted-foreground", children: signature.field_key }),
|
|
11137
|
+
/* @__PURE__ */ jsx43("span", { className: "text-muted-foreground", children: new Date(signature.signed_at).toLocaleString() }),
|
|
11138
|
+
/* @__PURE__ */ jsxs39("span", { className: "ml-auto font-mono text-[10px] text-muted-foreground", children: [
|
|
10810
11139
|
"v",
|
|
10811
11140
|
signature.document_version,
|
|
10812
11141
|
" \xB7 ",
|
|
10813
11142
|
signature.document_hash.slice(0, 12)
|
|
10814
11143
|
] })
|
|
10815
11144
|
] }),
|
|
10816
|
-
/* @__PURE__ */
|
|
11145
|
+
/* @__PURE__ */ jsx43("p", { className: cn37("mt-0.5", intact === false ? "text-destructive" : "text-muted-foreground"), children: intact === true ? "The store says what was signed is still exactly what is there." : intact === false ? typeof verdict === "object" && verdict !== null && "reason" in verdict ? String(verdict.reason) : "The store says what is there no longer matches what was signed." : String(verdict ?? "") })
|
|
10817
11146
|
] }, signature.id);
|
|
10818
11147
|
}) }) : null,
|
|
10819
|
-
!render ? /* @__PURE__ */
|
|
11148
|
+
!render ? /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: 'Nothing has been frozen yet, so there is no version to sign. A signature seals one exact document version and its hash \u2014 "the document as it reads today" is not something anyone can agree to.' }) : signable.length === 0 ? /* @__PURE__ */ jsxs39("p", { className: "text-xs text-muted-foreground", children: [
|
|
10820
11149
|
"This table has no signature Field, so there is nowhere to write a signature. A signature is a Value on a text Field whose format is ",
|
|
10821
|
-
/* @__PURE__ */
|
|
11150
|
+
/* @__PURE__ */ jsx43("code", { children: "signature" }),
|
|
10822
11151
|
" \u2014 add one and the signing appears."
|
|
10823
|
-
] }) : !rights.write ? /* @__PURE__ */
|
|
11152
|
+
] }) : !rights.write ? /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: rights.why("write") }) : /* @__PURE__ */ jsx43("div", { className: "flex flex-col gap-1", children: signable.map((field) => {
|
|
10824
11153
|
const already = signatures.find((s) => s.field_key === field.key);
|
|
10825
11154
|
if (already) {
|
|
10826
|
-
return /* @__PURE__ */
|
|
11155
|
+
return /* @__PURE__ */ jsxs39("p", { className: "text-xs text-muted-foreground", children: [
|
|
10827
11156
|
fieldName(field),
|
|
10828
11157
|
" is signed, and a signature is immutable once made. A further agreement is a further Field with its own signature, or a further version with its own seal."
|
|
10829
11158
|
] }, field.id);
|
|
10830
11159
|
}
|
|
10831
|
-
return /* @__PURE__ */
|
|
11160
|
+
return /* @__PURE__ */ jsxs39(
|
|
10832
11161
|
"form",
|
|
10833
11162
|
{
|
|
10834
11163
|
className: "flex items-center gap-1",
|
|
@@ -10837,8 +11166,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
10837
11166
|
if (name.trim() !== "") void sign(field);
|
|
10838
11167
|
},
|
|
10839
11168
|
children: [
|
|
10840
|
-
/* @__PURE__ */
|
|
10841
|
-
|
|
11169
|
+
/* @__PURE__ */ jsx43(
|
|
11170
|
+
BasicInput13,
|
|
10842
11171
|
{
|
|
10843
11172
|
className: "h-8 w-56 text-xs",
|
|
10844
11173
|
"aria-label": `Sign ${fieldName(field)}`,
|
|
@@ -10847,8 +11176,8 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
10847
11176
|
onChange: (e) => setName(e.target.value)
|
|
10848
11177
|
}
|
|
10849
11178
|
),
|
|
10850
|
-
/* @__PURE__ */
|
|
10851
|
-
/* @__PURE__ */
|
|
11179
|
+
/* @__PURE__ */ jsx43(Button35, { size: "sm", type: "submit", disabled: busy || name.trim() === "", children: "Sign" }),
|
|
11180
|
+
/* @__PURE__ */ jsxs39("span", { className: "text-xs text-muted-foreground", children: [
|
|
10852
11181
|
"seals version ",
|
|
10853
11182
|
render.template_version,
|
|
10854
11183
|
" \xB7 ",
|
|
@@ -10864,32 +11193,32 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
10864
11193
|
}
|
|
10865
11194
|
|
|
10866
11195
|
// src/NotifyRuleEditor.tsx
|
|
10867
|
-
import { useCallback as
|
|
10868
|
-
import { useRecordsClient as
|
|
10869
|
-
import { Button as
|
|
10870
|
-
import { jsx as
|
|
10871
|
-
var
|
|
11196
|
+
import { useCallback as useCallback26, useEffect as useEffect30, useState as useState40 } from "react";
|
|
11197
|
+
import { useRecordsClient as useRecordsClient32, useTable as useTable16 } from "@ai-matrx/records/react";
|
|
11198
|
+
import { Button as Button36, Skeleton as Skeleton23, cn as cn38 } from "@ai-matrx/design-system";
|
|
11199
|
+
import { jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
11200
|
+
var CADENCE_WORDS2 = {
|
|
10872
11201
|
instant: "as it happens",
|
|
10873
11202
|
hourly: "hourly summary",
|
|
10874
11203
|
daily: "daily summary",
|
|
10875
11204
|
weekly: "weekly summary"
|
|
10876
11205
|
};
|
|
10877
|
-
var
|
|
11206
|
+
var CHANNEL_WORDS3 = {
|
|
10878
11207
|
in_app: "in the app",
|
|
10879
11208
|
email: "by email",
|
|
10880
11209
|
sms: "by text"
|
|
10881
11210
|
};
|
|
10882
11211
|
function NotifyRuleEditor({ tableId, seed, className }) {
|
|
10883
|
-
const client =
|
|
11212
|
+
const client = useRecordsClient32();
|
|
10884
11213
|
const host = useRecordsUi();
|
|
10885
11214
|
const table = useTable16(tableId);
|
|
10886
11215
|
const rights = useTableRights(table.data);
|
|
10887
|
-
const [subscriptions, setSubscriptions] =
|
|
10888
|
-
const [cadences, setCadences] =
|
|
10889
|
-
const [views, setViews] =
|
|
10890
|
-
const [error, setError] =
|
|
10891
|
-
const [busy, setBusy] =
|
|
10892
|
-
const load =
|
|
11216
|
+
const [subscriptions, setSubscriptions] = useState40(null);
|
|
11217
|
+
const [cadences, setCadences] = useState40([]);
|
|
11218
|
+
const [views, setViews] = useState40(null);
|
|
11219
|
+
const [error, setError] = useState40(null);
|
|
11220
|
+
const [busy, setBusy] = useState40(false);
|
|
11221
|
+
const load = useCallback26(async () => {
|
|
10893
11222
|
const [held, offered] = await Promise.all([
|
|
10894
11223
|
// THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
|
|
10895
11224
|
// to them plus — only where they hold admin on this Table — anyone's over
|
|
@@ -10909,10 +11238,10 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
10909
11238
|
if (host.savedViews) setViews(await host.savedViews());
|
|
10910
11239
|
else setViews(null);
|
|
10911
11240
|
}, [client, host, tableId]);
|
|
10912
|
-
|
|
11241
|
+
useEffect30(() => {
|
|
10913
11242
|
void load();
|
|
10914
11243
|
}, [load]);
|
|
10915
|
-
const write =
|
|
11244
|
+
const write = useCallback26(
|
|
10916
11245
|
async (spec) => {
|
|
10917
11246
|
const declared = await client.subscriptionDeclare({
|
|
10918
11247
|
table_id: tableId,
|
|
@@ -10937,7 +11266,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
10937
11266
|
},
|
|
10938
11267
|
[client, tableId]
|
|
10939
11268
|
);
|
|
10940
|
-
|
|
11269
|
+
useEffect30(() => {
|
|
10941
11270
|
if (!subscriptions || !seed || seed.length === 0) return;
|
|
10942
11271
|
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
10943
11272
|
if (missing.length === 0) return;
|
|
@@ -10951,7 +11280,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
10951
11280
|
async function subscribe(viewId, viewName, cadence, channel, schedule, quiet) {
|
|
10952
11281
|
setBusy(true);
|
|
10953
11282
|
const ok = await write({
|
|
10954
|
-
name: `${viewName} \u2014 ${
|
|
11283
|
+
name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
|
|
10955
11284
|
savedViewId: viewId,
|
|
10956
11285
|
cadence,
|
|
10957
11286
|
channel,
|
|
@@ -10972,45 +11301,45 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
10972
11301
|
await load();
|
|
10973
11302
|
}
|
|
10974
11303
|
if (error) {
|
|
10975
|
-
return /* @__PURE__ */
|
|
11304
|
+
return /* @__PURE__ */ jsx44(
|
|
10976
11305
|
RefusalNotice,
|
|
10977
11306
|
{
|
|
10978
11307
|
error,
|
|
10979
11308
|
className,
|
|
10980
|
-
actions: /* @__PURE__ */
|
|
11309
|
+
actions: /* @__PURE__ */ jsx44(Button36, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
10981
11310
|
}
|
|
10982
11311
|
);
|
|
10983
11312
|
}
|
|
10984
|
-
if (subscriptions === null) return /* @__PURE__ */
|
|
10985
|
-
return /* @__PURE__ */
|
|
10986
|
-
/* @__PURE__ */
|
|
10987
|
-
/* @__PURE__ */
|
|
10988
|
-
/* @__PURE__ */
|
|
11313
|
+
if (subscriptions === null) return /* @__PURE__ */ jsx44(Skeleton23, { className: cn38("h-40 w-full", className) });
|
|
11314
|
+
return /* @__PURE__ */ jsxs40("div", { className: cn38("flex flex-col gap-2", className), children: [
|
|
11315
|
+
/* @__PURE__ */ jsxs40("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
11316
|
+
/* @__PURE__ */ jsx44("span", { className: "font-medium text-foreground", children: "Tell me when" }),
|
|
11317
|
+
/* @__PURE__ */ jsxs40("span", { children: [
|
|
10989
11318
|
subscriptions.length,
|
|
10990
11319
|
" subscription",
|
|
10991
11320
|
subscriptions.length === 1 ? "" : "s"
|
|
10992
11321
|
] })
|
|
10993
11322
|
] }),
|
|
10994
|
-
subscriptions.length === 0 ? /* @__PURE__ */
|
|
10995
|
-
/* @__PURE__ */
|
|
10996
|
-
/* @__PURE__ */
|
|
10997
|
-
/* @__PURE__ */
|
|
10998
|
-
held.schedule ? /* @__PURE__ */
|
|
10999
|
-
held.next_digest_at ? /* @__PURE__ */
|
|
11323
|
+
subscriptions.length === 0 ? /* @__PURE__ */ jsx44("p", { className: "text-xs text-muted-foreground", children: "Nothing is telling anyone about this yet. A subscription is a Rule over a saved view, so what you are told about is exactly what that view holds." }) : /* @__PURE__ */ jsx44("ul", { className: "flex flex-col gap-1", children: subscriptions.map((held) => /* @__PURE__ */ jsxs40("li", { className: "flex items-center gap-2 rounded border px-2 py-1.5 text-xs", children: [
|
|
11324
|
+
/* @__PURE__ */ jsx44("span", { className: "truncate font-medium", children: held.name }),
|
|
11325
|
+
/* @__PURE__ */ jsx44("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
|
|
11326
|
+
/* @__PURE__ */ jsx44("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
|
|
11327
|
+
held.schedule ? /* @__PURE__ */ jsx44("span", { className: "text-muted-foreground", children: held.schedule }) : null,
|
|
11328
|
+
held.next_digest_at ? /* @__PURE__ */ jsxs40("span", { className: "text-muted-foreground", children: [
|
|
11000
11329
|
"next ",
|
|
11001
11330
|
new Date(held.next_digest_at).toLocaleString()
|
|
11002
11331
|
] }) : null,
|
|
11003
|
-
held.quiet_hours ? /* @__PURE__ */
|
|
11332
|
+
held.quiet_hours ? /* @__PURE__ */ jsxs40("span", { className: "text-muted-foreground", children: [
|
|
11004
11333
|
"quiet ",
|
|
11005
11334
|
held.quiet_hours.start,
|
|
11006
11335
|
"\u2013",
|
|
11007
11336
|
held.quiet_hours.end
|
|
11008
11337
|
] }) : null,
|
|
11009
|
-
held.recipient_user_id === null ? /* @__PURE__ */
|
|
11010
|
-
held.saved_view_id === null ? /* @__PURE__ */
|
|
11011
|
-
held.muted ? /* @__PURE__ */
|
|
11012
|
-
rights.write ? /* @__PURE__ */
|
|
11013
|
-
|
|
11338
|
+
held.recipient_user_id === null ? /* @__PURE__ */ jsx44("span", { className: "text-destructive", children: "This one has nobody to tell, so it fires at nobody." }) : null,
|
|
11339
|
+
held.saved_view_id === null ? /* @__PURE__ */ jsx44("span", { className: "text-destructive", children: "This one names no view, so the store never admits a record to it." }) : null,
|
|
11340
|
+
held.muted ? /* @__PURE__ */ jsx44("span", { className: "text-muted-foreground", children: "Switched off \u2014 it tells nobody until somebody switches it back on." }) : null,
|
|
11341
|
+
rights.write ? /* @__PURE__ */ jsx44(
|
|
11342
|
+
Button36,
|
|
11014
11343
|
{
|
|
11015
11344
|
size: "sm",
|
|
11016
11345
|
variant: "ghost",
|
|
@@ -11021,7 +11350,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
11021
11350
|
}
|
|
11022
11351
|
) : null
|
|
11023
11352
|
] }, held.rule_id)) }),
|
|
11024
|
-
views === null ? /* @__PURE__ */
|
|
11353
|
+
views === null ? /* @__PURE__ */ jsx44("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_SAVED_VIEWS_REASON }) : views.length === 0 ? /* @__PURE__ */ jsx44("p", { className: "text-xs text-muted-foreground", children: "This organization has no saved views yet, so there is nothing to subscribe to. Save a view first." }) : rights.write ? /* @__PURE__ */ jsxs40(
|
|
11025
11354
|
"form",
|
|
11026
11355
|
{
|
|
11027
11356
|
className: "flex flex-wrap items-center gap-1",
|
|
@@ -11039,10 +11368,10 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
11039
11368
|
if (view) void subscribe(view, name, cadence, channel, schedule, quiet);
|
|
11040
11369
|
},
|
|
11041
11370
|
children: [
|
|
11042
|
-
/* @__PURE__ */
|
|
11043
|
-
/* @__PURE__ */
|
|
11044
|
-
/* @__PURE__ */
|
|
11045
|
-
/* @__PURE__ */
|
|
11371
|
+
/* @__PURE__ */ jsx44("select", { name: "view", "aria-label": "View to be told about", className: "h-7 rounded border bg-background px-1 text-xs", children: views.map((view) => /* @__PURE__ */ jsx44("option", { value: view.id, children: view.name }, view.id)) }),
|
|
11372
|
+
/* @__PURE__ */ jsx44("select", { name: "cadence", "aria-label": "How often", className: "h-7 rounded border bg-background px-1 text-xs", children: (cadences.length > 0 ? cadences : ["instant"]).map((cadence) => /* @__PURE__ */ jsx44("option", { value: cadence, children: CADENCE_WORDS2[cadence] ?? cadence }, cadence)) }),
|
|
11373
|
+
/* @__PURE__ */ jsx44("select", { name: "channel", "aria-label": "Where it arrives", className: "h-7 rounded border bg-background px-1 text-xs", children: ["in_app", "email", "sms"].map((channel) => /* @__PURE__ */ jsx44("option", { value: channel, children: CHANNEL_WORDS3[channel] }, channel)) }),
|
|
11374
|
+
/* @__PURE__ */ jsx44(
|
|
11046
11375
|
"input",
|
|
11047
11376
|
{
|
|
11048
11377
|
name: "schedule",
|
|
@@ -11051,8 +11380,8 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
11051
11380
|
className: "h-7 w-28 rounded border bg-background px-1 text-xs"
|
|
11052
11381
|
}
|
|
11053
11382
|
),
|
|
11054
|
-
/* @__PURE__ */
|
|
11055
|
-
/* @__PURE__ */
|
|
11383
|
+
/* @__PURE__ */ jsx44("span", { className: "text-xs text-muted-foreground", children: "not between" }),
|
|
11384
|
+
/* @__PURE__ */ jsx44(
|
|
11056
11385
|
"input",
|
|
11057
11386
|
{
|
|
11058
11387
|
name: "quiet_start",
|
|
@@ -11061,8 +11390,8 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
11061
11390
|
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
11062
11391
|
}
|
|
11063
11392
|
),
|
|
11064
|
-
/* @__PURE__ */
|
|
11065
|
-
/* @__PURE__ */
|
|
11393
|
+
/* @__PURE__ */ jsx44("span", { className: "text-xs text-muted-foreground", children: "and" }),
|
|
11394
|
+
/* @__PURE__ */ jsx44(
|
|
11066
11395
|
"input",
|
|
11067
11396
|
{
|
|
11068
11397
|
name: "quiet_end",
|
|
@@ -11071,16 +11400,16 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
11071
11400
|
className: "h-7 rounded border bg-background px-1 text-xs"
|
|
11072
11401
|
}
|
|
11073
11402
|
),
|
|
11074
|
-
/* @__PURE__ */
|
|
11075
|
-
/* @__PURE__ */
|
|
11403
|
+
/* @__PURE__ */ jsx44(Button36, { size: "sm", type: "submit", disabled: busy, children: "Tell me" }),
|
|
11404
|
+
/* @__PURE__ */ jsx44("p", { className: "w-full text-xs text-muted-foreground", children: "A send inside your quiet hours is held until they end \u2014 it is never dropped. Leave the schedule blank for the store's own default of 08:00." })
|
|
11076
11405
|
]
|
|
11077
11406
|
}
|
|
11078
|
-
) : /* @__PURE__ */
|
|
11407
|
+
) : /* @__PURE__ */ jsx44("p", { className: "text-xs text-muted-foreground", children: rights.why("write") })
|
|
11079
11408
|
] });
|
|
11080
11409
|
}
|
|
11081
11410
|
|
|
11082
11411
|
// src/ChartBlock.tsx
|
|
11083
|
-
import { useMemo as
|
|
11412
|
+
import { useMemo as useMemo26 } from "react";
|
|
11084
11413
|
import {
|
|
11085
11414
|
Bar,
|
|
11086
11415
|
BarChart,
|
|
@@ -11099,8 +11428,8 @@ import {
|
|
|
11099
11428
|
measureKey
|
|
11100
11429
|
} from "@ai-matrx/records";
|
|
11101
11430
|
import { mapPgError } from "@ai-matrx/records/core";
|
|
11102
|
-
import { cn as
|
|
11103
|
-
import { Fragment as
|
|
11431
|
+
import { cn as cn39 } from "@ai-matrx/design-system";
|
|
11432
|
+
import { Fragment as Fragment19, jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
11104
11433
|
function pretty(n) {
|
|
11105
11434
|
return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
|
|
11106
11435
|
}
|
|
@@ -11120,7 +11449,7 @@ function ChartBlock({ block, subject, className }) {
|
|
|
11120
11449
|
const measures = block.measures && block.measures.length > 0 ? block.measures : [{ op: "count" }];
|
|
11121
11450
|
const primary = measureKey(measures[0]);
|
|
11122
11451
|
const series = [primary];
|
|
11123
|
-
const points =
|
|
11452
|
+
const points = useMemo26(() => {
|
|
11124
11453
|
if (kind === "stuck") return [];
|
|
11125
11454
|
const rows = block.rows ?? [];
|
|
11126
11455
|
return rows.map((row) => ({
|
|
@@ -11132,7 +11461,7 @@ function ChartBlock({ block, subject, className }) {
|
|
|
11132
11461
|
n: row.row_count
|
|
11133
11462
|
}));
|
|
11134
11463
|
}, [block.rows, kind, series.join("|")]);
|
|
11135
|
-
const config =
|
|
11464
|
+
const config = useMemo26(() => {
|
|
11136
11465
|
const out = {};
|
|
11137
11466
|
series.forEach((key, index) => {
|
|
11138
11467
|
out[key] = {
|
|
@@ -11156,18 +11485,18 @@ function ChartBlock({ block, subject, className }) {
|
|
|
11156
11485
|
label: point.label === "All records" ? block.title : `${block.title} \xB7 ${point.label}`
|
|
11157
11486
|
});
|
|
11158
11487
|
} : null;
|
|
11159
|
-
return /* @__PURE__ */
|
|
11160
|
-
/* @__PURE__ */
|
|
11161
|
-
/* @__PURE__ */
|
|
11162
|
-
/* @__PURE__ */
|
|
11163
|
-
typeof block.ms === "number" ? /* @__PURE__ */
|
|
11488
|
+
return /* @__PURE__ */ jsxs41("figure", { className: cn39("flex min-w-0 flex-col gap-1.5 rounded border p-2", className), children: [
|
|
11489
|
+
/* @__PURE__ */ jsxs41("figcaption", { className: "flex items-baseline gap-2 text-xs", children: [
|
|
11490
|
+
/* @__PURE__ */ jsx45("span", { className: "truncate font-medium", children: block.title || CHART_KIND_LABEL[kind] }),
|
|
11491
|
+
/* @__PURE__ */ jsx45("span", { className: "ml-auto shrink-0 text-muted-foreground", children: kind === "stuck" ? `${block.days ?? 14} days` : primary.replace("_", " of ") }),
|
|
11492
|
+
typeof block.ms === "number" ? /* @__PURE__ */ jsxs41("span", { className: "shrink-0 tabular-nums text-muted-foreground/70", title: "how long the store took", children: [
|
|
11164
11493
|
pretty(block.ms),
|
|
11165
11494
|
" ms"
|
|
11166
11495
|
] }) : null
|
|
11167
11496
|
] }),
|
|
11168
|
-
block.refused ? /* @__PURE__ */
|
|
11169
|
-
/* @__PURE__ */
|
|
11170
|
-
/* @__PURE__ */
|
|
11497
|
+
block.refused ? /* @__PURE__ */ jsx45(RefusalNotice, { error: asRefusal(block) }) : needs ? /* @__PURE__ */ jsx45("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: needs }) : kind === "stuck" ? /* @__PURE__ */ jsx45(StuckList, { rows: block.rows ?? [] }) : points.length === 0 ? /* @__PURE__ */ jsx45("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: "The store answered this question with no groups at all, so there is nothing to draw yet." }) : /* @__PURE__ */ jsx45(Fragment19, { children: kind === "number" ? /* @__PURE__ */ jsx45(BigNumber, { points, measure: primary, onDrill: drill }) : kind === "table" ? /* @__PURE__ */ jsx45(GroupTable, { points, series, onDrill: drill }) : /* @__PURE__ */ jsxs41(Fragment19, { children: [
|
|
11498
|
+
/* @__PURE__ */ jsx45(Drawing, { kind, points, series, config, onDrill: drill }),
|
|
11499
|
+
/* @__PURE__ */ jsx45(Values, { points, measure: primary, config, onDrill: drill })
|
|
11171
11500
|
] }) })
|
|
11172
11501
|
] });
|
|
11173
11502
|
}
|
|
@@ -11178,16 +11507,16 @@ function BigNumber({
|
|
|
11178
11507
|
}) {
|
|
11179
11508
|
const total = points.reduce((sum, p) => sum + (p.values[measure] ?? 0), 0);
|
|
11180
11509
|
const records = points.reduce((sum, p) => sum + p.n, 0);
|
|
11181
|
-
const body = /* @__PURE__ */
|
|
11182
|
-
/* @__PURE__ */
|
|
11183
|
-
/* @__PURE__ */
|
|
11510
|
+
const body = /* @__PURE__ */ jsxs41(Fragment19, { children: [
|
|
11511
|
+
/* @__PURE__ */ jsx45("span", { className: "text-2xl font-semibold tabular-nums", children: pretty(total) }),
|
|
11512
|
+
/* @__PURE__ */ jsxs41("span", { className: "text-xs text-muted-foreground", children: [
|
|
11184
11513
|
"from ",
|
|
11185
11514
|
records.toLocaleString(),
|
|
11186
11515
|
" records"
|
|
11187
11516
|
] })
|
|
11188
11517
|
] });
|
|
11189
|
-
if (!onDrill || !points[0]) return /* @__PURE__ */
|
|
11190
|
-
return /* @__PURE__ */
|
|
11518
|
+
if (!onDrill || !points[0]) return /* @__PURE__ */ jsx45("div", { className: "flex items-baseline gap-2 px-1 py-2", children: body });
|
|
11519
|
+
return /* @__PURE__ */ jsx45(
|
|
11191
11520
|
"button",
|
|
11192
11521
|
{
|
|
11193
11522
|
type: "button",
|
|
@@ -11203,19 +11532,19 @@ function Values({
|
|
|
11203
11532
|
config,
|
|
11204
11533
|
onDrill
|
|
11205
11534
|
}) {
|
|
11206
|
-
return /* @__PURE__ */
|
|
11207
|
-
const text = /* @__PURE__ */
|
|
11208
|
-
/* @__PURE__ */
|
|
11535
|
+
return /* @__PURE__ */ jsx45("ul", { className: "flex flex-wrap gap-x-2 gap-y-0.5", children: points.map((point) => {
|
|
11536
|
+
const text = /* @__PURE__ */ jsxs41(Fragment19, { children: [
|
|
11537
|
+
/* @__PURE__ */ jsx45(
|
|
11209
11538
|
"span",
|
|
11210
11539
|
{
|
|
11211
11540
|
className: "h-2 w-2 shrink-0 rounded-[2px]",
|
|
11212
11541
|
style: { background: String(config[point.label]?.color ?? SERIES_COLORS[0]) }
|
|
11213
11542
|
}
|
|
11214
11543
|
),
|
|
11215
|
-
/* @__PURE__ */
|
|
11216
|
-
/* @__PURE__ */
|
|
11544
|
+
/* @__PURE__ */ jsx45("span", { className: "truncate", children: point.label }),
|
|
11545
|
+
/* @__PURE__ */ jsx45("span", { className: "tabular-nums text-muted-foreground", children: pretty(point.values[measure] ?? 0) })
|
|
11217
11546
|
] });
|
|
11218
|
-
return /* @__PURE__ */
|
|
11547
|
+
return /* @__PURE__ */ jsx45("li", { className: "min-w-0", children: onDrill ? /* @__PURE__ */ jsx45(
|
|
11219
11548
|
"button",
|
|
11220
11549
|
{
|
|
11221
11550
|
type: "button",
|
|
@@ -11223,7 +11552,7 @@ function Values({
|
|
|
11223
11552
|
onClick: () => onDrill(point),
|
|
11224
11553
|
children: text
|
|
11225
11554
|
}
|
|
11226
|
-
) : /* @__PURE__ */
|
|
11555
|
+
) : /* @__PURE__ */ jsx45("span", { className: "flex min-w-0 items-center gap-1.5 px-1 py-0.5 text-xs", children: text }) }, point.label);
|
|
11227
11556
|
}) });
|
|
11228
11557
|
}
|
|
11229
11558
|
function GroupTable({
|
|
@@ -11231,8 +11560,8 @@ function GroupTable({
|
|
|
11231
11560
|
series,
|
|
11232
11561
|
onDrill
|
|
11233
11562
|
}) {
|
|
11234
|
-
return /* @__PURE__ */
|
|
11235
|
-
/* @__PURE__ */
|
|
11563
|
+
return /* @__PURE__ */ jsx45("table", { className: "w-full text-xs", children: /* @__PURE__ */ jsx45("tbody", { children: points.map((point) => /* @__PURE__ */ jsxs41("tr", { className: "border-t first:border-t-0", children: [
|
|
11564
|
+
/* @__PURE__ */ jsx45("td", { className: "min-w-0 truncate py-0.5 pr-2", children: onDrill ? /* @__PURE__ */ jsx45(
|
|
11236
11565
|
"button",
|
|
11237
11566
|
{
|
|
11238
11567
|
type: "button",
|
|
@@ -11241,29 +11570,29 @@ function GroupTable({
|
|
|
11241
11570
|
children: point.label
|
|
11242
11571
|
}
|
|
11243
11572
|
) : point.label }),
|
|
11244
|
-
series.map((key) => /* @__PURE__ */
|
|
11245
|
-
/* @__PURE__ */
|
|
11573
|
+
series.map((key) => /* @__PURE__ */ jsx45("td", { className: "py-0.5 pl-2 text-right tabular-nums", children: pretty(point.values[key] ?? 0) }, key)),
|
|
11574
|
+
/* @__PURE__ */ jsx45("td", { className: "py-0.5 pl-2 text-right tabular-nums text-muted-foreground", children: point.n })
|
|
11246
11575
|
] }, point.label)) }) });
|
|
11247
11576
|
}
|
|
11248
11577
|
function StuckList({ rows }) {
|
|
11249
11578
|
if (rows.length === 0) {
|
|
11250
|
-
return /* @__PURE__ */
|
|
11579
|
+
return /* @__PURE__ */ jsx45("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: "Nothing has sat still this long. This block goes quiet when the work is moving." });
|
|
11251
11580
|
}
|
|
11252
|
-
return /* @__PURE__ */
|
|
11253
|
-
/* @__PURE__ */
|
|
11254
|
-
/* @__PURE__ */
|
|
11255
|
-
/* @__PURE__ */
|
|
11256
|
-
/* @__PURE__ */
|
|
11581
|
+
return /* @__PURE__ */ jsxs41("table", { className: "w-full table-fixed text-xs", children: [
|
|
11582
|
+
/* @__PURE__ */ jsxs41("colgroup", { children: [
|
|
11583
|
+
/* @__PURE__ */ jsx45("col", { className: "w-[45%]" }),
|
|
11584
|
+
/* @__PURE__ */ jsx45("col", { className: "w-[33%]" }),
|
|
11585
|
+
/* @__PURE__ */ jsx45("col", { className: "w-[22%]" })
|
|
11257
11586
|
] }),
|
|
11258
|
-
/* @__PURE__ */
|
|
11259
|
-
/* @__PURE__ */
|
|
11260
|
-
/* @__PURE__ */
|
|
11261
|
-
/* @__PURE__ */
|
|
11587
|
+
/* @__PURE__ */ jsx45("thead", { className: "sr-only", children: /* @__PURE__ */ jsxs41("tr", { children: [
|
|
11588
|
+
/* @__PURE__ */ jsx45("th", { children: "Record" }),
|
|
11589
|
+
/* @__PURE__ */ jsx45("th", { children: "Where it is" }),
|
|
11590
|
+
/* @__PURE__ */ jsx45("th", { children: "Days unchanged" })
|
|
11262
11591
|
] }) }),
|
|
11263
|
-
/* @__PURE__ */
|
|
11264
|
-
/* @__PURE__ */
|
|
11265
|
-
/* @__PURE__ */
|
|
11266
|
-
/* @__PURE__ */
|
|
11592
|
+
/* @__PURE__ */ jsx45("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs41("tr", { className: "border-t first:border-t-0", children: [
|
|
11593
|
+
/* @__PURE__ */ jsx45("td", { className: "truncate py-0.5 pr-2", title: row.title ?? void 0, children: row.title ?? row.record_id.slice(0, 8) }),
|
|
11594
|
+
/* @__PURE__ */ jsx45("td", { className: "truncate py-0.5 pr-2 text-muted-foreground", title: row.state ?? void 0, children: row.state ?? "\u2014" }),
|
|
11595
|
+
/* @__PURE__ */ jsxs41(
|
|
11267
11596
|
"td",
|
|
11268
11597
|
{
|
|
11269
11598
|
className: "whitespace-nowrap py-0.5 text-right tabular-nums",
|
|
@@ -11313,12 +11642,12 @@ function Drawing({
|
|
|
11313
11642
|
axisLine: false,
|
|
11314
11643
|
tickFormatter: compactTick
|
|
11315
11644
|
};
|
|
11316
|
-
const tooltip = /* @__PURE__ */
|
|
11317
|
-
const legend = series.length > 1 ? /* @__PURE__ */
|
|
11645
|
+
const tooltip = /* @__PURE__ */ jsx45(Tooltip, { content: /* @__PURE__ */ jsx45(ChartTooltipContent, { config }), cursor: false });
|
|
11646
|
+
const legend = series.length > 1 ? /* @__PURE__ */ jsx45(Legend, { content: /* @__PURE__ */ jsx45(ChartLegendContent, { config }) }) : null;
|
|
11318
11647
|
if (kind === "donut") {
|
|
11319
|
-
return /* @__PURE__ */
|
|
11648
|
+
return /* @__PURE__ */ jsx45(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs41(PieChart, { width, height: HEIGHT, children: [
|
|
11320
11649
|
tooltip,
|
|
11321
|
-
/* @__PURE__ */
|
|
11650
|
+
/* @__PURE__ */ jsx45(
|
|
11322
11651
|
Pie,
|
|
11323
11652
|
{
|
|
11324
11653
|
data,
|
|
@@ -11328,19 +11657,19 @@ function Drawing({
|
|
|
11328
11657
|
outerRadius: 58,
|
|
11329
11658
|
isAnimationActive: false,
|
|
11330
11659
|
...click ? { onClick: click, className: "cursor-pointer" } : {},
|
|
11331
|
-
children: points.map((point) => /* @__PURE__ */
|
|
11660
|
+
children: points.map((point) => /* @__PURE__ */ jsx45(Cell, { fill: String(config[point.label]?.color ?? SERIES_COLORS[0]) }, point.label))
|
|
11332
11661
|
}
|
|
11333
11662
|
)
|
|
11334
11663
|
] }) });
|
|
11335
11664
|
}
|
|
11336
11665
|
if (kind === "line") {
|
|
11337
|
-
return /* @__PURE__ */
|
|
11338
|
-
/* @__PURE__ */
|
|
11339
|
-
/* @__PURE__ */
|
|
11340
|
-
/* @__PURE__ */
|
|
11666
|
+
return /* @__PURE__ */ jsx45(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs41(LineChart, { width, height: HEIGHT, data, margin: { top: 6, right: 6, bottom: 0, left: 0 }, children: [
|
|
11667
|
+
/* @__PURE__ */ jsx45(CartesianGrid, { vertical: false }),
|
|
11668
|
+
/* @__PURE__ */ jsx45(XAxis, { dataKey: "label", ...axis }),
|
|
11669
|
+
/* @__PURE__ */ jsx45(YAxis, { width: 44, ...axis }),
|
|
11341
11670
|
tooltip,
|
|
11342
11671
|
legend,
|
|
11343
|
-
series.map((key) => /* @__PURE__ */
|
|
11672
|
+
series.map((key) => /* @__PURE__ */ jsx45(
|
|
11344
11673
|
Line2,
|
|
11345
11674
|
{
|
|
11346
11675
|
type: "monotone",
|
|
@@ -11356,7 +11685,7 @@ function Drawing({
|
|
|
11356
11685
|
] }) });
|
|
11357
11686
|
}
|
|
11358
11687
|
const horizontal = kind === "bar";
|
|
11359
|
-
return /* @__PURE__ */
|
|
11688
|
+
return /* @__PURE__ */ jsx45(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs41(
|
|
11360
11689
|
BarChart,
|
|
11361
11690
|
{
|
|
11362
11691
|
width,
|
|
@@ -11365,17 +11694,17 @@ function Drawing({
|
|
|
11365
11694
|
layout: horizontal ? "vertical" : "horizontal",
|
|
11366
11695
|
margin: { top: 6, right: 6, bottom: 0, left: 0 },
|
|
11367
11696
|
children: [
|
|
11368
|
-
/* @__PURE__ */
|
|
11369
|
-
horizontal ? /* @__PURE__ */
|
|
11370
|
-
/* @__PURE__ */
|
|
11371
|
-
/* @__PURE__ */
|
|
11372
|
-
] }) : /* @__PURE__ */
|
|
11373
|
-
/* @__PURE__ */
|
|
11374
|
-
/* @__PURE__ */
|
|
11697
|
+
/* @__PURE__ */ jsx45(CartesianGrid, { vertical: horizontal, horizontal: !horizontal }),
|
|
11698
|
+
horizontal ? /* @__PURE__ */ jsxs41(Fragment19, { children: [
|
|
11699
|
+
/* @__PURE__ */ jsx45(XAxis, { type: "number", ...axis }),
|
|
11700
|
+
/* @__PURE__ */ jsx45(YAxis, { type: "category", dataKey: "label", width: 88, ...axis })
|
|
11701
|
+
] }) : /* @__PURE__ */ jsxs41(Fragment19, { children: [
|
|
11702
|
+
/* @__PURE__ */ jsx45(XAxis, { dataKey: "label", ...axis }),
|
|
11703
|
+
/* @__PURE__ */ jsx45(YAxis, { width: 44, ...axis })
|
|
11375
11704
|
] }),
|
|
11376
11705
|
tooltip,
|
|
11377
11706
|
legend,
|
|
11378
|
-
series.map((key, index) => /* @__PURE__ */
|
|
11707
|
+
series.map((key, index) => /* @__PURE__ */ jsx45(
|
|
11379
11708
|
Bar,
|
|
11380
11709
|
{
|
|
11381
11710
|
dataKey: key,
|
|
@@ -11383,7 +11712,7 @@ function Drawing({
|
|
|
11383
11712
|
isAnimationActive: false,
|
|
11384
11713
|
fill: `var(--color-${key})`,
|
|
11385
11714
|
...click ? { onClick: click, className: "cursor-pointer" } : {},
|
|
11386
|
-
children: series.length === 1 ? points.map((point) => /* @__PURE__ */
|
|
11715
|
+
children: series.length === 1 ? points.map((point) => /* @__PURE__ */ jsx45(Cell, { fill: String(config[point.label]?.color ?? SERIES_COLORS[index]) }, point.label)) : null
|
|
11387
11716
|
},
|
|
11388
11717
|
key
|
|
11389
11718
|
))
|
|
@@ -11393,24 +11722,25 @@ function Drawing({
|
|
|
11393
11722
|
}
|
|
11394
11723
|
|
|
11395
11724
|
// src/DashboardCanvas.tsx
|
|
11396
|
-
import { useCallback as
|
|
11397
|
-
import { useFields as useFields19, useRecordsClient as
|
|
11398
|
-
import { BasicInput as
|
|
11399
|
-
import { Fragment as
|
|
11725
|
+
import { useCallback as useCallback27, useEffect as useEffect31, useMemo as useMemo27, useState as useState41 } from "react";
|
|
11726
|
+
import { useFields as useFields19, useRecordsClient as useRecordsClient33, useTable as useTable17 } from "@ai-matrx/records/react";
|
|
11727
|
+
import { BasicInput as BasicInput14, Button as Button37, Skeleton as Skeleton24, cn as cn40 } from "@ai-matrx/design-system";
|
|
11728
|
+
import { Fragment as Fragment20, jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
11400
11729
|
function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
11401
|
-
const client =
|
|
11730
|
+
const client = useRecordsClient33();
|
|
11402
11731
|
const host = useRecordsUi();
|
|
11403
11732
|
const table = useTable17(tableId);
|
|
11404
11733
|
const rights = useTableRights(table.data);
|
|
11405
11734
|
const fields = useFields19(tableId);
|
|
11406
|
-
const [boards, setBoards] =
|
|
11407
|
-
const [error, setError] =
|
|
11408
|
-
const [activeId, setActiveId] =
|
|
11409
|
-
const [run, setRun] =
|
|
11410
|
-
const [running, setRunning] =
|
|
11411
|
-
const [question, setQuestion] =
|
|
11412
|
-
const [asking, setAsking] =
|
|
11413
|
-
const
|
|
11735
|
+
const [boards, setBoards] = useState41(null);
|
|
11736
|
+
const [error, setError] = useState41(null);
|
|
11737
|
+
const [activeId, setActiveId] = useState41(activeDashboardId ?? null);
|
|
11738
|
+
const [run, setRun] = useState41(null);
|
|
11739
|
+
const [running, setRunning] = useState41(false);
|
|
11740
|
+
const [question, setQuestion] = useState41("");
|
|
11741
|
+
const [asking, setAsking] = useState41(false);
|
|
11742
|
+
const [scheduling, setScheduling] = useState41(false);
|
|
11743
|
+
const load = useCallback27(async () => {
|
|
11414
11744
|
const answered = await client.dashboards({ table_id: tableId });
|
|
11415
11745
|
if (!answered.ok) {
|
|
11416
11746
|
setError(answered.error);
|
|
@@ -11419,17 +11749,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11419
11749
|
setError(null);
|
|
11420
11750
|
setBoards(answered.data.map(dashboardFromSummary));
|
|
11421
11751
|
}, [client, tableId]);
|
|
11422
|
-
|
|
11752
|
+
useEffect31(() => {
|
|
11423
11753
|
void load();
|
|
11424
11754
|
}, [load]);
|
|
11425
|
-
|
|
11755
|
+
useEffect31(() => {
|
|
11426
11756
|
if (!boards || boards.length === 0) return;
|
|
11427
11757
|
const chosen = boards.find((d) => d.id === (activeDashboardId ?? activeId)) ?? boards[0];
|
|
11428
11758
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
11429
11759
|
}, [boards, activeDashboardId]);
|
|
11430
|
-
const board =
|
|
11760
|
+
const board = useMemo27(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
|
|
11431
11761
|
const filterKey = JSON.stringify(filter ?? {});
|
|
11432
|
-
|
|
11762
|
+
useEffect31(() => {
|
|
11433
11763
|
if (!activeId) {
|
|
11434
11764
|
setRun(null);
|
|
11435
11765
|
return;
|
|
@@ -11447,7 +11777,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11447
11777
|
cancelled = true;
|
|
11448
11778
|
};
|
|
11449
11779
|
}, [client, activeId, filterKey]);
|
|
11450
|
-
const declare2 =
|
|
11780
|
+
const declare2 = useCallback27(
|
|
11451
11781
|
async (next, blocks) => {
|
|
11452
11782
|
const written = await client.dashboardDeclare(
|
|
11453
11783
|
dashboardDeclareArgs({ ...next, blocks }, tableId)
|
|
@@ -11493,7 +11823,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11493
11823
|
]
|
|
11494
11824
|
);
|
|
11495
11825
|
}
|
|
11496
|
-
const refresh =
|
|
11826
|
+
const refresh = useCallback27(async () => {
|
|
11497
11827
|
if (!activeId) return;
|
|
11498
11828
|
setRunning(true);
|
|
11499
11829
|
const again = await client.dashboardRun({
|
|
@@ -11534,20 +11864,20 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11534
11864
|
}
|
|
11535
11865
|
}
|
|
11536
11866
|
if (error) {
|
|
11537
|
-
return /* @__PURE__ */
|
|
11867
|
+
return /* @__PURE__ */ jsx46(
|
|
11538
11868
|
RefusalNotice,
|
|
11539
11869
|
{
|
|
11540
11870
|
error,
|
|
11541
11871
|
className,
|
|
11542
|
-
actions: /* @__PURE__ */
|
|
11872
|
+
actions: /* @__PURE__ */ jsx46(Button37, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
|
|
11543
11873
|
}
|
|
11544
11874
|
);
|
|
11545
11875
|
}
|
|
11546
|
-
if (boards === null) return /* @__PURE__ */
|
|
11547
|
-
return /* @__PURE__ */
|
|
11548
|
-
/* @__PURE__ */
|
|
11549
|
-
boards.map((d) => /* @__PURE__ */
|
|
11550
|
-
|
|
11876
|
+
if (boards === null) return /* @__PURE__ */ jsx46(Skeleton24, { className: cn40("h-64 w-full", className) });
|
|
11877
|
+
return /* @__PURE__ */ jsxs42("div", { className: cn40("flex min-h-0 flex-col gap-2", className), children: [
|
|
11878
|
+
/* @__PURE__ */ jsxs42("div", { className: "flex flex-wrap items-center gap-1", role: "group", "aria-label": "Dashboards", children: [
|
|
11879
|
+
boards.map((d) => /* @__PURE__ */ jsx46(
|
|
11880
|
+
Button37,
|
|
11551
11881
|
{
|
|
11552
11882
|
size: "sm",
|
|
11553
11883
|
variant: d.id === activeId ? "secondary" : "ghost",
|
|
@@ -11557,10 +11887,10 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11557
11887
|
},
|
|
11558
11888
|
d.id
|
|
11559
11889
|
)),
|
|
11560
|
-
rights.admin ? /* @__PURE__ */
|
|
11561
|
-
board ? /* @__PURE__ */
|
|
11562
|
-
/* @__PURE__ */
|
|
11563
|
-
|
|
11890
|
+
rights.admin ? /* @__PURE__ */ jsx46(Button37, { size: "sm", variant: "ghost", onClick: () => void create(), children: "New dashboard" }) : null,
|
|
11891
|
+
board ? /* @__PURE__ */ jsxs42("span", { className: "ml-auto flex items-center gap-1", children: [
|
|
11892
|
+
/* @__PURE__ */ jsx46(
|
|
11893
|
+
Button37,
|
|
11564
11894
|
{
|
|
11565
11895
|
size: "sm",
|
|
11566
11896
|
variant: "ghost",
|
|
@@ -11569,13 +11899,31 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11569
11899
|
children: running ? "Asking" : "Refresh"
|
|
11570
11900
|
}
|
|
11571
11901
|
),
|
|
11572
|
-
|
|
11902
|
+
/* @__PURE__ */ jsx46(
|
|
11903
|
+
Button37,
|
|
11904
|
+
{
|
|
11905
|
+
size: "sm",
|
|
11906
|
+
variant: scheduling ? "secondary" : "ghost",
|
|
11907
|
+
onClick: () => setScheduling((v) => !v),
|
|
11908
|
+
children: scheduling ? "Done" : "Send on a schedule"
|
|
11909
|
+
}
|
|
11910
|
+
),
|
|
11911
|
+
rights.admin ? /* @__PURE__ */ jsx46(Button37, { size: "sm", variant: "ghost", onClick: () => void remove(board), children: "Delete" }) : null
|
|
11573
11912
|
] }) : null
|
|
11574
11913
|
] }),
|
|
11575
|
-
board ? /* @__PURE__ */
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11914
|
+
board && scheduling ? /* @__PURE__ */ jsx46(
|
|
11915
|
+
DigestScheduler,
|
|
11916
|
+
{
|
|
11917
|
+
tableId,
|
|
11918
|
+
subjectName: board.name,
|
|
11919
|
+
...filter ? { filters: filter } : {},
|
|
11920
|
+
onClose: () => setScheduling(false)
|
|
11921
|
+
}
|
|
11922
|
+
) : null,
|
|
11923
|
+
board ? /* @__PURE__ */ jsxs42("div", { className: "flex flex-col gap-1", children: [
|
|
11924
|
+
/* @__PURE__ */ jsxs42("div", { className: "flex items-center gap-1", children: [
|
|
11925
|
+
/* @__PURE__ */ jsx46(
|
|
11926
|
+
BasicInput14,
|
|
11579
11927
|
{
|
|
11580
11928
|
value: question,
|
|
11581
11929
|
"aria-label": "Ask for a change to this dashboard",
|
|
@@ -11588,15 +11936,15 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11588
11936
|
className: "h-8 min-w-0 flex-1 text-xs"
|
|
11589
11937
|
}
|
|
11590
11938
|
),
|
|
11591
|
-
/* @__PURE__ */
|
|
11939
|
+
/* @__PURE__ */ jsx46(Button37, { size: "sm", disabled: !host.onReask || asking || question.trim() === "", onClick: () => void reask(), children: asking ? "Asking" : "Ask" })
|
|
11592
11940
|
] }),
|
|
11593
|
-
!host.onReask ? /* @__PURE__ */
|
|
11941
|
+
!host.onReask ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: NO_REASK_REASON }) : null
|
|
11594
11942
|
] }) : null,
|
|
11595
|
-
!board ? /* @__PURE__ */
|
|
11596
|
-
/* @__PURE__ */
|
|
11597
|
-
/* @__PURE__ */
|
|
11598
|
-
rights.admin && board.blocks[index] ? /* @__PURE__ */
|
|
11599
|
-
/* @__PURE__ */
|
|
11943
|
+
!board ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: rights.admin ? "No dashboard looks at this table yet. \u201CNew dashboard\u201D makes one, or ask an agent for the one you want and it writes the whole thing." : rights.why("structure") }) : run === null ? /* @__PURE__ */ jsx46(Skeleton24, { className: "h-64 w-full" }) : run.blocks.length === 0 ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: "This dashboard has no blocks yet. Each one is a single question the store answers \u2014 grouped, bucketed and measured inside the read door." }) : /* @__PURE__ */ jsxs42(Fragment20, { children: [
|
|
11944
|
+
/* @__PURE__ */ jsx46("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3", children: run.blocks.map((block, index) => /* @__PURE__ */ jsxs42("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
11945
|
+
/* @__PURE__ */ jsx46(ChartBlock, { block, subject: tableId }),
|
|
11946
|
+
rights.admin && board.blocks[index] ? /* @__PURE__ */ jsxs42("div", { className: "flex items-center gap-1 text-xs", children: [
|
|
11947
|
+
/* @__PURE__ */ jsx46(
|
|
11600
11948
|
"select",
|
|
11601
11949
|
{
|
|
11602
11950
|
"aria-label": `Shape of ${block.title}`,
|
|
@@ -11608,11 +11956,11 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11608
11956
|
(b, i) => i === index ? { ...b, kind: e.target.value } : b
|
|
11609
11957
|
)
|
|
11610
11958
|
),
|
|
11611
|
-
children: CHART_KINDS.map((kind) => /* @__PURE__ */
|
|
11959
|
+
children: CHART_KINDS.map((kind) => /* @__PURE__ */ jsx46("option", { value: kind, children: CHART_KIND_LABEL[kind] }, kind))
|
|
11612
11960
|
}
|
|
11613
11961
|
),
|
|
11614
|
-
/* @__PURE__ */
|
|
11615
|
-
|
|
11962
|
+
/* @__PURE__ */ jsx46(
|
|
11963
|
+
Button37,
|
|
11616
11964
|
{
|
|
11617
11965
|
size: "sm",
|
|
11618
11966
|
variant: "ghost",
|
|
@@ -11625,17 +11973,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
11625
11973
|
)
|
|
11626
11974
|
] }) : null
|
|
11627
11975
|
] }, `${block.title}-${index}`)) }),
|
|
11628
|
-
!host.openRecords ? /* @__PURE__ */
|
|
11976
|
+
!host.openRecords ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: NO_OPEN_RECORDS_REASON }) : null
|
|
11629
11977
|
] })
|
|
11630
11978
|
] });
|
|
11631
11979
|
}
|
|
11632
11980
|
|
|
11633
11981
|
// src/FormsPanel.tsx
|
|
11634
|
-
import { useCallback as
|
|
11635
|
-
import { useRecordsClient as
|
|
11982
|
+
import { useCallback as useCallback28, useEffect as useEffect32, useState as useState42 } from "react";
|
|
11983
|
+
import { useRecordsClient as useRecordsClient34, useTable as useTable18 } from "@ai-matrx/records/react";
|
|
11636
11984
|
import { publicFormPath as publicFormPath2 } from "@ai-matrx/records";
|
|
11637
|
-
import { Badge as Badge15, Button as
|
|
11638
|
-
import { Fragment as
|
|
11985
|
+
import { Badge as Badge15, Button as Button38, Skeleton as Skeleton25, cn as cn41 } from "@ai-matrx/design-system";
|
|
11986
|
+
import { Fragment as Fragment21, jsx as jsx47, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
11639
11987
|
var WHAT_A_FORM_IS = "A form asks for this table's own fields, and its answers land here as ordinary records stamped with the form they came through.";
|
|
11640
11988
|
var NO_ADMIN = "This table has no forms. Making one needs the admin level on it, because a form decides what people with no account may add here.";
|
|
11641
11989
|
function formSuggestion(tableName2) {
|
|
@@ -11643,16 +11991,16 @@ function formSuggestion(tableName2) {
|
|
|
11643
11991
|
return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
|
|
11644
11992
|
}
|
|
11645
11993
|
function FormsPanel({ tableId, className }) {
|
|
11646
|
-
const client =
|
|
11994
|
+
const client = useRecordsClient34();
|
|
11647
11995
|
const host = useRecordsUi();
|
|
11648
11996
|
const table = useTable18(tableId);
|
|
11649
11997
|
const rights = useTableRights(table.data);
|
|
11650
|
-
const [forms, setForms] =
|
|
11651
|
-
const [error, setError] =
|
|
11652
|
-
const [busy, setBusy] =
|
|
11653
|
-
const [copied, setCopied] =
|
|
11654
|
-
const [building, setBuilding] =
|
|
11655
|
-
const load =
|
|
11998
|
+
const [forms, setForms] = useState42(null);
|
|
11999
|
+
const [error, setError] = useState42(null);
|
|
12000
|
+
const [busy, setBusy] = useState42(null);
|
|
12001
|
+
const [copied, setCopied] = useState42(null);
|
|
12002
|
+
const [building, setBuilding] = useState42(false);
|
|
12003
|
+
const load = useCallback28(async () => {
|
|
11656
12004
|
const answered = await client.forms({ table_id: tableId });
|
|
11657
12005
|
if (!answered.ok) {
|
|
11658
12006
|
setError(answered.error);
|
|
@@ -11662,10 +12010,10 @@ function FormsPanel({ tableId, className }) {
|
|
|
11662
12010
|
setError(null);
|
|
11663
12011
|
setForms(answered.data);
|
|
11664
12012
|
}, [client, tableId]);
|
|
11665
|
-
|
|
12013
|
+
useEffect32(() => {
|
|
11666
12014
|
void load();
|
|
11667
12015
|
}, [load]);
|
|
11668
|
-
const toggle =
|
|
12016
|
+
const toggle = useCallback28(
|
|
11669
12017
|
async (form) => {
|
|
11670
12018
|
setBusy(form.form_id);
|
|
11671
12019
|
const wanted = form.published_at === null || form.closed_at !== null;
|
|
@@ -11679,8 +12027,8 @@ function FormsPanel({ tableId, className }) {
|
|
|
11679
12027
|
},
|
|
11680
12028
|
[client, load]
|
|
11681
12029
|
);
|
|
11682
|
-
const [shown, setShown] =
|
|
11683
|
-
const copy =
|
|
12030
|
+
const [shown, setShown] = useState42(null);
|
|
12031
|
+
const copy = useCallback28(async (url, formId) => {
|
|
11684
12032
|
try {
|
|
11685
12033
|
await navigator.clipboard.writeText(url);
|
|
11686
12034
|
setCopied(formId);
|
|
@@ -11690,17 +12038,17 @@ function FormsPanel({ tableId, className }) {
|
|
|
11690
12038
|
setShown(url);
|
|
11691
12039
|
}
|
|
11692
12040
|
}, []);
|
|
11693
|
-
if (forms === null) return /* @__PURE__ */
|
|
12041
|
+
if (forms === null) return /* @__PURE__ */ jsx47(Skeleton25, { className: cn41("h-32 w-full", className) });
|
|
11694
12042
|
const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
11695
|
-
return /* @__PURE__ */
|
|
11696
|
-
/* @__PURE__ */
|
|
11697
|
-
/* @__PURE__ */
|
|
11698
|
-
/* @__PURE__ */
|
|
11699
|
-
/* @__PURE__ */
|
|
11700
|
-
rights.structure && forms.length > 0 ? /* @__PURE__ */
|
|
12043
|
+
return /* @__PURE__ */ jsxs43("section", { className: cn41("flex flex-col gap-3", className), children: [
|
|
12044
|
+
/* @__PURE__ */ jsxs43("header", { className: "flex items-center gap-2", children: [
|
|
12045
|
+
/* @__PURE__ */ jsx47("h3", { className: "text-sm font-medium", children: "Forms" }),
|
|
12046
|
+
/* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground", children: forms.length === 0 ? "none yet" : `${forms.length}` }),
|
|
12047
|
+
/* @__PURE__ */ jsx47("div", { className: "flex-1" }),
|
|
12048
|
+
rights.structure && forms.length > 0 ? /* @__PURE__ */ jsx47(Button38, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a form" }) : null
|
|
11701
12049
|
] }),
|
|
11702
|
-
error ? /* @__PURE__ */
|
|
11703
|
-
building ? /* @__PURE__ */
|
|
12050
|
+
error ? /* @__PURE__ */ jsx47(RefusalNotice, { error }) : null,
|
|
12051
|
+
building ? /* @__PURE__ */ jsx47(
|
|
11704
12052
|
FormBuilder,
|
|
11705
12053
|
{
|
|
11706
12054
|
tableId,
|
|
@@ -11709,7 +12057,7 @@ function FormsPanel({ tableId, className }) {
|
|
|
11709
12057
|
}
|
|
11710
12058
|
}
|
|
11711
12059
|
) : null,
|
|
11712
|
-
forms.length === 0 && !building ? /* @__PURE__ */
|
|
12060
|
+
forms.length === 0 && !building ? /* @__PURE__ */ jsx47(
|
|
11713
12061
|
BuildOrAsk,
|
|
11714
12062
|
{
|
|
11715
12063
|
mayBuild: rights.structure,
|
|
@@ -11727,36 +12075,36 @@ function FormsPanel({ tableId, className }) {
|
|
|
11727
12075
|
children: WHAT_A_FORM_IS
|
|
11728
12076
|
}
|
|
11729
12077
|
) : null,
|
|
11730
|
-
/* @__PURE__ */
|
|
12078
|
+
/* @__PURE__ */ jsx47("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
|
|
11731
12079
|
const url = `${origin}${publicFormPath2(form.form_id)}`;
|
|
11732
|
-
return /* @__PURE__ */
|
|
11733
|
-
/* @__PURE__ */
|
|
11734
|
-
/* @__PURE__ */
|
|
11735
|
-
/* @__PURE__ */
|
|
12080
|
+
return /* @__PURE__ */ jsxs43("li", { className: "rounded-md border p-2.5", children: [
|
|
12081
|
+
/* @__PURE__ */ jsxs43("div", { className: "flex items-center gap-2", children: [
|
|
12082
|
+
/* @__PURE__ */ jsx47("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: form.title ?? form.slug }),
|
|
12083
|
+
/* @__PURE__ */ jsx47(Badge15, { variant: form.state === "open" ? "default" : "secondary", children: form.state })
|
|
11736
12084
|
] }),
|
|
11737
|
-
/* @__PURE__ */
|
|
11738
|
-
/* @__PURE__ */
|
|
11739
|
-
/* @__PURE__ */
|
|
12085
|
+
/* @__PURE__ */ jsx47("p", { className: "mt-0.5 text-xs text-muted-foreground", children: FORM_STATE_WORDS[form.state] }),
|
|
12086
|
+
/* @__PURE__ */ jsxs43("p", { className: "mt-1.5 text-xs", children: [
|
|
12087
|
+
/* @__PURE__ */ jsx47("span", { className: "font-medium", children: form.in_table }),
|
|
11740
12088
|
" in the table",
|
|
11741
|
-
form.held > 0 ? /* @__PURE__ */
|
|
12089
|
+
form.held > 0 ? /* @__PURE__ */ jsxs43(Fragment21, { children: [
|
|
11742
12090
|
" \xB7 ",
|
|
11743
|
-
/* @__PURE__ */
|
|
12091
|
+
/* @__PURE__ */ jsx47("span", { className: "font-medium", children: form.held }),
|
|
11744
12092
|
" waiting for someone"
|
|
11745
12093
|
] }) : null,
|
|
11746
|
-
form.rejected > 0 ? /* @__PURE__ */
|
|
12094
|
+
form.rejected > 0 ? /* @__PURE__ */ jsxs43(Fragment21, { children: [
|
|
11747
12095
|
" \xB7 ",
|
|
11748
|
-
/* @__PURE__ */
|
|
12096
|
+
/* @__PURE__ */ jsx47("span", { className: "font-medium", children: form.rejected }),
|
|
11749
12097
|
" turned away"
|
|
11750
12098
|
] }) : null,
|
|
11751
|
-
form.submission_cap !== null ? /* @__PURE__ */
|
|
12099
|
+
form.submission_cap !== null ? /* @__PURE__ */ jsxs43("span", { className: "text-muted-foreground", children: [
|
|
11752
12100
|
" \xB7 stops at ",
|
|
11753
12101
|
form.submission_cap
|
|
11754
12102
|
] }) : null
|
|
11755
12103
|
] }),
|
|
11756
|
-
/* @__PURE__ */
|
|
11757
|
-
form.published_at ? /* @__PURE__ */
|
|
11758
|
-
rights.structure ? /* @__PURE__ */
|
|
11759
|
-
|
|
12104
|
+
/* @__PURE__ */ jsxs43("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
12105
|
+
form.published_at ? /* @__PURE__ */ jsx47(Button38, { size: "sm", variant: "outline", onClick: () => void copy(url, form.form_id), children: copied === form.form_id ? "Copied" : "Copy link" }) : null,
|
|
12106
|
+
rights.structure ? /* @__PURE__ */ jsx47(
|
|
12107
|
+
Button38,
|
|
11760
12108
|
{
|
|
11761
12109
|
size: "sm",
|
|
11762
12110
|
variant: "ghost",
|
|
@@ -11766,25 +12114,25 @@ function FormsPanel({ tableId, className }) {
|
|
|
11766
12114
|
}
|
|
11767
12115
|
) : null
|
|
11768
12116
|
] }),
|
|
11769
|
-
shown && shown === url ? /* @__PURE__ */
|
|
12117
|
+
shown && shown === url ? /* @__PURE__ */ jsxs43("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
11770
12118
|
"This browser would not let the page copy for you, so here it is to copy by hand:",
|
|
11771
12119
|
" ",
|
|
11772
12120
|
url
|
|
11773
12121
|
] }) : null
|
|
11774
12122
|
] }, form.form_id);
|
|
11775
12123
|
}) }),
|
|
11776
|
-
!rights.known ? /* @__PURE__ */
|
|
12124
|
+
!rights.known ? /* @__PURE__ */ jsx47("p", { className: "text-xs text-muted-foreground", children: "Still asking what you may do with this table \u2014 nothing is offered until it answers." }) : null
|
|
11777
12125
|
] });
|
|
11778
12126
|
}
|
|
11779
12127
|
|
|
11780
12128
|
// src/BookingBuilder.tsx
|
|
11781
|
-
import { useCallback as
|
|
11782
|
-
import { useFields as useFields20, useRecordsClient as
|
|
12129
|
+
import { useCallback as useCallback29, useEffect as useEffect33, useMemo as useMemo28, useState as useState43 } from "react";
|
|
12130
|
+
import { useFields as useFields20, useRecordsClient as useRecordsClient35, useTable as useTable19 } from "@ai-matrx/records/react";
|
|
11783
12131
|
import {
|
|
11784
12132
|
bookingPath
|
|
11785
12133
|
} from "@ai-matrx/records";
|
|
11786
|
-
import { BasicInput as
|
|
11787
|
-
import { Fragment as
|
|
12134
|
+
import { BasicInput as BasicInput15, BasicTextarea as BasicTextarea7, Button as Button39, Checkbox as Checkbox7, Label as Label8, Skeleton as Skeleton26, cn as cn42 } from "@ai-matrx/design-system";
|
|
12135
|
+
import { Fragment as Fragment22, jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
11788
12136
|
var STORE_ANSWERS_THESE = ["slot", "status", "booked_with"];
|
|
11789
12137
|
var DAYS = [
|
|
11790
12138
|
{ weekday: 1, label: "Mon" },
|
|
@@ -11808,36 +12156,36 @@ function draftWindows(availability) {
|
|
|
11808
12156
|
});
|
|
11809
12157
|
}
|
|
11810
12158
|
function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
11811
|
-
const client =
|
|
12159
|
+
const client = useRecordsClient35();
|
|
11812
12160
|
const host = useRecordsUi();
|
|
11813
12161
|
const table = useTable19(tableId);
|
|
11814
12162
|
const rights = useTableRights(table.data);
|
|
11815
12163
|
const fields = useFields20(tableId);
|
|
11816
|
-
const [existing, setExisting] =
|
|
11817
|
-
const [loaded, setLoaded] =
|
|
11818
|
-
const [error, setError] =
|
|
11819
|
-
const [saving, setSaving] =
|
|
11820
|
-
const [publishing, setPublishing] =
|
|
11821
|
-
const [copied, setCopied] =
|
|
11822
|
-
const [title, setTitle] =
|
|
11823
|
-
const [minutes, setMinutes] =
|
|
11824
|
-
const [buffer, setBuffer] =
|
|
11825
|
-
const [lead, setLead] =
|
|
11826
|
-
const [perDay, setPerDay] =
|
|
11827
|
-
const [days, setDays] =
|
|
11828
|
-
const [windows, setWindows] =
|
|
11829
|
-
const [asked, setAsked] =
|
|
11830
|
-
const [confirmation, setConfirmation] =
|
|
11831
|
-
const [offer, setOffer] =
|
|
11832
|
-
const [formId, setFormId] =
|
|
12164
|
+
const [existing, setExisting] = useState43(null);
|
|
12165
|
+
const [loaded, setLoaded] = useState43(false);
|
|
12166
|
+
const [error, setError] = useState43(null);
|
|
12167
|
+
const [saving, setSaving] = useState43(false);
|
|
12168
|
+
const [publishing, setPublishing] = useState43(false);
|
|
12169
|
+
const [copied, setCopied] = useState43(false);
|
|
12170
|
+
const [title, setTitle] = useState43("");
|
|
12171
|
+
const [minutes, setMinutes] = useState43(30);
|
|
12172
|
+
const [buffer, setBuffer] = useState43(0);
|
|
12173
|
+
const [lead, setLead] = useState43(120);
|
|
12174
|
+
const [perDay, setPerDay] = useState43(8);
|
|
12175
|
+
const [days, setDays] = useState43(30);
|
|
12176
|
+
const [windows, setWindows] = useState43(() => draftWindows(null));
|
|
12177
|
+
const [asked, setAsked] = useState43([]);
|
|
12178
|
+
const [confirmation, setConfirmation] = useState43("");
|
|
12179
|
+
const [offer, setOffer] = useState43(null);
|
|
12180
|
+
const [formId, setFormId] = useState43(bookingId ?? null);
|
|
11833
12181
|
const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
11834
|
-
const askable =
|
|
12182
|
+
const askable = useMemo28(
|
|
11835
12183
|
() => (fields.data ?? []).filter(
|
|
11836
12184
|
(f) => !STORE_ANSWERS_THESE.includes(f.key)
|
|
11837
12185
|
),
|
|
11838
12186
|
[fields.data]
|
|
11839
12187
|
);
|
|
11840
|
-
const load =
|
|
12188
|
+
const load = useCallback29(async () => {
|
|
11841
12189
|
const answered = await client.bookings({ table_id: tableId });
|
|
11842
12190
|
if (!answered.ok) {
|
|
11843
12191
|
setError(answered.error);
|
|
@@ -11849,18 +12197,18 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
11849
12197
|
setLoaded(true);
|
|
11850
12198
|
if (mine) setFormId(mine.form_id);
|
|
11851
12199
|
}, [client, tableId, bookingId]);
|
|
11852
|
-
|
|
12200
|
+
useEffect33(() => {
|
|
11853
12201
|
void load();
|
|
11854
12202
|
}, [load]);
|
|
11855
|
-
|
|
12203
|
+
useEffect33(() => {
|
|
11856
12204
|
if (title !== "" || !table.data) return;
|
|
11857
12205
|
setTitle(existing?.title ?? `Book a ${minutes}-minute ${table.data.name} appointment`);
|
|
11858
12206
|
}, [table.data, existing]);
|
|
11859
|
-
|
|
12207
|
+
useEffect33(() => {
|
|
11860
12208
|
if (!existing) return;
|
|
11861
12209
|
setMinutes(existing.slot_minutes);
|
|
11862
12210
|
}, [existing]);
|
|
11863
|
-
const availability =
|
|
12211
|
+
const availability = useCallback29(
|
|
11864
12212
|
() => ({
|
|
11865
12213
|
slot_minutes: minutes,
|
|
11866
12214
|
buffer_minutes: buffer,
|
|
@@ -11909,68 +12257,68 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
11909
12257
|
await load();
|
|
11910
12258
|
onSaved?.({ form_id: formId });
|
|
11911
12259
|
}
|
|
11912
|
-
if (!loaded || fields.loading) return /* @__PURE__ */
|
|
12260
|
+
if (!loaded || fields.loading) return /* @__PURE__ */ jsx48(Skeleton26, { className: cn42("h-64 w-full", className) });
|
|
11913
12261
|
if (!rights.structure) {
|
|
11914
|
-
return /* @__PURE__ */
|
|
12262
|
+
return /* @__PURE__ */ jsx48("p", { className: cn42("text-xs text-muted-foreground", className), children: rights.why("structure") ?? "Making a booking page needs the admin level on this table, because it lets people with no account take time in your calendar." });
|
|
11915
12263
|
}
|
|
11916
12264
|
const url = formId ? `${publicOrigin}${bookingPath(formId)}` : null;
|
|
11917
12265
|
const shown = offer ?? null;
|
|
11918
|
-
return /* @__PURE__ */
|
|
11919
|
-
/* @__PURE__ */
|
|
11920
|
-
/* @__PURE__ */
|
|
11921
|
-
/* @__PURE__ */
|
|
11922
|
-
/* @__PURE__ */
|
|
11923
|
-
onClose ? /* @__PURE__ */
|
|
12266
|
+
return /* @__PURE__ */ jsxs44("section", { className: cn42("flex flex-col gap-3", className), children: [
|
|
12267
|
+
/* @__PURE__ */ jsxs44("header", { className: "flex items-center gap-2", children: [
|
|
12268
|
+
/* @__PURE__ */ jsx48("h3", { className: "text-sm font-medium", children: existing ? "Booking page" : "New booking page" }),
|
|
12269
|
+
/* @__PURE__ */ jsx48("div", { className: "flex-1" }),
|
|
12270
|
+
/* @__PURE__ */ jsx48(Button39, { size: "sm", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" }),
|
|
12271
|
+
onClose ? /* @__PURE__ */ jsx48(Button39, { size: "sm", variant: "ghost", onClick: onClose, children: "Close" }) : null
|
|
11924
12272
|
] }),
|
|
11925
|
-
error ? /* @__PURE__ */
|
|
11926
|
-
/* @__PURE__ */
|
|
11927
|
-
/* @__PURE__ */
|
|
11928
|
-
/* @__PURE__ */
|
|
12273
|
+
error ? /* @__PURE__ */ jsx48(RefusalNotice, { error }) : null,
|
|
12274
|
+
/* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
|
|
12275
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "What it is called" }),
|
|
12276
|
+
/* @__PURE__ */ jsx48(BasicInput15, { value: title, onChange: (e) => setTitle(e.target.value) })
|
|
11929
12277
|
] }),
|
|
11930
|
-
/* @__PURE__ */
|
|
11931
|
-
/* @__PURE__ */
|
|
11932
|
-
/* @__PURE__ */
|
|
11933
|
-
/* @__PURE__ */
|
|
12278
|
+
/* @__PURE__ */ jsxs44("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
12279
|
+
/* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
|
|
12280
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "How long one appointment is" }),
|
|
12281
|
+
/* @__PURE__ */ jsx48(
|
|
11934
12282
|
"select",
|
|
11935
12283
|
{
|
|
11936
12284
|
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
11937
12285
|
value: minutes,
|
|
11938
12286
|
onChange: (e) => setMinutes(Number(e.target.value)),
|
|
11939
|
-
children: LENGTHS.map((n) => /* @__PURE__ */
|
|
12287
|
+
children: LENGTHS.map((n) => /* @__PURE__ */ jsxs44("option", { value: n, children: [
|
|
11940
12288
|
n,
|
|
11941
12289
|
" minutes"
|
|
11942
12290
|
] }, n))
|
|
11943
12291
|
}
|
|
11944
12292
|
)
|
|
11945
12293
|
] }),
|
|
11946
|
-
/* @__PURE__ */
|
|
11947
|
-
/* @__PURE__ */
|
|
11948
|
-
/* @__PURE__ */
|
|
12294
|
+
/* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
|
|
12295
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "Gap kept after each one" }),
|
|
12296
|
+
/* @__PURE__ */ jsx48(
|
|
11949
12297
|
"select",
|
|
11950
12298
|
{
|
|
11951
12299
|
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
11952
12300
|
value: buffer,
|
|
11953
12301
|
onChange: (e) => setBuffer(Number(e.target.value)),
|
|
11954
|
-
children: [0, 5, 10, 15, 30].map((n) => /* @__PURE__ */
|
|
12302
|
+
children: [0, 5, 10, 15, 30].map((n) => /* @__PURE__ */ jsx48("option", { value: n, children: n === 0 ? "No gap \u2014 back to back" : `${n} minutes` }, n))
|
|
11955
12303
|
}
|
|
11956
12304
|
)
|
|
11957
12305
|
] }),
|
|
11958
|
-
/* @__PURE__ */
|
|
11959
|
-
/* @__PURE__ */
|
|
11960
|
-
/* @__PURE__ */
|
|
12306
|
+
/* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
|
|
12307
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "Earliest somebody may book" }),
|
|
12308
|
+
/* @__PURE__ */ jsx48(
|
|
11961
12309
|
"select",
|
|
11962
12310
|
{
|
|
11963
12311
|
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
11964
12312
|
value: lead,
|
|
11965
12313
|
onChange: (e) => setLead(Number(e.target.value)),
|
|
11966
|
-
children: [0, 60, 120, 240, 1440].map((n) => /* @__PURE__ */
|
|
12314
|
+
children: [0, 60, 120, 240, 1440].map((n) => /* @__PURE__ */ jsx48("option", { value: n, children: n === 0 ? "Right away" : n === 1440 ? "A day from now" : `${n / 60} hour${n === 60 ? "" : "s"} from now` }, n))
|
|
11967
12315
|
}
|
|
11968
12316
|
)
|
|
11969
12317
|
] }),
|
|
11970
|
-
/* @__PURE__ */
|
|
11971
|
-
/* @__PURE__ */
|
|
11972
|
-
/* @__PURE__ */
|
|
11973
|
-
|
|
12318
|
+
/* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
|
|
12319
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "Most in one day" }),
|
|
12320
|
+
/* @__PURE__ */ jsx48(
|
|
12321
|
+
BasicInput15,
|
|
11974
12322
|
{
|
|
11975
12323
|
type: "number",
|
|
11976
12324
|
min: 1,
|
|
@@ -11979,15 +12327,15 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
11979
12327
|
}
|
|
11980
12328
|
)
|
|
11981
12329
|
] }),
|
|
11982
|
-
/* @__PURE__ */
|
|
11983
|
-
/* @__PURE__ */
|
|
11984
|
-
/* @__PURE__ */
|
|
12330
|
+
/* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
|
|
12331
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "How far ahead the page offers" }),
|
|
12332
|
+
/* @__PURE__ */ jsx48(
|
|
11985
12333
|
"select",
|
|
11986
12334
|
{
|
|
11987
12335
|
className: "h-9 rounded border bg-background px-2 text-sm",
|
|
11988
12336
|
value: days,
|
|
11989
12337
|
onChange: (e) => setDays(Number(e.target.value)),
|
|
11990
|
-
children: [7, 14, 30, 60, 90].map((n) => /* @__PURE__ */
|
|
12338
|
+
children: [7, 14, 30, 60, 90].map((n) => /* @__PURE__ */ jsxs44("option", { value: n, children: [
|
|
11991
12339
|
n,
|
|
11992
12340
|
" days"
|
|
11993
12341
|
] }, n))
|
|
@@ -11995,12 +12343,12 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
11995
12343
|
)
|
|
11996
12344
|
] })
|
|
11997
12345
|
] }),
|
|
11998
|
-
/* @__PURE__ */
|
|
11999
|
-
/* @__PURE__ */
|
|
12000
|
-
windows.map((w, i) => /* @__PURE__ */
|
|
12001
|
-
/* @__PURE__ */
|
|
12002
|
-
/* @__PURE__ */
|
|
12003
|
-
|
|
12346
|
+
/* @__PURE__ */ jsxs44("div", { className: "flex flex-col gap-1.5", children: [
|
|
12347
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "Hours you are free" }),
|
|
12348
|
+
windows.map((w, i) => /* @__PURE__ */ jsxs44("div", { className: "flex items-center gap-2", children: [
|
|
12349
|
+
/* @__PURE__ */ jsxs44("label", { className: "flex w-20 items-center gap-1.5 text-xs", children: [
|
|
12350
|
+
/* @__PURE__ */ jsx48(
|
|
12351
|
+
Checkbox7,
|
|
12004
12352
|
{
|
|
12005
12353
|
checked: w.on,
|
|
12006
12354
|
onCheckedChange: (on) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, on: Boolean(on) } : x))
|
|
@@ -12008,9 +12356,9 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
12008
12356
|
),
|
|
12009
12357
|
DAYS[i]?.label
|
|
12010
12358
|
] }),
|
|
12011
|
-
w.on ? /* @__PURE__ */
|
|
12012
|
-
/* @__PURE__ */
|
|
12013
|
-
|
|
12359
|
+
w.on ? /* @__PURE__ */ jsxs44(Fragment22, { children: [
|
|
12360
|
+
/* @__PURE__ */ jsx48(
|
|
12361
|
+
BasicInput15,
|
|
12014
12362
|
{
|
|
12015
12363
|
type: "time",
|
|
12016
12364
|
className: "h-8 w-28",
|
|
@@ -12018,9 +12366,9 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
12018
12366
|
onChange: (e) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, from: e.target.value } : x))
|
|
12019
12367
|
}
|
|
12020
12368
|
),
|
|
12021
|
-
/* @__PURE__ */
|
|
12022
|
-
/* @__PURE__ */
|
|
12023
|
-
|
|
12369
|
+
/* @__PURE__ */ jsx48("span", { className: "text-xs text-muted-foreground", children: "to" }),
|
|
12370
|
+
/* @__PURE__ */ jsx48(
|
|
12371
|
+
BasicInput15,
|
|
12024
12372
|
{
|
|
12025
12373
|
type: "time",
|
|
12026
12374
|
className: "h-8 w-28",
|
|
@@ -12028,14 +12376,14 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
12028
12376
|
onChange: (e) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, to: e.target.value } : x))
|
|
12029
12377
|
}
|
|
12030
12378
|
)
|
|
12031
|
-
] }) : /* @__PURE__ */
|
|
12379
|
+
] }) : /* @__PURE__ */ jsx48("span", { className: "text-xs text-muted-foreground", children: "not taking bookings" })
|
|
12032
12380
|
] }, w.weekday))
|
|
12033
12381
|
] }),
|
|
12034
|
-
/* @__PURE__ */
|
|
12035
|
-
/* @__PURE__ */
|
|
12036
|
-
askable.length === 0 ? /* @__PURE__ */
|
|
12037
|
-
/* @__PURE__ */
|
|
12038
|
-
|
|
12382
|
+
/* @__PURE__ */ jsxs44("div", { className: "flex flex-col gap-1.5", children: [
|
|
12383
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "What to ask the person booking" }),
|
|
12384
|
+
askable.length === 0 ? /* @__PURE__ */ jsx48("p", { className: "text-xs text-muted-foreground", children: "This table has no fields yet, so there is nothing a booking page could ask for. Add a field first \u2014 every question is one of this table's own fields." }) : /* @__PURE__ */ jsx48("div", { className: "flex flex-wrap gap-x-4 gap-y-1.5", children: askable.map((f) => /* @__PURE__ */ jsxs44("label", { className: "flex items-center gap-1.5 text-xs", children: [
|
|
12385
|
+
/* @__PURE__ */ jsx48(
|
|
12386
|
+
Checkbox7,
|
|
12039
12387
|
{
|
|
12040
12388
|
checked: asked.includes(f.key),
|
|
12041
12389
|
onCheckedChange: (on) => setAsked((prev) => on ? [...prev, f.key] : prev.filter((k) => k !== f.key))
|
|
@@ -12043,11 +12391,11 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
12043
12391
|
),
|
|
12044
12392
|
fieldName(f)
|
|
12045
12393
|
] }, f.key)) }),
|
|
12046
|
-
/* @__PURE__ */
|
|
12394
|
+
/* @__PURE__ */ jsx48("p", { className: "text-xs text-muted-foreground", children: "The time, whether it is booked or cancelled, and who it is with are filled in by the store \u2014 a booking page cannot ask a visitor for any of the three." })
|
|
12047
12395
|
] }),
|
|
12048
|
-
/* @__PURE__ */
|
|
12049
|
-
/* @__PURE__ */
|
|
12050
|
-
/* @__PURE__ */
|
|
12396
|
+
/* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
|
|
12397
|
+
/* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "What they see after booking" }),
|
|
12398
|
+
/* @__PURE__ */ jsx48(
|
|
12051
12399
|
BasicTextarea7,
|
|
12052
12400
|
{
|
|
12053
12401
|
rows: 2,
|
|
@@ -12057,7 +12405,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
12057
12405
|
}
|
|
12058
12406
|
)
|
|
12059
12407
|
] }),
|
|
12060
|
-
shown ? /* @__PURE__ */
|
|
12408
|
+
shown ? /* @__PURE__ */ jsxs44("p", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
12061
12409
|
"Saved. The page offers ",
|
|
12062
12410
|
shown.slot_minutes,
|
|
12063
12411
|
"-minute appointments in ",
|
|
@@ -12076,10 +12424,10 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
12076
12424
|
).join(", "),
|
|
12077
12425
|
"."
|
|
12078
12426
|
] }) : null,
|
|
12079
|
-
url ? /* @__PURE__ */
|
|
12080
|
-
/* @__PURE__ */
|
|
12081
|
-
/* @__PURE__ */
|
|
12082
|
-
|
|
12427
|
+
url ? /* @__PURE__ */ jsxs44("div", { className: "flex flex-wrap items-center gap-2 rounded-md border p-2.5", children: [
|
|
12428
|
+
/* @__PURE__ */ jsx48("span", { className: "min-w-0 flex-1 break-all text-xs", children: url }),
|
|
12429
|
+
/* @__PURE__ */ jsx48(
|
|
12430
|
+
Button39,
|
|
12083
12431
|
{
|
|
12084
12432
|
size: "sm",
|
|
12085
12433
|
variant: "outline",
|
|
@@ -12092,8 +12440,8 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
12092
12440
|
children: copied ? "Copied" : "Copy link"
|
|
12093
12441
|
}
|
|
12094
12442
|
),
|
|
12095
|
-
/* @__PURE__ */
|
|
12096
|
-
|
|
12443
|
+
/* @__PURE__ */ jsx48(
|
|
12444
|
+
Button39,
|
|
12097
12445
|
{
|
|
12098
12446
|
size: "sm",
|
|
12099
12447
|
disabled: publishing,
|
|
@@ -12101,17 +12449,17 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
12101
12449
|
children: publishing ? "\u2026" : existing?.published_at && !existing.closed_at ? "Unpublish" : "Publish"
|
|
12102
12450
|
}
|
|
12103
12451
|
),
|
|
12104
|
-
/* @__PURE__ */
|
|
12452
|
+
/* @__PURE__ */ jsx48("p", { className: "w-full text-xs text-muted-foreground", children: existing?.published_at && !existing.closed_at ? "Open. Anyone with this link can book a time." : "Not open yet \u2014 the link does not take bookings until you publish it." })
|
|
12105
12453
|
] }) : null
|
|
12106
12454
|
] });
|
|
12107
12455
|
}
|
|
12108
12456
|
|
|
12109
12457
|
// src/BookingSlots.tsx
|
|
12110
|
-
import { useCallback as
|
|
12111
|
-
import { useRecordsClient as
|
|
12458
|
+
import { useCallback as useCallback30, useEffect as useEffect34, useMemo as useMemo29, useState as useState44 } from "react";
|
|
12459
|
+
import { useRecordsClient as useRecordsClient36, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
|
|
12112
12460
|
import { bookingPath as bookingPath2 } from "@ai-matrx/records";
|
|
12113
|
-
import { Badge as Badge16, Button as
|
|
12114
|
-
import { Fragment as
|
|
12461
|
+
import { Badge as Badge16, Button as Button40, Skeleton as Skeleton27, cn as cn43 } from "@ai-matrx/design-system";
|
|
12462
|
+
import { Fragment as Fragment23, jsx as jsx49, jsxs as jsxs45 } from "react/jsx-runtime";
|
|
12115
12463
|
var WHAT_A_BOOKING_PAGE_IS = "A booking page offers times you are free and writes each appointment into this table as an ordinary record.";
|
|
12116
12464
|
function bookingSuggestion(tableName2) {
|
|
12117
12465
|
const subject = tableName2?.trim() ? tableName2.trim() : "appointments";
|
|
@@ -12125,15 +12473,15 @@ var STATE_WORDS = {
|
|
|
12125
12473
|
full: "Every time you offered is taken."
|
|
12126
12474
|
};
|
|
12127
12475
|
function BookingSlots({ tableId, className }) {
|
|
12128
|
-
const client =
|
|
12476
|
+
const client = useRecordsClient36();
|
|
12129
12477
|
const host = useRecordsUi();
|
|
12130
|
-
const [pages, setPages] =
|
|
12131
|
-
const [error, setError] =
|
|
12132
|
-
const [busy, setBusy] =
|
|
12133
|
-
const [copied, setCopied] =
|
|
12134
|
-
const [shown, setShown] =
|
|
12135
|
-
const [building, setBuilding] =
|
|
12136
|
-
const load =
|
|
12478
|
+
const [pages, setPages] = useState44(null);
|
|
12479
|
+
const [error, setError] = useState44(null);
|
|
12480
|
+
const [busy, setBusy] = useState44(null);
|
|
12481
|
+
const [copied, setCopied] = useState44(null);
|
|
12482
|
+
const [shown, setShown] = useState44(null);
|
|
12483
|
+
const [building, setBuilding] = useState44(false);
|
|
12484
|
+
const load = useCallback30(async () => {
|
|
12137
12485
|
const answered = await client.bookings(tableId ? { table_id: tableId } : {});
|
|
12138
12486
|
if (!answered.ok) {
|
|
12139
12487
|
setError(answered.error);
|
|
@@ -12143,15 +12491,15 @@ function BookingSlots({ tableId, className }) {
|
|
|
12143
12491
|
setError(null);
|
|
12144
12492
|
setPages(answered.data);
|
|
12145
12493
|
}, [client, tableId]);
|
|
12146
|
-
|
|
12494
|
+
useEffect34(() => {
|
|
12147
12495
|
void load();
|
|
12148
12496
|
}, [load]);
|
|
12149
|
-
const subjectIds =
|
|
12497
|
+
const subjectIds = useMemo29(
|
|
12150
12498
|
() => Array.from(new Set((pages ?? []).map((p) => p.table_id))),
|
|
12151
12499
|
[pages]
|
|
12152
12500
|
);
|
|
12153
12501
|
const levels = useMyLevels2(subjectIds);
|
|
12154
|
-
const toggle =
|
|
12502
|
+
const toggle = useCallback30(
|
|
12155
12503
|
async (page) => {
|
|
12156
12504
|
setBusy(page.form_id);
|
|
12157
12505
|
const wanted = page.published_at === null || page.closed_at !== null;
|
|
@@ -12165,7 +12513,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
12165
12513
|
},
|
|
12166
12514
|
[client, load]
|
|
12167
12515
|
);
|
|
12168
|
-
const copy =
|
|
12516
|
+
const copy = useCallback30(async (url, formId) => {
|
|
12169
12517
|
try {
|
|
12170
12518
|
await navigator.clipboard.writeText(url);
|
|
12171
12519
|
setCopied(formId);
|
|
@@ -12175,18 +12523,18 @@ function BookingSlots({ tableId, className }) {
|
|
|
12175
12523
|
setShown(url);
|
|
12176
12524
|
}
|
|
12177
12525
|
}, []);
|
|
12178
|
-
if (pages === null) return /* @__PURE__ */
|
|
12526
|
+
if (pages === null) return /* @__PURE__ */ jsx49(Skeleton27, { className: cn43("h-32 w-full", className) });
|
|
12179
12527
|
if (pages.length === 0 && !tableId && !error && !building) return null;
|
|
12180
12528
|
const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
12181
|
-
return /* @__PURE__ */
|
|
12182
|
-
/* @__PURE__ */
|
|
12183
|
-
/* @__PURE__ */
|
|
12184
|
-
/* @__PURE__ */
|
|
12185
|
-
/* @__PURE__ */
|
|
12186
|
-
tableId && pages.length > 0 ? /* @__PURE__ */
|
|
12529
|
+
return /* @__PURE__ */ jsxs45("section", { className: cn43("flex flex-col gap-3", className), "data-testid": "booking-slots", children: [
|
|
12530
|
+
/* @__PURE__ */ jsxs45("header", { className: "flex items-center gap-2", children: [
|
|
12531
|
+
/* @__PURE__ */ jsx49("h3", { className: "text-sm font-medium", children: "Bookings" }),
|
|
12532
|
+
/* @__PURE__ */ jsx49("span", { className: "text-xs text-muted-foreground", children: pages.length === 0 ? "none yet" : `${pages.length}` }),
|
|
12533
|
+
/* @__PURE__ */ jsx49("div", { className: "flex-1" }),
|
|
12534
|
+
tableId && pages.length > 0 ? /* @__PURE__ */ jsx49(Button40, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a booking page" }) : null
|
|
12187
12535
|
] }),
|
|
12188
|
-
error ? /* @__PURE__ */
|
|
12189
|
-
building && tableId ? /* @__PURE__ */
|
|
12536
|
+
error ? /* @__PURE__ */ jsx49(RefusalNotice, { error }) : null,
|
|
12537
|
+
building && tableId ? /* @__PURE__ */ jsx49(
|
|
12190
12538
|
BookingBuilder,
|
|
12191
12539
|
{
|
|
12192
12540
|
tableId,
|
|
@@ -12196,7 +12544,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
12196
12544
|
onClose: () => setBuilding(false)
|
|
12197
12545
|
}
|
|
12198
12546
|
) : null,
|
|
12199
|
-
pages.length === 0 && !building ? tableId ? /* @__PURE__ */
|
|
12547
|
+
pages.length === 0 && !building ? tableId ? /* @__PURE__ */ jsx49(
|
|
12200
12548
|
BuildOrAsk,
|
|
12201
12549
|
{
|
|
12202
12550
|
mayBuild: true,
|
|
@@ -12212,47 +12560,47 @@ function BookingSlots({ tableId, className }) {
|
|
|
12212
12560
|
buildLabel: "Build the booking page",
|
|
12213
12561
|
children: WHAT_A_BOOKING_PAGE_IS
|
|
12214
12562
|
}
|
|
12215
|
-
) : /* @__PURE__ */
|
|
12563
|
+
) : /* @__PURE__ */ jsxs45("p", { className: "text-xs text-muted-foreground", children: [
|
|
12216
12564
|
WHAT_A_BOOKING_PAGE_IS,
|
|
12217
12565
|
" Open the table the appointments should land in to make one."
|
|
12218
12566
|
] }) : null,
|
|
12219
|
-
/* @__PURE__ */
|
|
12567
|
+
/* @__PURE__ */ jsx49("ul", { className: "flex flex-col gap-2", children: pages.map((page) => {
|
|
12220
12568
|
const url = `${origin}${bookingPath2(page.form_id)}`;
|
|
12221
12569
|
const mayOpen = levels.data?.[page.table_id] === "admin";
|
|
12222
12570
|
const open = page.published_at !== null && page.closed_at === null;
|
|
12223
|
-
return /* @__PURE__ */
|
|
12224
|
-
/* @__PURE__ */
|
|
12225
|
-
/* @__PURE__ */
|
|
12226
|
-
/* @__PURE__ */
|
|
12571
|
+
return /* @__PURE__ */ jsxs45("li", { className: "rounded-md border p-2.5", "data-testid": "booking-page", children: [
|
|
12572
|
+
/* @__PURE__ */ jsxs45("div", { className: "flex items-center gap-2", children: [
|
|
12573
|
+
/* @__PURE__ */ jsx49("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: page.title ?? page.slug }),
|
|
12574
|
+
/* @__PURE__ */ jsx49(Badge16, { variant: page.state === "open" ? "default" : "secondary", children: page.state })
|
|
12227
12575
|
] }),
|
|
12228
|
-
/* @__PURE__ */
|
|
12229
|
-
/* @__PURE__ */
|
|
12230
|
-
/* @__PURE__ */
|
|
12576
|
+
/* @__PURE__ */ jsx49("p", { className: "mt-0.5 text-xs text-muted-foreground", children: STATE_WORDS[page.state] ?? page.state }),
|
|
12577
|
+
/* @__PURE__ */ jsxs45("p", { className: "mt-1.5 text-xs", children: [
|
|
12578
|
+
/* @__PURE__ */ jsx49("span", { className: "font-medium", children: page.upcoming }),
|
|
12231
12579
|
" coming up",
|
|
12232
12580
|
" \xB7 ",
|
|
12233
|
-
/* @__PURE__ */
|
|
12581
|
+
/* @__PURE__ */ jsx49("span", { className: "font-medium", children: page.booked }),
|
|
12234
12582
|
" booked in all",
|
|
12235
|
-
page.held > 0 ? /* @__PURE__ */
|
|
12583
|
+
page.held > 0 ? /* @__PURE__ */ jsxs45(Fragment23, { children: [
|
|
12236
12584
|
" \xB7 ",
|
|
12237
|
-
/* @__PURE__ */
|
|
12585
|
+
/* @__PURE__ */ jsx49("span", { className: "font-medium", children: page.held }),
|
|
12238
12586
|
" being held right now"
|
|
12239
12587
|
] }) : null,
|
|
12240
|
-
page.cancelled > 0 ? /* @__PURE__ */
|
|
12588
|
+
page.cancelled > 0 ? /* @__PURE__ */ jsxs45(Fragment23, { children: [
|
|
12241
12589
|
" \xB7 ",
|
|
12242
|
-
/* @__PURE__ */
|
|
12590
|
+
/* @__PURE__ */ jsx49("span", { className: "font-medium", children: page.cancelled }),
|
|
12243
12591
|
" cancelled"
|
|
12244
12592
|
] }) : null,
|
|
12245
|
-
/* @__PURE__ */
|
|
12593
|
+
/* @__PURE__ */ jsxs45("span", { className: "text-muted-foreground", children: [
|
|
12246
12594
|
" \xB7 ",
|
|
12247
12595
|
page.slot_minutes,
|
|
12248
12596
|
" minutes each"
|
|
12249
12597
|
] })
|
|
12250
12598
|
] }),
|
|
12251
|
-
/* @__PURE__ */
|
|
12252
|
-
/* @__PURE__ */
|
|
12253
|
-
/* @__PURE__ */
|
|
12254
|
-
mayOpen ? /* @__PURE__ */
|
|
12255
|
-
|
|
12599
|
+
/* @__PURE__ */ jsx49("p", { className: "mt-1 text-xs text-muted-foreground", children: nextInWords(page) }),
|
|
12600
|
+
/* @__PURE__ */ jsxs45("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
12601
|
+
/* @__PURE__ */ jsx49(Button40, { size: "sm", variant: "outline", onClick: () => void copy(url, page.form_id), children: copied === page.form_id ? "Copied" : "Copy link" }),
|
|
12602
|
+
mayOpen ? /* @__PURE__ */ jsx49(
|
|
12603
|
+
Button40,
|
|
12256
12604
|
{
|
|
12257
12605
|
size: "sm",
|
|
12258
12606
|
variant: "ghost",
|
|
@@ -12262,13 +12610,13 @@ function BookingSlots({ tableId, className }) {
|
|
|
12262
12610
|
}
|
|
12263
12611
|
) : null
|
|
12264
12612
|
] }),
|
|
12265
|
-
shown && shown === url ? /* @__PURE__ */
|
|
12613
|
+
shown && shown === url ? /* @__PURE__ */ jsxs45("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
12266
12614
|
"This browser would not let the page copy for you, so here it is to copy by hand: ",
|
|
12267
12615
|
url
|
|
12268
12616
|
] }) : null
|
|
12269
12617
|
] }, page.form_id);
|
|
12270
12618
|
}) }),
|
|
12271
|
-
pages.length > 0 && !levels.loading && levels.data && subjectIds.every((id) => levels.data?.[id] !== "admin") ? /* @__PURE__ */
|
|
12619
|
+
pages.length > 0 && !levels.loading && levels.data && subjectIds.every((id) => levels.data?.[id] !== "admin") ? /* @__PURE__ */ jsx49("p", { className: "text-xs text-muted-foreground", children: NO_ADMIN2 }) : null
|
|
12272
12620
|
] });
|
|
12273
12621
|
}
|
|
12274
12622
|
function nextInWords(page) {
|
|
@@ -12300,18 +12648,18 @@ function nextInWords(page) {
|
|
|
12300
12648
|
}
|
|
12301
12649
|
|
|
12302
12650
|
// src/CaptureSheet.tsx
|
|
12303
|
-
import { useCallback as
|
|
12304
|
-
import { useFields as useFields21, useRecordsClient as
|
|
12305
|
-
import { Button as
|
|
12651
|
+
import { useCallback as useCallback32, useEffect as useEffect36, useRef as useRef13, useState as useState46 } from "react";
|
|
12652
|
+
import { useFields as useFields21, useRecordsClient as useRecordsClient38, useTable as useTable20 } from "@ai-matrx/records/react";
|
|
12653
|
+
import { Button as Button42, Input as Input4, Skeleton as Skeleton29, Textarea as Textarea3, cn as cn45 } from "@ai-matrx/design-system";
|
|
12306
12654
|
|
|
12307
12655
|
// src/CaptureRun.tsx
|
|
12308
|
-
import { useCallback as
|
|
12656
|
+
import { useCallback as useCallback31, useEffect as useEffect35, useMemo as useMemo30, useRef as useRef12, useState as useState45 } from "react";
|
|
12309
12657
|
import {
|
|
12310
12658
|
openCaptureQueue
|
|
12311
12659
|
} from "@ai-matrx/records";
|
|
12312
|
-
import { useRecordsClient as
|
|
12313
|
-
import { Button as
|
|
12314
|
-
import { jsx as
|
|
12660
|
+
import { useRecordsClient as useRecordsClient37 } from "@ai-matrx/records/react";
|
|
12661
|
+
import { Button as Button41, Input as Input3, Skeleton as Skeleton28, Textarea as Textarea2, cn as cn44 } from "@ai-matrx/design-system";
|
|
12662
|
+
import { jsx as jsx50, jsxs as jsxs46 } from "react/jsx-runtime";
|
|
12315
12663
|
function controlFor(field) {
|
|
12316
12664
|
if (!field) return "text";
|
|
12317
12665
|
const label = `${field.key} ${field.label ?? ""}`.toLowerCase();
|
|
@@ -12348,21 +12696,21 @@ function whereWeAre(timeoutMs = 4e3) {
|
|
|
12348
12696
|
});
|
|
12349
12697
|
}
|
|
12350
12698
|
function CaptureRun({ sheetId, face: given, className }) {
|
|
12351
|
-
const client =
|
|
12699
|
+
const client = useRecordsClient37();
|
|
12352
12700
|
const host = useRecordsUi();
|
|
12353
|
-
const [face, setFace] =
|
|
12354
|
-
const [loadFailed, setLoadFailed] =
|
|
12355
|
-
const [at, setAt] =
|
|
12356
|
-
const [answers, setAnswers] =
|
|
12357
|
-
const [files, setFiles] =
|
|
12358
|
-
const [missing, setMissing] =
|
|
12359
|
-
const [done, setDone] =
|
|
12360
|
-
const [counts, setCounts] =
|
|
12361
|
-
const [items, setItems] =
|
|
12362
|
-
const [lastSynced, setLastSynced] =
|
|
12363
|
-
const [sending, setSending] =
|
|
12701
|
+
const [face, setFace] = useState45(given);
|
|
12702
|
+
const [loadFailed, setLoadFailed] = useState45(null);
|
|
12703
|
+
const [at, setAt] = useState45(0);
|
|
12704
|
+
const [answers, setAnswers] = useState45({});
|
|
12705
|
+
const [files, setFiles] = useState45({});
|
|
12706
|
+
const [missing, setMissing] = useState45(null);
|
|
12707
|
+
const [done, setDone] = useState45(null);
|
|
12708
|
+
const [counts, setCounts] = useState45({ waiting: 0, sending: 0, refused: 0, landed: 0 });
|
|
12709
|
+
const [items, setItems] = useState45([]);
|
|
12710
|
+
const [lastSynced, setLastSynced] = useState45(null);
|
|
12711
|
+
const [sending, setSending] = useState45(false);
|
|
12364
12712
|
const queueRef = useRef12(null);
|
|
12365
|
-
|
|
12713
|
+
useEffect35(() => {
|
|
12366
12714
|
const q2 = openCaptureQueue({
|
|
12367
12715
|
onChange: (c, all) => {
|
|
12368
12716
|
setCounts(c);
|
|
@@ -12400,7 +12748,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12400
12748
|
void q2.sync().then(() => void q2.lastSyncedAt().then(setLastSynced));
|
|
12401
12749
|
return () => q2.dispose();
|
|
12402
12750
|
}, [client, host]);
|
|
12403
|
-
|
|
12751
|
+
useEffect35(() => {
|
|
12404
12752
|
if (given !== void 0) return;
|
|
12405
12753
|
let cancelled = false;
|
|
12406
12754
|
void client.captureOpen({ sheet_id: sheetId }).then((res) => {
|
|
@@ -12412,7 +12760,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12412
12760
|
cancelled = true;
|
|
12413
12761
|
};
|
|
12414
12762
|
}, [client, given, sheetId]);
|
|
12415
|
-
const questions =
|
|
12763
|
+
const questions = useMemo30(() => {
|
|
12416
12764
|
const asked = face?.presentation?.questions ?? [];
|
|
12417
12765
|
if (asked.length > 0) return asked;
|
|
12418
12766
|
return (face?.fields ?? []).map((f) => ({
|
|
@@ -12422,11 +12770,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12422
12770
|
required: f.required
|
|
12423
12771
|
}));
|
|
12424
12772
|
}, [face]);
|
|
12425
|
-
const fieldOf =
|
|
12773
|
+
const fieldOf = useCallback31(
|
|
12426
12774
|
(key) => (face?.fields ?? []).find((f) => f.key === key),
|
|
12427
12775
|
[face]
|
|
12428
12776
|
);
|
|
12429
|
-
const sync =
|
|
12777
|
+
const sync = useCallback31(async () => {
|
|
12430
12778
|
const q2 = queueRef.current;
|
|
12431
12779
|
if (!q2) return;
|
|
12432
12780
|
setSending(true);
|
|
@@ -12439,7 +12787,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12439
12787
|
setSending(false);
|
|
12440
12788
|
}
|
|
12441
12789
|
}, []);
|
|
12442
|
-
const answered =
|
|
12790
|
+
const answered = useCallback31(
|
|
12443
12791
|
(key) => {
|
|
12444
12792
|
if (files[key]) return true;
|
|
12445
12793
|
const v = answers[key];
|
|
@@ -12484,24 +12832,24 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12484
12832
|
if (online) await sync();
|
|
12485
12833
|
}
|
|
12486
12834
|
if (loadFailed) {
|
|
12487
|
-
return /* @__PURE__ */
|
|
12835
|
+
return /* @__PURE__ */ jsx50("section", { className: cn44("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx50("p", { className: "text-sm text-destructive", "data-testid": "capture-refusal", children: loadFailed }) });
|
|
12488
12836
|
}
|
|
12489
12837
|
if (face === void 0) {
|
|
12490
|
-
return /* @__PURE__ */
|
|
12838
|
+
return /* @__PURE__ */ jsx50(Skeleton28, { className: cn44("mx-auto h-64 w-full max-w-sm", className) });
|
|
12491
12839
|
}
|
|
12492
12840
|
if (face === null) {
|
|
12493
|
-
return /* @__PURE__ */
|
|
12841
|
+
return /* @__PURE__ */ jsx50("section", { className: cn44("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx50("p", { className: "text-sm", "data-testid": "capture-refusal", children: "This capture sheet is not here, or it is not yours to open. If somebody sent you this link, ask them to check it." }) });
|
|
12494
12842
|
}
|
|
12495
12843
|
const waiting = counts.waiting + counts.sending;
|
|
12496
|
-
const queueLine = /* @__PURE__ */
|
|
12497
|
-
/* @__PURE__ */
|
|
12498
|
-
counts.refused > 0 ? /* @__PURE__ */
|
|
12844
|
+
const queueLine = /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", "data-testid": "capture-queue-line", children: [
|
|
12845
|
+
/* @__PURE__ */ jsx50("span", { className: "tabular-nums", "data-testid": "capture-waiting", children: waiting === 0 ? "Nothing waiting" : `${waiting} waiting` }),
|
|
12846
|
+
counts.refused > 0 ? /* @__PURE__ */ jsxs46("span", { className: "text-destructive tabular-nums", "data-testid": "capture-refused", children: [
|
|
12499
12847
|
counts.refused,
|
|
12500
12848
|
" to fix"
|
|
12501
12849
|
] }) : null,
|
|
12502
|
-
/* @__PURE__ */
|
|
12503
|
-
waiting > 0 || counts.refused > 0 ? /* @__PURE__ */
|
|
12504
|
-
|
|
12850
|
+
/* @__PURE__ */ jsx50("span", { className: "ml-auto", "data-testid": "capture-last-synced", children: lastSynced ? `Last synced ${new Date(lastSynced).toLocaleTimeString()}` : "Not synced yet" }),
|
|
12851
|
+
waiting > 0 || counts.refused > 0 ? /* @__PURE__ */ jsx50(
|
|
12852
|
+
Button41,
|
|
12505
12853
|
{
|
|
12506
12854
|
size: "sm",
|
|
12507
12855
|
variant: "ghost",
|
|
@@ -12513,11 +12861,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12513
12861
|
}
|
|
12514
12862
|
) : null
|
|
12515
12863
|
] });
|
|
12516
|
-
const queuePanel = items.filter((i) => i.state !== "landed").length > 0 ? /* @__PURE__ */
|
|
12517
|
-
/* @__PURE__ */
|
|
12518
|
-
/* @__PURE__ */
|
|
12519
|
-
i.state === "refused" ? /* @__PURE__ */
|
|
12520
|
-
|
|
12864
|
+
const queuePanel = items.filter((i) => i.state !== "landed").length > 0 ? /* @__PURE__ */ jsx50("ul", { className: "flex flex-col gap-1 rounded border p-2", "data-testid": "capture-queue", children: items.filter((i) => i.state !== "landed").map((i) => /* @__PURE__ */ jsxs46("li", { className: "flex items-start gap-2 text-[11px]", children: [
|
|
12865
|
+
/* @__PURE__ */ jsx50("span", { className: "tabular-nums text-muted-foreground", children: new Date(i.captured_at).toLocaleTimeString() }),
|
|
12866
|
+
/* @__PURE__ */ jsx50("span", { className: cn44("flex-1", i.state === "refused" && "text-destructive"), children: i.state === "refused" ? i.last_error ?? "This one was refused." : i.last_error ?? "Waiting for a signal." }),
|
|
12867
|
+
i.state === "refused" ? /* @__PURE__ */ jsx50(
|
|
12868
|
+
Button41,
|
|
12521
12869
|
{
|
|
12522
12870
|
size: "sm",
|
|
12523
12871
|
variant: "ghost",
|
|
@@ -12528,19 +12876,19 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12528
12876
|
) : null
|
|
12529
12877
|
] }, i.client_key)) }) : null;
|
|
12530
12878
|
if (!face.may_capture) {
|
|
12531
|
-
return /* @__PURE__ */
|
|
12532
|
-
/* @__PURE__ */
|
|
12533
|
-
/* @__PURE__ */
|
|
12879
|
+
return /* @__PURE__ */ jsxs46("section", { className: cn44("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
|
|
12880
|
+
/* @__PURE__ */ jsx50("h1", { className: "text-lg font-medium", children: face.title }),
|
|
12881
|
+
/* @__PURE__ */ jsx50("p", { className: "text-sm", "data-testid": "capture-refusal", children: face.message }),
|
|
12534
12882
|
queueLine,
|
|
12535
12883
|
queuePanel
|
|
12536
12884
|
] });
|
|
12537
12885
|
}
|
|
12538
12886
|
if (done) {
|
|
12539
12887
|
const thanks = face.presentation?.thank_you ?? {};
|
|
12540
|
-
return /* @__PURE__ */
|
|
12541
|
-
/* @__PURE__ */
|
|
12542
|
-
/* @__PURE__ */
|
|
12543
|
-
/* @__PURE__ */
|
|
12888
|
+
return /* @__PURE__ */ jsxs46("section", { className: cn44("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
|
|
12889
|
+
/* @__PURE__ */ jsx50("h2", { className: "text-lg font-medium", "data-testid": "capture-thankyou", children: thanks.title ?? "Logged" }),
|
|
12890
|
+
/* @__PURE__ */ jsx50("p", { className: "text-sm text-muted-foreground", children: done.queued ? "There is no signal, so this one is on the phone and goes up the moment there is. Nothing is lost." : thanks.body ?? "Ready for the next one." }),
|
|
12891
|
+
/* @__PURE__ */ jsx50(Button41, { className: "h-12", onClick: () => setDone(null), "data-testid": "capture-next", children: "Next one" }),
|
|
12544
12892
|
queueLine,
|
|
12545
12893
|
queuePanel
|
|
12546
12894
|
] });
|
|
@@ -12549,14 +12897,14 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12549
12897
|
const field = fieldOf(q.field);
|
|
12550
12898
|
const control = controlFor(field);
|
|
12551
12899
|
const last = at >= questions.length - 1;
|
|
12552
|
-
return /* @__PURE__ */
|
|
12553
|
-
/* @__PURE__ */
|
|
12554
|
-
/* @__PURE__ */
|
|
12900
|
+
return /* @__PURE__ */ jsxs46("section", { className: cn44("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
|
|
12901
|
+
/* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", children: [
|
|
12902
|
+
/* @__PURE__ */ jsxs46("span", { className: "tabular-nums", "data-testid": "capture-progress", children: [
|
|
12555
12903
|
at + 1,
|
|
12556
12904
|
" of ",
|
|
12557
12905
|
questions.length
|
|
12558
12906
|
] }),
|
|
12559
|
-
/* @__PURE__ */
|
|
12907
|
+
/* @__PURE__ */ jsx50("div", { className: "h-1 flex-1 overflow-hidden rounded bg-muted", children: /* @__PURE__ */ jsx50(
|
|
12560
12908
|
"div",
|
|
12561
12909
|
{
|
|
12562
12910
|
className: "h-full bg-primary transition-[width] duration-200",
|
|
@@ -12564,7 +12912,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12564
12912
|
}
|
|
12565
12913
|
) })
|
|
12566
12914
|
] }),
|
|
12567
|
-
/* @__PURE__ */
|
|
12915
|
+
/* @__PURE__ */ jsxs46(
|
|
12568
12916
|
"label",
|
|
12569
12917
|
{
|
|
12570
12918
|
className: "text-xl font-medium leading-snug",
|
|
@@ -12572,13 +12920,13 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12572
12920
|
"data-testid": "capture-ask",
|
|
12573
12921
|
children: [
|
|
12574
12922
|
q.ask ?? q.field,
|
|
12575
|
-
q.required ? /* @__PURE__ */
|
|
12923
|
+
q.required ? /* @__PURE__ */ jsx50("span", { className: "text-muted-foreground", children: " *" }) : null
|
|
12576
12924
|
]
|
|
12577
12925
|
}
|
|
12578
12926
|
),
|
|
12579
|
-
q.help ? /* @__PURE__ */
|
|
12580
|
-
control === "photo" || control === "voice" ? !host.upload ? /* @__PURE__ */
|
|
12581
|
-
/* @__PURE__ */
|
|
12927
|
+
q.help ? /* @__PURE__ */ jsx50("p", { className: "-mt-1 text-sm text-muted-foreground", children: q.help }) : null,
|
|
12928
|
+
control === "photo" || control === "voice" ? !host.upload ? /* @__PURE__ */ jsx50("p", { className: "rounded border border-dashed px-2 py-2 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : /* @__PURE__ */ jsxs46("div", { className: "flex flex-col gap-1", children: [
|
|
12929
|
+
/* @__PURE__ */ jsx50(
|
|
12582
12930
|
"input",
|
|
12583
12931
|
{
|
|
12584
12932
|
id: `capture-${q.field}`,
|
|
@@ -12596,11 +12944,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12596
12944
|
}
|
|
12597
12945
|
}
|
|
12598
12946
|
),
|
|
12599
|
-
files[q.field] ? /* @__PURE__ */
|
|
12947
|
+
files[q.field] ? /* @__PURE__ */ jsxs46("p", { className: "text-[11px] text-muted-foreground", "data-testid": `capture-have-${q.field}`, children: [
|
|
12600
12948
|
files[q.field].name,
|
|
12601
12949
|
" is on the phone. It goes up with the capture."
|
|
12602
12950
|
] }) : null
|
|
12603
|
-
] }) : control === "number" ? /* @__PURE__ */
|
|
12951
|
+
] }) : control === "number" ? /* @__PURE__ */ jsx50(
|
|
12604
12952
|
Input3,
|
|
12605
12953
|
{
|
|
12606
12954
|
id: `capture-${q.field}`,
|
|
@@ -12616,7 +12964,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12616
12964
|
setMissing(null);
|
|
12617
12965
|
}
|
|
12618
12966
|
}
|
|
12619
|
-
) : /* @__PURE__ */
|
|
12967
|
+
) : /* @__PURE__ */ jsx50(
|
|
12620
12968
|
Textarea2,
|
|
12621
12969
|
{
|
|
12622
12970
|
id: `capture-${q.field}`,
|
|
@@ -12631,10 +12979,10 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12631
12979
|
}
|
|
12632
12980
|
}
|
|
12633
12981
|
),
|
|
12634
|
-
missing ? /* @__PURE__ */
|
|
12635
|
-
/* @__PURE__ */
|
|
12636
|
-
at > 0 ? /* @__PURE__ */
|
|
12637
|
-
|
|
12982
|
+
missing ? /* @__PURE__ */ jsx50("p", { className: "text-sm text-destructive", "data-testid": "capture-missing", children: missing }) : null,
|
|
12983
|
+
/* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2", children: [
|
|
12984
|
+
at > 0 ? /* @__PURE__ */ jsx50(
|
|
12985
|
+
Button41,
|
|
12638
12986
|
{
|
|
12639
12987
|
variant: "ghost",
|
|
12640
12988
|
className: "h-12 px-3",
|
|
@@ -12643,16 +12991,16 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12643
12991
|
children: "Back"
|
|
12644
12992
|
}
|
|
12645
12993
|
) : null,
|
|
12646
|
-
last ? /* @__PURE__ */
|
|
12647
|
-
|
|
12994
|
+
last ? /* @__PURE__ */ jsx50(
|
|
12995
|
+
Button41,
|
|
12648
12996
|
{
|
|
12649
12997
|
className: "h-12 flex-1 text-base",
|
|
12650
12998
|
onClick: () => void capture(),
|
|
12651
12999
|
"data-testid": "capture-submit",
|
|
12652
13000
|
children: "Capture"
|
|
12653
13001
|
}
|
|
12654
|
-
) : /* @__PURE__ */
|
|
12655
|
-
|
|
13002
|
+
) : /* @__PURE__ */ jsx50(
|
|
13003
|
+
Button41,
|
|
12656
13004
|
{
|
|
12657
13005
|
className: "h-12 flex-1 text-base",
|
|
12658
13006
|
onClick: () => setAt(at + 1),
|
|
@@ -12667,7 +13015,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
12667
13015
|
}
|
|
12668
13016
|
|
|
12669
13017
|
// src/CaptureSheet.tsx
|
|
12670
|
-
import { Fragment as
|
|
13018
|
+
import { Fragment as Fragment24, jsx as jsx51, jsxs as jsxs47 } from "react/jsx-runtime";
|
|
12671
13019
|
var CAPTURE_MODES = ["reading", "photo", "voice"];
|
|
12672
13020
|
var IN_MEMORY_QUEUE_REASON = "No capture queue is bound, so anything that cannot be sent right now is held in this page's memory and is lost if the page closes. Bind `captureQueue` on <RecordsUiProvider> with your host's own durable store (IndexedDB in a browser) and a queued capture survives the tab, the reload and the flight. It is said here rather than discovered later.";
|
|
12673
13021
|
function mintClientKey() {
|
|
@@ -12687,8 +13035,8 @@ function CaptureSheet({
|
|
|
12687
13035
|
attachmentField,
|
|
12688
13036
|
className
|
|
12689
13037
|
}) {
|
|
12690
|
-
if (sheetId) return /* @__PURE__ */
|
|
12691
|
-
return /* @__PURE__ */
|
|
13038
|
+
if (sheetId) return /* @__PURE__ */ jsx51(CaptureRun, { sheetId, className });
|
|
13039
|
+
return /* @__PURE__ */ jsx51(
|
|
12692
13040
|
AdHocCaptureSheet,
|
|
12693
13041
|
{
|
|
12694
13042
|
tableId,
|
|
@@ -12706,21 +13054,21 @@ function AdHocCaptureSheet({
|
|
|
12706
13054
|
attachmentField,
|
|
12707
13055
|
className
|
|
12708
13056
|
}) {
|
|
12709
|
-
const client =
|
|
13057
|
+
const client = useRecordsClient38();
|
|
12710
13058
|
const host = useRecordsUi();
|
|
12711
13059
|
const table = useTable20(tableId);
|
|
12712
13060
|
const fields = useFields21(tableId);
|
|
12713
|
-
const [mode, setMode] =
|
|
12714
|
-
const [reading, setReading] =
|
|
12715
|
-
const [note, setNote] =
|
|
12716
|
-
const [fileId, setFileId] =
|
|
12717
|
-
const [pending, setPending] =
|
|
12718
|
-
const [saved, setSaved] =
|
|
12719
|
-
const [error, setError] =
|
|
12720
|
-
const [uploadError, setUploadError] =
|
|
12721
|
-
const [flushing, setFlushing] =
|
|
13061
|
+
const [mode, setMode] = useState46("reading");
|
|
13062
|
+
const [reading, setReading] = useState46("");
|
|
13063
|
+
const [note, setNote] = useState46("");
|
|
13064
|
+
const [fileId, setFileId] = useState46(null);
|
|
13065
|
+
const [pending, setPending] = useState46(null);
|
|
13066
|
+
const [saved, setSaved] = useState46([]);
|
|
13067
|
+
const [error, setError] = useState46(null);
|
|
13068
|
+
const [uploadError, setUploadError] = useState46(null);
|
|
13069
|
+
const [flushing, setFlushing] = useState46(false);
|
|
12722
13070
|
const queue = useRef13([]);
|
|
12723
|
-
const keep =
|
|
13071
|
+
const keep = useCallback32(
|
|
12724
13072
|
async (next) => {
|
|
12725
13073
|
queue.current = next;
|
|
12726
13074
|
setPending(next);
|
|
@@ -12728,7 +13076,7 @@ function AdHocCaptureSheet({
|
|
|
12728
13076
|
},
|
|
12729
13077
|
[host]
|
|
12730
13078
|
);
|
|
12731
|
-
|
|
13079
|
+
useEffect36(() => {
|
|
12732
13080
|
let cancelled = false;
|
|
12733
13081
|
void (async () => {
|
|
12734
13082
|
const held = host.captureQueue ? await host.captureQueue.load() : [];
|
|
@@ -12740,7 +13088,7 @@ function AdHocCaptureSheet({
|
|
|
12740
13088
|
cancelled = true;
|
|
12741
13089
|
};
|
|
12742
13090
|
}, [host]);
|
|
12743
|
-
const resolved =
|
|
13091
|
+
const resolved = useCallback32(() => {
|
|
12744
13092
|
const all = fields.data ?? [];
|
|
12745
13093
|
return {
|
|
12746
13094
|
reading: readingField ?? all.find((f) => f.type === "range")?.key ?? null,
|
|
@@ -12748,7 +13096,7 @@ function AdHocCaptureSheet({
|
|
|
12748
13096
|
attachment: attachmentField ?? all.find((f) => f.format === "file" || f.format === "image")?.key ?? null
|
|
12749
13097
|
};
|
|
12750
13098
|
}, [attachmentField, fields.data, noteField, readingField, table.data]);
|
|
12751
|
-
const flush =
|
|
13099
|
+
const flush = useCallback32(async () => {
|
|
12752
13100
|
if (flushing || queue.current.length === 0) return;
|
|
12753
13101
|
setFlushing(true);
|
|
12754
13102
|
const left = [];
|
|
@@ -12800,14 +13148,14 @@ function AdHocCaptureSheet({
|
|
|
12800
13148
|
await keep([...queue.current, item]);
|
|
12801
13149
|
await flush();
|
|
12802
13150
|
}
|
|
12803
|
-
if (table.error) return /* @__PURE__ */
|
|
12804
|
-
if (fields.error) return /* @__PURE__ */
|
|
12805
|
-
if (fields.loading || pending === null) return /* @__PURE__ */
|
|
13151
|
+
if (table.error) return /* @__PURE__ */ jsx51(RefusalNotice, { error: table.error, className });
|
|
13152
|
+
if (fields.error) return /* @__PURE__ */ jsx51(RefusalNotice, { error: fields.error, className });
|
|
13153
|
+
if (fields.loading || pending === null) return /* @__PURE__ */ jsx51(Skeleton29, { className: cn45("h-64 w-full", className) });
|
|
12806
13154
|
const keys = resolved();
|
|
12807
|
-
return /* @__PURE__ */
|
|
12808
|
-
/* @__PURE__ */
|
|
12809
|
-
CAPTURE_MODES.map((m) => /* @__PURE__ */
|
|
12810
|
-
|
|
13155
|
+
return /* @__PURE__ */ jsxs47("section", { className: cn45("mx-auto flex w-full max-w-sm flex-col gap-2", className), children: [
|
|
13156
|
+
/* @__PURE__ */ jsxs47("header", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
13157
|
+
CAPTURE_MODES.map((m) => /* @__PURE__ */ jsx51(
|
|
13158
|
+
Button42,
|
|
12811
13159
|
{
|
|
12812
13160
|
size: "sm",
|
|
12813
13161
|
variant: m === mode ? "default" : "ghost",
|
|
@@ -12817,10 +13165,10 @@ function AdHocCaptureSheet({
|
|
|
12817
13165
|
},
|
|
12818
13166
|
m
|
|
12819
13167
|
)),
|
|
12820
|
-
/* @__PURE__ */
|
|
13168
|
+
/* @__PURE__ */ jsx51("span", { className: "ml-auto tabular-nums", children: pending.length > 0 ? `${pending.length} waiting` : `${saved.length} saved` })
|
|
12821
13169
|
] }),
|
|
12822
|
-
error ? /* @__PURE__ */
|
|
12823
|
-
mode === "reading" ? keys.reading ? /* @__PURE__ */
|
|
13170
|
+
error ? /* @__PURE__ */ jsx51(RefusalNotice, { error }) : null,
|
|
13171
|
+
mode === "reading" ? keys.reading ? /* @__PURE__ */ jsx51(
|
|
12824
13172
|
Input4,
|
|
12825
13173
|
{
|
|
12826
13174
|
"aria-label": "Reading",
|
|
@@ -12830,22 +13178,22 @@ function AdHocCaptureSheet({
|
|
|
12830
13178
|
value: reading,
|
|
12831
13179
|
onChange: (e) => setReading(e.target.value)
|
|
12832
13180
|
}
|
|
12833
|
-
) : /* @__PURE__ */
|
|
13181
|
+
) : /* @__PURE__ */ jsxs47("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
|
|
12834
13182
|
table.data?.name ?? "This table",
|
|
12835
13183
|
" has no number Field for a reading to go in, so this mode would have nowhere to put what you typed. Add one, or name it with ",
|
|
12836
|
-
/* @__PURE__ */
|
|
13184
|
+
/* @__PURE__ */ jsx51("code", { children: "readingField" }),
|
|
12837
13185
|
"."
|
|
12838
13186
|
] }) : null,
|
|
12839
|
-
mode !== "reading" ? !host.upload ? /* @__PURE__ */
|
|
13187
|
+
mode !== "reading" ? !host.upload ? /* @__PURE__ */ jsx51("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : !keys.attachment ? /* @__PURE__ */ jsxs47("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
|
|
12840
13188
|
table.data?.name ?? "This table",
|
|
12841
13189
|
" has no Field a file id can go in, so a ",
|
|
12842
13190
|
mode,
|
|
12843
13191
|
" capture would upload the bytes and then have nowhere to record them. Add one, or name it with",
|
|
12844
13192
|
" ",
|
|
12845
|
-
/* @__PURE__ */
|
|
13193
|
+
/* @__PURE__ */ jsx51("code", { children: "attachmentField" }),
|
|
12846
13194
|
"."
|
|
12847
|
-
] }) : /* @__PURE__ */
|
|
12848
|
-
/* @__PURE__ */
|
|
13195
|
+
] }) : /* @__PURE__ */ jsxs47(Fragment24, { children: [
|
|
13196
|
+
/* @__PURE__ */ jsx51(
|
|
12849
13197
|
"input",
|
|
12850
13198
|
{
|
|
12851
13199
|
"aria-label": mode === "photo" ? "Take a photo" : "Record a voice note",
|
|
@@ -12864,10 +13212,10 @@ function AdHocCaptureSheet({
|
|
|
12864
13212
|
}
|
|
12865
13213
|
}
|
|
12866
13214
|
),
|
|
12867
|
-
fileId ? /* @__PURE__ */
|
|
12868
|
-
uploadError ? /* @__PURE__ */
|
|
13215
|
+
fileId ? /* @__PURE__ */ jsx51("p", { className: "text-[11px] text-muted-foreground", children: "Attached." }) : null,
|
|
13216
|
+
uploadError ? /* @__PURE__ */ jsx51("p", { className: "text-xs text-destructive", children: uploadError }) : null
|
|
12869
13217
|
] }) : null,
|
|
12870
|
-
/* @__PURE__ */
|
|
13218
|
+
/* @__PURE__ */ jsx51(
|
|
12871
13219
|
Textarea3,
|
|
12872
13220
|
{
|
|
12873
13221
|
"aria-label": "Note",
|
|
@@ -12878,12 +13226,12 @@ function AdHocCaptureSheet({
|
|
|
12878
13226
|
onChange: (e) => setNote(e.target.value)
|
|
12879
13227
|
}
|
|
12880
13228
|
),
|
|
12881
|
-
/* @__PURE__ */
|
|
12882
|
-
pending.length > 0 ? /* @__PURE__ */
|
|
12883
|
-
/* @__PURE__ */
|
|
12884
|
-
/* @__PURE__ */
|
|
12885
|
-
/* @__PURE__ */
|
|
12886
|
-
|
|
13229
|
+
/* @__PURE__ */ jsx51(Button42, { className: "h-12", onClick: () => void capture(), children: "Capture" }),
|
|
13230
|
+
pending.length > 0 ? /* @__PURE__ */ jsxs47("div", { className: "flex flex-col gap-1 rounded border p-2", children: [
|
|
13231
|
+
/* @__PURE__ */ jsxs47("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
13232
|
+
/* @__PURE__ */ jsx51("span", { className: "font-medium", children: "Waiting to send" }),
|
|
13233
|
+
/* @__PURE__ */ jsx51(
|
|
13234
|
+
Button42,
|
|
12887
13235
|
{
|
|
12888
13236
|
size: "sm",
|
|
12889
13237
|
variant: "ghost",
|
|
@@ -12894,7 +13242,7 @@ function AdHocCaptureSheet({
|
|
|
12894
13242
|
}
|
|
12895
13243
|
)
|
|
12896
13244
|
] }),
|
|
12897
|
-
/* @__PURE__ */
|
|
13245
|
+
/* @__PURE__ */ jsx51("ul", { className: "flex flex-col gap-0.5", children: pending.map((item) => /* @__PURE__ */ jsxs47("li", { className: "text-[11px] text-muted-foreground", children: [
|
|
12898
13246
|
new Date(item.capturedAt).toLocaleTimeString(),
|
|
12899
13247
|
" \xB7 ",
|
|
12900
13248
|
item.attempts,
|
|
@@ -12902,23 +13250,23 @@ function AdHocCaptureSheet({
|
|
|
12902
13250
|
item.lastRefusal ? ` \xB7 ${item.lastRefusal}` : ""
|
|
12903
13251
|
] }, item.clientKey)) })
|
|
12904
13252
|
] }) : null,
|
|
12905
|
-
!host.captureQueue ? /* @__PURE__ */
|
|
13253
|
+
!host.captureQueue ? /* @__PURE__ */ jsx51("p", { className: "rounded border border-dashed px-2 py-1 text-[11px] text-muted-foreground", children: IN_MEMORY_QUEUE_REASON }) : null
|
|
12906
13254
|
] });
|
|
12907
13255
|
}
|
|
12908
13256
|
|
|
12909
13257
|
// src/PortalShell.tsx
|
|
12910
|
-
import { useCallback as
|
|
12911
|
-
import { useRecordsClient as
|
|
12912
|
-
import { Button as
|
|
12913
|
-
import { jsx as
|
|
13258
|
+
import { useCallback as useCallback33, useEffect as useEffect37, useState as useState47 } from "react";
|
|
13259
|
+
import { useRecordsClient as useRecordsClient39 } from "@ai-matrx/records/react";
|
|
13260
|
+
import { Button as Button43, Skeleton as Skeleton30, cn as cn46 } from "@ai-matrx/design-system";
|
|
13261
|
+
import { jsx as jsx52, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
12914
13262
|
function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
12915
|
-
const client =
|
|
12916
|
-
const [card, setCard] =
|
|
12917
|
-
const [reach, setReach] =
|
|
12918
|
-
const [error, setError] =
|
|
12919
|
-
const [open, setOpen] =
|
|
12920
|
-
const [sending, setSending] =
|
|
12921
|
-
const load =
|
|
13263
|
+
const client = useRecordsClient39();
|
|
13264
|
+
const [card, setCard] = useState47(null);
|
|
13265
|
+
const [reach, setReach] = useState47(null);
|
|
13266
|
+
const [error, setError] = useState47(null);
|
|
13267
|
+
const [open, setOpen] = useState47(null);
|
|
13268
|
+
const [sending, setSending] = useState47(false);
|
|
13269
|
+
const load = useCallback33(async () => {
|
|
12922
13270
|
const who = await client.externalPrincipalCard();
|
|
12923
13271
|
if (!who.ok) {
|
|
12924
13272
|
setError(who.error);
|
|
@@ -12933,23 +13281,23 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
|
12933
13281
|
}
|
|
12934
13282
|
setReach(reached.data.map((row) => row.resource_id));
|
|
12935
13283
|
}, [client, resourceType]);
|
|
12936
|
-
|
|
13284
|
+
useEffect37(() => {
|
|
12937
13285
|
void load();
|
|
12938
13286
|
}, [load]);
|
|
12939
13287
|
if (error) {
|
|
12940
|
-
return /* @__PURE__ */
|
|
13288
|
+
return /* @__PURE__ */ jsx52(RefusalNotice, { error, className });
|
|
12941
13289
|
}
|
|
12942
|
-
if (!card || reach === null) return /* @__PURE__ */
|
|
13290
|
+
if (!card || reach === null) return /* @__PURE__ */ jsx52(Skeleton30, { className: cn46("h-64 w-full", className) });
|
|
12943
13291
|
if (!card.external) {
|
|
12944
|
-
return /* @__PURE__ */
|
|
13292
|
+
return /* @__PURE__ */ jsx52("p", { className: cn46("rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", className), children: card.explanation || "You are a member of an organization here, so this portal is not yours \u2014 a portal is the scoped view an OUTSIDER gets. You have the full application instead." });
|
|
12945
13293
|
}
|
|
12946
13294
|
if (sending && form) {
|
|
12947
|
-
return /* @__PURE__ */
|
|
12948
|
-
/* @__PURE__ */
|
|
12949
|
-
/* @__PURE__ */
|
|
12950
|
-
/* @__PURE__ */
|
|
13295
|
+
return /* @__PURE__ */ jsxs48("section", { className: cn46("flex min-h-0 flex-col gap-2", className), children: [
|
|
13296
|
+
/* @__PURE__ */ jsxs48("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
13297
|
+
/* @__PURE__ */ jsx52("span", { className: "font-medium text-foreground", children: form.name }),
|
|
13298
|
+
/* @__PURE__ */ jsx52(Button43, { size: "sm", variant: "ghost", className: "ml-auto h-6 px-2 text-[11px]", onClick: () => setSending(false), children: "Back" })
|
|
12951
13299
|
] }),
|
|
12952
|
-
/* @__PURE__ */
|
|
13300
|
+
/* @__PURE__ */ jsx52(
|
|
12953
13301
|
FormRunner,
|
|
12954
13302
|
{
|
|
12955
13303
|
form,
|
|
@@ -12962,38 +13310,38 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
|
12962
13310
|
] });
|
|
12963
13311
|
}
|
|
12964
13312
|
if (open) {
|
|
12965
|
-
return /* @__PURE__ */
|
|
13313
|
+
return /* @__PURE__ */ jsx52("section", { className: cn46("flex min-h-0 flex-col gap-2", className), children: /* @__PURE__ */ jsx52(Peek, { tableId, recordId: open, onClose: () => setOpen(null) }) });
|
|
12966
13314
|
}
|
|
12967
|
-
return /* @__PURE__ */
|
|
12968
|
-
/* @__PURE__ */
|
|
12969
|
-
/* @__PURE__ */
|
|
12970
|
-
/* @__PURE__ */
|
|
12971
|
-
form ? /* @__PURE__ */
|
|
13315
|
+
return /* @__PURE__ */ jsxs48("section", { className: cn46("flex min-h-0 flex-col gap-2", className), children: [
|
|
13316
|
+
/* @__PURE__ */ jsxs48("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
13317
|
+
/* @__PURE__ */ jsx52("span", { className: "font-medium text-foreground", children: "Shared with you" }),
|
|
13318
|
+
/* @__PURE__ */ jsx52("span", { className: "tabular-nums", children: reach.length }),
|
|
13319
|
+
form ? /* @__PURE__ */ jsx52(Button43, { size: "sm", className: "ml-auto h-7 px-2 text-[11px]", onClick: () => setSending(true), children: form.submitLabel ?? form.name }) : null
|
|
12972
13320
|
] }),
|
|
12973
13321
|
reach.length === 0 ? (
|
|
12974
13322
|
// EMPTY, AND IT SAYS WHICH EMPTY. "Nobody has shared anything with you"
|
|
12975
13323
|
// and "the store would not tell us" are different sentences, and a
|
|
12976
13324
|
// person acts on them differently.
|
|
12977
|
-
/* @__PURE__ */
|
|
13325
|
+
/* @__PURE__ */ jsxs48("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
|
|
12978
13326
|
"Nothing has been shared with you yet. ",
|
|
12979
13327
|
card.explanation,
|
|
12980
13328
|
" When somebody shares a record with you it appears here \u2014 this list is exactly what Visibility gave you, one record at a time, never an organization's list."
|
|
12981
13329
|
] })
|
|
12982
|
-
) : /* @__PURE__ */
|
|
13330
|
+
) : /* @__PURE__ */ jsx52("ul", { className: "flex min-h-0 flex-col gap-1 overflow-y-auto", children: reach.map((id) => /* @__PURE__ */ jsx52("li", { children: /* @__PURE__ */ jsx52(
|
|
12983
13331
|
"button",
|
|
12984
13332
|
{
|
|
12985
13333
|
type: "button",
|
|
12986
13334
|
className: "w-full truncate rounded border px-2 py-1.5 text-left text-xs hover:bg-muted",
|
|
12987
13335
|
onClick: () => setOpen(id),
|
|
12988
|
-
children: /* @__PURE__ */
|
|
13336
|
+
children: /* @__PURE__ */ jsx52(PortalRow, { tableId, recordId: id })
|
|
12989
13337
|
}
|
|
12990
13338
|
) }, id)) })
|
|
12991
13339
|
] });
|
|
12992
13340
|
}
|
|
12993
13341
|
function PortalRow({ tableId, recordId }) {
|
|
12994
|
-
const client =
|
|
12995
|
-
const [title, setTitle] =
|
|
12996
|
-
|
|
13342
|
+
const client = useRecordsClient39();
|
|
13343
|
+
const [title, setTitle] = useState47(null);
|
|
13344
|
+
useEffect37(() => {
|
|
12997
13345
|
let cancelled = false;
|
|
12998
13346
|
void client.recordRead({ record_id: recordId }).then((answered) => {
|
|
12999
13347
|
if (cancelled) return;
|
|
@@ -13009,22 +13357,22 @@ function PortalRow({ tableId, recordId }) {
|
|
|
13009
13357
|
cancelled = true;
|
|
13010
13358
|
};
|
|
13011
13359
|
}, [client, recordId, tableId]);
|
|
13012
|
-
return /* @__PURE__ */
|
|
13360
|
+
return /* @__PURE__ */ jsx52("span", { children: title ?? "\u2026" });
|
|
13013
13361
|
}
|
|
13014
13362
|
|
|
13015
13363
|
// src/PublicViewPage.tsx
|
|
13016
|
-
import { useCallback as
|
|
13017
|
-
import { useRecordsClient as
|
|
13018
|
-
import { Skeleton as
|
|
13019
|
-
import { jsx as
|
|
13364
|
+
import { useCallback as useCallback34, useEffect as useEffect38, useState as useState48 } from "react";
|
|
13365
|
+
import { useRecordsClient as useRecordsClient40 } from "@ai-matrx/records/react";
|
|
13366
|
+
import { Skeleton as Skeleton31, cn as cn47 } from "@ai-matrx/design-system";
|
|
13367
|
+
import { jsx as jsx53, jsxs as jsxs49 } from "react/jsx-runtime";
|
|
13020
13368
|
function PublicViewPage({ slug, className }) {
|
|
13021
|
-
const client =
|
|
13022
|
-
const [binding, setBinding] =
|
|
13023
|
-
const [rows, setRows] =
|
|
13024
|
-
const [fields, setFields] =
|
|
13025
|
-
const [error, setError] =
|
|
13026
|
-
const [gap, setGap] =
|
|
13027
|
-
const load =
|
|
13369
|
+
const client = useRecordsClient40();
|
|
13370
|
+
const [binding, setBinding] = useState48(null);
|
|
13371
|
+
const [rows, setRows] = useState48(null);
|
|
13372
|
+
const [fields, setFields] = useState48(null);
|
|
13373
|
+
const [error, setError] = useState48(null);
|
|
13374
|
+
const [gap, setGap] = useState48(null);
|
|
13375
|
+
const load = useCallback34(async () => {
|
|
13028
13376
|
const notice = await client.worldPublishGapNotice();
|
|
13029
13377
|
if (notice.ok) setGap(notice.data);
|
|
13030
13378
|
const resolved = await client.resolvePublishBinding({ slug });
|
|
@@ -13057,48 +13405,48 @@ function PublicViewPage({ slug, className }) {
|
|
|
13057
13405
|
}
|
|
13058
13406
|
setRows([{ id: found.resource_id, document: read.data.document, level: "viewer", hidden: read.data.hidden }]);
|
|
13059
13407
|
}, [client, slug]);
|
|
13060
|
-
|
|
13408
|
+
useEffect38(() => {
|
|
13061
13409
|
void load();
|
|
13062
13410
|
}, [load]);
|
|
13063
|
-
if (error) return /* @__PURE__ */
|
|
13064
|
-
if (binding === null) return /* @__PURE__ */
|
|
13411
|
+
if (error) return /* @__PURE__ */ jsx53(RefusalNotice, { error, className });
|
|
13412
|
+
if (binding === null) return /* @__PURE__ */ jsx53(Skeleton31, { className: cn47("h-64 w-full", className) });
|
|
13065
13413
|
if (binding === "none") {
|
|
13066
|
-
return /* @__PURE__ */
|
|
13067
|
-
/* @__PURE__ */
|
|
13068
|
-
/* @__PURE__ */
|
|
13069
|
-
gap ? /* @__PURE__ */
|
|
13414
|
+
return /* @__PURE__ */ jsxs49("main", { className: cn47("mx-auto max-w-2xl py-16 text-center", className), children: [
|
|
13415
|
+
/* @__PURE__ */ jsx53("h1", { className: "text-lg font-medium", children: "Nothing here" }),
|
|
13416
|
+
/* @__PURE__ */ jsx53("p", { className: "mt-1 text-sm text-muted-foreground", children: "There is no public page at this address." }),
|
|
13417
|
+
gap ? /* @__PURE__ */ jsx53("p", { className: "mt-6 text-xs text-muted-foreground", children: gap }) : null
|
|
13070
13418
|
] });
|
|
13071
13419
|
}
|
|
13072
|
-
return /* @__PURE__ */
|
|
13073
|
-
/* @__PURE__ */
|
|
13074
|
-
/* @__PURE__ */
|
|
13075
|
-
/* @__PURE__ */
|
|
13420
|
+
return /* @__PURE__ */ jsxs49("main", { className: cn47("mx-auto flex max-w-2xl flex-col gap-3 py-8", className), children: [
|
|
13421
|
+
/* @__PURE__ */ jsxs49("header", { className: "flex items-baseline gap-2 text-xs text-muted-foreground", children: [
|
|
13422
|
+
/* @__PURE__ */ jsx53("span", { className: "font-medium text-foreground", children: binding.namespace ?? "Published" }),
|
|
13423
|
+
/* @__PURE__ */ jsx53("span", { children: binding.render_mode === "list" ? `${rows?.length ?? 0} items` : "Shared publicly" })
|
|
13076
13424
|
] }),
|
|
13077
|
-
rows === null ? /* @__PURE__ */
|
|
13078
|
-
/* @__PURE__ */
|
|
13425
|
+
rows === null ? /* @__PURE__ */ jsx53(Skeleton31, { className: "h-40 w-full" }) : rows.length === 0 ? /* @__PURE__ */ jsx53("p", { className: "text-sm text-muted-foreground", children: "This page has nothing on it yet." }) : /* @__PURE__ */ jsx53("ol", { className: "flex flex-col gap-2", children: rows.map((row) => /* @__PURE__ */ jsx53("li", { className: "rounded border p-3", children: /* @__PURE__ */ jsx53(PublicRow, { row, fields }) }, row.id)) }),
|
|
13426
|
+
/* @__PURE__ */ jsx53("footer", { className: "mt-4 border-t pt-3 text-xs text-muted-foreground", children: binding.notice || gap || "Nothing was scanned before this was listed." })
|
|
13079
13427
|
] });
|
|
13080
13428
|
}
|
|
13081
13429
|
function PublicRow({ row, fields }) {
|
|
13082
13430
|
const document2 = row.document ?? {};
|
|
13083
13431
|
if (!fields) {
|
|
13084
13432
|
const first = Object.entries(document2).filter(([key]) => !key.startsWith("_"));
|
|
13085
|
-
return /* @__PURE__ */
|
|
13086
|
-
/* @__PURE__ */
|
|
13087
|
-
/* @__PURE__ */
|
|
13433
|
+
return /* @__PURE__ */ jsx53("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-sm", children: first.map(([key, value]) => /* @__PURE__ */ jsxs49("div", { className: "contents", children: [
|
|
13434
|
+
/* @__PURE__ */ jsx53("dt", { className: "text-xs text-muted-foreground", children: key }),
|
|
13435
|
+
/* @__PURE__ */ jsx53("dd", { className: "text-sm", children: String(value ?? "") })
|
|
13088
13436
|
] }, key)) });
|
|
13089
13437
|
}
|
|
13090
|
-
return /* @__PURE__ */
|
|
13091
|
-
/* @__PURE__ */
|
|
13092
|
-
/* @__PURE__ */
|
|
13438
|
+
return /* @__PURE__ */ jsx53("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: fields.map((field) => /* @__PURE__ */ jsxs49("div", { className: "contents", children: [
|
|
13439
|
+
/* @__PURE__ */ jsx53("dt", { className: "text-xs text-muted-foreground", children: field.label }),
|
|
13440
|
+
/* @__PURE__ */ jsx53("dd", { className: "text-sm", children: /* @__PURE__ */ jsx53(RecordValue, { field, value: document2[field.key], document: row.document }) })
|
|
13093
13441
|
] }, field.key)) });
|
|
13094
13442
|
}
|
|
13095
13443
|
|
|
13096
13444
|
// src/EmbedFrame.tsx
|
|
13097
|
-
import { useCallback as
|
|
13098
|
-
import { useRecordsClient as
|
|
13099
|
-
import { Button as
|
|
13445
|
+
import { useCallback as useCallback35, useEffect as useEffect39, useState as useState49 } from "react";
|
|
13446
|
+
import { useRecordsClient as useRecordsClient41 } from "@ai-matrx/records/react";
|
|
13447
|
+
import { Button as Button44, Input as Input5, Skeleton as Skeleton32, Textarea as Textarea4, cn as cn48 } from "@ai-matrx/design-system";
|
|
13100
13448
|
import { useTable as useTable21 } from "@ai-matrx/records/react";
|
|
13101
|
-
import { Fragment as
|
|
13449
|
+
import { Fragment as Fragment25, jsx as jsx54, jsxs as jsxs50 } from "react/jsx-runtime";
|
|
13102
13450
|
function EmbedFrame({
|
|
13103
13451
|
tableId,
|
|
13104
13452
|
formId,
|
|
@@ -13107,18 +13455,18 @@ function EmbedFrame({
|
|
|
13107
13455
|
embedUrl,
|
|
13108
13456
|
className
|
|
13109
13457
|
}) {
|
|
13110
|
-
const client =
|
|
13458
|
+
const client = useRecordsClient41();
|
|
13111
13459
|
const host = useRecordsUi();
|
|
13112
13460
|
const table = useTable21(tableId);
|
|
13113
13461
|
const rights = useTableRights(table.data);
|
|
13114
|
-
const [origins, setOrigins] =
|
|
13115
|
-
const [secret, setSecret] =
|
|
13116
|
-
const [tokenId, setTokenId] =
|
|
13117
|
-
const [error, setError] =
|
|
13118
|
-
const [busy, setBusy] =
|
|
13462
|
+
const [origins, setOrigins] = useState49("");
|
|
13463
|
+
const [secret, setSecret] = useState49(null);
|
|
13464
|
+
const [tokenId, setTokenId] = useState49(null);
|
|
13465
|
+
const [error, setError] = useState49(null);
|
|
13466
|
+
const [busy, setBusy] = useState49(false);
|
|
13119
13467
|
const mode = formId ? "write" : "read";
|
|
13120
13468
|
const parsed = origins.split(/[\s,]+/).map((o) => o.trim()).filter((o) => o.length > 0);
|
|
13121
|
-
const issue =
|
|
13469
|
+
const issue = useCallback35(async () => {
|
|
13122
13470
|
setBusy(true);
|
|
13123
13471
|
setError(null);
|
|
13124
13472
|
const minted = await client.anonTokenIssue({
|
|
@@ -13137,7 +13485,7 @@ function EmbedFrame({
|
|
|
13137
13485
|
setTokenId(minted.data.token_id);
|
|
13138
13486
|
host.notify?.success("Embed token issued. Copy it now \u2014 it is never shown again.");
|
|
13139
13487
|
}, [client, formId, host, mode, parsed, recordId, savedViewId]);
|
|
13140
|
-
const revoke =
|
|
13488
|
+
const revoke = useCallback35(async () => {
|
|
13141
13489
|
if (!tokenId) return;
|
|
13142
13490
|
setBusy(true);
|
|
13143
13491
|
const done = await client.anonTokenRevoke({ token_id: tokenId });
|
|
@@ -13149,24 +13497,24 @@ function EmbedFrame({
|
|
|
13149
13497
|
setSecret(null);
|
|
13150
13498
|
setTokenId(null);
|
|
13151
13499
|
}, [client, tokenId]);
|
|
13152
|
-
if (table.error) return /* @__PURE__ */
|
|
13153
|
-
if (table.loading) return /* @__PURE__ */
|
|
13500
|
+
if (table.error) return /* @__PURE__ */ jsx54(RefusalNotice, { error: table.error, className });
|
|
13501
|
+
if (table.loading) return /* @__PURE__ */ jsx54(Skeleton32, { className: cn48("h-48 w-full", className) });
|
|
13154
13502
|
if (!rights.admin) {
|
|
13155
|
-
return /* @__PURE__ */
|
|
13503
|
+
return /* @__PURE__ */ jsxs50("p", { className: cn48("rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", className), children: [
|
|
13156
13504
|
rights.why("structure"),
|
|
13157
13505
|
" Issuing an embed opens a way in from somebody else's website, so the store asks for the admin level on this table before it will mint one."
|
|
13158
13506
|
] });
|
|
13159
13507
|
}
|
|
13160
13508
|
const snippet = secret ? `<iframe src="${embedUrl}#t=${secret}" style="width:100%;height:640px;border:0" loading="lazy"></iframe>` : null;
|
|
13161
|
-
return /* @__PURE__ */
|
|
13162
|
-
/* @__PURE__ */
|
|
13163
|
-
/* @__PURE__ */
|
|
13164
|
-
/* @__PURE__ */
|
|
13509
|
+
return /* @__PURE__ */ jsxs50("section", { className: cn48("flex min-h-0 flex-col gap-2", className), children: [
|
|
13510
|
+
/* @__PURE__ */ jsxs50("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
13511
|
+
/* @__PURE__ */ jsx54("span", { className: "font-medium text-foreground", children: "Embed" }),
|
|
13512
|
+
/* @__PURE__ */ jsx54("span", { children: mode === "write" ? "a form people can send" : "a read-only view" })
|
|
13165
13513
|
] }),
|
|
13166
|
-
error ? /* @__PURE__ */
|
|
13167
|
-
/* @__PURE__ */
|
|
13168
|
-
/* @__PURE__ */
|
|
13169
|
-
/* @__PURE__ */
|
|
13514
|
+
error ? /* @__PURE__ */ jsx54(RefusalNotice, { error }) : null,
|
|
13515
|
+
/* @__PURE__ */ jsxs50("label", { className: "flex flex-col gap-1 text-xs", children: [
|
|
13516
|
+
/* @__PURE__ */ jsx54("span", { className: "text-muted-foreground", children: "Sites this embed works on" }),
|
|
13517
|
+
/* @__PURE__ */ jsx54(
|
|
13170
13518
|
Input5,
|
|
13171
13519
|
{
|
|
13172
13520
|
"aria-label": "Allowed origins",
|
|
@@ -13176,18 +13524,18 @@ function EmbedFrame({
|
|
|
13176
13524
|
onChange: (e) => setOrigins(e.target.value)
|
|
13177
13525
|
}
|
|
13178
13526
|
),
|
|
13179
|
-
/* @__PURE__ */
|
|
13527
|
+
/* @__PURE__ */ jsxs50("span", { className: "text-[11px] text-muted-foreground", children: [
|
|
13180
13528
|
`Name every site, scheme and host and port. An empty list is refused rather than quietly meaning "anywhere": a token that works from any page on the internet includes an attacker's. The match is exact, so `,
|
|
13181
|
-
/* @__PURE__ */
|
|
13529
|
+
/* @__PURE__ */ jsx54("code", { children: "example.com.evil.test" }),
|
|
13182
13530
|
" will not pass."
|
|
13183
13531
|
] })
|
|
13184
13532
|
] }),
|
|
13185
|
-
!secret ? /* @__PURE__ */
|
|
13186
|
-
/* @__PURE__ */
|
|
13187
|
-
/* @__PURE__ */
|
|
13188
|
-
/* @__PURE__ */
|
|
13189
|
-
/* @__PURE__ */
|
|
13190
|
-
|
|
13533
|
+
!secret ? /* @__PURE__ */ jsx54(Button44, { size: "sm", className: "self-start", disabled: busy || parsed.length === 0, onClick: () => void issue(), children: busy ? "Issuing\u2026" : "Issue embed" }) : /* @__PURE__ */ jsxs50(Fragment25, { children: [
|
|
13534
|
+
/* @__PURE__ */ jsx54("p", { className: "rounded border px-2 py-1 text-xs text-muted-foreground", children: "Copy this now. Only its digest is stored, so nobody \u2014 including us \u2014 can show it to you again; if it is lost, issue a new one and replace the old." }),
|
|
13535
|
+
/* @__PURE__ */ jsx54(Textarea4, { "aria-label": "Embed snippet", readOnly: true, rows: 3, className: "font-mono text-[11px]", value: snippet ?? "" }),
|
|
13536
|
+
/* @__PURE__ */ jsxs50("div", { className: "flex gap-2", children: [
|
|
13537
|
+
/* @__PURE__ */ jsx54(
|
|
13538
|
+
Button44,
|
|
13191
13539
|
{
|
|
13192
13540
|
size: "sm",
|
|
13193
13541
|
variant: "outline",
|
|
@@ -13195,19 +13543,19 @@ function EmbedFrame({
|
|
|
13195
13543
|
children: "Copy"
|
|
13196
13544
|
}
|
|
13197
13545
|
),
|
|
13198
|
-
/* @__PURE__ */
|
|
13546
|
+
/* @__PURE__ */ jsx54(Button44, { size: "sm", variant: "ghost", disabled: busy, onClick: () => void revoke(), children: "Revoke" })
|
|
13199
13547
|
] })
|
|
13200
13548
|
] })
|
|
13201
13549
|
] });
|
|
13202
13550
|
}
|
|
13203
13551
|
function useEmbedHandshake(args) {
|
|
13204
|
-
const client =
|
|
13205
|
-
const [binding, setBinding] =
|
|
13206
|
-
const [error, setError] =
|
|
13207
|
-
const [loading, setLoading] =
|
|
13552
|
+
const client = useRecordsClient41();
|
|
13553
|
+
const [binding, setBinding] = useState49(null);
|
|
13554
|
+
const [error, setError] = useState49(null);
|
|
13555
|
+
const [loading, setLoading] = useState49(true);
|
|
13208
13556
|
const origin = args.origin ?? (typeof location === "undefined" ? "" : location.origin);
|
|
13209
13557
|
const { secret, requiredMode } = args;
|
|
13210
|
-
|
|
13558
|
+
useEffect39(() => {
|
|
13211
13559
|
let cancelled = false;
|
|
13212
13560
|
setLoading(true);
|
|
13213
13561
|
setError(null);
|
|
@@ -13230,7 +13578,7 @@ function useEmbedHandshake(args) {
|
|
|
13230
13578
|
|
|
13231
13579
|
// src/RecordsMount.tsx
|
|
13232
13580
|
import { RecordsProvider } from "@ai-matrx/records/react";
|
|
13233
|
-
import { jsx as
|
|
13581
|
+
import { jsx as jsx55 } from "react/jsx-runtime";
|
|
13234
13582
|
var STORE_DECIDES_REASON = "This host offers exactly what the record store says this person may do: the level comes back with the table on the read door, so a control they cannot use is never drawn in the first place.";
|
|
13235
13583
|
function storeDecidesRights(_table) {
|
|
13236
13584
|
return NO_RIGHTS;
|
|
@@ -13243,7 +13591,7 @@ function RecordsMount({
|
|
|
13243
13591
|
}) {
|
|
13244
13592
|
const bound = { ...host ?? {} };
|
|
13245
13593
|
void letTheStoreDecideRights;
|
|
13246
|
-
return /* @__PURE__ */
|
|
13594
|
+
return /* @__PURE__ */ jsx55(RecordsProvider, { config, children: /* @__PURE__ */ jsx55(RecordsUiProvider, { value: bound, children: /* @__PURE__ */ jsx55(RecordLabelProvider, { children }) }) });
|
|
13247
13595
|
}
|
|
13248
13596
|
function personActor(userId) {
|
|
13249
13597
|
return userId ? { actor: "user", user_id: userId } : { actor: "user" };
|
|
@@ -13262,9 +13610,9 @@ function recordsDataSource(client, fallbackSchema = "custom") {
|
|
|
13262
13610
|
}
|
|
13263
13611
|
|
|
13264
13612
|
// src/TablesHome.tsx
|
|
13265
|
-
import { useCallback as
|
|
13266
|
-
import { useRecordsClient as
|
|
13267
|
-
import { BasicInput as
|
|
13613
|
+
import { useCallback as useCallback36, useEffect as useEffect40, useState as useState50 } from "react";
|
|
13614
|
+
import { useRecordsClient as useRecordsClient42, useTables as useTables3 } from "@ai-matrx/records/react";
|
|
13615
|
+
import { BasicInput as BasicInput16, Button as Button45, Skeleton as Skeleton33, cn as cn49 } from "@ai-matrx/design-system";
|
|
13268
13616
|
|
|
13269
13617
|
// src/createTable.ts
|
|
13270
13618
|
function tokenFor(name) {
|
|
@@ -13374,7 +13722,7 @@ function declaredType(field) {
|
|
|
13374
13722
|
}
|
|
13375
13723
|
|
|
13376
13724
|
// src/TablesHome.tsx
|
|
13377
|
-
import { Fragment as
|
|
13725
|
+
import { Fragment as Fragment26, jsx as jsx56, jsxs as jsxs51 } from "react/jsx-runtime";
|
|
13378
13726
|
var TABLE_LANES = ["mine", "organization", "system", "community", "app"];
|
|
13379
13727
|
var LANE_TITLE = {
|
|
13380
13728
|
mine: "Mine",
|
|
@@ -13399,15 +13747,15 @@ function laneFor(table) {
|
|
|
13399
13747
|
return "organization";
|
|
13400
13748
|
}
|
|
13401
13749
|
function TablesHome({ onOpenTable, className }) {
|
|
13402
|
-
const client =
|
|
13750
|
+
const client = useRecordsClient42();
|
|
13403
13751
|
const tables = useTables3();
|
|
13404
|
-
const [creating, setCreating] =
|
|
13405
|
-
const [name, setName] =
|
|
13406
|
-
const [busy, setBusy] =
|
|
13407
|
-
const [error, setError] =
|
|
13408
|
-
const [importInto, setImportInto] =
|
|
13409
|
-
const [boards, setBoards] =
|
|
13410
|
-
|
|
13752
|
+
const [creating, setCreating] = useState50(false);
|
|
13753
|
+
const [name, setName] = useState50("");
|
|
13754
|
+
const [busy, setBusy] = useState50(false);
|
|
13755
|
+
const [error, setError] = useState50(null);
|
|
13756
|
+
const [importInto, setImportInto] = useState50(null);
|
|
13757
|
+
const [boards, setBoards] = useState50(null);
|
|
13758
|
+
useEffect40(() => {
|
|
13411
13759
|
let cancelled = false;
|
|
13412
13760
|
void client.dashboards({}).then((result) => {
|
|
13413
13761
|
if (cancelled) return;
|
|
@@ -13417,7 +13765,7 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
13417
13765
|
cancelled = true;
|
|
13418
13766
|
};
|
|
13419
13767
|
}, [client]);
|
|
13420
|
-
const create =
|
|
13768
|
+
const create = useCallback36(
|
|
13421
13769
|
async (mode) => {
|
|
13422
13770
|
const trimmed = name.trim();
|
|
13423
13771
|
if (!trimmed) return;
|
|
@@ -13437,10 +13785,10 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
13437
13785
|
},
|
|
13438
13786
|
[client, name, onOpenTable, tables]
|
|
13439
13787
|
);
|
|
13440
|
-
return /* @__PURE__ */
|
|
13441
|
-
/* @__PURE__ */
|
|
13442
|
-
/* @__PURE__ */
|
|
13443
|
-
|
|
13788
|
+
return /* @__PURE__ */ jsxs51("div", { className: cn49("flex min-h-0 flex-col gap-4", className), children: [
|
|
13789
|
+
/* @__PURE__ */ jsx56("div", { className: "flex items-center gap-2", children: creating ? /* @__PURE__ */ jsxs51(Fragment26, { children: [
|
|
13790
|
+
/* @__PURE__ */ jsx56(
|
|
13791
|
+
BasicInput16,
|
|
13444
13792
|
{
|
|
13445
13793
|
autoFocus: true,
|
|
13446
13794
|
value: name,
|
|
@@ -13453,9 +13801,9 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
13453
13801
|
className: "h-8 max-w-xs"
|
|
13454
13802
|
}
|
|
13455
13803
|
),
|
|
13456
|
-
/* @__PURE__ */
|
|
13457
|
-
/* @__PURE__ */
|
|
13458
|
-
|
|
13804
|
+
/* @__PURE__ */ jsx56(Button45, { size: "sm", disabled: busy || name.trim().length === 0, onClick: () => void create("open"), children: busy ? "Declaring\u2026" : "Create" }),
|
|
13805
|
+
/* @__PURE__ */ jsx56(
|
|
13806
|
+
Button45,
|
|
13459
13807
|
{
|
|
13460
13808
|
size: "sm",
|
|
13461
13809
|
variant: "outline",
|
|
@@ -13464,37 +13812,37 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
13464
13812
|
children: "Create and import a file"
|
|
13465
13813
|
}
|
|
13466
13814
|
),
|
|
13467
|
-
/* @__PURE__ */
|
|
13468
|
-
] }) : /* @__PURE__ */
|
|
13469
|
-
error ? /* @__PURE__ */
|
|
13470
|
-
importInto ? /* @__PURE__ */
|
|
13471
|
-
/* @__PURE__ */
|
|
13472
|
-
/* @__PURE__ */
|
|
13815
|
+
/* @__PURE__ */ jsx56(Button45, { size: "sm", variant: "ghost", onClick: () => setCreating(false), children: "Cancel" })
|
|
13816
|
+
] }) : /* @__PURE__ */ jsx56(Button45, { size: "sm", onClick: () => setCreating(true), children: "New table" }) }),
|
|
13817
|
+
error ? /* @__PURE__ */ jsx56(RefusalNotice, { error }) : null,
|
|
13818
|
+
importInto ? /* @__PURE__ */ jsxs51("div", { className: "rounded-md border p-3", children: [
|
|
13819
|
+
/* @__PURE__ */ jsx56(ImportWizard, { tableId: importInto, onDone: () => setImportInto(null) }),
|
|
13820
|
+
/* @__PURE__ */ jsx56(Button45, { size: "sm", variant: "ghost", className: "mt-2", onClick: () => onOpenTable?.(importInto), children: "Open the table" })
|
|
13473
13821
|
] }) : null,
|
|
13474
|
-
tables.error ? /* @__PURE__ */
|
|
13475
|
-
tables.loading && !tables.data ? /* @__PURE__ */
|
|
13476
|
-
/* @__PURE__ */
|
|
13477
|
-
/* @__PURE__ */
|
|
13822
|
+
tables.error ? /* @__PURE__ */ jsx56(RefusalNotice, { error: tables.error }) : null,
|
|
13823
|
+
tables.loading && !tables.data ? /* @__PURE__ */ jsxs51("div", { className: "space-y-2", children: [
|
|
13824
|
+
/* @__PURE__ */ jsx56(Skeleton33, { className: "h-6 w-40" }),
|
|
13825
|
+
/* @__PURE__ */ jsx56(Skeleton33, { className: "h-16 w-full" })
|
|
13478
13826
|
] }) : null,
|
|
13479
|
-
/* @__PURE__ */
|
|
13480
|
-
boards && boards.length > 0 ? /* @__PURE__ */
|
|
13481
|
-
/* @__PURE__ */
|
|
13482
|
-
/* @__PURE__ */
|
|
13483
|
-
/* @__PURE__ */
|
|
13827
|
+
/* @__PURE__ */ jsx56(BookingSlots, {}),
|
|
13828
|
+
boards && boards.length > 0 ? /* @__PURE__ */ jsxs51("section", { className: "space-y-1.5", children: [
|
|
13829
|
+
/* @__PURE__ */ jsxs51("div", { className: "flex items-baseline gap-2", children: [
|
|
13830
|
+
/* @__PURE__ */ jsx56("h2", { className: "text-sm font-medium", children: "Dashboards" }),
|
|
13831
|
+
/* @__PURE__ */ jsx56("span", { className: "text-xs opacity-60", children: boards.length })
|
|
13484
13832
|
] }),
|
|
13485
|
-
/* @__PURE__ */
|
|
13833
|
+
/* @__PURE__ */ jsx56("ul", { className: "grid gap-1.5 sm:grid-cols-2 lg:grid-cols-3", children: boards.map((board) => /* @__PURE__ */ jsx56("li", { children: /* @__PURE__ */ jsxs51(
|
|
13486
13834
|
"button",
|
|
13487
13835
|
{
|
|
13488
13836
|
type: "button",
|
|
13489
13837
|
disabled: !board.table_id,
|
|
13490
13838
|
onClick: () => board.table_id ? onOpenTable?.(board.table_id, board.dashboard_id) : void 0,
|
|
13491
|
-
className:
|
|
13839
|
+
className: cn49(
|
|
13492
13840
|
"w-full rounded-md border px-3 py-2 text-left transition-colors",
|
|
13493
13841
|
onOpenTable && board.table_id ? "hover:bg-accent" : "cursor-default"
|
|
13494
13842
|
),
|
|
13495
13843
|
children: [
|
|
13496
|
-
/* @__PURE__ */
|
|
13497
|
-
/* @__PURE__ */
|
|
13844
|
+
/* @__PURE__ */ jsx56("span", { className: "block truncate text-sm", children: board.name }),
|
|
13845
|
+
/* @__PURE__ */ jsxs51("span", { className: "block truncate text-xs opacity-60", children: [
|
|
13498
13846
|
board.block_count,
|
|
13499
13847
|
" block",
|
|
13500
13848
|
board.block_count === 1 ? "" : "s",
|
|
@@ -13510,23 +13858,23 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
13510
13858
|
] }) : null,
|
|
13511
13859
|
tables.data ? TABLE_LANES.map((lane) => {
|
|
13512
13860
|
const rows = tables.data.filter((t) => laneFor(t) === lane);
|
|
13513
|
-
return /* @__PURE__ */
|
|
13514
|
-
/* @__PURE__ */
|
|
13515
|
-
/* @__PURE__ */
|
|
13516
|
-
/* @__PURE__ */
|
|
13861
|
+
return /* @__PURE__ */ jsxs51("section", { className: "space-y-1.5", children: [
|
|
13862
|
+
/* @__PURE__ */ jsxs51("div", { className: "flex items-baseline gap-2", children: [
|
|
13863
|
+
/* @__PURE__ */ jsx56("h2", { className: "text-sm font-medium", children: LANE_TITLE[lane] }),
|
|
13864
|
+
/* @__PURE__ */ jsx56("span", { className: "text-xs opacity-60", children: rows.length })
|
|
13517
13865
|
] }),
|
|
13518
|
-
rows.length === 0 ? /* @__PURE__ */
|
|
13866
|
+
rows.length === 0 ? /* @__PURE__ */ jsx56("p", { className: "text-xs opacity-70", children: LANE_EMPTY[lane] }) : /* @__PURE__ */ jsx56("ul", { className: "grid gap-1.5 sm:grid-cols-2 lg:grid-cols-3", children: rows.map((table) => /* @__PURE__ */ jsx56("li", { children: /* @__PURE__ */ jsxs51(
|
|
13519
13867
|
"button",
|
|
13520
13868
|
{
|
|
13521
13869
|
type: "button",
|
|
13522
13870
|
onClick: () => onOpenTable?.(table.id),
|
|
13523
|
-
className:
|
|
13871
|
+
className: cn49(
|
|
13524
13872
|
"w-full rounded-md border px-3 py-2 text-left transition-colors",
|
|
13525
13873
|
onOpenTable ? "hover:bg-accent" : "cursor-default"
|
|
13526
13874
|
),
|
|
13527
13875
|
children: [
|
|
13528
|
-
/* @__PURE__ */
|
|
13529
|
-
/* @__PURE__ */
|
|
13876
|
+
/* @__PURE__ */ jsx56("span", { className: "block truncate text-sm", children: tableName(table) }),
|
|
13877
|
+
/* @__PURE__ */ jsxs51("span", { className: "block truncate text-xs opacity-60", children: [
|
|
13530
13878
|
table.fields.length,
|
|
13531
13879
|
" field",
|
|
13532
13880
|
table.fields.length === 1 ? "" : "s",
|
|
@@ -13541,10 +13889,10 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
13541
13889
|
}
|
|
13542
13890
|
|
|
13543
13891
|
// src/TablePage.tsx
|
|
13544
|
-
import { useCallback as
|
|
13545
|
-
import { useRecordsClient as
|
|
13546
|
-
import { Button as
|
|
13547
|
-
import { Fragment as
|
|
13892
|
+
import { useCallback as useCallback37, useEffect as useEffect41, useState as useState51 } from "react";
|
|
13893
|
+
import { useRecordsClient as useRecordsClient43, useTable as useTable22 } from "@ai-matrx/records/react";
|
|
13894
|
+
import { Button as Button46, Separator as Separator12, Skeleton as Skeleton34, cn as cn50 } from "@ai-matrx/design-system";
|
|
13895
|
+
import { Fragment as Fragment27, jsx as jsx57, jsxs as jsxs52 } from "react/jsx-runtime";
|
|
13548
13896
|
var TABLE_NOT_REACHABLE = "This table is not in the organization you are working in, so there is nothing here to show. Switch to the organization that owns it and open it again \u2014 or it may have been deleted.";
|
|
13549
13897
|
var DEFAULT_VIEW_NAME = "All records";
|
|
13550
13898
|
var VIEW_NOT_SAVED_YET = "This table has no saved view yet, so the layout you pick here is not being kept. Save one from the bar above and it is remembered.";
|
|
@@ -13575,29 +13923,29 @@ function TablePage({
|
|
|
13575
13923
|
activeRecordId,
|
|
13576
13924
|
className
|
|
13577
13925
|
}) {
|
|
13578
|
-
const client =
|
|
13926
|
+
const client = useRecordsClient43();
|
|
13579
13927
|
const table = useTable22(tableId);
|
|
13580
13928
|
const rights = useTableRights(table.data);
|
|
13581
|
-
const organizationId =
|
|
13582
|
-
const [view, setView] =
|
|
13929
|
+
const organizationId = useRecordsClient43().config.organizationId;
|
|
13930
|
+
const [view, setView] = useState51(null);
|
|
13583
13931
|
const opening = openingRail(activeRecordId);
|
|
13584
|
-
const [asking, setAsking] =
|
|
13585
|
-
const [surface, setSurface] =
|
|
13932
|
+
const [asking, setAsking] = useState51(null);
|
|
13933
|
+
const [surface, setSurface] = useState51({
|
|
13586
13934
|
main: activeDashboardId ? "dashboards" : "records",
|
|
13587
13935
|
rail: opening.rail
|
|
13588
13936
|
});
|
|
13589
13937
|
const { main, rail } = surface;
|
|
13590
13938
|
const setRail = (next) => setSurface((now) => ({ ...now, rail: next }));
|
|
13591
|
-
const [openRecord, setOpenRecord] =
|
|
13939
|
+
const [openRecord, setOpenRecord] = useState51(opening.record);
|
|
13592
13940
|
const viewVersion = useRecordVersion(view?.id ?? null);
|
|
13593
13941
|
const press = (pressed) => setSurface((now) => chooseSurface(now, pressed));
|
|
13594
13942
|
const show = (next) => press({ rail: next });
|
|
13595
|
-
|
|
13943
|
+
useEffect41(() => {
|
|
13596
13944
|
if (!activeRecordId) return;
|
|
13597
13945
|
setOpenRecord(activeRecordId);
|
|
13598
13946
|
setSurface((now) => ({ ...now, rail: "record" }));
|
|
13599
13947
|
}, [activeRecordId]);
|
|
13600
|
-
const patchView =
|
|
13948
|
+
const patchView = useCallback37(
|
|
13601
13949
|
async (patch) => {
|
|
13602
13950
|
const current = view;
|
|
13603
13951
|
if (!current) return;
|
|
@@ -13613,19 +13961,19 @@ function TablePage({
|
|
|
13613
13961
|
},
|
|
13614
13962
|
[client, view, viewVersion]
|
|
13615
13963
|
);
|
|
13616
|
-
if (table.error) return /* @__PURE__ */
|
|
13617
|
-
if (table.loading) return /* @__PURE__ */
|
|
13964
|
+
if (table.error) return /* @__PURE__ */ jsx57(RefusalNotice, { error: table.error });
|
|
13965
|
+
if (table.loading) return /* @__PURE__ */ jsx57(Skeleton34, { className: "h-40 w-full" });
|
|
13618
13966
|
if (!table.data) {
|
|
13619
|
-
return /* @__PURE__ */
|
|
13620
|
-
/* @__PURE__ */
|
|
13621
|
-
/* @__PURE__ */
|
|
13622
|
-
onLeave ? /* @__PURE__ */
|
|
13967
|
+
return /* @__PURE__ */ jsxs52("div", { className: cn50("flex flex-col items-start gap-2 rounded-md border border-dashed p-6", className), children: [
|
|
13968
|
+
/* @__PURE__ */ jsx57("p", { className: "text-sm font-medium", children: "This table is not here" }),
|
|
13969
|
+
/* @__PURE__ */ jsx57("p", { className: "max-w-prose text-xs text-muted-foreground", children: TABLE_NOT_REACHABLE }),
|
|
13970
|
+
onLeave ? /* @__PURE__ */ jsx57(Button46, { size: "sm", variant: "outline", onClick: onLeave, children: leaveLabel }) : null
|
|
13623
13971
|
] });
|
|
13624
13972
|
}
|
|
13625
|
-
return /* @__PURE__ */
|
|
13626
|
-
/* @__PURE__ */
|
|
13627
|
-
/* @__PURE__ */
|
|
13628
|
-
/* @__PURE__ */
|
|
13973
|
+
return /* @__PURE__ */ jsxs52("div", { className: cn50("flex min-h-0 gap-4", className), children: [
|
|
13974
|
+
/* @__PURE__ */ jsxs52("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
|
|
13975
|
+
/* @__PURE__ */ jsxs52("div", { className: "flex flex-wrap items-center gap-1.5", children: [
|
|
13976
|
+
/* @__PURE__ */ jsx57(
|
|
13629
13977
|
ViewBar,
|
|
13630
13978
|
{
|
|
13631
13979
|
tableId,
|
|
@@ -13634,10 +13982,10 @@ function TablePage({
|
|
|
13634
13982
|
className: "min-w-0 flex-1"
|
|
13635
13983
|
}
|
|
13636
13984
|
),
|
|
13637
|
-
rights.write && view && view.layout !== "grid" ? /* @__PURE__ */
|
|
13638
|
-
rights.structure ? /* @__PURE__ */
|
|
13639
|
-
/* @__PURE__ */
|
|
13640
|
-
|
|
13985
|
+
rights.write && view && view.layout !== "grid" ? /* @__PURE__ */ jsx57(Button46, { size: "sm", onClick: () => show("new-record"), children: "New record" }) : null,
|
|
13986
|
+
rights.structure ? /* @__PURE__ */ jsxs52(Fragment27, { children: [
|
|
13987
|
+
/* @__PURE__ */ jsx57(
|
|
13988
|
+
Button46,
|
|
13641
13989
|
{
|
|
13642
13990
|
size: "sm",
|
|
13643
13991
|
variant: surfaceChosen(surface, { rail: "field" }) ? "secondary" : "outline",
|
|
@@ -13646,8 +13994,8 @@ function TablePage({
|
|
|
13646
13994
|
children: "Add field"
|
|
13647
13995
|
}
|
|
13648
13996
|
),
|
|
13649
|
-
/* @__PURE__ */
|
|
13650
|
-
|
|
13997
|
+
/* @__PURE__ */ jsx57(
|
|
13998
|
+
Button46,
|
|
13651
13999
|
{
|
|
13652
14000
|
size: "sm",
|
|
13653
14001
|
variant: surfaceChosen(surface, { rail: "settings" }) ? "secondary" : "ghost",
|
|
@@ -13656,8 +14004,8 @@ function TablePage({
|
|
|
13656
14004
|
children: "Settings"
|
|
13657
14005
|
}
|
|
13658
14006
|
),
|
|
13659
|
-
/* @__PURE__ */
|
|
13660
|
-
|
|
14007
|
+
/* @__PURE__ */ jsx57(
|
|
14008
|
+
Button46,
|
|
13661
14009
|
{
|
|
13662
14010
|
size: "sm",
|
|
13663
14011
|
variant: surfaceChosen(surface, { rail: "import" }) ? "secondary" : "ghost",
|
|
@@ -13667,8 +14015,8 @@ function TablePage({
|
|
|
13667
14015
|
}
|
|
13668
14016
|
)
|
|
13669
14017
|
] }) : null,
|
|
13670
|
-
/* @__PURE__ */
|
|
13671
|
-
|
|
14018
|
+
/* @__PURE__ */ jsx57(
|
|
14019
|
+
Button46,
|
|
13672
14020
|
{
|
|
13673
14021
|
size: "sm",
|
|
13674
14022
|
variant: surfaceChosen(surface, { main: "dashboards" }) ? "secondary" : "ghost",
|
|
@@ -13677,8 +14025,8 @@ function TablePage({
|
|
|
13677
14025
|
children: "Dashboards"
|
|
13678
14026
|
}
|
|
13679
14027
|
),
|
|
13680
|
-
/* @__PURE__ */
|
|
13681
|
-
|
|
14028
|
+
/* @__PURE__ */ jsx57(
|
|
14029
|
+
Button46,
|
|
13682
14030
|
{
|
|
13683
14031
|
size: "sm",
|
|
13684
14032
|
variant: surfaceChosen(surface, { rail: "forms" }) ? "secondary" : "ghost",
|
|
@@ -13687,8 +14035,8 @@ function TablePage({
|
|
|
13687
14035
|
children: "Forms"
|
|
13688
14036
|
}
|
|
13689
14037
|
),
|
|
13690
|
-
/* @__PURE__ */
|
|
13691
|
-
|
|
14038
|
+
/* @__PURE__ */ jsx57(
|
|
14039
|
+
Button46,
|
|
13692
14040
|
{
|
|
13693
14041
|
size: "sm",
|
|
13694
14042
|
variant: surfaceChosen(surface, { rail: "bookings" }) ? "secondary" : "ghost",
|
|
@@ -13697,8 +14045,8 @@ function TablePage({
|
|
|
13697
14045
|
children: "Bookings"
|
|
13698
14046
|
}
|
|
13699
14047
|
),
|
|
13700
|
-
/* @__PURE__ */
|
|
13701
|
-
|
|
14048
|
+
/* @__PURE__ */ jsx57(
|
|
14049
|
+
Button46,
|
|
13702
14050
|
{
|
|
13703
14051
|
size: "sm",
|
|
13704
14052
|
variant: surfaceChosen(surface, { rail: "checklists" }) ? "secondary" : "ghost",
|
|
@@ -13707,8 +14055,8 @@ function TablePage({
|
|
|
13707
14055
|
children: "Checklists"
|
|
13708
14056
|
}
|
|
13709
14057
|
),
|
|
13710
|
-
/* @__PURE__ */
|
|
13711
|
-
|
|
14058
|
+
/* @__PURE__ */ jsx57(
|
|
14059
|
+
Button46,
|
|
13712
14060
|
{
|
|
13713
14061
|
size: "sm",
|
|
13714
14062
|
variant: surfaceChosen(surface, { rail: "notifications" }) ? "secondary" : "ghost",
|
|
@@ -13717,8 +14065,8 @@ function TablePage({
|
|
|
13717
14065
|
children: "Notifications"
|
|
13718
14066
|
}
|
|
13719
14067
|
),
|
|
13720
|
-
/* @__PURE__ */
|
|
13721
|
-
|
|
14068
|
+
/* @__PURE__ */ jsx57(
|
|
14069
|
+
Button46,
|
|
13722
14070
|
{
|
|
13723
14071
|
size: "sm",
|
|
13724
14072
|
variant: surfaceChosen(surface, { rail: "portals" }) ? "secondary" : "ghost",
|
|
@@ -13727,8 +14075,8 @@ function TablePage({
|
|
|
13727
14075
|
children: "Portals"
|
|
13728
14076
|
}
|
|
13729
14077
|
),
|
|
13730
|
-
/* @__PURE__ */
|
|
13731
|
-
|
|
14078
|
+
/* @__PURE__ */ jsx57(
|
|
14079
|
+
Button46,
|
|
13732
14080
|
{
|
|
13733
14081
|
size: "sm",
|
|
13734
14082
|
variant: surfaceChosen(surface, { rail: "inbox" }) ? "secondary" : "ghost",
|
|
@@ -13737,7 +14085,7 @@ function TablePage({
|
|
|
13737
14085
|
children: "Inbox"
|
|
13738
14086
|
}
|
|
13739
14087
|
),
|
|
13740
|
-
/* @__PURE__ */
|
|
14088
|
+
/* @__PURE__ */ jsx57(
|
|
13741
14089
|
ShareControl,
|
|
13742
14090
|
{
|
|
13743
14091
|
kind: "table",
|
|
@@ -13747,10 +14095,10 @@ function TablePage({
|
|
|
13747
14095
|
may: rights.share
|
|
13748
14096
|
}
|
|
13749
14097
|
),
|
|
13750
|
-
/* @__PURE__ */
|
|
14098
|
+
/* @__PURE__ */ jsx57(ExportMenu, { tableId })
|
|
13751
14099
|
] }),
|
|
13752
|
-
main === "dashboards" ? /* @__PURE__ */
|
|
13753
|
-
/* @__PURE__ */
|
|
14100
|
+
main === "dashboards" ? /* @__PURE__ */ jsx57(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : /* @__PURE__ */ jsxs52(Fragment27, { children: [
|
|
14101
|
+
/* @__PURE__ */ jsx57(
|
|
13754
14102
|
ViewSwitcher,
|
|
13755
14103
|
{
|
|
13756
14104
|
view: view ?? defaultView(tableId),
|
|
@@ -13767,18 +14115,18 @@ function TablePage({
|
|
|
13767
14115
|
}
|
|
13768
14116
|
}
|
|
13769
14117
|
),
|
|
13770
|
-
view ? null : /* @__PURE__ */
|
|
14118
|
+
view ? null : /* @__PURE__ */ jsx57("p", { className: "text-xs text-muted-foreground", children: VIEW_NOT_SAVED_YET })
|
|
13771
14119
|
] })
|
|
13772
14120
|
] }),
|
|
13773
|
-
rail === "none" ? null : /* @__PURE__ */
|
|
13774
|
-
rail === "settings" ? /* @__PURE__ */
|
|
14121
|
+
rail === "none" ? null : /* @__PURE__ */ jsxs52("aside", { className: "w-[26rem] shrink-0 overflow-y-auto rounded-md border p-3", children: [
|
|
14122
|
+
rail === "settings" ? /* @__PURE__ */ jsx57(
|
|
13775
14123
|
TableSettings,
|
|
13776
14124
|
{
|
|
13777
14125
|
tableId,
|
|
13778
14126
|
...onLeave ? { onDeleted: onLeave } : {}
|
|
13779
14127
|
}
|
|
13780
14128
|
) : null,
|
|
13781
|
-
rail === "inbox" ? /* @__PURE__ */
|
|
14129
|
+
rail === "inbox" ? /* @__PURE__ */ jsx57(
|
|
13782
14130
|
ActionInbox,
|
|
13783
14131
|
{
|
|
13784
14132
|
tableId,
|
|
@@ -13788,9 +14136,9 @@ function TablePage({
|
|
|
13788
14136
|
}
|
|
13789
14137
|
}
|
|
13790
14138
|
) : null,
|
|
13791
|
-
rail === "forms" ? /* @__PURE__ */
|
|
13792
|
-
rail === "bookings" ? /* @__PURE__ */
|
|
13793
|
-
rail === "checklists" ? /* @__PURE__ */
|
|
14139
|
+
rail === "forms" ? /* @__PURE__ */ jsx57(FormsPanel, { tableId }) : null,
|
|
14140
|
+
rail === "bookings" ? /* @__PURE__ */ jsx57(BookingSlots, { tableId }) : null,
|
|
14141
|
+
rail === "checklists" ? /* @__PURE__ */ jsx57(
|
|
13794
14142
|
ChecklistsPanel,
|
|
13795
14143
|
{
|
|
13796
14144
|
tableId,
|
|
@@ -13800,11 +14148,11 @@ function TablePage({
|
|
|
13800
14148
|
}
|
|
13801
14149
|
}
|
|
13802
14150
|
) : null,
|
|
13803
|
-
rail === "notifications" ? /* @__PURE__ */
|
|
13804
|
-
rail === "portals" ? /* @__PURE__ */
|
|
13805
|
-
rail === "import" ? /* @__PURE__ */
|
|
13806
|
-
rail === "field" ? /* @__PURE__ */
|
|
13807
|
-
rail === "new-record" ? /* @__PURE__ */
|
|
14151
|
+
rail === "notifications" ? /* @__PURE__ */ jsx57(SubscriptionsPanel, { tableId }) : null,
|
|
14152
|
+
rail === "portals" ? /* @__PURE__ */ jsx57(PortalsPanel, { tableId }) : null,
|
|
14153
|
+
rail === "import" ? /* @__PURE__ */ jsx57(ImportWizard, { tableId, onDone: () => setRail("none") }) : null,
|
|
14154
|
+
rail === "field" ? /* @__PURE__ */ jsx57(FieldEditor, { tableId, onSaved: () => setRail("none"), onCancel: () => setRail("none") }) : null,
|
|
14155
|
+
rail === "new-record" ? /* @__PURE__ */ jsx57(
|
|
13808
14156
|
RecordForm,
|
|
13809
14157
|
{
|
|
13810
14158
|
tableId,
|
|
@@ -13815,7 +14163,7 @@ function TablePage({
|
|
|
13815
14163
|
onCancel: () => setRail("none")
|
|
13816
14164
|
}
|
|
13817
14165
|
) : null,
|
|
13818
|
-
rail === "who-changed" && asking ? /* @__PURE__ */
|
|
14166
|
+
rail === "who-changed" && asking ? /* @__PURE__ */ jsx57(
|
|
13819
14167
|
FieldHistoryPanel,
|
|
13820
14168
|
{
|
|
13821
14169
|
tableId,
|
|
@@ -13828,10 +14176,10 @@ function TablePage({
|
|
|
13828
14176
|
}
|
|
13829
14177
|
}
|
|
13830
14178
|
) : null,
|
|
13831
|
-
rail === "record" && openRecord ? /* @__PURE__ */
|
|
13832
|
-
/* @__PURE__ */
|
|
13833
|
-
/* @__PURE__ */
|
|
13834
|
-
/* @__PURE__ */
|
|
14179
|
+
rail === "record" && openRecord ? /* @__PURE__ */ jsxs52("div", { className: "space-y-3", children: [
|
|
14180
|
+
/* @__PURE__ */ jsx57(Peek, { tableId, recordId: openRecord, onClose: () => setRail("none") }),
|
|
14181
|
+
/* @__PURE__ */ jsx57(Separator12, {}),
|
|
14182
|
+
/* @__PURE__ */ jsx57(
|
|
13835
14183
|
HistoryPanel,
|
|
13836
14184
|
{
|
|
13837
14185
|
tableId,
|
|
@@ -13842,10 +14190,10 @@ function TablePage({
|
|
|
13842
14190
|
}
|
|
13843
14191
|
}
|
|
13844
14192
|
),
|
|
13845
|
-
/* @__PURE__ */
|
|
13846
|
-
/* @__PURE__ */
|
|
13847
|
-
/* @__PURE__ */
|
|
13848
|
-
/* @__PURE__ */
|
|
14193
|
+
/* @__PURE__ */ jsx57(Separator12, {}),
|
|
14194
|
+
/* @__PURE__ */ jsx57(ChecklistRunner, { tableId, recordId: openRecord }),
|
|
14195
|
+
/* @__PURE__ */ jsx57(Separator12, {}),
|
|
14196
|
+
/* @__PURE__ */ jsx57(CommentThread, { tableId, recordId: openRecord })
|
|
13849
14197
|
] }) : null
|
|
13850
14198
|
] })
|
|
13851
14199
|
] });
|
|
@@ -13876,6 +14224,7 @@ export {
|
|
|
13876
14224
|
DEFAULT_FIELDS,
|
|
13877
14225
|
DEFAULT_VIEW_NAME,
|
|
13878
14226
|
DashboardCanvas,
|
|
14227
|
+
DigestScheduler,
|
|
13879
14228
|
DocRender,
|
|
13880
14229
|
DocTemplate,
|
|
13881
14230
|
EMPTY_PRESENTATION,
|