@ai-matrx/records-ui 0.81.0 → 0.83.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 +56 -0
- package/dist/index.cjs +1179 -467
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +184 -12
- package/dist/index.d.ts +184 -12
- package/dist/index.js +1255 -543
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -538,7 +538,7 @@ import {
|
|
|
538
538
|
PopoverContent as PopoverContent2,
|
|
539
539
|
PopoverTrigger as PopoverTrigger2,
|
|
540
540
|
ScrollArea as ScrollArea2,
|
|
541
|
-
cn as
|
|
541
|
+
cn as cn5
|
|
542
542
|
} from "@ai-matrx/design-system";
|
|
543
543
|
|
|
544
544
|
// src/editors.tsx
|
|
@@ -557,7 +557,7 @@ import {
|
|
|
557
557
|
SelectItem as SelectItem2,
|
|
558
558
|
SelectTrigger as SelectTrigger2,
|
|
559
559
|
SelectValue as SelectValue2,
|
|
560
|
-
cn as
|
|
560
|
+
cn as cn4
|
|
561
561
|
} from "@ai-matrx/design-system";
|
|
562
562
|
|
|
563
563
|
// src/parity.ts
|
|
@@ -671,15 +671,126 @@ import {
|
|
|
671
671
|
PopoverContent,
|
|
672
672
|
PopoverTrigger,
|
|
673
673
|
ScrollArea,
|
|
674
|
-
cn as
|
|
674
|
+
cn as cn3
|
|
675
675
|
} from "@ai-matrx/design-system";
|
|
676
|
+
|
|
677
|
+
// src/Refusal.tsx
|
|
678
|
+
import { Alert, AlertDescription, AlertTitle, cn as cn2 } from "@ai-matrx/design-system";
|
|
676
679
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
680
|
+
function hintIsMachineIdentity(hint) {
|
|
681
|
+
return isMachineIdentity(hint);
|
|
682
|
+
}
|
|
683
|
+
function hintForAPerson(hint) {
|
|
684
|
+
const said = (hint ?? "").trim();
|
|
685
|
+
if (said === "") return null;
|
|
686
|
+
return isMachineIdentity(said) ? null : said;
|
|
687
|
+
}
|
|
688
|
+
function RefusalNotice({
|
|
689
|
+
error,
|
|
690
|
+
className,
|
|
691
|
+
actions,
|
|
692
|
+
onKeepEditing,
|
|
693
|
+
onDiscard
|
|
694
|
+
}) {
|
|
695
|
+
const plain = refusalForAPerson(error);
|
|
696
|
+
const doors = onKeepEditing || onDiscard ? /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-1 pt-0.5", "data-refusal-doors": "", children: [
|
|
697
|
+
onKeepEditing ? /* @__PURE__ */ jsx3(
|
|
698
|
+
"button",
|
|
699
|
+
{
|
|
700
|
+
type: "button",
|
|
701
|
+
"data-refusal-keep-editing": "",
|
|
702
|
+
className: "rounded border px-2 py-0.5 text-xs hover:bg-muted",
|
|
703
|
+
onClick: onKeepEditing,
|
|
704
|
+
children: "Keep editing"
|
|
705
|
+
}
|
|
706
|
+
) : null,
|
|
707
|
+
onDiscard ? /* @__PURE__ */ jsx3(
|
|
708
|
+
"button",
|
|
709
|
+
{
|
|
710
|
+
type: "button",
|
|
711
|
+
"data-refusal-discard": "",
|
|
712
|
+
className: "rounded border px-2 py-0.5 text-xs hover:bg-muted",
|
|
713
|
+
onClick: onDiscard,
|
|
714
|
+
children: "Discard"
|
|
715
|
+
}
|
|
716
|
+
) : null
|
|
717
|
+
] }) : null;
|
|
718
|
+
return /* @__PURE__ */ jsxs2(
|
|
719
|
+
Alert,
|
|
720
|
+
{
|
|
721
|
+
variant: "destructive",
|
|
722
|
+
role: "alert",
|
|
723
|
+
"data-refusal-notice": "",
|
|
724
|
+
className: cn2("text-xs", className),
|
|
725
|
+
...plain.forEngineers ? { title: plain.forEngineers } : {},
|
|
726
|
+
children: [
|
|
727
|
+
/* @__PURE__ */ jsx3(AlertTitle, { className: "text-xs font-medium", children: plain.title }),
|
|
728
|
+
/* @__PURE__ */ jsxs2(AlertDescription, { className: "space-y-1 text-xs", children: [
|
|
729
|
+
plain.sentence.replace(/\.$/, "") === plain.title ? null : /* @__PURE__ */ jsx3("p", { children: plain.sentence }),
|
|
730
|
+
/* @__PURE__ */ jsx3("p", { className: "opacity-80", children: plain.remedy }),
|
|
731
|
+
plain.forEngineers ? /* @__PURE__ */ jsx3(
|
|
732
|
+
"p",
|
|
733
|
+
{
|
|
734
|
+
className: "sr-only",
|
|
735
|
+
"aria-hidden": "true",
|
|
736
|
+
"data-for-engineers": "",
|
|
737
|
+
...error.sqlstate ? { "data-sqlstate": error.sqlstate } : {},
|
|
738
|
+
children: plain.forEngineers
|
|
739
|
+
}
|
|
740
|
+
) : null,
|
|
741
|
+
actions,
|
|
742
|
+
doors
|
|
743
|
+
] })
|
|
744
|
+
]
|
|
745
|
+
}
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
function RefusalLine({ error, className }) {
|
|
749
|
+
const plain = refusalForAPerson(error);
|
|
750
|
+
return /* @__PURE__ */ jsx3(
|
|
751
|
+
"p",
|
|
752
|
+
{
|
|
753
|
+
role: "alert",
|
|
754
|
+
"data-refusal-line": "",
|
|
755
|
+
className: cn2("text-xs text-destructive", className),
|
|
756
|
+
title: plain.forEngineers || void 0,
|
|
757
|
+
children: refusalLineForAPerson(error)
|
|
758
|
+
}
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// src/refusals.ts
|
|
763
|
+
function refusal(code, sentence2, remedy, diagnostic) {
|
|
764
|
+
return {
|
|
765
|
+
code,
|
|
766
|
+
message: sentence2,
|
|
767
|
+
...remedy ? { hint: remedy } : {},
|
|
768
|
+
...diagnostic ? { diagnostic } : {}
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
function refusalFromThrown(thrown, sentence2, remedy, code = "internal") {
|
|
772
|
+
const said = thrown instanceof Error ? thrown.message : String(thrown ?? "");
|
|
773
|
+
return refusal(code, sentence2, remedy, said ? `Thrown: ${said}` : null);
|
|
774
|
+
}
|
|
775
|
+
function refusalOr(error, code, sentence2) {
|
|
776
|
+
return error ?? refusal(code, sentence2);
|
|
777
|
+
}
|
|
778
|
+
function asRefusal(thrown, sentence2, remedy, code = "internal") {
|
|
779
|
+
const t = thrown;
|
|
780
|
+
if (t && typeof t === "object" && typeof t.code === "string" && typeof t.message === "string" && !(thrown instanceof Error)) {
|
|
781
|
+
return t;
|
|
782
|
+
}
|
|
783
|
+
return refusalFromThrown(thrown, sentence2, remedy, code);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// src/RelationPicker.tsx
|
|
787
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
677
788
|
var NO_STORE_ON_THIS_SCREEN_REASON = "This question points at a record in somebody's own data, and a page answered without an account is never allowed to search that. Leave it blank \u2014 whoever receives your answer will fill it in.";
|
|
678
789
|
function RelationPicker({ field, value, onChange, disabled, id, className, allowCreate = true }) {
|
|
679
790
|
const client = useOptionalRecordsClient();
|
|
680
791
|
const [rows, setRows] = useState2(null);
|
|
681
792
|
const [words, setWords] = useState2({});
|
|
682
|
-
const [
|
|
793
|
+
const [refusal2, setRefusal] = useState2(null);
|
|
683
794
|
const [search, setSearch] = useState2("");
|
|
684
795
|
const [open, setOpen] = useState2(false);
|
|
685
796
|
const [target, setTarget] = useState2(null);
|
|
@@ -699,7 +810,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
699
810
|
void client.list({ table_id: target2, limit: 200 }).then((result) => {
|
|
700
811
|
if (cancelled) return;
|
|
701
812
|
if (result.ok) setRows(result.data.rows);
|
|
702
|
-
else setRefusal(
|
|
813
|
+
else setRefusal(result.error);
|
|
703
814
|
});
|
|
704
815
|
return () => {
|
|
705
816
|
cancelled = true;
|
|
@@ -733,14 +844,17 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
733
844
|
void client.tableList().then((result) => {
|
|
734
845
|
if (cancelled) return;
|
|
735
846
|
if (!result.ok) {
|
|
736
|
-
setTargetRefusal(
|
|
847
|
+
setTargetRefusal(result.error);
|
|
737
848
|
return;
|
|
738
849
|
}
|
|
739
850
|
const found = result.data.find((t) => t.id === id2) ?? null;
|
|
740
851
|
if (found) setTarget(found);
|
|
741
852
|
else
|
|
742
853
|
setTargetRefusal(
|
|
743
|
-
|
|
854
|
+
refusal(
|
|
855
|
+
"door",
|
|
856
|
+
"That Table is not one you can open, so a new record cannot be added to it from here."
|
|
857
|
+
)
|
|
744
858
|
);
|
|
745
859
|
});
|
|
746
860
|
return () => {
|
|
@@ -751,11 +865,11 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
751
865
|
return (
|
|
752
866
|
// The Field's own name is already printed above this control by
|
|
753
867
|
// `FieldLabel`, so the sentence does not say it a second time.
|
|
754
|
-
/* @__PURE__ */
|
|
868
|
+
/* @__PURE__ */ jsx4("p", { id, className: cn3("text-xs text-muted-foreground", className), children: NO_STORE_ON_THIS_SCREEN_REASON })
|
|
755
869
|
);
|
|
756
870
|
}
|
|
757
871
|
if (!field.relation_target) {
|
|
758
|
-
return /* @__PURE__ */
|
|
872
|
+
return /* @__PURE__ */ jsxs3("p", { className: "text-xs text-muted-foreground", children: [
|
|
759
873
|
fieldName(field),
|
|
760
874
|
" points at records, but it does not say which Table yet, so there is nothing to pick from. A table admin sets its target in the field editor."
|
|
761
875
|
] });
|
|
@@ -779,13 +893,17 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
779
893
|
const tableId = field.relation_target;
|
|
780
894
|
if (!tableId || title === "" || busy) return;
|
|
781
895
|
if (!target) {
|
|
782
|
-
setCreateRefusal("Still reading that Table. Try again in a moment.");
|
|
896
|
+
setCreateRefusal(refusal("unreachable", "Still reading that Table.", "Try again in a moment."));
|
|
783
897
|
return;
|
|
784
898
|
}
|
|
785
899
|
const titleField = target.title_field;
|
|
786
900
|
if (!titleField) {
|
|
787
901
|
setCreateRefusal(
|
|
788
|
-
|
|
902
|
+
refusal(
|
|
903
|
+
"not_supported",
|
|
904
|
+
`${target.name} has no column that names a record yet, so a new one would have nothing to show here.`,
|
|
905
|
+
"A table admin picks its title column in that table's settings."
|
|
906
|
+
)
|
|
789
907
|
);
|
|
790
908
|
return;
|
|
791
909
|
}
|
|
@@ -794,7 +912,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
794
912
|
const written = await client.recordWrite({ table_id: tableId, data: { [titleField]: title } });
|
|
795
913
|
if (!written.ok) {
|
|
796
914
|
setBusy(false);
|
|
797
|
-
setCreateRefusal(
|
|
915
|
+
setCreateRefusal(written.error);
|
|
798
916
|
return;
|
|
799
917
|
}
|
|
800
918
|
const createdId = String(written.data);
|
|
@@ -809,8 +927,8 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
809
927
|
const visible = (rows ?? []).filter(
|
|
810
928
|
(row) => search.trim() === "" ? true : titleOf(row.id).toLowerCase().includes(search.trim().toLowerCase())
|
|
811
929
|
);
|
|
812
|
-
return /* @__PURE__ */
|
|
813
|
-
picked.map((recordId) => /* @__PURE__ */
|
|
930
|
+
return /* @__PURE__ */ jsxs3("div", { className: cn3("flex flex-wrap items-center gap-1", className), children: [
|
|
931
|
+
picked.map((recordId) => /* @__PURE__ */ jsx4(
|
|
814
932
|
RecordChip,
|
|
815
933
|
{
|
|
816
934
|
title: titleOf(recordId),
|
|
@@ -818,10 +936,10 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
818
936
|
},
|
|
819
937
|
recordId
|
|
820
938
|
)),
|
|
821
|
-
disabled ? null : /* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
824
|
-
/* @__PURE__ */
|
|
939
|
+
disabled ? null : /* @__PURE__ */ jsxs3(Popover, { open, onOpenChange: setOpen, children: [
|
|
940
|
+
/* @__PURE__ */ jsx4(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx4(Button2, { id, type: "button", size: "sm", variant: "outline", "aria-label": `Pick for ${fieldName(field)}`, children: picked.length === 0 ? "Pick" : "Change" }) }),
|
|
941
|
+
/* @__PURE__ */ jsxs3(PopoverContent, { sizing: "content", className: "p-1", align: "start", children: [
|
|
942
|
+
/* @__PURE__ */ jsx4(
|
|
825
943
|
BasicInput2,
|
|
826
944
|
{
|
|
827
945
|
value: search,
|
|
@@ -830,33 +948,33 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
830
948
|
className: "mb-1 h-7 text-xs"
|
|
831
949
|
}
|
|
832
950
|
),
|
|
833
|
-
|
|
834
|
-
!rows && !
|
|
835
|
-
full ? /* @__PURE__ */
|
|
951
|
+
refusal2 ? /* @__PURE__ */ jsx4(RefusalLine, { error: refusal2, className: "p-2" }) : null,
|
|
952
|
+
!rows && !refusal2 ? /* @__PURE__ */ jsx4("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading\u2026" }) : null,
|
|
953
|
+
full ? /* @__PURE__ */ jsxs3("p", { className: "p-2 text-xs text-muted-foreground", children: [
|
|
836
954
|
fieldName(field),
|
|
837
955
|
" can point at ",
|
|
838
956
|
field.relation_max,
|
|
839
957
|
" at most. Remove one to add another."
|
|
840
958
|
] }) : null,
|
|
841
|
-
/* @__PURE__ */
|
|
842
|
-
/* @__PURE__ */
|
|
959
|
+
/* @__PURE__ */ jsxs3(ScrollArea, { className: "max-h-56", children: [
|
|
960
|
+
/* @__PURE__ */ jsx4("ul", { children: visible.map((row) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
|
|
843
961
|
"button",
|
|
844
962
|
{
|
|
845
963
|
type: "button",
|
|
846
964
|
onClick: () => toggle(row.id),
|
|
847
965
|
title: titleOf(row.id),
|
|
848
|
-
className:
|
|
966
|
+
className: cn3(
|
|
849
967
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
850
968
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
851
969
|
),
|
|
852
970
|
children: titleOf(row.id)
|
|
853
971
|
}
|
|
854
972
|
) }, row.id)) }),
|
|
855
|
-
rows && visible.length === 0 ? /* @__PURE__ */
|
|
973
|
+
rows && visible.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "p-2 text-xs text-muted-foreground", children: "Nothing in that table matches." }) : null
|
|
856
974
|
] }),
|
|
857
|
-
allowCreate && !full ? /* @__PURE__ */
|
|
858
|
-
targetRefusal ? /* @__PURE__ */
|
|
859
|
-
/* @__PURE__ */
|
|
975
|
+
allowCreate && !full ? /* @__PURE__ */ jsxs3("div", { className: "mt-1 border-t pt-1", children: [
|
|
976
|
+
targetRefusal ? /* @__PURE__ */ jsx4(RefusalLine, { error: targetRefusal, className: "p-2" }) : !target ? /* @__PURE__ */ jsx4("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading that table\u2026" }) : creating ? /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 p-1", children: [
|
|
977
|
+
/* @__PURE__ */ jsx4(
|
|
860
978
|
BasicInput2,
|
|
861
979
|
{
|
|
862
980
|
autoFocus: true,
|
|
@@ -873,7 +991,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
873
991
|
"aria-label": `Name the new ${targetWord}`
|
|
874
992
|
}
|
|
875
993
|
),
|
|
876
|
-
/* @__PURE__ */
|
|
994
|
+
/* @__PURE__ */ jsx4(
|
|
877
995
|
Button2,
|
|
878
996
|
{
|
|
879
997
|
type: "button",
|
|
@@ -884,7 +1002,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
884
1002
|
children: busy ? "Creating\u2026" : "Create"
|
|
885
1003
|
}
|
|
886
1004
|
)
|
|
887
|
-
] }) : /* @__PURE__ */
|
|
1005
|
+
] }) : /* @__PURE__ */ jsx4(
|
|
888
1006
|
Button2,
|
|
889
1007
|
{
|
|
890
1008
|
type: "button",
|
|
@@ -900,10 +1018,25 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
900
1018
|
setCreating(true);
|
|
901
1019
|
}
|
|
902
1020
|
},
|
|
903
|
-
children: /* @__PURE__ */
|
|
1021
|
+
children: /* @__PURE__ */ jsx4("span", { className: "block w-full truncate text-left", children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}` })
|
|
904
1022
|
}
|
|
905
1023
|
),
|
|
906
|
-
createRefusal ?
|
|
1024
|
+
createRefusal ? (
|
|
1025
|
+
// The typed title is still in the search box: the notice owns the
|
|
1026
|
+
// two doors out (lane REFUSAL-SWEEP).
|
|
1027
|
+
/* @__PURE__ */ jsx4(
|
|
1028
|
+
RefusalNotice,
|
|
1029
|
+
{
|
|
1030
|
+
error: createRefusal,
|
|
1031
|
+
className: "mt-1 max-w-72",
|
|
1032
|
+
onKeepEditing: () => setCreateRefusal(null),
|
|
1033
|
+
onDiscard: () => {
|
|
1034
|
+
setCreateRefusal(null);
|
|
1035
|
+
setSearch("");
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
)
|
|
1039
|
+
) : null
|
|
907
1040
|
] }) : null
|
|
908
1041
|
] })
|
|
909
1042
|
] })
|
|
@@ -911,12 +1044,12 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
911
1044
|
}
|
|
912
1045
|
|
|
913
1046
|
// src/editors.tsx
|
|
914
|
-
import { Fragment as Fragment2, jsx as
|
|
1047
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
915
1048
|
function FieldLabel({ field, htmlFor, children }) {
|
|
916
|
-
return /* @__PURE__ */
|
|
917
|
-
/* @__PURE__ */
|
|
918
|
-
field.required ? /* @__PURE__ */
|
|
919
|
-
field.unit ? /* @__PURE__ */
|
|
1049
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1050
|
+
/* @__PURE__ */ jsx5(Label2, { htmlFor, className: "text-xs font-medium", children: fieldName(field) }),
|
|
1051
|
+
field.required ? /* @__PURE__ */ jsx5("span", { className: "text-xs text-destructive", children: "required" }) : null,
|
|
1052
|
+
field.unit ? /* @__PURE__ */ jsx5("span", { className: "text-xs text-muted-foreground", children: field.unit }) : null,
|
|
920
1053
|
children
|
|
921
1054
|
] });
|
|
922
1055
|
}
|
|
@@ -927,17 +1060,17 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
927
1060
|
case "formula":
|
|
928
1061
|
case "lookup":
|
|
929
1062
|
case "rollup":
|
|
930
|
-
return /* @__PURE__ */
|
|
1063
|
+
return /* @__PURE__ */ jsxs4("div", { id: inputId, className: "rounded border border-dashed px-2 py-1.5 text-xs text-muted-foreground", children: [
|
|
931
1064
|
value === null || value === void 0 ? "\u2014" : String(value),
|
|
932
|
-
/* @__PURE__ */
|
|
1065
|
+
/* @__PURE__ */ jsxs4("span", { className: "ml-2 italic", children: [
|
|
933
1066
|
fieldName(field),
|
|
934
1067
|
" is worked out by the system, so it cannot be typed in"
|
|
935
1068
|
] })
|
|
936
1069
|
] });
|
|
937
1070
|
case "json":
|
|
938
|
-
return /* @__PURE__ */
|
|
1071
|
+
return /* @__PURE__ */ jsx5(JsonControl, { field, value, onChange, disabled, id: inputId });
|
|
939
1072
|
case "long_text":
|
|
940
|
-
return /* @__PURE__ */
|
|
1073
|
+
return /* @__PURE__ */ jsx5(
|
|
941
1074
|
BasicTextarea,
|
|
942
1075
|
{
|
|
943
1076
|
id: inputId,
|
|
@@ -950,7 +1083,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
950
1083
|
case "number":
|
|
951
1084
|
case "currency":
|
|
952
1085
|
case "percent":
|
|
953
|
-
return /* @__PURE__ */
|
|
1086
|
+
return /* @__PURE__ */ jsx5(
|
|
954
1087
|
BasicInput3,
|
|
955
1088
|
{
|
|
956
1089
|
id: inputId,
|
|
@@ -963,7 +1096,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
963
1096
|
}
|
|
964
1097
|
);
|
|
965
1098
|
case "datetime":
|
|
966
|
-
return /* @__PURE__ */
|
|
1099
|
+
return /* @__PURE__ */ jsx5(
|
|
967
1100
|
BasicInput3,
|
|
968
1101
|
{
|
|
969
1102
|
id: inputId,
|
|
@@ -976,7 +1109,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
976
1109
|
case "email":
|
|
977
1110
|
case "phone":
|
|
978
1111
|
case "url":
|
|
979
|
-
return /* @__PURE__ */
|
|
1112
|
+
return /* @__PURE__ */ jsx5(
|
|
980
1113
|
BasicInput3,
|
|
981
1114
|
{
|
|
982
1115
|
id: inputId,
|
|
@@ -987,18 +1120,18 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
987
1120
|
}
|
|
988
1121
|
);
|
|
989
1122
|
case "checkbox":
|
|
990
|
-
return /* @__PURE__ */
|
|
1123
|
+
return /* @__PURE__ */ jsx5(CheckboxControl, { field, value, onChange, disabled, id: inputId });
|
|
991
1124
|
case "select":
|
|
992
1125
|
case "multi_select":
|
|
993
|
-
return /* @__PURE__ */
|
|
1126
|
+
return /* @__PURE__ */ jsx5(OptionControl, { field, value, onChange, disabled, id: inputId });
|
|
994
1127
|
case "member":
|
|
995
|
-
return /* @__PURE__ */
|
|
1128
|
+
return /* @__PURE__ */ jsx5(PersonPicker, { field, value, onChange, disabled, id: inputId });
|
|
996
1129
|
case "relation":
|
|
997
1130
|
case "attachment":
|
|
998
|
-
return /* @__PURE__ */
|
|
1131
|
+
return /* @__PURE__ */ jsx5(RelationPicker, { field, value, onChange, disabled, id: inputId });
|
|
999
1132
|
default:
|
|
1000
1133
|
if (typeof value === "boolean" || field.config?.["kind"] === "boolean") {
|
|
1001
|
-
return /* @__PURE__ */
|
|
1134
|
+
return /* @__PURE__ */ jsx5(
|
|
1002
1135
|
Checkbox2,
|
|
1003
1136
|
{
|
|
1004
1137
|
id: inputId,
|
|
@@ -1008,7 +1141,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1008
1141
|
}
|
|
1009
1142
|
);
|
|
1010
1143
|
}
|
|
1011
|
-
return /* @__PURE__ */
|
|
1144
|
+
return /* @__PURE__ */ jsx5(
|
|
1012
1145
|
BasicInput3,
|
|
1013
1146
|
{
|
|
1014
1147
|
id: inputId,
|
|
@@ -1021,8 +1154,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1021
1154
|
}
|
|
1022
1155
|
function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
1023
1156
|
const unanswered = value === null || value === void 0;
|
|
1024
|
-
return /* @__PURE__ */
|
|
1025
|
-
/* @__PURE__ */
|
|
1157
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
1158
|
+
/* @__PURE__ */ jsx5(
|
|
1026
1159
|
Checkbox2,
|
|
1027
1160
|
{
|
|
1028
1161
|
id,
|
|
@@ -1032,9 +1165,9 @@ function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
|
1032
1165
|
onCheckedChange: (next) => onChange(next === true)
|
|
1033
1166
|
}
|
|
1034
1167
|
),
|
|
1035
|
-
unanswered ? /* @__PURE__ */
|
|
1036
|
-
/* @__PURE__ */
|
|
1037
|
-
field.required ? null : /* @__PURE__ */
|
|
1168
|
+
unanswered ? /* @__PURE__ */ jsx5("span", { className: "text-xs italic text-muted-foreground", children: "not answered yet" }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1169
|
+
/* @__PURE__ */ jsx5("span", { className: "text-xs text-muted-foreground", children: value === true ? "Yes" : "No" }),
|
|
1170
|
+
field.required ? null : /* @__PURE__ */ jsx5(
|
|
1038
1171
|
Button3,
|
|
1039
1172
|
{
|
|
1040
1173
|
size: "sm",
|
|
@@ -1052,8 +1185,8 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1052
1185
|
const initial = value === null || value === void 0 ? "" : typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
1053
1186
|
const [text, setText] = useState3(initial);
|
|
1054
1187
|
const [broken, setBroken] = useState3(null);
|
|
1055
|
-
return /* @__PURE__ */
|
|
1056
|
-
/* @__PURE__ */
|
|
1188
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
|
|
1189
|
+
/* @__PURE__ */ jsx5(
|
|
1057
1190
|
BasicTextarea,
|
|
1058
1191
|
{
|
|
1059
1192
|
id,
|
|
@@ -1073,37 +1206,53 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1073
1206
|
onChange(JSON.parse(next));
|
|
1074
1207
|
setBroken(null);
|
|
1075
1208
|
} catch (thrown) {
|
|
1076
|
-
setBroken(
|
|
1209
|
+
setBroken(
|
|
1210
|
+
refusalFromThrown(
|
|
1211
|
+
thrown,
|
|
1212
|
+
`${fieldName(field)} is not readable as structured data yet.`,
|
|
1213
|
+
"Fix the text so it reads as structured data, or discard it to put back what was there.",
|
|
1214
|
+
"invalid_argument"
|
|
1215
|
+
)
|
|
1216
|
+
);
|
|
1077
1217
|
}
|
|
1078
1218
|
}
|
|
1079
1219
|
}
|
|
1080
1220
|
),
|
|
1081
|
-
broken ?
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1221
|
+
broken ? (
|
|
1222
|
+
// The typed text is still in the box: the notice owns the two doors out.
|
|
1223
|
+
/* @__PURE__ */ jsx5(
|
|
1224
|
+
RefusalNotice,
|
|
1225
|
+
{
|
|
1226
|
+
error: broken,
|
|
1227
|
+
onKeepEditing: () => id ? document.getElementById(id)?.focus() : void 0,
|
|
1228
|
+
onDiscard: () => {
|
|
1229
|
+
setText(initial);
|
|
1230
|
+
setBroken(null);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
)
|
|
1234
|
+
) : null
|
|
1086
1235
|
] });
|
|
1087
1236
|
}
|
|
1088
1237
|
function OptionControl({ field, value, onChange, disabled, id }) {
|
|
1089
1238
|
const client = useOptionalRecordsClient2();
|
|
1090
1239
|
const [options, setOptions] = useState3(null);
|
|
1091
|
-
const [
|
|
1240
|
+
const [refusal2, setRefusal] = useState3(null);
|
|
1092
1241
|
useEffect2(() => {
|
|
1093
1242
|
if (!client) return;
|
|
1094
1243
|
let cancelled = false;
|
|
1095
1244
|
void client.fieldOptions({ field_id: field.id }).then((result) => {
|
|
1096
1245
|
if (cancelled) return;
|
|
1097
1246
|
if (result.ok) setOptions(result.data ?? []);
|
|
1098
|
-
else setRefusal(
|
|
1247
|
+
else setRefusal(result.error);
|
|
1099
1248
|
});
|
|
1100
1249
|
return () => {
|
|
1101
1250
|
cancelled = true;
|
|
1102
1251
|
};
|
|
1103
1252
|
}, [client, field.id]);
|
|
1104
1253
|
if (!client) {
|
|
1105
|
-
return /* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */
|
|
1254
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
|
|
1255
|
+
/* @__PURE__ */ jsx5(
|
|
1107
1256
|
BasicInput3,
|
|
1108
1257
|
{
|
|
1109
1258
|
id,
|
|
@@ -1112,19 +1261,20 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1112
1261
|
onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
|
|
1113
1262
|
}
|
|
1114
1263
|
),
|
|
1115
|
-
/* @__PURE__ */
|
|
1264
|
+
/* @__PURE__ */ jsx5("p", { className: "text-xs text-muted-foreground", children: "This is normally picked from a list. A page answered without an account cannot read that list, so type your answer and whoever receives it will match it up." })
|
|
1116
1265
|
] });
|
|
1117
1266
|
}
|
|
1118
|
-
if (
|
|
1119
|
-
|
|
1267
|
+
if (refusal2)
|
|
1268
|
+
return /* @__PURE__ */ jsx5("div", { id, children: /* @__PURE__ */ jsx5(RefusalLine, { error: refusal2 }) });
|
|
1269
|
+
if (!options) return /* @__PURE__ */ jsx5("p", { id, className: "text-xs text-muted-foreground", children: "Reading this field's choices\u2026" });
|
|
1120
1270
|
if (options.length === 0) {
|
|
1121
|
-
return /* @__PURE__ */
|
|
1271
|
+
return /* @__PURE__ */ jsx5("p", { id, className: "text-xs text-muted-foreground", children: field.options_table_id ? `${fieldName(field)} takes its choices from a Table that has no records yet \u2014 add the choices there and they appear here.` : `${fieldName(field)} does not name the Table its choices come from yet, so there is nothing to choose. A table admin sets it in the field editor.` });
|
|
1122
1272
|
}
|
|
1123
1273
|
const titleOf = (option) => recordName(option.data, null, "Unnamed choice");
|
|
1124
1274
|
if (field.multi) {
|
|
1125
1275
|
const chosen = options.filter((option) => choiceValuesOf(value).some((v) => isTheChosen(option, v)));
|
|
1126
1276
|
const picked = new Set(chosen.map((option) => optionKey(option)));
|
|
1127
|
-
return /* @__PURE__ */
|
|
1277
|
+
return /* @__PURE__ */ jsx5("div", { id, className: "flex flex-wrap gap-1", children: options.map((option) => /* @__PURE__ */ jsx5(
|
|
1128
1278
|
Button3,
|
|
1129
1279
|
{
|
|
1130
1280
|
type: "button",
|
|
@@ -1143,15 +1293,15 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1143
1293
|
)) });
|
|
1144
1294
|
}
|
|
1145
1295
|
const current = options.find((option) => isTheChosen(option, value));
|
|
1146
|
-
return /* @__PURE__ */
|
|
1296
|
+
return /* @__PURE__ */ jsxs4(
|
|
1147
1297
|
Select2,
|
|
1148
1298
|
{
|
|
1149
1299
|
value: current ? optionKey(current) : "",
|
|
1150
1300
|
disabled: disabled ?? false,
|
|
1151
1301
|
onValueChange: (next) => onChange(next === "" ? null : next),
|
|
1152
1302
|
children: [
|
|
1153
|
-
/* @__PURE__ */
|
|
1154
|
-
/* @__PURE__ */
|
|
1303
|
+
/* @__PURE__ */ jsx5(SelectTrigger2, { id, size: "sm", children: /* @__PURE__ */ jsx5(SelectValue2, { placeholder: "Choose" }) }),
|
|
1304
|
+
/* @__PURE__ */ jsx5(SelectContent2, { children: options.map((option) => /* @__PURE__ */ jsx5(SelectItem2, { value: optionKey(option), children: titleOf(option) }, option.id)) })
|
|
1155
1305
|
]
|
|
1156
1306
|
}
|
|
1157
1307
|
);
|
|
@@ -1167,15 +1317,15 @@ function RecordChip({
|
|
|
1167
1317
|
// name. So it caps at its container and ends in an ellipsis with the whole name in
|
|
1168
1318
|
// its tooltip, rather than being cut mid-word by whatever box it landed in
|
|
1169
1319
|
// (lane FIX-14, 2026-09-22).
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
onRemove ? /* @__PURE__ */
|
|
1320
|
+
/* @__PURE__ */ jsxs4(Badge, { variant: "secondary", className: cn4("max-w-full gap-1 text-[11px]", className), title, children: [
|
|
1321
|
+
/* @__PURE__ */ jsx5("span", { className: "min-w-0 truncate", children: title }),
|
|
1322
|
+
onRemove ? /* @__PURE__ */ jsx5("button", { type: "button", onClick: onRemove, "aria-label": `Remove ${title}`, className: "shrink-0 opacity-60 hover:opacity-100", children: "\xD7" }) : null
|
|
1173
1323
|
] })
|
|
1174
1324
|
);
|
|
1175
1325
|
}
|
|
1176
1326
|
|
|
1177
1327
|
// src/PersonPicker.tsx
|
|
1178
|
-
import { jsx as
|
|
1328
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1179
1329
|
function personFromRow(row) {
|
|
1180
1330
|
const data = row.document ?? {};
|
|
1181
1331
|
const userId = data["user_id"];
|
|
@@ -1228,7 +1378,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1228
1378
|
]);
|
|
1229
1379
|
setMembers(roster);
|
|
1230
1380
|
if (records && !records.ok) {
|
|
1231
|
-
setError(
|
|
1381
|
+
setError(records.error);
|
|
1232
1382
|
return;
|
|
1233
1383
|
}
|
|
1234
1384
|
setPeople(records ? records.data.rows.map(personFromRow).filter((p) => p.userId !== null) : []);
|
|
@@ -1249,10 +1399,10 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1249
1399
|
};
|
|
1250
1400
|
}, [client, personTable, picked.length, people.length]);
|
|
1251
1401
|
if (!membersPort || !client) {
|
|
1252
|
-
return /* @__PURE__ */
|
|
1402
|
+
return /* @__PURE__ */ jsx6("p", { className: cn5("text-xs text-muted-foreground", className), children: NO_MEMBERS_REASON });
|
|
1253
1403
|
}
|
|
1254
1404
|
if (!personTable) {
|
|
1255
|
-
return /* @__PURE__ */
|
|
1405
|
+
return /* @__PURE__ */ jsxs5("p", { className: cn5("text-xs text-muted-foreground", className), children: [
|
|
1256
1406
|
fieldName(field),
|
|
1257
1407
|
" is a person field, but it does not say which Table the people are records of yet, so there is nothing to pick from. A table admin sets its target in the field editor."
|
|
1258
1408
|
] });
|
|
@@ -1270,7 +1420,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1270
1420
|
const resolved = await personRecordForMember(client, personTable, people, member);
|
|
1271
1421
|
setBusy(false);
|
|
1272
1422
|
if (!resolved.ok) {
|
|
1273
|
-
setError(
|
|
1423
|
+
setError(resolved.error);
|
|
1274
1424
|
return;
|
|
1275
1425
|
}
|
|
1276
1426
|
if (!people.some((p) => p.id === resolved.data)) {
|
|
@@ -1296,8 +1446,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1296
1446
|
const pickedUserIds = new Set(
|
|
1297
1447
|
picked.map((recordId) => people.find((p) => p.id === recordId)?.userId).filter(Boolean)
|
|
1298
1448
|
);
|
|
1299
|
-
return /* @__PURE__ */
|
|
1300
|
-
picked.map((recordId) => /* @__PURE__ */
|
|
1449
|
+
return /* @__PURE__ */ jsxs5("div", { className: cn5("flex flex-wrap items-center gap-1", className), children: [
|
|
1450
|
+
picked.map((recordId) => /* @__PURE__ */ jsx6(
|
|
1301
1451
|
RecordChip,
|
|
1302
1452
|
{
|
|
1303
1453
|
title: nameOfRecord(recordId),
|
|
@@ -1305,8 +1455,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1305
1455
|
},
|
|
1306
1456
|
recordId
|
|
1307
1457
|
)),
|
|
1308
|
-
disabled ? null : /* @__PURE__ */
|
|
1309
|
-
/* @__PURE__ */
|
|
1458
|
+
disabled ? null : /* @__PURE__ */ jsxs5(Popover2, { open, onOpenChange: setOpen, children: [
|
|
1459
|
+
/* @__PURE__ */ jsx6(PopoverTrigger2, { asChild: true, children: /* @__PURE__ */ jsx6(
|
|
1310
1460
|
Button4,
|
|
1311
1461
|
{
|
|
1312
1462
|
id,
|
|
@@ -1317,8 +1467,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1317
1467
|
children: picked.length === 0 ? "Pick a person" : "Change"
|
|
1318
1468
|
}
|
|
1319
1469
|
) }),
|
|
1320
|
-
/* @__PURE__ */
|
|
1321
|
-
/* @__PURE__ */
|
|
1470
|
+
/* @__PURE__ */ jsxs5(PopoverContent2, { sizing: "content", className: "p-1", align: "start", children: [
|
|
1471
|
+
/* @__PURE__ */ jsx6(
|
|
1322
1472
|
BasicInput4,
|
|
1323
1473
|
{
|
|
1324
1474
|
value: search,
|
|
@@ -1327,29 +1477,29 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1327
1477
|
className: "mb-1 h-7 text-xs"
|
|
1328
1478
|
}
|
|
1329
1479
|
),
|
|
1330
|
-
error ? /* @__PURE__ */
|
|
1331
|
-
members === null ? /* @__PURE__ */
|
|
1332
|
-
/* @__PURE__ */
|
|
1333
|
-
/* @__PURE__ */
|
|
1480
|
+
error ? /* @__PURE__ */ jsx6(RefusalLine, { error, className: "p-2" }) : null,
|
|
1481
|
+
members === null ? /* @__PURE__ */ jsx6("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading the members\u2026" }) : null,
|
|
1482
|
+
/* @__PURE__ */ jsxs5(ScrollArea2, { className: "max-h-56", children: [
|
|
1483
|
+
/* @__PURE__ */ jsx6("ul", { children: visible.map((member) => /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsxs5(
|
|
1334
1484
|
"button",
|
|
1335
1485
|
{
|
|
1336
1486
|
type: "button",
|
|
1337
1487
|
disabled: busy,
|
|
1338
1488
|
onClick: () => void pick(member),
|
|
1339
|
-
className:
|
|
1489
|
+
className: cn5(
|
|
1340
1490
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1341
1491
|
pickedUserIds.has(member.userId) ? "bg-accent text-accent-foreground" : ""
|
|
1342
1492
|
),
|
|
1343
1493
|
children: [
|
|
1344
|
-
/* @__PURE__ */
|
|
1345
|
-
member.email && member.email !== memberName(member) ? /* @__PURE__ */
|
|
1494
|
+
/* @__PURE__ */ jsx6("span", { children: memberName(member) }),
|
|
1495
|
+
member.email && member.email !== memberName(member) ? /* @__PURE__ */ jsxs5("span", { className: "ml-1.5 opacity-60", children: [
|
|
1346
1496
|
"\xB7 ",
|
|
1347
1497
|
member.email
|
|
1348
1498
|
] }) : null
|
|
1349
1499
|
]
|
|
1350
1500
|
}
|
|
1351
1501
|
) }, member.userId)) }),
|
|
1352
|
-
members !== null && visible.length === 0 ? /* @__PURE__ */
|
|
1502
|
+
members !== null && visible.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "p-2 text-xs text-muted-foreground", children: members.length === 0 ? "This organization has no other members yet, so there is nobody to assign." : "Nobody in this organization matches." }) : null
|
|
1353
1503
|
] })
|
|
1354
1504
|
] })
|
|
1355
1505
|
] })
|
|
@@ -1362,55 +1512,6 @@ import { asWriteConflict } from "@ai-matrx/records/core";
|
|
|
1362
1512
|
import { useRecordsClient as useRecordsClient3 } from "@ai-matrx/records/react";
|
|
1363
1513
|
import { Button as Button5, Checkbox as Checkbox3, cn as cn7 } from "@ai-matrx/design-system";
|
|
1364
1514
|
|
|
1365
|
-
// src/Refusal.tsx
|
|
1366
|
-
import { Alert, AlertDescription, AlertTitle, cn as cn5 } from "@ai-matrx/design-system";
|
|
1367
|
-
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1368
|
-
function hintIsMachineIdentity(hint) {
|
|
1369
|
-
return isMachineIdentity(hint);
|
|
1370
|
-
}
|
|
1371
|
-
function hintForAPerson(hint) {
|
|
1372
|
-
const said = (hint ?? "").trim();
|
|
1373
|
-
if (said === "") return null;
|
|
1374
|
-
return isMachineIdentity(said) ? null : said;
|
|
1375
|
-
}
|
|
1376
|
-
function RefusalNotice({
|
|
1377
|
-
error,
|
|
1378
|
-
className,
|
|
1379
|
-
actions
|
|
1380
|
-
}) {
|
|
1381
|
-
const plain = refusalForAPerson(error);
|
|
1382
|
-
return /* @__PURE__ */ jsxs5(
|
|
1383
|
-
Alert,
|
|
1384
|
-
{
|
|
1385
|
-
variant: "destructive",
|
|
1386
|
-
className: cn5("text-xs", className),
|
|
1387
|
-
...plain.forEngineers ? { title: plain.forEngineers } : {},
|
|
1388
|
-
children: [
|
|
1389
|
-
/* @__PURE__ */ jsx6(AlertTitle, { className: "text-xs font-medium", children: plain.title }),
|
|
1390
|
-
/* @__PURE__ */ jsxs5(AlertDescription, { className: "space-y-1 text-xs", children: [
|
|
1391
|
-
plain.sentence.replace(/\.$/, "") === plain.title ? null : /* @__PURE__ */ jsx6("p", { children: plain.sentence }),
|
|
1392
|
-
/* @__PURE__ */ jsx6("p", { className: "opacity-80", children: plain.remedy }),
|
|
1393
|
-
plain.forEngineers ? /* @__PURE__ */ jsx6(
|
|
1394
|
-
"p",
|
|
1395
|
-
{
|
|
1396
|
-
className: "sr-only",
|
|
1397
|
-
"aria-hidden": "true",
|
|
1398
|
-
"data-for-engineers": "",
|
|
1399
|
-
...error.sqlstate ? { "data-sqlstate": error.sqlstate } : {},
|
|
1400
|
-
children: plain.forEngineers
|
|
1401
|
-
}
|
|
1402
|
-
) : null,
|
|
1403
|
-
actions
|
|
1404
|
-
] })
|
|
1405
|
-
]
|
|
1406
|
-
}
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
function RefusalLine({ error, className }) {
|
|
1410
|
-
const plain = refusalForAPerson(error);
|
|
1411
|
-
return /* @__PURE__ */ jsx6("p", { className: cn5("text-xs text-destructive", className), title: plain.forEngineers || void 0, children: refusalLineForAPerson(error) });
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
1515
|
// src/whoChangedSource.tsx
|
|
1415
1516
|
import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect4, useMemo as useMemo5, useRef, useState as useState5 } from "react";
|
|
1416
1517
|
import { useRecordsClient } from "@ai-matrx/records/react";
|
|
@@ -2013,7 +2114,7 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2013
2114
|
startedFrom.current = null;
|
|
2014
2115
|
}, []);
|
|
2015
2116
|
const writeCellNow = useCallback4(
|
|
2016
|
-
async (rowId, key, value) => {
|
|
2117
|
+
async (rowId, key, value, resent = false) => {
|
|
2017
2118
|
const id = cellKey(rowId, key);
|
|
2018
2119
|
setOptimistic((held) => ({ ...held, [id]: value }));
|
|
2019
2120
|
setSaving((held) => ({ ...held, [id]: true }));
|
|
@@ -2056,16 +2157,25 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2056
2157
|
delete next[id];
|
|
2057
2158
|
return next;
|
|
2058
2159
|
});
|
|
2059
|
-
|
|
2160
|
+
const conflict = asWriteConflict(written.error, rowId);
|
|
2161
|
+
if (written.error.code === "stale_write") {
|
|
2162
|
+
if (conflict) rememberVersion(versions.current, rowId, conflict.current_version);
|
|
2163
|
+
else versions.current.delete(rowId);
|
|
2164
|
+
if (conflict && !resent && !Object.prototype.hasOwnProperty.call(conflict.contested_fields, key)) {
|
|
2165
|
+
await writeCellNow(rowId, key, value, true);
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2060
2169
|
setRefusals((held) => ({
|
|
2061
2170
|
...held,
|
|
2062
2171
|
[id]: {
|
|
2063
2172
|
error: written.error,
|
|
2064
|
-
conflict
|
|
2173
|
+
conflict,
|
|
2065
2174
|
attempted: value
|
|
2066
2175
|
}
|
|
2067
2176
|
}));
|
|
2068
2177
|
},
|
|
2178
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2069
2179
|
[client, reload]
|
|
2070
2180
|
);
|
|
2071
2181
|
const writeCell = useCallback4(
|
|
@@ -2140,6 +2250,33 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2140
2250
|
},
|
|
2141
2251
|
[reload]
|
|
2142
2252
|
);
|
|
2253
|
+
const keepEditing = useCallback4(
|
|
2254
|
+
(rowId, key) => {
|
|
2255
|
+
const held = refusals[cellKey(rowId, key)];
|
|
2256
|
+
if (!held || !writable(rowId)) return;
|
|
2257
|
+
startedFrom.current = shownValue(rowId, key);
|
|
2258
|
+
typed.current = held.attempted;
|
|
2259
|
+
setDraft(held.attempted);
|
|
2260
|
+
setEditing({ rowId, key });
|
|
2261
|
+
},
|
|
2262
|
+
[refusals, writable, shownValue]
|
|
2263
|
+
);
|
|
2264
|
+
const discard = useCallback4((rowId, key) => {
|
|
2265
|
+
const id = cellKey(rowId, key);
|
|
2266
|
+
setRefusals((held) => {
|
|
2267
|
+
if (!held[id]) return held;
|
|
2268
|
+
const next = { ...held };
|
|
2269
|
+
delete next[id];
|
|
2270
|
+
return next;
|
|
2271
|
+
});
|
|
2272
|
+
setOptimistic((held) => {
|
|
2273
|
+
if (!(id in held)) return held;
|
|
2274
|
+
const next = { ...held };
|
|
2275
|
+
delete next[id];
|
|
2276
|
+
return next;
|
|
2277
|
+
});
|
|
2278
|
+
setEditing((open) => open && open.rowId === rowId && open.key === key ? null : open);
|
|
2279
|
+
}, []);
|
|
2143
2280
|
const remove = useCallback4(
|
|
2144
2281
|
async (rowId) => {
|
|
2145
2282
|
setBusy(true);
|
|
@@ -2191,6 +2328,8 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow }
|
|
|
2191
2328
|
},
|
|
2192
2329
|
retry,
|
|
2193
2330
|
keepTheirs,
|
|
2331
|
+
keepEditing,
|
|
2332
|
+
discard,
|
|
2194
2333
|
remove,
|
|
2195
2334
|
addRecord,
|
|
2196
2335
|
rowError,
|
|
@@ -2249,7 +2388,7 @@ function GridCell({
|
|
|
2249
2388
|
);
|
|
2250
2389
|
const value = editing ? editing.valueFor(row, field) : row.document?.[field.key];
|
|
2251
2390
|
const state = editing ? editing.stateOf(row.id, field.key) : null;
|
|
2252
|
-
const
|
|
2391
|
+
const refusal2 = editing ? editing.refusalOf(row.id, field.key) : null;
|
|
2253
2392
|
const editable = Boolean(editing) && canWrite && fieldIsEditable(field);
|
|
2254
2393
|
if (open && editing) {
|
|
2255
2394
|
return /* @__PURE__ */ jsx10(
|
|
@@ -2259,13 +2398,28 @@ function GridCell({
|
|
|
2259
2398
|
draft: editing.draft,
|
|
2260
2399
|
onType: editing.type,
|
|
2261
2400
|
onCommit: (move) => editing.commit(move),
|
|
2262
|
-
onCancel: editing.cancel
|
|
2401
|
+
onCancel: editing.cancel,
|
|
2402
|
+
below: (
|
|
2403
|
+
// KEPT EDITING: the refusal stays beside the value it is about, so the
|
|
2404
|
+
// person reads what to change while changing it. Keep editing is the
|
|
2405
|
+
// state they are in, so only Discard is offered. It is INSIDE the open
|
|
2406
|
+
// cell, so pressing Discard is not "focus left the cell" — which would
|
|
2407
|
+
// commit the refused value one more time on its way out.
|
|
2408
|
+
refusal2 && !refusal2.conflict ? /* @__PURE__ */ jsx10(
|
|
2409
|
+
RefusalNotice,
|
|
2410
|
+
{
|
|
2411
|
+
error: refusal2.error,
|
|
2412
|
+
className: "py-1.5",
|
|
2413
|
+
onDiscard: () => editing.discard(row.id, field.key)
|
|
2414
|
+
}
|
|
2415
|
+
) : null
|
|
2416
|
+
)
|
|
2263
2417
|
}
|
|
2264
2418
|
);
|
|
2265
2419
|
}
|
|
2266
2420
|
if (editorKindFor(field) === "checkbox") {
|
|
2267
2421
|
const answered = value === true || value === false;
|
|
2268
|
-
return /* @__PURE__ */ jsxs6("span", { "data-matrx-cell-control": "", className: "flex w-full items-center gap-1.5", children: [
|
|
2422
|
+
return /* @__PURE__ */ jsxs6("span", { "data-matrx-cell-control": "", className: "flex w-full flex-wrap items-center gap-1.5", children: [
|
|
2269
2423
|
/* @__PURE__ */ jsx10(
|
|
2270
2424
|
Checkbox3,
|
|
2271
2425
|
{
|
|
@@ -2278,10 +2432,7 @@ function GridCell({
|
|
|
2278
2432
|
}
|
|
2279
2433
|
),
|
|
2280
2434
|
answered ? null : /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", title: "nobody has answered this yet", children: "\u2014" }),
|
|
2281
|
-
|
|
2282
|
-
/* @__PURE__ */ jsx10(RefusalLine, { error: refusal.error }),
|
|
2283
|
-
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" })
|
|
2284
|
-
] }) : null
|
|
2435
|
+
refusal2 ? /* @__PURE__ */ jsx10(CellRefusalNotice, { refusal: refusal2, field, row, editing, typed: false }) : null
|
|
2285
2436
|
] });
|
|
2286
2437
|
}
|
|
2287
2438
|
const openThisCell = (event) => {
|
|
@@ -2354,22 +2505,48 @@ function GridCell({
|
|
|
2354
2505
|
),
|
|
2355
2506
|
onAskWhoChanged ? /* @__PURE__ */ jsx10(WhoBadge, { field, row, onAskWhoChanged }) : null,
|
|
2356
2507
|
state === "saving" ? /* @__PURE__ */ jsx10("span", { className: "px-1 text-[10px] text-muted-foreground", children: "Saving\u2026" }) : null,
|
|
2357
|
-
|
|
2358
|
-
/* @__PURE__ */ jsx10(RefusalLine, { error: refusal.error }),
|
|
2359
|
-
refusal.conflict ? /* @__PURE__ */ jsxs6("span", { className: "text-[11px]", children: [
|
|
2360
|
-
"Theirs: ",
|
|
2361
|
-
/* @__PURE__ */ jsx10("strong", { children: formatTheirs(refusal.conflict, field.key) })
|
|
2362
|
-
] }) : null,
|
|
2363
|
-
/* @__PURE__ */ jsxs6("span", { className: "flex gap-1", children: [
|
|
2364
|
-
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" }),
|
|
2365
|
-
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "ghost", onClick: () => editing?.keepTheirs(row.id, field.key), children: "Keep theirs" })
|
|
2366
|
-
] })
|
|
2367
|
-
] }) : null
|
|
2508
|
+
refusal2 ? /* @__PURE__ */ jsx10(CellRefusalNotice, { refusal: refusal2, field, row, editing, typed: true }) : null
|
|
2368
2509
|
]
|
|
2369
2510
|
}
|
|
2370
2511
|
)
|
|
2371
2512
|
);
|
|
2372
2513
|
}
|
|
2514
|
+
function CellRefusalNotice({
|
|
2515
|
+
refusal: refusal2,
|
|
2516
|
+
field,
|
|
2517
|
+
row,
|
|
2518
|
+
editing,
|
|
2519
|
+
typed
|
|
2520
|
+
}) {
|
|
2521
|
+
if (refusal2.conflict) {
|
|
2522
|
+
return /* @__PURE__ */ jsx10(
|
|
2523
|
+
RefusalNotice,
|
|
2524
|
+
{
|
|
2525
|
+
error: refusal2.error,
|
|
2526
|
+
className: "py-1.5",
|
|
2527
|
+
actions: /* @__PURE__ */ jsxs6("span", { className: "flex flex-col gap-1", "data-refusal-conflict": "", children: [
|
|
2528
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-[11px]", children: [
|
|
2529
|
+
"Theirs: ",
|
|
2530
|
+
/* @__PURE__ */ jsx10("strong", { children: formatTheirs(refusal2.conflict, field.key) })
|
|
2531
|
+
] }),
|
|
2532
|
+
/* @__PURE__ */ jsxs6("span", { className: "flex flex-wrap gap-1", children: [
|
|
2533
|
+
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" }),
|
|
2534
|
+
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "ghost", onClick: () => editing?.keepTheirs(row.id, field.key), children: "Keep theirs" })
|
|
2535
|
+
] })
|
|
2536
|
+
] })
|
|
2537
|
+
}
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
return /* @__PURE__ */ jsx10(
|
|
2541
|
+
RefusalNotice,
|
|
2542
|
+
{
|
|
2543
|
+
error: refusal2.error,
|
|
2544
|
+
className: "py-1.5",
|
|
2545
|
+
...typed ? { onKeepEditing: () => editing?.keepEditing(row.id, field.key) } : {},
|
|
2546
|
+
onDiscard: () => editing?.discard(row.id, field.key)
|
|
2547
|
+
}
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2373
2550
|
function WhoBadge({
|
|
2374
2551
|
field,
|
|
2375
2552
|
row,
|
|
@@ -2422,7 +2599,8 @@ function OpenCell({
|
|
|
2422
2599
|
draft,
|
|
2423
2600
|
onType,
|
|
2424
2601
|
onCommit,
|
|
2425
|
-
onCancel
|
|
2602
|
+
onCancel,
|
|
2603
|
+
below
|
|
2426
2604
|
}) {
|
|
2427
2605
|
const multiline = takesANewline(field);
|
|
2428
2606
|
const blurCommits = closesOnBlur(field);
|
|
@@ -2451,6 +2629,7 @@ function OpenCell({
|
|
|
2451
2629
|
className: "flex w-full min-w-[10rem] flex-col gap-1",
|
|
2452
2630
|
onDoubleClick: (event) => event.stopPropagation(),
|
|
2453
2631
|
onKeyDown: (event) => {
|
|
2632
|
+
if (event.target?.closest?.("[data-refusal-notice]")) return;
|
|
2454
2633
|
if (event.key === "Escape") {
|
|
2455
2634
|
event.preventDefault();
|
|
2456
2635
|
event.stopPropagation();
|
|
@@ -2476,7 +2655,8 @@ function OpenCell({
|
|
|
2476
2655
|
blurCommits ? /* @__PURE__ */ jsx10("span", { className: "text-[10px] text-muted-foreground", children: multiline ? "\u23CE saves \xB7 \u21E7\u23CE new line \xB7 Esc cancels \xB7 Tab next" : "\u23CE saves \xB7 Esc cancels \xB7 Tab next" }) : /* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-1", children: [
|
|
2477
2656
|
/* @__PURE__ */ jsx10(Button5, { size: "sm", onClick: () => once(() => onCommit(null)), children: "Save" }),
|
|
2478
2657
|
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "ghost", onClick: () => once(onCancel), children: "Cancel" })
|
|
2479
|
-
] })
|
|
2658
|
+
] }),
|
|
2659
|
+
below
|
|
2480
2660
|
]
|
|
2481
2661
|
}
|
|
2482
2662
|
);
|
|
@@ -3081,7 +3261,20 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3081
3261
|
" in one go."
|
|
3082
3262
|
] }) : null
|
|
3083
3263
|
] }) : null,
|
|
3084
|
-
outcome ? /* @__PURE__ */ jsx11("p", { className:
|
|
3264
|
+
outcome ? outcome.ok ? /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: outcome.message }) : (
|
|
3265
|
+
// A run that did not happen is a refusal, with a heading and a remedy,
|
|
3266
|
+
// never the host's words in red (lane REFUSAL-SWEEP).
|
|
3267
|
+
/* @__PURE__ */ jsx11(
|
|
3268
|
+
RefusalLine,
|
|
3269
|
+
{
|
|
3270
|
+
error: refusal(
|
|
3271
|
+
"internal",
|
|
3272
|
+
outcome.message,
|
|
3273
|
+
"Nothing was filled in. Try the run again in a moment."
|
|
3274
|
+
)
|
|
3275
|
+
}
|
|
3276
|
+
)
|
|
3277
|
+
) : null,
|
|
3085
3278
|
field ? null : null
|
|
3086
3279
|
] }, row.field_id);
|
|
3087
3280
|
}) });
|
|
@@ -3126,15 +3319,15 @@ function PastePreview({
|
|
|
3126
3319
|
/* @__PURE__ */ jsx12(PlanTable, { plan }),
|
|
3127
3320
|
plan.refusals.length > 0 ? /* @__PURE__ */ jsxs8("div", { "data-matrx-paste-refusals": true, className: "flex flex-col gap-1", children: [
|
|
3128
3321
|
/* @__PURE__ */ jsx12("p", { className: "text-xs font-medium", children: plan.refusals.length === 1 ? "One cell will be left alone:" : `${plan.refusals.length} cells will be left alone:` }),
|
|
3129
|
-
/* @__PURE__ */ jsx12("ul", { className: "flex flex-col gap-1", children: plan.refusals.slice(0, 12).map((
|
|
3322
|
+
/* @__PURE__ */ jsx12("ul", { className: "flex flex-col gap-1", children: plan.refusals.slice(0, 12).map((refusal2, index) => /* @__PURE__ */ jsxs8("li", { className: "text-xs text-muted-foreground", children: [
|
|
3130
3323
|
/* @__PURE__ */ jsxs8("span", { className: "font-medium", children: [
|
|
3131
3324
|
"Row ",
|
|
3132
|
-
|
|
3325
|
+
refusal2.fromLine,
|
|
3133
3326
|
", ",
|
|
3134
|
-
|
|
3327
|
+
refusal2.column
|
|
3135
3328
|
] }),
|
|
3136
3329
|
" \u2014 ",
|
|
3137
|
-
|
|
3330
|
+
refusal2.why
|
|
3138
3331
|
] }, index)) }),
|
|
3139
3332
|
plan.refusals.length > 12 ? /* @__PURE__ */ jsxs8("p", { className: "text-xs text-muted-foreground", children: [
|
|
3140
3333
|
plan.refusals.length - 12,
|
|
@@ -3162,7 +3355,13 @@ function PastePreview({
|
|
|
3162
3355
|
setAdding(group.fieldKey);
|
|
3163
3356
|
void onAddOptions(group.fieldKey, group.words).catch(
|
|
3164
3357
|
(err) => setAddFailed(
|
|
3165
|
-
|
|
3358
|
+
// The error's own text is an engineer's; the person
|
|
3359
|
+
// reads what did not happen and what to do.
|
|
3360
|
+
asRefusal(
|
|
3361
|
+
err,
|
|
3362
|
+
"Those options were not added, so the pasted words still do not match a choice.",
|
|
3363
|
+
"Try adding them again, or add them from the column's settings."
|
|
3364
|
+
)
|
|
3166
3365
|
)
|
|
3167
3366
|
).finally(() => setAdding(null));
|
|
3168
3367
|
},
|
|
@@ -3170,7 +3369,7 @@ function PastePreview({
|
|
|
3170
3369
|
}
|
|
3171
3370
|
)
|
|
3172
3371
|
] }, group.fieldKey)),
|
|
3173
|
-
addFailed ? /* @__PURE__ */ jsx12(
|
|
3372
|
+
addFailed ? /* @__PURE__ */ jsx12(RefusalLine, { error: addFailed }) : null
|
|
3174
3373
|
] }) : null,
|
|
3175
3374
|
plan.pastRightEdge > 0 ? /* @__PURE__ */ jsxs8("p", { className: "text-xs text-muted-foreground", children: [
|
|
3176
3375
|
plan.pastRightEdge,
|
|
@@ -3350,19 +3549,19 @@ function planPastedBlock(args) {
|
|
|
3350
3549
|
accepted[key] = judged.value;
|
|
3351
3550
|
}
|
|
3352
3551
|
const predicted = predictValueRefusals(args.fields, accepted);
|
|
3353
|
-
for (const
|
|
3354
|
-
const key =
|
|
3552
|
+
for (const refusal2 of predicted) {
|
|
3553
|
+
const key = refusal2.field_key;
|
|
3355
3554
|
if (key === void 0) continue;
|
|
3356
3555
|
const cell = built.find((b) => b.key === key && b.refusal === null);
|
|
3357
3556
|
if (!cell) continue;
|
|
3358
|
-
cell.refusal =
|
|
3557
|
+
cell.refusal = refusal2.message;
|
|
3359
3558
|
cell.value = null;
|
|
3360
3559
|
delete accepted[key];
|
|
3361
3560
|
refusals.push({
|
|
3362
3561
|
fromLine,
|
|
3363
3562
|
column: fieldName(byKey.get(key) ?? { key }),
|
|
3364
3563
|
raw: cell.raw,
|
|
3365
|
-
why:
|
|
3564
|
+
why: refusal2.message
|
|
3366
3565
|
});
|
|
3367
3566
|
}
|
|
3368
3567
|
cells += Object.keys(accepted).length;
|
|
@@ -4126,18 +4325,24 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4126
4325
|
size: "sm",
|
|
4127
4326
|
variant: "outline",
|
|
4128
4327
|
onClick: () => {
|
|
4328
|
+
const failed = (thrown) => setFailure(
|
|
4329
|
+
refusalFromThrown(
|
|
4330
|
+
thrown,
|
|
4331
|
+
"The spreadsheet could not be written.",
|
|
4332
|
+
"The CSV beside this button carries the same rows."
|
|
4333
|
+
)
|
|
4334
|
+
);
|
|
4335
|
+
setFailure(null);
|
|
4129
4336
|
try {
|
|
4130
|
-
|
|
4337
|
+
exportXlsx(fields.data ?? [], exported(), name).then(
|
|
4131
4338
|
(bytes) => download(
|
|
4132
4339
|
`${name}.xlsx`,
|
|
4133
4340
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
4134
4341
|
bytes
|
|
4135
4342
|
)
|
|
4136
|
-
);
|
|
4343
|
+
).catch(failed);
|
|
4137
4344
|
} catch (thrown) {
|
|
4138
|
-
|
|
4139
|
-
`The spreadsheet could not be written (${thrown instanceof Error ? thrown.message : String(thrown)}). The CSV beside this button carries the same rows.`
|
|
4140
|
-
);
|
|
4345
|
+
failed(thrown);
|
|
4141
4346
|
}
|
|
4142
4347
|
},
|
|
4143
4348
|
children: "XLSX"
|
|
@@ -4153,7 +4358,7 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4153
4358
|
human: () => exportCsv(fields.data ?? [], exported())
|
|
4154
4359
|
}
|
|
4155
4360
|
),
|
|
4156
|
-
failure ? /* @__PURE__ */ jsx14(
|
|
4361
|
+
failure ? /* @__PURE__ */ jsx14(RefusalLine, { error: failure }) : null
|
|
4157
4362
|
] });
|
|
4158
4363
|
}
|
|
4159
4364
|
|
|
@@ -4269,6 +4474,45 @@ import {
|
|
|
4269
4474
|
Separator as Separator2,
|
|
4270
4475
|
cn as cn12
|
|
4271
4476
|
} from "@ai-matrx/design-system";
|
|
4477
|
+
|
|
4478
|
+
// src/importRuleCheck.ts
|
|
4479
|
+
import { predictValueRefusals as predictValueRefusals2 } from "@ai-matrx/records/core";
|
|
4480
|
+
function checkImportRules(fields, rows, mapping) {
|
|
4481
|
+
const byKey = new Map(fields.map((f) => [f.key, f]));
|
|
4482
|
+
const verdicts = [];
|
|
4483
|
+
for (const [header, key] of Object.entries(mapping)) {
|
|
4484
|
+
const field = byKey.get(key);
|
|
4485
|
+
if (!field) continue;
|
|
4486
|
+
let refused = 0;
|
|
4487
|
+
let judged = 0;
|
|
4488
|
+
let first = null;
|
|
4489
|
+
rows.forEach((row, i) => {
|
|
4490
|
+
const raw = String(row[header] ?? "").trim();
|
|
4491
|
+
if (raw === "") return;
|
|
4492
|
+
judged += 1;
|
|
4493
|
+
const meant = valueFromPastedText(field, raw);
|
|
4494
|
+
let why = null;
|
|
4495
|
+
if ("refusal" in meant) why = meant.refusal;
|
|
4496
|
+
else {
|
|
4497
|
+
const predicted = predictValueRefusals2([field], { [key]: meant.value }).filter(
|
|
4498
|
+
(p) => p.field_key === void 0 || p.field_key === key
|
|
4499
|
+
);
|
|
4500
|
+
if (predicted.length > 0) why = predicted[0].message;
|
|
4501
|
+
}
|
|
4502
|
+
if (why !== null) {
|
|
4503
|
+
refused += 1;
|
|
4504
|
+
if (!first) first = { why, line: i + 1, raw };
|
|
4505
|
+
}
|
|
4506
|
+
});
|
|
4507
|
+
if (refused > 0 && first) {
|
|
4508
|
+
const f = first;
|
|
4509
|
+
verdicts.push({ header, field, refused, judged, firstWhy: f.why, firstLine: f.line, firstRaw: f.raw });
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
return verdicts;
|
|
4513
|
+
}
|
|
4514
|
+
|
|
4515
|
+
// src/ImportWizard.tsx
|
|
4272
4516
|
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4273
4517
|
var BATCH = 250;
|
|
4274
4518
|
var SAMPLES = 40;
|
|
@@ -4321,9 +4565,13 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4321
4565
|
const [openRun, setOpenRun] = useState13(null);
|
|
4322
4566
|
const [runRows, setRunRows] = useState13(null);
|
|
4323
4567
|
const [runProblem, setRunProblem] = useState13(null);
|
|
4568
|
+
const [runsProblem, setRunsProblem] = useState13(null);
|
|
4324
4569
|
const loadRuns = useCallback8(async () => {
|
|
4325
4570
|
const answered = await client.imports({ table_id: tableId, limit: 10 });
|
|
4326
|
-
if (answered.ok)
|
|
4571
|
+
if (answered.ok) {
|
|
4572
|
+
setRuns(answered.data);
|
|
4573
|
+
setRunsProblem(null);
|
|
4574
|
+
} else setRunsProblem(answered.error);
|
|
4327
4575
|
}, [client, tableId]);
|
|
4328
4576
|
useEffect8(() => {
|
|
4329
4577
|
void loadRuns();
|
|
@@ -4344,7 +4592,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4344
4592
|
limit: 200
|
|
4345
4593
|
});
|
|
4346
4594
|
if (!answered.ok) {
|
|
4347
|
-
setRunProblem(answered.error
|
|
4595
|
+
setRunProblem(answered.error);
|
|
4348
4596
|
return;
|
|
4349
4597
|
}
|
|
4350
4598
|
setRunRows({
|
|
@@ -4385,7 +4633,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4385
4633
|
}));
|
|
4386
4634
|
const answered = await client.importPlan({ table_id: tableId, columns });
|
|
4387
4635
|
if (!answered.ok) {
|
|
4388
|
-
setProblem(answered.error
|
|
4636
|
+
setProblem(answered.error);
|
|
4389
4637
|
setPhase("waiting");
|
|
4390
4638
|
return;
|
|
4391
4639
|
}
|
|
@@ -4402,7 +4650,14 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4402
4650
|
setMapping(next);
|
|
4403
4651
|
setPhase("ready");
|
|
4404
4652
|
} catch (thrown) {
|
|
4405
|
-
setProblem(
|
|
4653
|
+
setProblem(
|
|
4654
|
+
refusalFromThrown(
|
|
4655
|
+
thrown,
|
|
4656
|
+
"That file could not be read, so nothing was imported.",
|
|
4657
|
+
"Save it again as CSV or Excel and pick it again.",
|
|
4658
|
+
"invalid_argument"
|
|
4659
|
+
)
|
|
4660
|
+
);
|
|
4406
4661
|
setPhase("waiting");
|
|
4407
4662
|
}
|
|
4408
4663
|
},
|
|
@@ -4419,7 +4674,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4419
4674
|
setPhase("declaring");
|
|
4420
4675
|
const made = await client.importDeclareColumns({ table_id: tableId, rows, mapping });
|
|
4421
4676
|
if (!made.ok) {
|
|
4422
|
-
setProblem(made.error
|
|
4677
|
+
setProblem(made.error);
|
|
4423
4678
|
setPhase("ready");
|
|
4424
4679
|
return;
|
|
4425
4680
|
}
|
|
@@ -4438,7 +4693,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4438
4693
|
policy: { on_duplicate: onDuplicate, unmapped }
|
|
4439
4694
|
});
|
|
4440
4695
|
if (!opened.ok) {
|
|
4441
|
-
setProblem(opened.error
|
|
4696
|
+
setProblem(opened.error);
|
|
4442
4697
|
setPhase("ready");
|
|
4443
4698
|
return;
|
|
4444
4699
|
}
|
|
@@ -4457,7 +4712,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4457
4712
|
const batch = rows.slice(at, at + take2);
|
|
4458
4713
|
const answered = await client.importRows({ import_id: importId, rows: batch, mapping });
|
|
4459
4714
|
if (!answered.ok) {
|
|
4460
|
-
setProblem(answered.error
|
|
4715
|
+
setProblem(answered.error);
|
|
4461
4716
|
setOutcomes([...collected]);
|
|
4462
4717
|
setLedger({ ...told, final: false });
|
|
4463
4718
|
setPhase("ready");
|
|
@@ -4476,7 +4731,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4476
4731
|
}
|
|
4477
4732
|
const finished = await client.importFinish({ import_id: importId, unmapped });
|
|
4478
4733
|
if (!finished.ok) {
|
|
4479
|
-
setProblem(finished.error
|
|
4734
|
+
setProblem(finished.error);
|
|
4480
4735
|
setPhase("done");
|
|
4481
4736
|
return;
|
|
4482
4737
|
}
|
|
@@ -4514,6 +4769,11 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4514
4769
|
const titleMissing = Boolean(titleKey) && titleMappedTo === null;
|
|
4515
4770
|
const titleFieldLabel = (titleKey ? plan?.fields.find((f) => f.key === titleKey)?.label : null) || (titleKey ? declared.find((f) => f.key === titleKey)?.label : null) || "the name column";
|
|
4516
4771
|
const busy = phase === "reading" || phase === "planning" || phase === "declaring" || phase === "writing";
|
|
4772
|
+
const ruleVerdicts = useMemo10(() => checkImportRules(declared, rows, mapping), [declared, rows, mapping]);
|
|
4773
|
+
const ruleVerdictOf = useMemo10(() => new Map(ruleVerdicts.map((v) => [v.header, v])), [ruleVerdicts]);
|
|
4774
|
+
const refusedByRules = ruleVerdicts.reduce((n, v) => n + v.refused, 0);
|
|
4775
|
+
const [goAheadAnyway, setGoAheadAnyway] = useState13(false);
|
|
4776
|
+
useEffect8(() => setGoAheadAnyway(false), [ruleVerdicts]);
|
|
4517
4777
|
return /* @__PURE__ */ jsxs12("div", { className: cn12("flex min-w-0 flex-col gap-2 text-xs", className), children: [
|
|
4518
4778
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
4519
4779
|
/* @__PURE__ */ jsx16("span", { className: "font-medium", children: "Import" }),
|
|
@@ -4541,7 +4801,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4541
4801
|
} : {}
|
|
4542
4802
|
}
|
|
4543
4803
|
) : null,
|
|
4544
|
-
problem ? /* @__PURE__ */ jsx16(
|
|
4804
|
+
problem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: problem }) : null,
|
|
4545
4805
|
phase === "reading" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
|
|
4546
4806
|
phase === "planning" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
|
|
4547
4807
|
plan && parsed ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
@@ -4638,7 +4898,28 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4638
4898
|
column.unit ? /* @__PURE__ */ jsx16("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
|
|
4639
4899
|
/* @__PURE__ */ jsx16("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
|
|
4640
4900
|
column.ambiguous ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
|
|
4641
|
-
column.collides_with ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null
|
|
4901
|
+
column.collides_with ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null,
|
|
4902
|
+
ruleVerdictOf.get(column.header) ? (() => {
|
|
4903
|
+
const v = ruleVerdictOf.get(column.header);
|
|
4904
|
+
return /* @__PURE__ */ jsxs12("div", { "data-import-rule-refusal": column.header, className: "mt-1", children: [
|
|
4905
|
+
/* @__PURE__ */ jsx16(
|
|
4906
|
+
RefusalLine,
|
|
4907
|
+
{
|
|
4908
|
+
error: refusal(
|
|
4909
|
+
"refused_by_rule",
|
|
4910
|
+
`${v.refused} of ${v.judged} row${v.judged === 1 ? "" : "s"} would be refused by ${fieldName(v.field)}'s rules. ${v.firstWhy}`,
|
|
4911
|
+
"Send this column somewhere else, fix those rows in your file, or import the rest and they are listed afterwards."
|
|
4912
|
+
)
|
|
4913
|
+
}
|
|
4914
|
+
),
|
|
4915
|
+
/* @__PURE__ */ jsxs12("p", { className: "text-muted-foreground", "data-import-rule-sample": "", children: [
|
|
4916
|
+
"Line ",
|
|
4917
|
+
v.firstLine,
|
|
4918
|
+
" in your file: ",
|
|
4919
|
+
v.firstRaw
|
|
4920
|
+
] })
|
|
4921
|
+
] });
|
|
4922
|
+
})() : null
|
|
4642
4923
|
] }),
|
|
4643
4924
|
/* @__PURE__ */ jsx16("span", { className: "truncate pt-1.5 text-muted-foreground", children: (column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ") })
|
|
4644
4925
|
]
|
|
@@ -4702,12 +4983,34 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4702
4983
|
)
|
|
4703
4984
|
] })
|
|
4704
4985
|
] }),
|
|
4986
|
+
ruleVerdicts.length > 0 ? /* @__PURE__ */ jsx16(
|
|
4987
|
+
RefusalNotice,
|
|
4988
|
+
{
|
|
4989
|
+
error: refusal(
|
|
4990
|
+
"refused_by_rule",
|
|
4991
|
+
`${refusedByRules} value${refusedByRules === 1 ? "" : "s"} in this file would be refused by ${ruleVerdicts.length === 1 ? `the rules of ${fieldName(ruleVerdicts[0].field)}` : `the rules of ${ruleVerdicts.length} columns`} before anything is written. Nothing has been written yet.`,
|
|
4992
|
+
"Change where those columns go, or fix the file and pick it again. To go ahead, the rest is written and every refused row is listed afterwards."
|
|
4993
|
+
),
|
|
4994
|
+
actions: /* @__PURE__ */ jsxs12("label", { className: "mt-1 flex items-center gap-1.5", "data-import-go-ahead": "", children: [
|
|
4995
|
+
/* @__PURE__ */ jsx16(
|
|
4996
|
+
"input",
|
|
4997
|
+
{
|
|
4998
|
+
type: "checkbox",
|
|
4999
|
+
checked: goAheadAnyway,
|
|
5000
|
+
disabled: busy,
|
|
5001
|
+
onChange: (e) => setGoAheadAnyway(e.target.checked)
|
|
5002
|
+
}
|
|
5003
|
+
),
|
|
5004
|
+
"Import the rest anyway"
|
|
5005
|
+
] })
|
|
5006
|
+
}
|
|
5007
|
+
) : null,
|
|
4705
5008
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
4706
5009
|
/* @__PURE__ */ jsx16(
|
|
4707
5010
|
Button11,
|
|
4708
5011
|
{
|
|
4709
5012
|
size: "sm",
|
|
4710
|
-
disabled: busy || !rights.write || rows.length === 0 || titleMissing,
|
|
5013
|
+
disabled: busy || !rights.write || rows.length === 0 || titleMissing || ruleVerdicts.length > 0 && !goAheadAnyway,
|
|
4711
5014
|
onClick: () => void run(),
|
|
4712
5015
|
children: phase === "declaring" ? "Adding the columns\u2026" : phase === "writing" ? `Writing\u2026 ${progress} of ${rows.length}` : `Import ${rows.length} row${rows.length === 1 ? "" : "s"}`
|
|
4713
5016
|
}
|
|
@@ -4762,7 +5065,12 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4762
5065
|
] }) }),
|
|
4763
5066
|
/* @__PURE__ */ jsx16("tbody", { children: interesting.slice(0, 200).map((o) => /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
|
|
4764
5067
|
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: o.row }),
|
|
4765
|
-
/* @__PURE__ */ jsx16("td", { className:
|
|
5068
|
+
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: o.outcome === "refused" ? /* @__PURE__ */ jsx16(
|
|
5069
|
+
RefusalLine,
|
|
5070
|
+
{
|
|
5071
|
+
error: refusal("refused_by_rule", o.reason, "Fix this line in your file and import it again.")
|
|
5072
|
+
}
|
|
5073
|
+
) : o.outcome === "duplicate" ? /* @__PURE__ */ jsx16("span", { className: "text-muted-foreground", children: o.reason }) : "written" }),
|
|
4766
5074
|
/* @__PURE__ */ jsx16("td", { className: "max-w-96 px-2 py-1 text-muted-foreground", children: o.outcome === "refused" ? Object.entries(o.source).map(([k, v]) => `${k}: ${String(v ?? "")}`).join(" \xB7 ") : "" })
|
|
4767
5075
|
] }, `${o.outcome}-${o.row}`)) })
|
|
4768
5076
|
] }) }) : null,
|
|
@@ -4785,6 +5093,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4785
5093
|
] }) : null
|
|
4786
5094
|
] }) : null
|
|
4787
5095
|
] }) : null,
|
|
5096
|
+
runsProblem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: runsProblem }) : null,
|
|
4788
5097
|
runs && runs.length > 0 ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
4789
5098
|
/* @__PURE__ */ jsx16(Separator2, {}),
|
|
4790
5099
|
/* @__PURE__ */ jsx16("p", { className: "font-medium", children: "Imports into this table" }),
|
|
@@ -4817,7 +5126,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4817
5126
|
}
|
|
4818
5127
|
) : null
|
|
4819
5128
|
] }),
|
|
4820
|
-
openRun === r.import_id ? runProblem ? /* @__PURE__ */ jsx16(
|
|
5129
|
+
openRun === r.import_id ? runProblem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: runProblem }) : runRows ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
4821
5130
|
runRows.note ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: runRows.note }) : null,
|
|
4822
5131
|
/* @__PURE__ */ jsx16("div", { className: "max-h-64 overflow-auto rounded border", children: /* @__PURE__ */ jsxs12("table", { className: "w-full", children: [
|
|
4823
5132
|
/* @__PURE__ */ jsx16("thead", { className: "sticky top-0 bg-muted", children: /* @__PURE__ */ jsxs12("tr", { children: [
|
|
@@ -4829,7 +5138,16 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4829
5138
|
const source = row.source ?? {};
|
|
4830
5139
|
return /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
|
|
4831
5140
|
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: String(row.row ?? "") }),
|
|
4832
|
-
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1
|
|
5141
|
+
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: /* @__PURE__ */ jsx16(
|
|
5142
|
+
RefusalLine,
|
|
5143
|
+
{
|
|
5144
|
+
error: refusal(
|
|
5145
|
+
"refused_by_rule",
|
|
5146
|
+
String(row.reason ?? "This row was refused."),
|
|
5147
|
+
"Fix this line in your file and import it again."
|
|
5148
|
+
)
|
|
5149
|
+
}
|
|
5150
|
+
) }),
|
|
4833
5151
|
/* @__PURE__ */ jsx16("td", { className: "max-w-96 px-2 py-1 text-muted-foreground", children: Object.entries(source).map(([k, v]) => `${k}: ${String(v ?? "")}`).join(" \xB7 ") })
|
|
4834
5152
|
] }, `${r.import_id}-${String(row.row ?? i)}`);
|
|
4835
5153
|
}) })
|
|
@@ -5067,7 +5385,9 @@ function keyFor(label) {
|
|
|
5067
5385
|
if (token === "") return "";
|
|
5068
5386
|
return /^[a-z]/.test(token) ? token : `f_${token}`;
|
|
5069
5387
|
}
|
|
5070
|
-
var
|
|
5388
|
+
var COLUMN_DID_NOT_LAND_SENTENCE = "The store accepted this column and the table does not have it yet. Nothing was lost \u2014 your record values are untouched \u2014 but the column is not there, so this panel is staying open.";
|
|
5389
|
+
var COLUMN_DID_NOT_LAND_REMEDY = "Press Save again; if it keeps happening, tell us, because the store and the table disagree and that is ours to fix.";
|
|
5390
|
+
var COLUMN_DID_NOT_LAND = `${COLUMN_DID_NOT_LAND_SENTENCE} ${COLUMN_DID_NOT_LAND_REMEDY}`;
|
|
5071
5391
|
function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }) {
|
|
5072
5392
|
const table = useTable4(tableId);
|
|
5073
5393
|
const fields = useFields6(tableId);
|
|
@@ -5367,14 +5687,20 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5367
5687
|
] }, `${rule.kind}-${index}`))
|
|
5368
5688
|
] }),
|
|
5369
5689
|
mutation.error ? /* @__PURE__ */ jsx18(RefusalNotice, { error: mutation.error }) : null,
|
|
5370
|
-
missing ? /* @__PURE__ */ jsx18("p", { className: "text-[11px] text-
|
|
5371
|
-
notLanded ? /* @__PURE__ */ jsx18(
|
|
5690
|
+
missing ? /* @__PURE__ */ jsx18("p", { id: "field-editor-missing", className: "text-[11px] text-muted-foreground", "data-field-editor-missing": "", children: missing }) : null,
|
|
5691
|
+
notLanded ? /* @__PURE__ */ jsx18(
|
|
5692
|
+
RefusalNotice,
|
|
5693
|
+
{
|
|
5694
|
+
error: refusal("internal", COLUMN_DID_NOT_LAND_SENTENCE, COLUMN_DID_NOT_LAND_REMEDY)
|
|
5695
|
+
}
|
|
5696
|
+
) : null,
|
|
5372
5697
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5373
5698
|
/* @__PURE__ */ jsx18(
|
|
5374
5699
|
Button13,
|
|
5375
5700
|
{
|
|
5376
5701
|
size: "sm",
|
|
5377
5702
|
disabled: mutation.saving || missing !== null,
|
|
5703
|
+
...missing ? { "aria-describedby": "field-editor-missing" } : {},
|
|
5378
5704
|
onClick: () => void save(),
|
|
5379
5705
|
children: mutation.saving ? "Saving\u2026" : field ? EDIT_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
|
|
5380
5706
|
}
|
|
@@ -6357,8 +6683,8 @@ function TableSettings({
|
|
|
6357
6683
|
/* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "ghost", onClick: () => setAskingToRemove(null), children: "Keep it" })
|
|
6358
6684
|
] }) : null,
|
|
6359
6685
|
/* @__PURE__ */ jsxs17("ul", { className: "divide-y rounded border", children: [
|
|
6360
|
-
rows.map((field, index) => /* @__PURE__ */ jsxs17("li", { className: "flex items-center gap-2 px-2 py-1.5", children: [
|
|
6361
|
-
/* @__PURE__ */ jsx21("span", { className: "min-w-
|
|
6686
|
+
rows.map((field, index) => /* @__PURE__ */ jsxs17("li", { "data-settings-field": "", className: "flex flex-wrap items-center gap-x-2 gap-y-1 px-2 py-1.5", children: [
|
|
6687
|
+
/* @__PURE__ */ jsx21("span", { "data-settings-field-name": "", className: "min-w-[8rem] flex-1 break-words", children: fieldName(field) }),
|
|
6362
6688
|
/* @__PURE__ */ jsx21(Badge6, { variant: "outline", className: "text-[10px] font-normal", children: fieldTypeLabel(field) }),
|
|
6363
6689
|
field.required ? /* @__PURE__ */ jsx21("span", { className: "text-[10px] text-destructive", children: "required" }) : null,
|
|
6364
6690
|
/* @__PURE__ */ jsx21(
|
|
@@ -6415,9 +6741,9 @@ function TableSettings({
|
|
|
6415
6741
|
/* @__PURE__ */ jsx21("span", { className: "font-medium", children: "Proposed" }),
|
|
6416
6742
|
/* @__PURE__ */ jsx21("span", { className: "text-muted-foreground", children: proposals?.length ?? 0 })
|
|
6417
6743
|
] }),
|
|
6418
|
-
proposals === void 0 ? /* @__PURE__ */ jsx21("p", { className: "text-muted-foreground", children: "Nothing is listing proposals here yet: the store has no `custom.field_propose` door, so this panel would be inventing an empty queue. When the door lands, its rows appear in this list with the same accept and reject." }) : proposals.length === 0 ? /* @__PURE__ */ jsx21("p", { className: "text-muted-foreground", children: "No field is waiting for a decision." }) : /* @__PURE__ */ jsx21("ul", { className: "divide-y rounded border", children: proposals.map((proposal) => /* @__PURE__ */ jsxs17("li", { className: "flex items-center gap-2 px-2 py-1.5", children: [
|
|
6419
|
-
/* @__PURE__ */ jsx21("span", { className: "min-w-
|
|
6420
|
-
/* @__PURE__ */ jsx21("span", { className: "min-w-
|
|
6744
|
+
proposals === void 0 ? /* @__PURE__ */ jsx21("p", { className: "text-muted-foreground", children: "Nothing is listing proposals here yet: the store has no `custom.field_propose` door, so this panel would be inventing an empty queue. When the door lands, its rows appear in this list with the same accept and reject." }) : proposals.length === 0 ? /* @__PURE__ */ jsx21("p", { className: "text-muted-foreground", children: "No field is waiting for a decision." }) : /* @__PURE__ */ jsx21("ul", { className: "divide-y rounded border", children: proposals.map((proposal) => /* @__PURE__ */ jsxs17("li", { className: "flex flex-wrap items-center gap-x-2 gap-y-1 px-2 py-1.5", children: [
|
|
6745
|
+
/* @__PURE__ */ jsx21("span", { className: "min-w-[8rem] flex-1 break-words", children: proposal.field.label || proposal.field.key }),
|
|
6746
|
+
/* @__PURE__ */ jsx21("span", { className: "min-w-[8rem] flex-1 break-words text-muted-foreground", children: proposal.why }),
|
|
6421
6747
|
/* @__PURE__ */ jsx21(Badge6, { variant: "secondary", className: "text-[10px] font-normal", children: proposal.proposed_by }),
|
|
6422
6748
|
/* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "outline", onClick: () => onAcceptProposal?.(proposal), children: "Accept" }),
|
|
6423
6749
|
/* @__PURE__ */ jsx21(Button16, { size: "sm", variant: "ghost", onClick: () => onRejectProposal?.(proposal), children: "Reject" })
|
|
@@ -6786,6 +7112,8 @@ function RecordForm({
|
|
|
6786
7112
|
const mutation = useRecordMutation();
|
|
6787
7113
|
const [draft, setDraft] = useState21({});
|
|
6788
7114
|
const [touched, setTouched] = useState21(false);
|
|
7115
|
+
const [changed, setChanged] = useState21({});
|
|
7116
|
+
const [answered, setAnswered] = useState21(null);
|
|
6789
7117
|
const loaded = useRecordVersion(recordId ?? null);
|
|
6790
7118
|
const loadedVersion = loaded.version;
|
|
6791
7119
|
useEffect12(() => {
|
|
@@ -6806,8 +7134,38 @@ function RecordForm({
|
|
|
6806
7134
|
);
|
|
6807
7135
|
if (fields.error) return /* @__PURE__ */ jsx23(RefusalNotice, { error: fields.error, className });
|
|
6808
7136
|
if (existing.error) return /* @__PURE__ */ jsx23(RefusalNotice, { error: existing.error, className });
|
|
7137
|
+
const storeSaid = mutation.error && mutation.error !== answered ? mutation.error : null;
|
|
7138
|
+
const fieldProblems = predicted.filter((p) => p.field_key);
|
|
7139
|
+
const problemsShown = (key) => touched || changed[key] ? fieldProblems.filter((p) => p.field_key === key) : [];
|
|
7140
|
+
const storeRefusalIsOnAField = storeSaid !== null && !mutation.conflict && storeSaid.code === "refused_by_rule" && (fields.data ?? []).some((f) => problemsShown(f.key).length > 0);
|
|
7141
|
+
const stored = existing.data?.document;
|
|
7142
|
+
const focusField = (field) => {
|
|
7143
|
+
if (!field) return;
|
|
7144
|
+
globalThis.document?.getElementById(`field-${field.id}`)?.focus();
|
|
7145
|
+
};
|
|
7146
|
+
const keepEditing = (field) => {
|
|
7147
|
+
if (storeSaid) setAnswered(storeSaid);
|
|
7148
|
+
const first = field ?? (fields.data ?? []).find((f) => problemsShown(f.key).length > 0) ?? (fields.data ?? [])[0];
|
|
7149
|
+
focusField(first);
|
|
7150
|
+
};
|
|
7151
|
+
const discard = (field) => {
|
|
7152
|
+
if (storeSaid) setAnswered(storeSaid);
|
|
7153
|
+
if (field) {
|
|
7154
|
+
setDraft((d) => ({ ...d, [field.key]: stored ? stored[field.key] : void 0 }));
|
|
7155
|
+
setChanged((c) => {
|
|
7156
|
+
const next = { ...c };
|
|
7157
|
+
delete next[field.key];
|
|
7158
|
+
return next;
|
|
7159
|
+
});
|
|
7160
|
+
return;
|
|
7161
|
+
}
|
|
7162
|
+
setDraft(stored ? { ...stored } : {});
|
|
7163
|
+
setChanged({});
|
|
7164
|
+
setTouched(false);
|
|
7165
|
+
};
|
|
6809
7166
|
const save = async () => {
|
|
6810
7167
|
setTouched(true);
|
|
7168
|
+
setAnswered(null);
|
|
6811
7169
|
if (recordId) {
|
|
6812
7170
|
const version = loadedVersion;
|
|
6813
7171
|
const moved = await mutation.update({
|
|
@@ -6844,16 +7202,27 @@ function RecordForm({
|
|
|
6844
7202
|
field,
|
|
6845
7203
|
value: draft[field.key],
|
|
6846
7204
|
document: existing.data?.document,
|
|
6847
|
-
problems:
|
|
6848
|
-
onChange: (value) =>
|
|
7205
|
+
problems: problemsShown(field.key),
|
|
7206
|
+
onChange: (value) => {
|
|
7207
|
+
setDraft((d) => ({ ...d, [field.key]: value }));
|
|
7208
|
+
setChanged((c) => c[field.key] ? c : { ...c, [field.key]: true });
|
|
7209
|
+
},
|
|
7210
|
+
onKeepEditing: () => keepEditing(field),
|
|
7211
|
+
onDiscard: () => discard(field)
|
|
6849
7212
|
},
|
|
6850
7213
|
field.id
|
|
6851
7214
|
)) }),
|
|
6852
|
-
touched ? predicted.filter((p) => !p.field_key).map((p) => /* @__PURE__ */ jsx23(
|
|
6853
|
-
|
|
7215
|
+
touched ? predicted.filter((p) => !p.field_key).map((p) => /* @__PURE__ */ jsx23(
|
|
7216
|
+
RefusalNotice,
|
|
7217
|
+
{
|
|
7218
|
+
error: { code: "refused_by_rule", message: p.message }
|
|
7219
|
+
},
|
|
7220
|
+
p.message
|
|
7221
|
+
)) : null,
|
|
7222
|
+
storeSaid && !storeRefusalIsOnAField ? /* @__PURE__ */ jsx23(
|
|
6854
7223
|
RefusalNotice,
|
|
6855
7224
|
{
|
|
6856
|
-
error:
|
|
7225
|
+
error: storeSaid,
|
|
6857
7226
|
...mutation.conflict ? {
|
|
6858
7227
|
actions: /* @__PURE__ */ jsxs19("div", { className: "mt-1 space-y-1", children: [
|
|
6859
7228
|
Object.entries(mutation.conflict.contested_fields).map(([key, theirs]) => {
|
|
@@ -6874,14 +7243,16 @@ function RecordForm({
|
|
|
6874
7243
|
size: "sm",
|
|
6875
7244
|
variant: "outline",
|
|
6876
7245
|
onClick: () => {
|
|
7246
|
+
if (mutation.conflict) loaded.note(mutation.conflict.current_version);
|
|
6877
7247
|
existing.reload();
|
|
6878
7248
|
mutation.clearConflict();
|
|
7249
|
+
setAnswered(storeSaid);
|
|
6879
7250
|
},
|
|
6880
7251
|
children: "Take theirs and start again"
|
|
6881
7252
|
}
|
|
6882
7253
|
)
|
|
6883
7254
|
] })
|
|
6884
|
-
} : {}
|
|
7255
|
+
} : { onKeepEditing: () => keepEditing(), onDiscard: () => discard() }
|
|
6885
7256
|
}
|
|
6886
7257
|
) : null,
|
|
6887
7258
|
/* @__PURE__ */ jsx23(Separator7, {}),
|
|
@@ -6898,13 +7269,23 @@ function FieldRow({
|
|
|
6898
7269
|
value,
|
|
6899
7270
|
document: document2,
|
|
6900
7271
|
problems,
|
|
6901
|
-
onChange
|
|
7272
|
+
onChange,
|
|
7273
|
+
onKeepEditing,
|
|
7274
|
+
onDiscard
|
|
6902
7275
|
}) {
|
|
6903
7276
|
const id = `field-${field.id}`;
|
|
6904
7277
|
return /* @__PURE__ */ jsxs19("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
6905
7278
|
/* @__PURE__ */ jsx23(FieldLabel, { field, htmlFor: id, children: /* @__PURE__ */ jsx23(ProvenanceBadge, { document: document2, fieldKey: field.key }) }),
|
|
6906
7279
|
/* @__PURE__ */ jsx23(FieldControl, { field, value, onChange, id }),
|
|
6907
|
-
problems.map((problem) => /* @__PURE__ */ jsx23(
|
|
7280
|
+
problems.map((problem, index) => /* @__PURE__ */ jsx23(
|
|
7281
|
+
RefusalNotice,
|
|
7282
|
+
{
|
|
7283
|
+
error: { code: "refused_by_rule", message: problem.message },
|
|
7284
|
+
className: "border-0 p-0",
|
|
7285
|
+
...index === 0 ? { onKeepEditing, onDiscard } : {}
|
|
7286
|
+
},
|
|
7287
|
+
problem.message
|
|
7288
|
+
))
|
|
6908
7289
|
] });
|
|
6909
7290
|
}
|
|
6910
7291
|
function asWords(value) {
|
|
@@ -6915,7 +7296,7 @@ function asWords(value) {
|
|
|
6915
7296
|
}
|
|
6916
7297
|
|
|
6917
7298
|
// src/ShareControl.tsx
|
|
6918
|
-
import { useState as useState22 } from "react";
|
|
7299
|
+
import { useEffect as useEffect13, useState as useState22 } from "react";
|
|
6919
7300
|
import { Button as Button18 } from "@ai-matrx/design-system";
|
|
6920
7301
|
import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
6921
7302
|
function useCanShare() {
|
|
@@ -6930,12 +7311,16 @@ function ShareControl({
|
|
|
6930
7311
|
subjectId,
|
|
6931
7312
|
name,
|
|
6932
7313
|
may,
|
|
7314
|
+
initiallyOpen = false,
|
|
6933
7315
|
size = "sm",
|
|
6934
7316
|
variant = "ghost",
|
|
6935
7317
|
className
|
|
6936
7318
|
}) {
|
|
6937
7319
|
const host = useRecordsUi();
|
|
6938
|
-
const [open, setOpen] = useState22(
|
|
7320
|
+
const [open, setOpen] = useState22(initiallyOpen);
|
|
7321
|
+
useEffect13(() => {
|
|
7322
|
+
if (initiallyOpen) setOpen(true);
|
|
7323
|
+
}, [initiallyOpen]);
|
|
6939
7324
|
const asked = useRecordRights(may === void 0 ? subjectId : null);
|
|
6940
7325
|
const mayShare = may ?? asked.share;
|
|
6941
7326
|
if (!host.share) return null;
|
|
@@ -7067,6 +7452,7 @@ function computedInPlainWords(row, field) {
|
|
|
7067
7452
|
}
|
|
7068
7453
|
|
|
7069
7454
|
// src/views.ts
|
|
7455
|
+
import { asWriteConflict as asWriteConflict2 } from "@ai-matrx/records/core";
|
|
7070
7456
|
var VIEW_LAYOUTS = ["grid", "kanban", "calendar", "gallery"];
|
|
7071
7457
|
var LAYOUT_LABEL = {
|
|
7072
7458
|
grid: "Grid",
|
|
@@ -7190,6 +7576,18 @@ function viewFromRecord(row) {
|
|
|
7190
7576
|
presentation: parseGridPresentation(data["presentation"])
|
|
7191
7577
|
};
|
|
7192
7578
|
}
|
|
7579
|
+
async function saveViewPatch(client, viewId, document2, expectedVersion) {
|
|
7580
|
+
const send = (expected) => client.recordUpdate({
|
|
7581
|
+
record_id: viewId,
|
|
7582
|
+
patch: document2,
|
|
7583
|
+
...expected === null ? {} : { expectedVersion: expected }
|
|
7584
|
+
});
|
|
7585
|
+
const first = await send(expectedVersion);
|
|
7586
|
+
if (first.ok) return first;
|
|
7587
|
+
const conflict = asWriteConflict2(first.error, viewId);
|
|
7588
|
+
if (!conflict) return first;
|
|
7589
|
+
return send(conflict.current_version);
|
|
7590
|
+
}
|
|
7193
7591
|
function viewPatchDocument(patch) {
|
|
7194
7592
|
const out = {};
|
|
7195
7593
|
if (patch.name !== void 0) out["name"] = patch.name;
|
|
@@ -7217,12 +7615,12 @@ function parseSorts(raw) {
|
|
|
7217
7615
|
}
|
|
7218
7616
|
|
|
7219
7617
|
// src/ViewSwitcher.tsx
|
|
7220
|
-
import { useEffect as
|
|
7618
|
+
import { useEffect as useEffect15, useMemo as useMemo18, useState as useState25 } from "react";
|
|
7221
7619
|
import { useFields as useFields12, useRecords as useRecords5, useRecordsClient as useRecordsClient13 } from "@ai-matrx/records/react";
|
|
7222
7620
|
import { Button as Button21, Skeleton as Skeleton7, cn as cn21 } from "@ai-matrx/design-system";
|
|
7223
7621
|
|
|
7224
7622
|
// src/Pipeline.tsx
|
|
7225
|
-
import { useCallback as useCallback10, useEffect as
|
|
7623
|
+
import { useCallback as useCallback10, useEffect as useEffect14, useMemo as useMemo17, useRef as useRef6, useState as useState24 } from "react";
|
|
7226
7624
|
import {
|
|
7227
7625
|
mayDrag,
|
|
7228
7626
|
useFields as useFields11,
|
|
@@ -7271,7 +7669,7 @@ function PipelineBoard({
|
|
|
7271
7669
|
const [dragging, setDragging] = useState24(null);
|
|
7272
7670
|
const [over, setOver] = useState24(null);
|
|
7273
7671
|
const alive = useRef6(true);
|
|
7274
|
-
|
|
7672
|
+
useEffect14(() => {
|
|
7275
7673
|
alive.current = true;
|
|
7276
7674
|
return () => {
|
|
7277
7675
|
alive.current = false;
|
|
@@ -7295,7 +7693,7 @@ function PipelineBoard({
|
|
|
7295
7693
|
setWaiting(held.ok ? new Map((held.data ?? []).map((p) => [p.record_id, p])) : /* @__PURE__ */ new Map());
|
|
7296
7694
|
setColumns(board.ok ? board.data ?? [] : []);
|
|
7297
7695
|
}, [client, tableId, measure]);
|
|
7298
|
-
|
|
7696
|
+
useEffect14(() => {
|
|
7299
7697
|
void reload();
|
|
7300
7698
|
}, [reload]);
|
|
7301
7699
|
const stageKey = definition?.stage_field ?? null;
|
|
@@ -7517,10 +7915,22 @@ function Held({
|
|
|
7517
7915
|
);
|
|
7518
7916
|
}
|
|
7519
7917
|
if (pending.kind === "refused") {
|
|
7520
|
-
return
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7918
|
+
return (
|
|
7919
|
+
// THE STAGE GATE SAID NO: the one refusal surface, heading and remedy both,
|
|
7920
|
+
// never its sentence alone in a red box (lane REFUSAL-SWEEP).
|
|
7921
|
+
/* @__PURE__ */ jsx26(
|
|
7922
|
+
RefusalNotice,
|
|
7923
|
+
{
|
|
7924
|
+
className: "mt-1",
|
|
7925
|
+
error: refusal(
|
|
7926
|
+
"refused_by_rule",
|
|
7927
|
+
pending.verdict.why,
|
|
7928
|
+
"The card stays where it was. Fill in what this stage asks for, then move it again."
|
|
7929
|
+
),
|
|
7930
|
+
actions: /* @__PURE__ */ jsx26(Button20, { size: "sm", variant: "ghost", className: "mt-1 h-6", onClick: onCancel, children: "Close" })
|
|
7931
|
+
}
|
|
7932
|
+
)
|
|
7933
|
+
);
|
|
7524
7934
|
}
|
|
7525
7935
|
if (pending.kind === "approval") {
|
|
7526
7936
|
return /* @__PURE__ */ jsxs22("div", { className: "mt-1 flex flex-col gap-1 rounded border p-2 text-xs", children: [
|
|
@@ -7653,7 +8063,7 @@ function useViewRecords(view, pageSize = 200, filter) {
|
|
|
7653
8063
|
error: null
|
|
7654
8064
|
});
|
|
7655
8065
|
const ruleId = view.ruleId ?? null;
|
|
7656
|
-
|
|
8066
|
+
useEffect15(() => {
|
|
7657
8067
|
if (!ruleId) return;
|
|
7658
8068
|
let cancelled = false;
|
|
7659
8069
|
setRuled({ rows: [], loading: true, error: null });
|
|
@@ -7717,7 +8127,7 @@ function ViewSwitcher({
|
|
|
7717
8127
|
}) {
|
|
7718
8128
|
const [layout, setLayout] = useState25(view.layout);
|
|
7719
8129
|
const [local, setLocal] = useState25({});
|
|
7720
|
-
|
|
8130
|
+
useEffect15(() => {
|
|
7721
8131
|
setLayout(view.layout);
|
|
7722
8132
|
setLocal({});
|
|
7723
8133
|
}, [view.layout, view.name, view.subject]);
|
|
@@ -7785,7 +8195,7 @@ function useStageField(tableId) {
|
|
|
7785
8195
|
asked: false,
|
|
7786
8196
|
key: null
|
|
7787
8197
|
});
|
|
7788
|
-
|
|
8198
|
+
useEffect15(() => {
|
|
7789
8199
|
let cancelled = false;
|
|
7790
8200
|
setStage({ asked: false, key: null });
|
|
7791
8201
|
void client.tableStageField({ table_id: tableId }).then((r) => {
|
|
@@ -8054,7 +8464,7 @@ function Card2({
|
|
|
8054
8464
|
}
|
|
8055
8465
|
|
|
8056
8466
|
// src/ArchivedView.tsx
|
|
8057
|
-
import { useCallback as useCallback11, useEffect as
|
|
8467
|
+
import { useCallback as useCallback11, useEffect as useEffect16, useState as useState26 } from "react";
|
|
8058
8468
|
import {
|
|
8059
8469
|
ARCHIVE_LANES,
|
|
8060
8470
|
ARCHIVE_LANE_LABEL,
|
|
@@ -8091,8 +8501,8 @@ function ArchivedView({
|
|
|
8091
8501
|
const [loading, setLoading] = useState26(true);
|
|
8092
8502
|
const [error, setError] = useState26(null);
|
|
8093
8503
|
const [restoring, setRestoring] = useState26(null);
|
|
8094
|
-
const [
|
|
8095
|
-
|
|
8504
|
+
const [refusal2, setRefusal] = useState26(null);
|
|
8505
|
+
useEffect16(() => {
|
|
8096
8506
|
if (laneFromHost) setLane(laneFromHost);
|
|
8097
8507
|
}, [laneFromHost]);
|
|
8098
8508
|
const read = useCallback11(async () => {
|
|
@@ -8107,7 +8517,7 @@ function ArchivedView({
|
|
|
8107
8517
|
}
|
|
8108
8518
|
setLoading(false);
|
|
8109
8519
|
}, [client, tableId, lane, pageSize]);
|
|
8110
|
-
|
|
8520
|
+
useEffect16(() => {
|
|
8111
8521
|
void read();
|
|
8112
8522
|
}, [read]);
|
|
8113
8523
|
const pick = (next) => {
|
|
@@ -8168,7 +8578,7 @@ function ArchivedView({
|
|
|
8168
8578
|
/* @__PURE__ */ jsxs24("div", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
8169
8579
|
/* @__PURE__ */ jsx28("span", { className: "truncate text-sm", children: recordName(row.document, titleKey) }),
|
|
8170
8580
|
/* @__PURE__ */ jsx28("span", { className: "truncate text-xs text-muted-foreground", "data-testid": "archived-who", children: archivedByLine(row) }),
|
|
8171
|
-
|
|
8581
|
+
refusal2 && refusal2.recordId === row.id ? /* @__PURE__ */ jsx28(RefusalNotice, { error: refusal2.error, className: "mt-2" }) : null
|
|
8172
8582
|
] }),
|
|
8173
8583
|
mayRestore ? /* @__PURE__ */ jsx28(
|
|
8174
8584
|
Button22,
|
|
@@ -8189,7 +8599,7 @@ function ArchivedView({
|
|
|
8189
8599
|
}
|
|
8190
8600
|
|
|
8191
8601
|
// src/ArchivedDisclosure.tsx
|
|
8192
|
-
import { useCallback as useCallback12, useEffect as
|
|
8602
|
+
import { useCallback as useCallback12, useEffect as useEffect17, useState as useState27 } from "react";
|
|
8193
8603
|
import {
|
|
8194
8604
|
emptyPortalArchiveLine,
|
|
8195
8605
|
portalConfirmLine,
|
|
@@ -8246,7 +8656,7 @@ function ArchivedPortals({
|
|
|
8246
8656
|
const [confirming, setConfirming] = useState27(null);
|
|
8247
8657
|
const [typed, setTyped] = useState27("");
|
|
8248
8658
|
const [restoring, setRestoring] = useState27(null);
|
|
8249
|
-
const [
|
|
8659
|
+
const [refusal2, setRefusal] = useState27(null);
|
|
8250
8660
|
const [said, setSaid] = useState27(null);
|
|
8251
8661
|
const read = useCallback12(async () => {
|
|
8252
8662
|
const answered = await client.listPortals({ archived: "archived" });
|
|
@@ -8258,7 +8668,7 @@ function ArchivedPortals({
|
|
|
8258
8668
|
setError(answered.error);
|
|
8259
8669
|
}
|
|
8260
8670
|
}, [client]);
|
|
8261
|
-
|
|
8671
|
+
useEffect17(() => {
|
|
8262
8672
|
void read();
|
|
8263
8673
|
}, [read, refreshToken]);
|
|
8264
8674
|
const restore = async (portal) => {
|
|
@@ -8362,7 +8772,7 @@ function ArchivedPortals({
|
|
|
8362
8772
|
)
|
|
8363
8773
|
] })
|
|
8364
8774
|
] }) : null,
|
|
8365
|
-
|
|
8775
|
+
refusal2 && refusal2.portalId === portal.portal_id ? /* @__PURE__ */ jsx29(RefusalNotice, { error: refusal2.error }) : null
|
|
8366
8776
|
]
|
|
8367
8777
|
},
|
|
8368
8778
|
portal.portal_id
|
|
@@ -8374,7 +8784,7 @@ function ArchivedPortals({
|
|
|
8374
8784
|
}
|
|
8375
8785
|
|
|
8376
8786
|
// src/ViewBar.tsx
|
|
8377
|
-
import { useCallback as useCallback14, useEffect as
|
|
8787
|
+
import { useCallback as useCallback14, useEffect as useEffect18, useState as useState28 } from "react";
|
|
8378
8788
|
import { useRecordsClient as useRecordsClient16 } from "@ai-matrx/records/react";
|
|
8379
8789
|
import { Button as Button24, Input as Input2, Skeleton as Skeleton10, cn as cn24 } from "@ai-matrx/design-system";
|
|
8380
8790
|
|
|
@@ -8437,10 +8847,10 @@ function ViewBar({ tableId, activeViewId, onActiveView, seed, className }) {
|
|
|
8437
8847
|
setError(null);
|
|
8438
8848
|
setViews(mine);
|
|
8439
8849
|
}, [client, viewTableId, tableId, seed]);
|
|
8440
|
-
|
|
8850
|
+
useEffect18(() => {
|
|
8441
8851
|
void load();
|
|
8442
8852
|
}, [load]);
|
|
8443
|
-
|
|
8853
|
+
useEffect18(() => {
|
|
8444
8854
|
if (!views || views.length === 0) return;
|
|
8445
8855
|
const chosen = views.find((v) => v.id === (activeViewId ?? active)) ?? views.find((v) => v.isDefault) ?? views[0];
|
|
8446
8856
|
if (chosen.id !== active) setActive(chosen.id);
|
|
@@ -8616,12 +9026,12 @@ function ProposalRow({
|
|
|
8616
9026
|
}
|
|
8617
9027
|
|
|
8618
9028
|
// src/ActionInbox.tsx
|
|
8619
|
-
import { useCallback as useCallback16, useEffect as
|
|
9029
|
+
import { useCallback as useCallback16, useEffect as useEffect20, useMemo as useMemo20, useRef as useRef8, useState as useState31 } from "react";
|
|
8620
9030
|
import { useRecordsClient as useRecordsClient19 } from "@ai-matrx/records/react";
|
|
8621
9031
|
import { Badge as Badge9, Button as Button27, Skeleton as Skeleton12, cn as cn27 } from "@ai-matrx/design-system";
|
|
8622
9032
|
|
|
8623
9033
|
// src/ChecklistRunner.tsx
|
|
8624
|
-
import { useCallback as useCallback15, useEffect as
|
|
9034
|
+
import { useCallback as useCallback15, useEffect as useEffect19, useMemo as useMemo19, useState as useState30 } from "react";
|
|
8625
9035
|
import { useRecordsClient as useRecordsClient18, useTable as useTable10 } from "@ai-matrx/records/react";
|
|
8626
9036
|
import { Badge as Badge8, BasicInput as BasicInput9, BasicTextarea as BasicTextarea4, Button as Button26, Skeleton as Skeleton11, cn as cn26 } from "@ai-matrx/design-system";
|
|
8627
9037
|
import { Fragment as Fragment13, jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
@@ -8669,10 +9079,10 @@ function ChecklistRunner({
|
|
|
8669
9079
|
(held) => held && answered.data.some((r) => r.run_id === held) ? held : answered.data[0]?.run_id ?? null
|
|
8670
9080
|
);
|
|
8671
9081
|
}, [client, includeClosed, recordId, runId, tableId]);
|
|
8672
|
-
|
|
9082
|
+
useEffect19(() => {
|
|
8673
9083
|
void loadRuns();
|
|
8674
9084
|
}, [loadRuns]);
|
|
8675
|
-
|
|
9085
|
+
useEffect19(() => {
|
|
8676
9086
|
if (runId) setActiveId(runId);
|
|
8677
9087
|
}, [runId]);
|
|
8678
9088
|
const loadSteps = useCallback15(async () => {
|
|
@@ -8689,10 +9099,10 @@ function ChecklistRunner({
|
|
|
8689
9099
|
setError(null);
|
|
8690
9100
|
setSteps(answered.data);
|
|
8691
9101
|
}, [client, activeId]);
|
|
8692
|
-
|
|
9102
|
+
useEffect19(() => {
|
|
8693
9103
|
void loadSteps();
|
|
8694
9104
|
}, [loadSteps]);
|
|
8695
|
-
|
|
9105
|
+
useEffect19(() => {
|
|
8696
9106
|
if (!mayStart || !tableId) return;
|
|
8697
9107
|
let cancelled = false;
|
|
8698
9108
|
void client.checklistTemplates({ about_table_id: tableId, limit: 50 }).then((answered) => {
|
|
@@ -8918,7 +9328,7 @@ function useMyChecklistSteps(limit = 25) {
|
|
|
8918
9328
|
setSteps(held);
|
|
8919
9329
|
setLoading(false);
|
|
8920
9330
|
}, [client, limit, me]);
|
|
8921
|
-
|
|
9331
|
+
useEffect19(() => {
|
|
8922
9332
|
void refresh();
|
|
8923
9333
|
}, [refresh]);
|
|
8924
9334
|
return { steps, loading, error, refresh };
|
|
@@ -8927,7 +9337,7 @@ function MyChecklistSteps({ className }) {
|
|
|
8927
9337
|
const client = useRecordsClient18();
|
|
8928
9338
|
const { steps, loading, error, refresh } = useMyChecklistSteps();
|
|
8929
9339
|
const [busy, setBusy] = useState30(null);
|
|
8930
|
-
const [
|
|
9340
|
+
const [refusal2, setRefusal] = useState30(null);
|
|
8931
9341
|
const complete = useCallback15(
|
|
8932
9342
|
async (step2, evidence) => {
|
|
8933
9343
|
setBusy(step2.step_id);
|
|
@@ -8950,7 +9360,7 @@ function MyChecklistSteps({ className }) {
|
|
|
8950
9360
|
/* @__PURE__ */ jsx32("span", { className: "tabular-nums", children: steps.length })
|
|
8951
9361
|
] }),
|
|
8952
9362
|
error ? /* @__PURE__ */ jsx32(RefusalNotice, { error }) : null,
|
|
8953
|
-
|
|
9363
|
+
refusal2 ? /* @__PURE__ */ jsx32(RefusalNotice, { error: refusal2 }) : null,
|
|
8954
9364
|
/* @__PURE__ */ jsx32("ol", { className: "flex flex-col gap-1.5", children: steps.map((step2) => /* @__PURE__ */ jsx32(
|
|
8955
9365
|
StepRow,
|
|
8956
9366
|
{
|
|
@@ -8985,7 +9395,7 @@ function ChecklistsPanel({ tableId, onOpenRecord, className }) {
|
|
|
8985
9395
|
if (!r.ok) setRuns([]);
|
|
8986
9396
|
else setRuns(r.data);
|
|
8987
9397
|
}, [client, tableId]);
|
|
8988
|
-
|
|
9398
|
+
useEffect19(() => {
|
|
8989
9399
|
void load();
|
|
8990
9400
|
}, [load]);
|
|
8991
9401
|
if (templates === null) return /* @__PURE__ */ jsx32(Skeleton11, { className: cn26("h-40 w-full", className) });
|
|
@@ -9127,11 +9537,11 @@ function ChecklistTemplateEditor({
|
|
|
9127
9537
|
const client = useRecordsClient18();
|
|
9128
9538
|
const [name, setName] = useState30("");
|
|
9129
9539
|
const [rows, setRows] = useState30([{ ...EMPTY_ROW }]);
|
|
9130
|
-
const [
|
|
9540
|
+
const [refusal2, setRefusal] = useState30(null);
|
|
9131
9541
|
const [error, setError] = useState30(null);
|
|
9132
9542
|
const [busy, setBusy] = useState30(false);
|
|
9133
9543
|
const [loading, setLoading] = useState30(Boolean(templateId));
|
|
9134
|
-
|
|
9544
|
+
useEffect19(() => {
|
|
9135
9545
|
if (!templateId) return;
|
|
9136
9546
|
let cancelled = false;
|
|
9137
9547
|
void client.checklistTemplateShape({ template_id: templateId }).then((answered) => {
|
|
@@ -9168,7 +9578,7 @@ function ChecklistTemplateEditor({
|
|
|
9168
9578
|
}),
|
|
9169
9579
|
[aboutTableId, name, rows]
|
|
9170
9580
|
);
|
|
9171
|
-
|
|
9581
|
+
useEffect19(() => {
|
|
9172
9582
|
if (spec.steps.length === 0 || spec.name.length === 0) {
|
|
9173
9583
|
setRefusal(null);
|
|
9174
9584
|
return;
|
|
@@ -9295,9 +9705,9 @@ function ChecklistTemplateEditor({
|
|
|
9295
9705
|
] }, index)) }),
|
|
9296
9706
|
/* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-1.5", children: [
|
|
9297
9707
|
/* @__PURE__ */ jsx32(Button26, { size: "sm", variant: "outline", onClick: () => setRows((held) => [...held, { ...EMPTY_ROW }]), children: "Add a step" }),
|
|
9298
|
-
/* @__PURE__ */ jsx32(Button26, { size: "sm", disabled: busy ||
|
|
9708
|
+
/* @__PURE__ */ jsx32(Button26, { size: "sm", disabled: busy || refusal2 !== null || spec.steps.length === 0, onClick: () => void save(), children: busy ? "\u2026" : "Save" })
|
|
9299
9709
|
] }),
|
|
9300
|
-
|
|
9710
|
+
refusal2 ? /* @__PURE__ */ jsx32("p", { className: "text-xs text-muted-foreground", "data-testid": "checklist-editor-refusal", children: refusal2 }) : null
|
|
9301
9711
|
] });
|
|
9302
9712
|
}
|
|
9303
9713
|
function toStepSpec(row, index) {
|
|
@@ -9354,7 +9764,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9354
9764
|
setError(null);
|
|
9355
9765
|
setItems(result.data);
|
|
9356
9766
|
}, [client, includeSettled]);
|
|
9357
|
-
|
|
9767
|
+
useEffect20(() => {
|
|
9358
9768
|
void load();
|
|
9359
9769
|
}, [load]);
|
|
9360
9770
|
const shown = useMemo20(() => {
|
|
@@ -9362,10 +9772,10 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9362
9772
|
if (!tableId) return all;
|
|
9363
9773
|
return all.filter((i) => i.kind !== "assignment" || i.subject_kind !== "record" || true);
|
|
9364
9774
|
}, [items, tableId]);
|
|
9365
|
-
|
|
9775
|
+
useEffect20(() => {
|
|
9366
9776
|
if (cursor >= shown.length) setCursor(Math.max(0, shown.length - 1));
|
|
9367
9777
|
}, [shown.length, cursor]);
|
|
9368
|
-
|
|
9778
|
+
useEffect20(() => {
|
|
9369
9779
|
const el = listRef.current?.querySelector(`[data-row="${cursor}"]`);
|
|
9370
9780
|
el?.scrollIntoView({ block: "nearest" });
|
|
9371
9781
|
}, [cursor, shown.length]);
|
|
@@ -9491,7 +9901,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9491
9901
|
}
|
|
9492
9902
|
|
|
9493
9903
|
// src/HistoryPanel.tsx
|
|
9494
|
-
import { useCallback as useCallback17, useEffect as
|
|
9904
|
+
import { useCallback as useCallback17, useEffect as useEffect21, useRef as useRef9, useState as useState32 } from "react";
|
|
9495
9905
|
import {
|
|
9496
9906
|
useFields as useFields14,
|
|
9497
9907
|
useRecordsClient as useRecordsClient20,
|
|
@@ -9519,7 +9929,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
9519
9929
|
setError(null);
|
|
9520
9930
|
setEntries(answered.data);
|
|
9521
9931
|
}, [client, recordId]);
|
|
9522
|
-
|
|
9932
|
+
useEffect21(() => {
|
|
9523
9933
|
void load();
|
|
9524
9934
|
}, [load]);
|
|
9525
9935
|
if (error) return /* @__PURE__ */ jsx34(RefusalNotice, { error, className });
|
|
@@ -9800,7 +10210,7 @@ function say(value, field) {
|
|
|
9800
10210
|
}
|
|
9801
10211
|
|
|
9802
10212
|
// src/CommentThread.tsx
|
|
9803
|
-
import { useCallback as useCallback18, useEffect as
|
|
10213
|
+
import { useCallback as useCallback18, useEffect as useEffect22, useMemo as useMemo21, useRef as useRef10, useState as useState33 } from "react";
|
|
9804
10214
|
import {
|
|
9805
10215
|
useFields as useFields15,
|
|
9806
10216
|
useRecordsClient as useRecordsClient21,
|
|
@@ -9837,10 +10247,10 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
9837
10247
|
mayResolve: answered.data.may_resolve
|
|
9838
10248
|
});
|
|
9839
10249
|
}, [client, recordId, showResolved]);
|
|
9840
|
-
|
|
10250
|
+
useEffect22(() => {
|
|
9841
10251
|
void load();
|
|
9842
10252
|
}, [load]);
|
|
9843
|
-
|
|
10253
|
+
useEffect22(() => {
|
|
9844
10254
|
let alive = true;
|
|
9845
10255
|
if (!host.members) return;
|
|
9846
10256
|
void host.members().then((roster) => {
|
|
@@ -10043,7 +10453,7 @@ function Line({
|
|
|
10043
10453
|
}
|
|
10044
10454
|
|
|
10045
10455
|
// src/FieldHistoryPanel.tsx
|
|
10046
|
-
import { useCallback as useCallback19, useEffect as
|
|
10456
|
+
import { useCallback as useCallback19, useEffect as useEffect23, useState as useState34 } from "react";
|
|
10047
10457
|
import {
|
|
10048
10458
|
useFields as useFields16,
|
|
10049
10459
|
useRecordsClient as useRecordsClient22,
|
|
@@ -10078,7 +10488,7 @@ function FieldHistoryPanel({
|
|
|
10078
10488
|
setError(null);
|
|
10079
10489
|
setRows(answered.data);
|
|
10080
10490
|
}, [client, tableId, fieldKey, recordId]);
|
|
10081
|
-
|
|
10491
|
+
useEffect23(() => {
|
|
10082
10492
|
void load();
|
|
10083
10493
|
}, [load]);
|
|
10084
10494
|
const label = (fields.data ?? []).find((f) => f.key === fieldKey)?.label || humanize(fieldKey);
|
|
@@ -10271,7 +10681,7 @@ function submissionStamp(args) {
|
|
|
10271
10681
|
}
|
|
10272
10682
|
|
|
10273
10683
|
// src/PortalBuilder.tsx
|
|
10274
|
-
import { useCallback as useCallback20, useEffect as
|
|
10684
|
+
import { useCallback as useCallback20, useEffect as useEffect24, useMemo as useMemo22, useState as useState35 } from "react";
|
|
10275
10685
|
import { useRecordsClient as useRecordsClient23, useTables as useTables3 } from "@ai-matrx/records/react";
|
|
10276
10686
|
import { BasicInput as BasicInput10, Button as Button31, Checkbox as Checkbox5, Label as Label5, Separator as Separator9, Skeleton as Skeleton16, cn as cn31 } from "@ai-matrx/design-system";
|
|
10277
10687
|
import { Fragment as Fragment15, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
@@ -10304,7 +10714,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10304
10714
|
},
|
|
10305
10715
|
[client, fieldsByTable]
|
|
10306
10716
|
);
|
|
10307
|
-
|
|
10717
|
+
useEffect24(() => {
|
|
10308
10718
|
if (!portalId) return;
|
|
10309
10719
|
void (async () => {
|
|
10310
10720
|
const answered = await client.portalCard({ portal_id: portalId });
|
|
@@ -10318,7 +10728,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10318
10728
|
setClientTableId(answered.data.client_table_id);
|
|
10319
10729
|
})();
|
|
10320
10730
|
}, [client, portalId]);
|
|
10321
|
-
|
|
10731
|
+
useEffect24(() => {
|
|
10322
10732
|
if (!tableId) return;
|
|
10323
10733
|
setExposures(
|
|
10324
10734
|
(prev) => prev[tableId] ? prev : {
|
|
@@ -10566,7 +10976,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10566
10976
|
}
|
|
10567
10977
|
|
|
10568
10978
|
// src/PortalsPanel.tsx
|
|
10569
|
-
import { useCallback as useCallback21, useEffect as
|
|
10979
|
+
import { useCallback as useCallback21, useEffect as useEffect25, useMemo as useMemo23, useState as useState36 } from "react";
|
|
10570
10980
|
import { useRecordsClient as useRecordsClient24 } from "@ai-matrx/records/react";
|
|
10571
10981
|
import {
|
|
10572
10982
|
portalArchiveConsequence,
|
|
@@ -10607,6 +11017,7 @@ function BuildOrAsk({
|
|
|
10607
11017
|
|
|
10608
11018
|
// src/PortalsPanel.tsx
|
|
10609
11019
|
import { Fragment as Fragment16, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
11020
|
+
var PORTAL_NOT_HERE_LINE = "The link named a portal this table is not part of \u2014 it may have been archived, or it shows other tables. The portals this table is in are listed here.";
|
|
10610
11021
|
function revokeConsequence(person, portalTitle) {
|
|
10611
11022
|
const who = person.client ? `${person.email} (${person.client})` : person.email;
|
|
10612
11023
|
return `${who} will no longer be able to sign in to \u201C${portalTitle}\u201D, and the records that named their client will stop reaching them. Nothing is deleted \u2014 you can invite them again.`;
|
|
@@ -10636,13 +11047,16 @@ function stateWords(person) {
|
|
|
10636
11047
|
};
|
|
10637
11048
|
}
|
|
10638
11049
|
var PORTAL_SUGGESTION = "Let each of my customers sign in and see their own jobs and invoices, and nothing else.";
|
|
10639
|
-
function PortalsPanel({ tableId, className }) {
|
|
11050
|
+
function PortalsPanel({ tableId, activePortalId, className }) {
|
|
10640
11051
|
const client = useRecordsClient24();
|
|
10641
11052
|
const host = useRecordsUi();
|
|
10642
11053
|
const [portals, setPortals] = useState36(null);
|
|
10643
11054
|
const [exposures, setExposures] = useState36([]);
|
|
10644
11055
|
const [listError, setListError] = useState36(null);
|
|
10645
|
-
const [openId, setOpenId] = useState36(null);
|
|
11056
|
+
const [openId, setOpenId] = useState36(activePortalId ?? null);
|
|
11057
|
+
useEffect25(() => {
|
|
11058
|
+
if (activePortalId) setOpenId(activePortalId);
|
|
11059
|
+
}, [activePortalId]);
|
|
10646
11060
|
const [building, setBuilding] = useState36(false);
|
|
10647
11061
|
const [adding, setAdding] = useState36(null);
|
|
10648
11062
|
const [archiveToken, setArchiveToken] = useState36(0);
|
|
@@ -10658,7 +11072,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
10658
11072
|
setPortals(answered.data);
|
|
10659
11073
|
setExposures(mapped.ok ? mapped.data : []);
|
|
10660
11074
|
}, [client]);
|
|
10661
|
-
|
|
11075
|
+
useEffect25(() => {
|
|
10662
11076
|
void load();
|
|
10663
11077
|
}, [load]);
|
|
10664
11078
|
if (portals === null) return /* @__PURE__ */ jsx39(Skeleton17, { className: cn33("h-32 w-full", className) });
|
|
@@ -10739,59 +11153,73 @@ function PortalsPanel({ tableId, className }) {
|
|
|
10739
11153
|
portal.portal_id
|
|
10740
11154
|
)) })
|
|
10741
11155
|
] }) : null,
|
|
11156
|
+
activePortalId && !shown.some((p) => p.portal_id === activePortalId) ? /* @__PURE__ */ jsx39("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: PORTAL_NOT_HERE_LINE }) : null,
|
|
10742
11157
|
/* @__PURE__ */ jsx39("ul", { className: "flex flex-col gap-2", children: shown.map((portal) => {
|
|
10743
11158
|
const url = `${origin}${portalPath(portal.slug)}`;
|
|
10744
11159
|
const open = openId === portal.portal_id;
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
"."
|
|
10755
|
-
] }),
|
|
10756
|
-
/* @__PURE__ */ jsxs35("p", { className: "mt-1.5 text-xs", children: [
|
|
10757
|
-
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.signed_in }),
|
|
10758
|
-
" signed in",
|
|
10759
|
-
" \xB7 ",
|
|
10760
|
-
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.invited - portal.signed_in }),
|
|
10761
|
-
" invited and waiting"
|
|
10762
|
-
] }),
|
|
10763
|
-
/* @__PURE__ */ jsxs35("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
10764
|
-
/* @__PURE__ */ jsx39(CopyLink, { url }),
|
|
10765
|
-
/* @__PURE__ */ jsx39(
|
|
10766
|
-
Button33,
|
|
10767
|
-
{
|
|
10768
|
-
size: "sm",
|
|
10769
|
-
variant: "ghost",
|
|
10770
|
-
onClick: () => setOpenId(open ? null : portal.portal_id),
|
|
10771
|
-
children: open ? "Close" : "Open it"
|
|
10772
|
-
}
|
|
11160
|
+
const linked = portal.portal_id === activePortalId;
|
|
11161
|
+
return /* @__PURE__ */ jsxs35(
|
|
11162
|
+
"li",
|
|
11163
|
+
{
|
|
11164
|
+
"data-linked": linked ? "true" : void 0,
|
|
11165
|
+
ref: linked ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
11166
|
+
className: cn33(
|
|
11167
|
+
"rounded-md border border-border bg-card p-2.5",
|
|
11168
|
+
linked && "border-primary ring-1 ring-primary"
|
|
10773
11169
|
),
|
|
10774
|
-
|
|
10775
|
-
|
|
10776
|
-
|
|
10777
|
-
portal,
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
11170
|
+
children: [
|
|
11171
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-2", children: [
|
|
11172
|
+
/* @__PURE__ */ jsx39("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: portal.title }),
|
|
11173
|
+
portal.is_active ? null : /* @__PURE__ */ jsx39(Badge13, { variant: "outline", children: "closed" })
|
|
11174
|
+
] }),
|
|
11175
|
+
/* @__PURE__ */ jsxs35("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
11176
|
+
"Clients come from ",
|
|
11177
|
+
portal.client_table,
|
|
11178
|
+
portal.tables === 1 ? ", and see 1 Table" : `, and see ${portal.tables} Tables`,
|
|
11179
|
+
"."
|
|
11180
|
+
] }),
|
|
11181
|
+
/* @__PURE__ */ jsxs35("p", { className: "mt-1.5 text-xs", children: [
|
|
11182
|
+
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.signed_in }),
|
|
11183
|
+
" signed in",
|
|
11184
|
+
" \xB7 ",
|
|
11185
|
+
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.invited - portal.signed_in }),
|
|
11186
|
+
" invited and waiting"
|
|
11187
|
+
] }),
|
|
11188
|
+
/* @__PURE__ */ jsxs35("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
11189
|
+
/* @__PURE__ */ jsx39(CopyLink, { url }),
|
|
11190
|
+
/* @__PURE__ */ jsx39(
|
|
11191
|
+
Button33,
|
|
11192
|
+
{
|
|
11193
|
+
size: "sm",
|
|
11194
|
+
variant: "ghost",
|
|
11195
|
+
onClick: () => setOpenId(open ? null : portal.portal_id),
|
|
11196
|
+
children: open ? "Close" : "Open it"
|
|
11197
|
+
}
|
|
11198
|
+
),
|
|
11199
|
+
/* @__PURE__ */ jsx39(
|
|
11200
|
+
ArchivePortalControl,
|
|
11201
|
+
{
|
|
11202
|
+
portal,
|
|
11203
|
+
onArchived: () => {
|
|
11204
|
+
setArchiveToken((t) => t + 1);
|
|
11205
|
+
void load();
|
|
11206
|
+
}
|
|
11207
|
+
}
|
|
11208
|
+
)
|
|
11209
|
+
] }),
|
|
11210
|
+
open ? /* @__PURE__ */ jsx39(
|
|
11211
|
+
PortalDetail,
|
|
11212
|
+
{
|
|
11213
|
+
portalId: portal.portal_id,
|
|
11214
|
+
...tableId ? { tableId } : {},
|
|
11215
|
+
origin,
|
|
11216
|
+
onChanged: () => void load()
|
|
10781
11217
|
}
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
{
|
|
10788
|
-
portalId: portal.portal_id,
|
|
10789
|
-
...tableId ? { tableId } : {},
|
|
10790
|
-
origin,
|
|
10791
|
-
onChanged: () => void load()
|
|
10792
|
-
}
|
|
10793
|
-
) : null
|
|
10794
|
-
] }, portal.portal_id);
|
|
11218
|
+
) : null
|
|
11219
|
+
]
|
|
11220
|
+
},
|
|
11221
|
+
portal.portal_id
|
|
11222
|
+
);
|
|
10795
11223
|
}) }),
|
|
10796
11224
|
/* @__PURE__ */ jsx39(ArchivedPortals, { refreshToken: archiveToken, onRestored: () => void load() })
|
|
10797
11225
|
] });
|
|
@@ -10920,7 +11348,7 @@ function PortalDetail({
|
|
|
10920
11348
|
setError(null);
|
|
10921
11349
|
setCard(answered.data);
|
|
10922
11350
|
}, [client, portalId]);
|
|
10923
|
-
|
|
11351
|
+
useEffect25(() => {
|
|
10924
11352
|
void load();
|
|
10925
11353
|
}, [load]);
|
|
10926
11354
|
const revoke = useCallback21(
|
|
@@ -11113,7 +11541,7 @@ function Preview({
|
|
|
11113
11541
|
const [which, setWhich] = useState36(first?.table_id ?? null);
|
|
11114
11542
|
const [rows, setRows] = useState36(null);
|
|
11115
11543
|
const [error, setError] = useState36(null);
|
|
11116
|
-
|
|
11544
|
+
useEffect25(() => {
|
|
11117
11545
|
if (!which) return;
|
|
11118
11546
|
let cancelled = false;
|
|
11119
11547
|
setRows(null);
|
|
@@ -11159,7 +11587,7 @@ function Invite({ card, onInvited }) {
|
|
|
11159
11587
|
const [busy, setBusy] = useState36(false);
|
|
11160
11588
|
const [said, setSaid] = useState36(null);
|
|
11161
11589
|
const [error, setError] = useState36(null);
|
|
11162
|
-
|
|
11590
|
+
useEffect25(() => {
|
|
11163
11591
|
let cancelled = false;
|
|
11164
11592
|
void client.list({ table_id: card.client_table_id, limit: 200 }).then((answered) => {
|
|
11165
11593
|
if (cancelled) return;
|
|
@@ -11261,7 +11689,7 @@ function Invite({ card, onInvited }) {
|
|
|
11261
11689
|
}
|
|
11262
11690
|
|
|
11263
11691
|
// src/DigestScheduler.tsx
|
|
11264
|
-
import { useCallback as useCallback22, useEffect as
|
|
11692
|
+
import { useCallback as useCallback22, useEffect as useEffect26, useMemo as useMemo24, useState as useState37 } from "react";
|
|
11265
11693
|
import { useRecordsClient as useRecordsClient25 } from "@ai-matrx/records/react";
|
|
11266
11694
|
import { BasicInput as BasicInput12, Button as Button34, Checkbox as Checkbox6, Label as Label6, Separator as Separator11, Skeleton as Skeleton18, cn as cn34 } from "@ai-matrx/design-system";
|
|
11267
11695
|
import { Fragment as Fragment17, jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
@@ -11325,7 +11753,7 @@ function DigestScheduler({
|
|
|
11325
11753
|
setMembers([]);
|
|
11326
11754
|
}
|
|
11327
11755
|
}, [client, tableId, host]);
|
|
11328
|
-
|
|
11756
|
+
useEffect26(() => {
|
|
11329
11757
|
void load();
|
|
11330
11758
|
}, [load]);
|
|
11331
11759
|
const schedule = useMemo24(() => {
|
|
@@ -11549,7 +11977,17 @@ function DigestScheduler({
|
|
|
11549
11977
|
preview ? /* @__PURE__ */ jsxs36("div", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
11550
11978
|
/* @__PURE__ */ jsx40("p", { className: "font-medium", children: preview.subject }),
|
|
11551
11979
|
/* @__PURE__ */ jsx40("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
11552
|
-
preview.incomplete ?
|
|
11980
|
+
preview.incomplete ? (
|
|
11981
|
+
// The summary could not be finished: a refusal line naming the missing
|
|
11982
|
+
// piece and what to do, never the bare sentence in red (lane REFUSAL-SWEEP).
|
|
11983
|
+
/* @__PURE__ */ jsx40(
|
|
11984
|
+
RefusalLine,
|
|
11985
|
+
{
|
|
11986
|
+
className: "mt-1",
|
|
11987
|
+
error: refusal("not_supported", preview.incomplete, "Fill in the missing piece above, then preview again.")
|
|
11988
|
+
}
|
|
11989
|
+
)
|
|
11990
|
+
) : null,
|
|
11553
11991
|
preview.entered.length > 0 ? /* @__PURE__ */ jsxs36("p", { className: "mt-1", children: [
|
|
11554
11992
|
/* @__PURE__ */ jsx40("span", { className: "font-medium", children: "Arrived:" }),
|
|
11555
11993
|
" ",
|
|
@@ -11567,18 +12005,34 @@ function DigestScheduler({
|
|
|
11567
12005
|
}
|
|
11568
12006
|
|
|
11569
12007
|
// src/SubscriptionsPanel.tsx
|
|
11570
|
-
import { useCallback as useCallback23, useEffect as
|
|
12008
|
+
import { useCallback as useCallback23, useEffect as useEffect27, useState as useState38 } from "react";
|
|
11571
12009
|
import { useRecordsClient as useRecordsClient26 } from "@ai-matrx/records/react";
|
|
11572
12010
|
import { Badge as Badge14, Button as Button35, Skeleton as Skeleton19, Switch as Switch2, cn as cn35 } from "@ai-matrx/design-system";
|
|
11573
12011
|
import { jsx as jsx41, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
12012
|
+
var RULE_NOT_HERE_LINE = "The link named a digest that is not one of yours on this table \u2014 it belongs to somebody else, or it was switched off for good. Yours are listed here; its owner can change theirs.";
|
|
11574
12013
|
function whenItFires(subscription) {
|
|
11575
12014
|
if (subscription.cadence === "instant") return "as it happens";
|
|
11576
12015
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
11577
12016
|
return subscription.schedule ? `${every}, ${subscription.schedule}` : every;
|
|
11578
12017
|
}
|
|
12018
|
+
var PERIOD_MS = {
|
|
12019
|
+
hourly: 60 * 60 * 1e3,
|
|
12020
|
+
daily: 24 * 60 * 60 * 1e3,
|
|
12021
|
+
weekly: 7 * 24 * 60 * 60 * 1e3
|
|
12022
|
+
};
|
|
12023
|
+
function nextSummaryAt(subscription, now) {
|
|
12024
|
+
if (!subscription.next_digest_at) return null;
|
|
12025
|
+
const due = new Date(subscription.next_digest_at);
|
|
12026
|
+
if (Number.isNaN(due.getTime())) return null;
|
|
12027
|
+
if (due.getTime() > now.getTime()) return due;
|
|
12028
|
+
const period = PERIOD_MS[subscription.cadence];
|
|
12029
|
+
if (!period) return due;
|
|
12030
|
+
const behind = now.getTime() - due.getTime();
|
|
12031
|
+
return new Date(due.getTime() + (Math.floor(behind / period) + 1) * period);
|
|
12032
|
+
}
|
|
11579
12033
|
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
11580
12034
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
11581
|
-
function SubscriptionsPanel({ tableId, className }) {
|
|
12035
|
+
function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
11582
12036
|
const client = useRecordsClient26();
|
|
11583
12037
|
const [rows, setRows] = useState38(null);
|
|
11584
12038
|
const [error, setError] = useState38(null);
|
|
@@ -11596,7 +12050,7 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
11596
12050
|
setError(null);
|
|
11597
12051
|
setRows(answered.data);
|
|
11598
12052
|
}, [client, tableId]);
|
|
11599
|
-
|
|
12053
|
+
useEffect27(() => {
|
|
11600
12054
|
void load();
|
|
11601
12055
|
}, [load]);
|
|
11602
12056
|
const flip = useCallback23(
|
|
@@ -11669,96 +12123,119 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
11669
12123
|
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."
|
|
11670
12124
|
}
|
|
11671
12125
|
) : null,
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
"
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
" ",
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
12126
|
+
activeRuleId && !rows.some((r) => r.rule_id === activeRuleId) ? /* @__PURE__ */ jsx41("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: RULE_NOT_HERE_LINE }) : null,
|
|
12127
|
+
/* @__PURE__ */ jsx41("ul", { className: "flex flex-col gap-2", children: rows.map((subscription) => /* @__PURE__ */ jsxs37(
|
|
12128
|
+
"li",
|
|
12129
|
+
{
|
|
12130
|
+
"data-linked": subscription.rule_id === activeRuleId ? "true" : void 0,
|
|
12131
|
+
ref: subscription.rule_id === activeRuleId ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
12132
|
+
className: cn35(
|
|
12133
|
+
"rounded-md border p-2.5",
|
|
12134
|
+
subscription.rule_id === activeRuleId && "border-primary ring-1 ring-primary"
|
|
12135
|
+
),
|
|
12136
|
+
children: [
|
|
12137
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-start gap-2", children: [
|
|
12138
|
+
/* @__PURE__ */ jsxs37("div", { className: "min-w-0 flex-1", children: [
|
|
12139
|
+
/* @__PURE__ */ jsx41("p", { className: "truncate text-sm font-medium", children: subscription.name }),
|
|
12140
|
+
/* @__PURE__ */ jsxs37("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
12141
|
+
CHANNEL_WORDS2[subscription.channel] ?? `on the ${subscription.channel} channel`,
|
|
12142
|
+
" \xB7 ",
|
|
12143
|
+
whenItFires(subscription)
|
|
12144
|
+
] })
|
|
12145
|
+
] }),
|
|
12146
|
+
subscription.i_may_mute ? /* @__PURE__ */ jsx41(
|
|
12147
|
+
Switch2,
|
|
12148
|
+
{
|
|
12149
|
+
checked: !subscription.muted,
|
|
12150
|
+
disabled: busy === subscription.rule_id,
|
|
12151
|
+
"aria-label": `Tell me about ${subscription.name}`,
|
|
12152
|
+
onCheckedChange: (on) => void flip(subscription, on)
|
|
12153
|
+
}
|
|
12154
|
+
) : /* @__PURE__ */ jsx41(Badge14, { variant: "secondary", children: "someone else's" })
|
|
12155
|
+
] }),
|
|
12156
|
+
/* @__PURE__ */ jsx41("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." }),
|
|
12157
|
+
/* @__PURE__ */ jsxs37("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
12158
|
+
subscription.next_digest_at ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
12159
|
+
"Next summary ",
|
|
12160
|
+
nextSummaryAt(subscription, /* @__PURE__ */ new Date())?.toLocaleString()
|
|
12161
|
+
] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */ jsx41("span", { children: "No next summary while it is off." }) : null,
|
|
12162
|
+
subscription.last_sent_at ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
12163
|
+
"Last told you ",
|
|
12164
|
+
new Date(subscription.last_sent_at).toLocaleString()
|
|
12165
|
+
] }) : /* @__PURE__ */ jsx41("span", { children: "It has not told you anything yet." }),
|
|
12166
|
+
subscription.quiet_hours ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
12167
|
+
"Not between ",
|
|
12168
|
+
subscription.quiet_hours.start,
|
|
12169
|
+
" and ",
|
|
12170
|
+
subscription.quiet_hours.end,
|
|
12171
|
+
" \u2014 a send inside those hours waits until they end."
|
|
12172
|
+
] }) : null,
|
|
12173
|
+
subscription.cadence === "instant" ? null : /* @__PURE__ */ jsx41(
|
|
12174
|
+
Button35,
|
|
12175
|
+
{
|
|
12176
|
+
size: "sm",
|
|
12177
|
+
variant: "ghost",
|
|
12178
|
+
disabled: busy === subscription.rule_id,
|
|
12179
|
+
onClick: () => void showOne(subscription),
|
|
12180
|
+
children: "Send me a preview now"
|
|
12181
|
+
}
|
|
12182
|
+
)
|
|
12183
|
+
] }),
|
|
12184
|
+
preview && preview.rule_id === subscription.rule_id ? /* @__PURE__ */ jsxs37("div", { className: "mt-2 rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
12185
|
+
/* @__PURE__ */ jsx41("p", { className: "font-medium", children: preview.subject }),
|
|
12186
|
+
/* @__PURE__ */ jsx41("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
12187
|
+
preview.incomplete ? (
|
|
12188
|
+
// Absent or honest: a subscription that cannot produce a summary
|
|
12189
|
+
// says which piece is missing instead of showing an empty one.
|
|
12190
|
+
/* @__PURE__ */ jsx41(
|
|
12191
|
+
RefusalLine,
|
|
12192
|
+
{
|
|
12193
|
+
className: "mt-1",
|
|
12194
|
+
error: refusal(
|
|
12195
|
+
"not_supported",
|
|
12196
|
+
preview.incomplete,
|
|
12197
|
+
"Fill in the missing piece, then preview again."
|
|
12198
|
+
)
|
|
12199
|
+
}
|
|
12200
|
+
)
|
|
12201
|
+
) : null,
|
|
12202
|
+
preview.entered.length > 0 ? /* @__PURE__ */ jsxs37("p", { className: "mt-1", children: [
|
|
12203
|
+
/* @__PURE__ */ jsx41("span", { className: "font-medium", children: "Arrived:" }),
|
|
12204
|
+
" ",
|
|
12205
|
+
preview.entered.map((e) => e.name).join(", ")
|
|
12206
|
+
] }) : null,
|
|
12207
|
+
preview.left.length > 0 ? /* @__PURE__ */ jsxs37("p", { className: "mt-1", children: [
|
|
12208
|
+
/* @__PURE__ */ jsx41("span", { className: "font-medium", children: "Left:" }),
|
|
12209
|
+
" ",
|
|
12210
|
+
preview.left.map((e) => e.name).join(", ")
|
|
12211
|
+
] }) : null,
|
|
12212
|
+
preview.changed.length > 0 ? /* @__PURE__ */ jsxs37("p", { className: "mt-1", children: [
|
|
12213
|
+
/* @__PURE__ */ jsx41("span", { className: "font-medium", children: "Changed:" }),
|
|
12214
|
+
" ",
|
|
12215
|
+
preview.changed.map((e) => e.name).join(", ")
|
|
12216
|
+
] }) : null,
|
|
12217
|
+
/* @__PURE__ */ jsx41("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." }),
|
|
12218
|
+
/* @__PURE__ */ jsx41(Button35, { size: "sm", variant: "ghost", className: "mt-1", onClick: () => setPreview(null), children: "Close" })
|
|
12219
|
+
] }) : null
|
|
12220
|
+
]
|
|
12221
|
+
},
|
|
12222
|
+
subscription.rule_id
|
|
12223
|
+
)) })
|
|
11747
12224
|
] });
|
|
11748
12225
|
}
|
|
11749
12226
|
|
|
11750
12227
|
// src/FormBuilder.tsx
|
|
11751
|
-
import { useCallback as useCallback25, useEffect as
|
|
12228
|
+
import { useCallback as useCallback25, useEffect as useEffect30, useMemo as useMemo26, useState as useState41 } from "react";
|
|
11752
12229
|
import { useFields as useFields18, useRecordsClient as useRecordsClient28, useTable as useTable14 } from "@ai-matrx/records/react";
|
|
11753
12230
|
|
|
11754
12231
|
// src/publish-gate.ts
|
|
11755
|
-
import { useEffect as
|
|
12232
|
+
import { useEffect as useEffect28, useState as useState39 } from "react";
|
|
11756
12233
|
import { useRecordsClient as useRecordsClient27 } from "@ai-matrx/records/react";
|
|
11757
12234
|
var PUBLISH_NEEDS_THE_STORE_ON = "This organization has its record store switched off, so publishing would hand you a link that opens nothing. An owner or an administrator turns it on under Database settings, on the unified data screen \u2014 everything you have already built here is kept and starts working the moment they do.";
|
|
11758
12235
|
function usePublishGate() {
|
|
11759
12236
|
const client = useRecordsClient27();
|
|
11760
12237
|
const [storeOpen, setStoreOpen] = useState39(null);
|
|
11761
|
-
|
|
12238
|
+
useEffect28(() => {
|
|
11762
12239
|
let alive = true;
|
|
11763
12240
|
void (async () => {
|
|
11764
12241
|
const answered = await client.storeIsOpen();
|
|
@@ -11789,7 +12266,7 @@ import {
|
|
|
11789
12266
|
} from "@ai-matrx/design-system";
|
|
11790
12267
|
|
|
11791
12268
|
// src/FormRunner.tsx
|
|
11792
|
-
import { useCallback as useCallback24, useEffect as
|
|
12269
|
+
import { useCallback as useCallback24, useEffect as useEffect29, useMemo as useMemo25, useRef as useRef11, useState as useState40 } from "react";
|
|
11793
12270
|
import { useFields as useFields17, useOptionalRecordsClient as useOptionalRecordsClient4 } from "@ai-matrx/records/react";
|
|
11794
12271
|
import { Button as Button36, Progress, Skeleton as Skeleton20, cn as cn36 } from "@ai-matrx/design-system";
|
|
11795
12272
|
import { Fragment as Fragment18, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
@@ -11806,9 +12283,16 @@ function ConnectedFormRunner(props) {
|
|
|
11806
12283
|
const submit = useCallback24(
|
|
11807
12284
|
async (values) => {
|
|
11808
12285
|
if (!client) {
|
|
12286
|
+
const said = "This form cannot send answers from here, so nothing was sent.";
|
|
11809
12287
|
return {
|
|
11810
12288
|
ok: false,
|
|
11811
|
-
message:
|
|
12289
|
+
message: said,
|
|
12290
|
+
error: refusal(
|
|
12291
|
+
"not_supported",
|
|
12292
|
+
said,
|
|
12293
|
+
"Open the form from its own link and answer it there.",
|
|
12294
|
+
"No submit port: a public form is served with its questions and its submit port by the server; a signed-in one needs <RecordsProvider> above it."
|
|
12295
|
+
)
|
|
11812
12296
|
};
|
|
11813
12297
|
}
|
|
11814
12298
|
const document2 = {
|
|
@@ -11821,7 +12305,7 @@ function ConnectedFormRunner(props) {
|
|
|
11821
12305
|
})
|
|
11822
12306
|
};
|
|
11823
12307
|
const written = await client.recordWrite({ table_id: form.subject, data: document2 });
|
|
11824
|
-
if (!written.ok) return { ok: false, message: written.error.message };
|
|
12308
|
+
if (!written.ok) return { ok: false, message: written.error.message, error: written.error };
|
|
11825
12309
|
return { ok: true, recordId: written.data };
|
|
11826
12310
|
},
|
|
11827
12311
|
[client, form]
|
|
@@ -11863,7 +12347,7 @@ function FormStage({
|
|
|
11863
12347
|
const [answers, setAnswers] = useState40({});
|
|
11864
12348
|
const [at, setAt] = useState40(0);
|
|
11865
12349
|
const [error, setError] = useState40(null);
|
|
11866
|
-
const [
|
|
12350
|
+
const [refusal2, setRefusal] = useState40(null);
|
|
11867
12351
|
const [writing, setWriting] = useState40(false);
|
|
11868
12352
|
const [done, setDone] = useState40(null);
|
|
11869
12353
|
const [hidden, setHidden] = useState40({});
|
|
@@ -11883,7 +12367,7 @@ function FormStage({
|
|
|
11883
12367
|
};
|
|
11884
12368
|
});
|
|
11885
12369
|
}, [fields, form.questions]);
|
|
11886
|
-
|
|
12370
|
+
useEffect29(() => {
|
|
11887
12371
|
let cancelled = false;
|
|
11888
12372
|
const conditional = questions.filter((q) => q.showIf);
|
|
11889
12373
|
if (conditional.length === 0 || !evaluate) return;
|
|
@@ -11932,12 +12416,21 @@ function FormStage({
|
|
|
11932
12416
|
const outcome = await onSubmit(values);
|
|
11933
12417
|
setWriting(false);
|
|
11934
12418
|
if (!outcome.ok) {
|
|
11935
|
-
|
|
12419
|
+
const inWords = (text) => questions.reduce(
|
|
12420
|
+
(said, q) => said.replace(new RegExp(`\\b${q.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g"), q.ask),
|
|
12421
|
+
text
|
|
12422
|
+
);
|
|
12423
|
+
const told = outcome.error ?? refusal("invalid_argument", outcome.message, "Check your answers, then send again.");
|
|
12424
|
+
setRefusal({
|
|
12425
|
+
...told,
|
|
12426
|
+
message: inWords(told.message),
|
|
12427
|
+
...told.hint ? { hint: inWords(told.hint) } : {}
|
|
12428
|
+
});
|
|
11936
12429
|
return;
|
|
11937
12430
|
}
|
|
11938
12431
|
setDone(outcome.message ?? "sent");
|
|
11939
12432
|
onSubmitted?.(outcome.recordId ?? null);
|
|
11940
|
-
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview]);
|
|
12433
|
+
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
11941
12434
|
function advance() {
|
|
11942
12435
|
if (!oneAtATime) return;
|
|
11943
12436
|
if (index < live.length - 1) setAt(index + 1);
|
|
@@ -11954,7 +12447,7 @@ function FormStage({
|
|
|
11954
12447
|
if (event.shiftKey) retreat();
|
|
11955
12448
|
else advance();
|
|
11956
12449
|
}
|
|
11957
|
-
|
|
12450
|
+
useEffect29(() => {
|
|
11958
12451
|
const input = stage.current?.querySelector(
|
|
11959
12452
|
"input:not([tabindex='-1']), textarea, select, [role='combobox']"
|
|
11960
12453
|
);
|
|
@@ -11996,7 +12489,22 @@ function FormStage({
|
|
|
11996
12489
|
] }) : null,
|
|
11997
12490
|
form.intro && index === 0 ? /* @__PURE__ */ jsx42("p", { className: "text-sm text-muted-foreground", children: form.intro }) : null,
|
|
11998
12491
|
error ? /* @__PURE__ */ jsx42(RefusalNotice, { error, className: "text-left" }) : null,
|
|
11999
|
-
|
|
12492
|
+
refusal2 ? /* @__PURE__ */ jsx42(
|
|
12493
|
+
RefusalNotice,
|
|
12494
|
+
{
|
|
12495
|
+
error: refusal2,
|
|
12496
|
+
className: "text-left",
|
|
12497
|
+
onKeepEditing: () => {
|
|
12498
|
+
setRefusal(null);
|
|
12499
|
+
stage.current?.querySelector("input, textarea, select, button")?.focus();
|
|
12500
|
+
},
|
|
12501
|
+
onDiscard: () => {
|
|
12502
|
+
setRefusal(null);
|
|
12503
|
+
setAnswers({});
|
|
12504
|
+
setAt(0);
|
|
12505
|
+
}
|
|
12506
|
+
}
|
|
12507
|
+
) : null,
|
|
12000
12508
|
/* @__PURE__ */ jsxs38("div", { ref: stage, className: "flex flex-col gap-4 text-left", children: [
|
|
12001
12509
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ jsx42(
|
|
12002
12510
|
Question,
|
|
@@ -12070,12 +12578,15 @@ function Question({
|
|
|
12070
12578
|
void upload?.(file).then((result) => {
|
|
12071
12579
|
if (!result) return;
|
|
12072
12580
|
if (result.ok) onChange(result.fileId);
|
|
12073
|
-
else
|
|
12581
|
+
else
|
|
12582
|
+
setUploadError(
|
|
12583
|
+
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
12584
|
+
);
|
|
12074
12585
|
});
|
|
12075
12586
|
}
|
|
12076
12587
|
}
|
|
12077
12588
|
),
|
|
12078
|
-
uploadError ? /* @__PURE__ */ jsx42(
|
|
12589
|
+
uploadError ? /* @__PURE__ */ jsx42(RefusalLine, { error: uploadError }) : null
|
|
12079
12590
|
] }) : /* @__PURE__ */ jsx42(FieldControl, { field, value, onChange, id })
|
|
12080
12591
|
] });
|
|
12081
12592
|
}
|
|
@@ -12149,10 +12660,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
12149
12660
|
setError(null);
|
|
12150
12661
|
setForms(mine);
|
|
12151
12662
|
}, [client, tableId, seed, claimSeed]);
|
|
12152
|
-
|
|
12663
|
+
useEffect30(() => {
|
|
12153
12664
|
void load();
|
|
12154
12665
|
}, [load]);
|
|
12155
|
-
|
|
12666
|
+
useEffect30(() => {
|
|
12156
12667
|
if (!forms || forms.length === 0) return;
|
|
12157
12668
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
12158
12669
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -12563,7 +13074,7 @@ function groupLabel(groups) {
|
|
|
12563
13074
|
|
|
12564
13075
|
// src/chartFrame.tsx
|
|
12565
13076
|
import {
|
|
12566
|
-
useEffect as
|
|
13077
|
+
useEffect as useEffect31,
|
|
12567
13078
|
useId as useId2,
|
|
12568
13079
|
useRef as useRef12,
|
|
12569
13080
|
useState as useState42
|
|
@@ -12573,7 +13084,7 @@ import { jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
|
12573
13084
|
function useMeasuredWidth(fallback = 480) {
|
|
12574
13085
|
const ref = useRef12(null);
|
|
12575
13086
|
const [width, setWidth] = useState42(fallback);
|
|
12576
|
-
|
|
13087
|
+
useEffect31(() => {
|
|
12577
13088
|
const node = ref.current;
|
|
12578
13089
|
if (!node) return;
|
|
12579
13090
|
const apply = () => {
|
|
@@ -12697,7 +13208,7 @@ function isSignatureField(field) {
|
|
|
12697
13208
|
}
|
|
12698
13209
|
|
|
12699
13210
|
// src/DocTemplate.tsx
|
|
12700
|
-
import { useCallback as useCallback26, useEffect as
|
|
13211
|
+
import { useCallback as useCallback26, useEffect as useEffect32, useState as useState43 } from "react";
|
|
12701
13212
|
import { useFields as useFields19, useRecordsClient as useRecordsClient29, useTable as useTable15 } from "@ai-matrx/records/react";
|
|
12702
13213
|
import { BasicInput as BasicInput14, BasicTextarea as BasicTextarea6, Button as Button38, Label as Label8, Skeleton as Skeleton22, cn as cn39 } from "@ai-matrx/design-system";
|
|
12703
13214
|
import { jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
@@ -12745,10 +13256,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12745
13256
|
setError(null);
|
|
12746
13257
|
setTemplates(rows);
|
|
12747
13258
|
}, [client, tableId, seed, fields.data]);
|
|
12748
|
-
|
|
13259
|
+
useEffect32(() => {
|
|
12749
13260
|
void load();
|
|
12750
13261
|
}, [load]);
|
|
12751
|
-
|
|
13262
|
+
useEffect32(() => {
|
|
12752
13263
|
if (!templates || templates.length === 0) return;
|
|
12753
13264
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
12754
13265
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -12756,7 +13267,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12756
13267
|
setDraftBody(chosen.body);
|
|
12757
13268
|
onActiveTemplate?.(chosen);
|
|
12758
13269
|
}, [templates, activeTemplateId]);
|
|
12759
|
-
|
|
13270
|
+
useEffect32(() => {
|
|
12760
13271
|
let cancelled = false;
|
|
12761
13272
|
if (draftBody.trim() === "") {
|
|
12762
13273
|
setUnresolved([]);
|
|
@@ -12867,16 +13378,21 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12867
13378
|
onChange: (e) => setDraftBody(e.target.value)
|
|
12868
13379
|
}
|
|
12869
13380
|
),
|
|
12870
|
-
unresolved.length > 0 ? /* @__PURE__ */ jsx45("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ jsxs41("li", { className: "
|
|
12871
|
-
/* @__PURE__ */ jsx45("code", { children: token.raw }),
|
|
12872
|
-
" \u2014
|
|
12873
|
-
|
|
13381
|
+
unresolved.length > 0 ? /* @__PURE__ */ jsx45("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ jsxs41("li", { className: "flex items-baseline gap-1", children: [
|
|
13382
|
+
/* @__PURE__ */ jsx45("code", { className: "text-destructive", children: token.raw }),
|
|
13383
|
+
/* @__PURE__ */ jsx45("span", { "aria-hidden": "true", children: "\u2014" }),
|
|
13384
|
+
/* @__PURE__ */ jsx45(
|
|
13385
|
+
RefusalLine,
|
|
13386
|
+
{
|
|
13387
|
+
error: refusal("invalid_argument", token.why, "Change it to a Field this table has, or take it out.")
|
|
13388
|
+
}
|
|
13389
|
+
)
|
|
12874
13390
|
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ jsx45("p", { className: "text-xs text-muted-foreground", children: "Every token in this body points at a Field this table can answer." }) : null
|
|
12875
13391
|
] });
|
|
12876
13392
|
}
|
|
12877
13393
|
|
|
12878
13394
|
// src/DocRender.tsx
|
|
12879
|
-
import { useCallback as useCallback27, useEffect as
|
|
13395
|
+
import { useCallback as useCallback27, useEffect as useEffect33, useRef as useRef13, useState as useState44 } from "react";
|
|
12880
13396
|
import { useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
|
|
12881
13397
|
import { Button as Button39, Skeleton as Skeleton23, cn as cn40 } from "@ai-matrx/design-system";
|
|
12882
13398
|
import { jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
@@ -12905,7 +13421,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
12905
13421
|
setPreview(body.data);
|
|
12906
13422
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
12907
13423
|
}, [client, templateId, recordId]);
|
|
12908
|
-
|
|
13424
|
+
useEffect33(() => {
|
|
12909
13425
|
void load();
|
|
12910
13426
|
}, [load]);
|
|
12911
13427
|
async function freeze() {
|
|
@@ -12989,7 +13505,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
12989
13505
|
}
|
|
12990
13506
|
|
|
12991
13507
|
// src/SignBlock.tsx
|
|
12992
|
-
import { useCallback as useCallback28, useEffect as
|
|
13508
|
+
import { useCallback as useCallback28, useEffect as useEffect34, useState as useState45 } from "react";
|
|
12993
13509
|
import { useFields as useFields20, useRecordsClient as useRecordsClient31 } from "@ai-matrx/records/react";
|
|
12994
13510
|
import { BasicInput as BasicInput15, Button as Button40, Skeleton as Skeleton24, cn as cn41 } from "@ai-matrx/design-system";
|
|
12995
13511
|
import { useTable as useTable16 } from "@ai-matrx/records/react";
|
|
@@ -13019,7 +13535,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
13019
13535
|
}
|
|
13020
13536
|
setVerdicts(answers);
|
|
13021
13537
|
}, [client, recordId]);
|
|
13022
|
-
|
|
13538
|
+
useEffect34(() => {
|
|
13023
13539
|
void load();
|
|
13024
13540
|
}, [load]);
|
|
13025
13541
|
async function sign(field) {
|
|
@@ -13075,7 +13591,21 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
13075
13591
|
signature.document_hash.slice(0, 12)
|
|
13076
13592
|
] })
|
|
13077
13593
|
] }),
|
|
13078
|
-
|
|
13594
|
+
intact === false ? (
|
|
13595
|
+
// THE STORE REFUSED TO CALL IT INTACT: its reason goes through the one
|
|
13596
|
+
// formatter, with what to do next, never raw in red (lane REFUSAL-SWEEP).
|
|
13597
|
+
/* @__PURE__ */ jsx47(
|
|
13598
|
+
RefusalLine,
|
|
13599
|
+
{
|
|
13600
|
+
className: "mt-0.5",
|
|
13601
|
+
error: refusal(
|
|
13602
|
+
"refused_by_rule",
|
|
13603
|
+
typeof verdict === "object" && verdict !== null && "reason" in verdict ? String(verdict.reason) : "The store says what is there no longer matches what was signed.",
|
|
13604
|
+
"Ask for it to be signed again on the version that is there now."
|
|
13605
|
+
)
|
|
13606
|
+
}
|
|
13607
|
+
)
|
|
13608
|
+
) : /* @__PURE__ */ jsx47("p", { className: "mt-0.5 text-muted-foreground", children: intact === true ? "The store says what was signed is still exactly what is there." : String(verdict ?? "") })
|
|
13079
13609
|
] }, signature.id);
|
|
13080
13610
|
}) }) : null,
|
|
13081
13611
|
!render ? /* @__PURE__ */ jsx47("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__ */ jsxs43("p", { className: "text-xs text-muted-foreground", children: [
|
|
@@ -13126,7 +13656,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
13126
13656
|
}
|
|
13127
13657
|
|
|
13128
13658
|
// src/NotifyRuleEditor.tsx
|
|
13129
|
-
import { useCallback as useCallback29, useEffect as
|
|
13659
|
+
import { useCallback as useCallback29, useEffect as useEffect35, useState as useState46 } from "react";
|
|
13130
13660
|
import { useRecordsClient as useRecordsClient32, useTable as useTable17 } from "@ai-matrx/records/react";
|
|
13131
13661
|
import { Button as Button41, Skeleton as Skeleton25, cn as cn42 } from "@ai-matrx/design-system";
|
|
13132
13662
|
import { jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
@@ -13162,7 +13692,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13162
13692
|
if (host.savedViews) setViews(await host.savedViews());
|
|
13163
13693
|
else setViews(null);
|
|
13164
13694
|
}, [client, host, tableId]);
|
|
13165
|
-
|
|
13695
|
+
useEffect35(() => {
|
|
13166
13696
|
void load();
|
|
13167
13697
|
}, [load]);
|
|
13168
13698
|
const write = useCallback29(
|
|
@@ -13190,7 +13720,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13190
13720
|
},
|
|
13191
13721
|
[client, tableId]
|
|
13192
13722
|
);
|
|
13193
|
-
|
|
13723
|
+
useEffect35(() => {
|
|
13194
13724
|
if (!subscriptions || !seed || seed.length === 0) return;
|
|
13195
13725
|
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
13196
13726
|
if (missing.length === 0) return;
|
|
@@ -13357,7 +13887,7 @@ import { Fragment as Fragment20, jsx as jsx49, jsxs as jsxs45 } from "react/jsx-
|
|
|
13357
13887
|
function pretty(n) {
|
|
13358
13888
|
return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
|
|
13359
13889
|
}
|
|
13360
|
-
function
|
|
13890
|
+
function asRefusal2(block) {
|
|
13361
13891
|
return mapPgError(
|
|
13362
13892
|
{
|
|
13363
13893
|
message: block.refused ?? "",
|
|
@@ -13418,7 +13948,7 @@ function ChartBlock({ block, subject, className }) {
|
|
|
13418
13948
|
" ms"
|
|
13419
13949
|
] }) : null
|
|
13420
13950
|
] }),
|
|
13421
|
-
block.refused ? /* @__PURE__ */ jsx49(RefusalNotice, { error:
|
|
13951
|
+
block.refused ? /* @__PURE__ */ jsx49(RefusalNotice, { error: asRefusal2(block) }) : needs ? /* @__PURE__ */ jsx49("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: needs }) : kind === "stuck" ? /* @__PURE__ */ jsx49(StuckList, { rows: block.rows ?? [] }) : points.length === 0 ? /* @__PURE__ */ jsx49("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__ */ jsx49(Fragment20, { children: kind === "number" ? /* @__PURE__ */ jsx49(BigNumber, { points, measure: primary, onDrill: drill }) : kind === "table" ? /* @__PURE__ */ jsx49(GroupTable, { points, series, onDrill: drill }) : /* @__PURE__ */ jsxs45(Fragment20, { children: [
|
|
13422
13952
|
/* @__PURE__ */ jsx49(Drawing, { kind, points, series, config, onDrill: drill }),
|
|
13423
13953
|
/* @__PURE__ */ jsx49(Values, { points, measure: primary, config, onDrill: drill })
|
|
13424
13954
|
] }) })
|
|
@@ -13646,7 +14176,7 @@ function Drawing({
|
|
|
13646
14176
|
}
|
|
13647
14177
|
|
|
13648
14178
|
// src/DashboardCanvas.tsx
|
|
13649
|
-
import { useCallback as useCallback30, useEffect as
|
|
14179
|
+
import { useCallback as useCallback30, useEffect as useEffect36, useMemo as useMemo28, useState as useState47 } from "react";
|
|
13650
14180
|
import { useFields as useFields21, useRecordsClient as useRecordsClient33, useTable as useTable18 } from "@ai-matrx/records/react";
|
|
13651
14181
|
import {
|
|
13652
14182
|
BasicInput as BasicInput16,
|
|
@@ -13683,17 +14213,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13683
14213
|
setError(null);
|
|
13684
14214
|
setBoards(answered.data.map(dashboardFromSummary));
|
|
13685
14215
|
}, [client, tableId]);
|
|
13686
|
-
|
|
14216
|
+
useEffect36(() => {
|
|
13687
14217
|
void load();
|
|
13688
14218
|
}, [load]);
|
|
13689
|
-
|
|
14219
|
+
useEffect36(() => {
|
|
13690
14220
|
if (!boards || boards.length === 0) return;
|
|
13691
14221
|
const chosen = boards.find((d) => d.id === (activeDashboardId ?? activeId)) ?? boards[0];
|
|
13692
14222
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
13693
14223
|
}, [boards, activeDashboardId]);
|
|
13694
14224
|
const board = useMemo28(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
|
|
13695
14225
|
const filterKey = JSON.stringify(filter ?? {});
|
|
13696
|
-
|
|
14226
|
+
useEffect36(() => {
|
|
13697
14227
|
if (!activeId) {
|
|
13698
14228
|
setRun(null);
|
|
13699
14229
|
return;
|
|
@@ -13989,18 +14519,19 @@ function GroupingPicker({
|
|
|
13989
14519
|
}
|
|
13990
14520
|
|
|
13991
14521
|
// src/FormsPanel.tsx
|
|
13992
|
-
import { useCallback as useCallback31, useEffect as
|
|
14522
|
+
import { useCallback as useCallback31, useEffect as useEffect37, useState as useState48 } from "react";
|
|
13993
14523
|
import { useRecordsClient as useRecordsClient34, useTable as useTable19 } from "@ai-matrx/records/react";
|
|
13994
14524
|
import { publicFormPath as publicFormPath2 } from "@ai-matrx/records";
|
|
13995
14525
|
import { Badge as Badge15, Button as Button43, Skeleton as Skeleton27, cn as cn45 } from "@ai-matrx/design-system";
|
|
13996
14526
|
import { Fragment as Fragment22, jsx as jsx51, jsxs as jsxs47 } from "react/jsx-runtime";
|
|
14527
|
+
var FORM_NOT_HERE_LINE = "The link named a form this table does not have \u2014 it may have been removed, or it belongs to another table. The forms this table does have are listed here.";
|
|
13997
14528
|
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.";
|
|
13998
14529
|
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.";
|
|
13999
14530
|
function formSuggestion(tableName2) {
|
|
14000
14531
|
const subject = tableName2?.trim() ? tableName2.trim() : "this table";
|
|
14001
14532
|
return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
|
|
14002
14533
|
}
|
|
14003
|
-
function FormsPanel({ tableId, className }) {
|
|
14534
|
+
function FormsPanel({ tableId, activeFormId, className }) {
|
|
14004
14535
|
const client = useRecordsClient34();
|
|
14005
14536
|
const publishGate = usePublishGate();
|
|
14006
14537
|
const host = useRecordsUi();
|
|
@@ -14011,6 +14542,11 @@ function FormsPanel({ tableId, className }) {
|
|
|
14011
14542
|
const [busy, setBusy] = useState48(null);
|
|
14012
14543
|
const [copied, setCopied] = useState48(null);
|
|
14013
14544
|
const [building, setBuilding] = useState48(false);
|
|
14545
|
+
const linked = activeFormId && forms !== null ? forms.find((f) => f.form_id === activeFormId) ?? null : null;
|
|
14546
|
+
const linkedMissing = Boolean(activeFormId) && forms !== null && linked === null;
|
|
14547
|
+
useEffect37(() => {
|
|
14548
|
+
if (linked && rights.structure) setBuilding(true);
|
|
14549
|
+
}, [linked, rights.structure]);
|
|
14014
14550
|
const load = useCallback31(async () => {
|
|
14015
14551
|
const answered = await client.forms({ table_id: tableId });
|
|
14016
14552
|
if (!answered.ok) {
|
|
@@ -14021,7 +14557,7 @@ function FormsPanel({ tableId, className }) {
|
|
|
14021
14557
|
setError(null);
|
|
14022
14558
|
setForms(answered.data);
|
|
14023
14559
|
}, [client, tableId]);
|
|
14024
|
-
|
|
14560
|
+
useEffect37(() => {
|
|
14025
14561
|
void load();
|
|
14026
14562
|
}, [load]);
|
|
14027
14563
|
const toggle = useCallback31(
|
|
@@ -14059,10 +14595,12 @@ function FormsPanel({ tableId, className }) {
|
|
|
14059
14595
|
rights.structure && forms.length > 0 ? /* @__PURE__ */ jsx51(Button43, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a form" }) : null
|
|
14060
14596
|
] }),
|
|
14061
14597
|
error ? /* @__PURE__ */ jsx51(RefusalNotice, { error }) : null,
|
|
14598
|
+
linkedMissing ? /* @__PURE__ */ jsx51("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: FORM_NOT_HERE_LINE }) : null,
|
|
14062
14599
|
building ? /* @__PURE__ */ jsx51(
|
|
14063
14600
|
FormBuilder,
|
|
14064
14601
|
{
|
|
14065
14602
|
tableId,
|
|
14603
|
+
activeFormId: linked ? linked.form_id : null,
|
|
14066
14604
|
onActiveForm: () => {
|
|
14067
14605
|
void load();
|
|
14068
14606
|
}
|
|
@@ -14088,57 +14626,69 @@ function FormsPanel({ tableId, className }) {
|
|
|
14088
14626
|
) : null,
|
|
14089
14627
|
/* @__PURE__ */ jsx51("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
|
|
14090
14628
|
const url = `${origin}${publicFormPath2(form.form_id)}`;
|
|
14091
|
-
return /* @__PURE__ */ jsxs47(
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
|
|
14101
|
-
"
|
|
14102
|
-
|
|
14103
|
-
|
|
14104
|
-
|
|
14105
|
-
|
|
14106
|
-
"
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14629
|
+
return /* @__PURE__ */ jsxs47(
|
|
14630
|
+
"li",
|
|
14631
|
+
{
|
|
14632
|
+
"data-linked": form.form_id === activeFormId ? "true" : void 0,
|
|
14633
|
+
ref: form.form_id === activeFormId ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
14634
|
+
className: cn45(
|
|
14635
|
+
"rounded-md border p-2.5",
|
|
14636
|
+
form.form_id === activeFormId && "border-primary ring-1 ring-primary"
|
|
14637
|
+
),
|
|
14638
|
+
children: [
|
|
14639
|
+
/* @__PURE__ */ jsxs47("div", { className: "flex items-center gap-2", children: [
|
|
14640
|
+
/* @__PURE__ */ jsx51("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: form.title ?? form.slug }),
|
|
14641
|
+
/* @__PURE__ */ jsx51(Badge15, { variant: form.state === "open" ? "default" : "secondary", children: form.state })
|
|
14642
|
+
] }),
|
|
14643
|
+
/* @__PURE__ */ jsx51("p", { className: "mt-0.5 text-xs text-muted-foreground", children: FORM_STATE_WORDS[form.state] }),
|
|
14644
|
+
/* @__PURE__ */ jsxs47("p", { className: "mt-1.5 text-xs", children: [
|
|
14645
|
+
/* @__PURE__ */ jsx51("span", { className: "font-medium", children: form.in_table }),
|
|
14646
|
+
" in the table",
|
|
14647
|
+
form.held > 0 ? /* @__PURE__ */ jsxs47(Fragment22, { children: [
|
|
14648
|
+
" \xB7 ",
|
|
14649
|
+
/* @__PURE__ */ jsx51("span", { className: "font-medium", children: form.held }),
|
|
14650
|
+
" waiting for someone"
|
|
14651
|
+
] }) : null,
|
|
14652
|
+
form.rejected > 0 ? /* @__PURE__ */ jsxs47(Fragment22, { children: [
|
|
14653
|
+
" \xB7 ",
|
|
14654
|
+
/* @__PURE__ */ jsx51("span", { className: "font-medium", children: form.rejected }),
|
|
14655
|
+
" turned away"
|
|
14656
|
+
] }) : null,
|
|
14657
|
+
form.submission_cap !== null ? /* @__PURE__ */ jsxs47("span", { className: "text-muted-foreground", children: [
|
|
14658
|
+
" \xB7 stops at ",
|
|
14659
|
+
form.submission_cap
|
|
14660
|
+
] }) : null
|
|
14661
|
+
] }),
|
|
14662
|
+
/* @__PURE__ */ jsxs47("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
14663
|
+
form.published_at ? /* @__PURE__ */ jsx51(Button43, { size: "sm", variant: "outline", onClick: () => void copy(url, form.form_id), children: copied === form.form_id ? "Copied" : "Copy link" }) : null,
|
|
14664
|
+
rights.structure && !publishGate.blocked ? /* @__PURE__ */ jsx51(
|
|
14665
|
+
Button43,
|
|
14666
|
+
{
|
|
14667
|
+
size: "sm",
|
|
14668
|
+
variant: "ghost",
|
|
14669
|
+
disabled: busy === form.form_id,
|
|
14670
|
+
onClick: () => void toggle(form),
|
|
14671
|
+
children: busy === form.form_id ? "\u2026" : form.published_at && !form.closed_at ? "Unpublish" : "Publish"
|
|
14672
|
+
}
|
|
14673
|
+
) : null
|
|
14674
|
+
] }),
|
|
14675
|
+
rights.structure && publishGate.why ? /* @__PURE__ */ jsx51("p", { className: "mt-1.5 text-xs text-muted-foreground", children: publishGate.why }) : null,
|
|
14676
|
+
shown && shown === url ? /* @__PURE__ */ jsxs47("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
14677
|
+
"This browser would not let the page copy for you, so here it is to copy by hand:",
|
|
14678
|
+
" ",
|
|
14679
|
+
url
|
|
14680
|
+
] }) : null
|
|
14681
|
+
]
|
|
14682
|
+
},
|
|
14683
|
+
form.form_id
|
|
14684
|
+
);
|
|
14135
14685
|
}) }),
|
|
14136
14686
|
!rights.known ? /* @__PURE__ */ jsx51("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
|
|
14137
14687
|
] });
|
|
14138
14688
|
}
|
|
14139
14689
|
|
|
14140
14690
|
// src/BookingBuilder.tsx
|
|
14141
|
-
import { useCallback as useCallback32, useEffect as
|
|
14691
|
+
import { useCallback as useCallback32, useEffect as useEffect38, useMemo as useMemo29, useState as useState49 } from "react";
|
|
14142
14692
|
import { useFields as useFields22, useRecordsClient as useRecordsClient35, useTable as useTable20 } from "@ai-matrx/records/react";
|
|
14143
14693
|
import {
|
|
14144
14694
|
bookingPath
|
|
@@ -14213,18 +14763,18 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
14213
14763
|
setLoaded(true);
|
|
14214
14764
|
if (mine) setFormId(mine.form_id);
|
|
14215
14765
|
}, [client, tableId, bookingId]);
|
|
14216
|
-
|
|
14766
|
+
useEffect38(() => {
|
|
14217
14767
|
void load();
|
|
14218
14768
|
}, [load]);
|
|
14219
|
-
|
|
14769
|
+
useEffect38(() => {
|
|
14220
14770
|
if (title !== "" || !table.data) return;
|
|
14221
14771
|
setTitle(existing?.title ?? `Book a ${minutes}-minute ${table.data.name} appointment`);
|
|
14222
14772
|
}, [table.data, existing]);
|
|
14223
|
-
|
|
14773
|
+
useEffect38(() => {
|
|
14224
14774
|
if (!existing) return;
|
|
14225
14775
|
setMinutes(existing.slot_minutes);
|
|
14226
14776
|
}, [existing]);
|
|
14227
|
-
|
|
14777
|
+
useEffect38(() => {
|
|
14228
14778
|
if (!offer) return;
|
|
14229
14779
|
setWindows(draftWindows(offer));
|
|
14230
14780
|
setMinutes(offer.slot_minutes);
|
|
@@ -14486,7 +15036,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
14486
15036
|
}
|
|
14487
15037
|
|
|
14488
15038
|
// src/BookingSlots.tsx
|
|
14489
|
-
import { useCallback as useCallback33, useEffect as
|
|
15039
|
+
import { useCallback as useCallback33, useEffect as useEffect39, useMemo as useMemo30, useState as useState50 } from "react";
|
|
14490
15040
|
import { useRecordsClient as useRecordsClient36, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
|
|
14491
15041
|
import { bookingPath as bookingPath2 } from "@ai-matrx/records";
|
|
14492
15042
|
import { Badge as Badge16, Button as Button45, Skeleton as Skeleton29, cn as cn47 } from "@ai-matrx/design-system";
|
|
@@ -14517,7 +15067,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
14517
15067
|
setError(null);
|
|
14518
15068
|
setPages(answered.data);
|
|
14519
15069
|
}, [client, tableId]);
|
|
14520
|
-
|
|
15070
|
+
useEffect39(() => {
|
|
14521
15071
|
void load();
|
|
14522
15072
|
}, [load]);
|
|
14523
15073
|
const subjectIds = useMemo30(
|
|
@@ -14686,12 +15236,12 @@ function nextInWords(page) {
|
|
|
14686
15236
|
}
|
|
14687
15237
|
|
|
14688
15238
|
// src/CaptureSheet.tsx
|
|
14689
|
-
import { useCallback as useCallback35, useEffect as
|
|
15239
|
+
import { useCallback as useCallback35, useEffect as useEffect41, useRef as useRef15, useState as useState52 } from "react";
|
|
14690
15240
|
import { useFields as useFields23, useRecordsClient as useRecordsClient38, useTable as useTable21 } from "@ai-matrx/records/react";
|
|
14691
15241
|
import { Button as Button47, Input as Input4, Skeleton as Skeleton31, Textarea as Textarea3, cn as cn49 } from "@ai-matrx/design-system";
|
|
14692
15242
|
|
|
14693
15243
|
// src/CaptureRun.tsx
|
|
14694
|
-
import { useCallback as useCallback34, useEffect as
|
|
15244
|
+
import { useCallback as useCallback34, useEffect as useEffect40, useMemo as useMemo31, useRef as useRef14, useState as useState51 } from "react";
|
|
14695
15245
|
import {
|
|
14696
15246
|
coerceTypedAnswer as coerceTypedAnswer2,
|
|
14697
15247
|
coercionLabel,
|
|
@@ -14750,7 +15300,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14750
15300
|
const [lastSynced, setLastSynced] = useState51(null);
|
|
14751
15301
|
const [sending, setSending] = useState51(false);
|
|
14752
15302
|
const queueRef = useRef14(null);
|
|
14753
|
-
|
|
15303
|
+
useEffect40(() => {
|
|
14754
15304
|
const q2 = openCaptureQueue({
|
|
14755
15305
|
onChange: (c, all) => {
|
|
14756
15306
|
setCounts(c);
|
|
@@ -14788,13 +15338,13 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14788
15338
|
void q2.sync().then(() => void q2.lastSyncedAt().then(setLastSynced));
|
|
14789
15339
|
return () => q2.dispose();
|
|
14790
15340
|
}, [client, host]);
|
|
14791
|
-
|
|
15341
|
+
useEffect40(() => {
|
|
14792
15342
|
if (given !== void 0) return;
|
|
14793
15343
|
let cancelled = false;
|
|
14794
15344
|
void client.captureOpen({ sheet_id: sheetId }).then((res) => {
|
|
14795
15345
|
if (cancelled) return;
|
|
14796
15346
|
if (res.ok) setFace(res.data);
|
|
14797
|
-
else setLoadFailed(res.error
|
|
15347
|
+
else setLoadFailed(refusalOr(res.error, "unreachable", "This sheet could not be opened."));
|
|
14798
15348
|
});
|
|
14799
15349
|
return () => {
|
|
14800
15350
|
cancelled = true;
|
|
@@ -14841,7 +15391,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14841
15391
|
const needed = questions.filter((x) => x.required && !answered(x.field));
|
|
14842
15392
|
if (needed.length > 0) {
|
|
14843
15393
|
setMissing(
|
|
14844
|
-
|
|
15394
|
+
refusal(
|
|
15395
|
+
"invalid_argument",
|
|
15396
|
+
`${needed.map((x) => (x.ask ?? x.field).replace(/\?$/, "")).join(", ")} still ${needed.length === 1 ? "needs" : "need"} an answer.`,
|
|
15397
|
+
"Answer it, then capture again. Nothing has been queued yet."
|
|
15398
|
+
)
|
|
14845
15399
|
);
|
|
14846
15400
|
setAt(questions.findIndex((x) => x.field === needed[0].field));
|
|
14847
15401
|
return;
|
|
@@ -14872,7 +15426,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14872
15426
|
if (online) await sync();
|
|
14873
15427
|
}
|
|
14874
15428
|
if (loadFailed) {
|
|
14875
|
-
return /* @__PURE__ */ jsx54("section", { className: cn48("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx54("
|
|
15429
|
+
return /* @__PURE__ */ jsx54("section", { className: cn48("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx54("div", { "data-testid": "capture-refusal", children: /* @__PURE__ */ jsx54(RefusalNotice, { error: loadFailed, className: "text-sm" }) }) });
|
|
14876
15430
|
}
|
|
14877
15431
|
if (face === void 0) {
|
|
14878
15432
|
return /* @__PURE__ */ jsx54(Skeleton30, { className: cn48("mx-auto h-64 w-full max-w-sm", className) });
|
|
@@ -14903,7 +15457,21 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14903
15457
|
] });
|
|
14904
15458
|
const queuePanel = items.filter((i) => i.state !== "landed").length > 0 ? /* @__PURE__ */ jsx54("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__ */ jsxs50("li", { className: "flex items-start gap-2 text-[11px]", children: [
|
|
14905
15459
|
/* @__PURE__ */ jsx54("span", { className: "tabular-nums text-muted-foreground", children: new Date(i.captured_at).toLocaleTimeString() }),
|
|
14906
|
-
|
|
15460
|
+
i.state === "refused" ? (
|
|
15461
|
+
// THE STORE REFUSED THIS ONE: a refusal line, heading and remedy
|
|
15462
|
+
// both — never its raw words in red (lane REFUSAL-SWEEP).
|
|
15463
|
+
/* @__PURE__ */ jsx54(
|
|
15464
|
+
RefusalLine,
|
|
15465
|
+
{
|
|
15466
|
+
className: "flex-1",
|
|
15467
|
+
error: refusal(
|
|
15468
|
+
"refused_by_rule",
|
|
15469
|
+
i.last_error ?? "This one was refused.",
|
|
15470
|
+
"Throw it away and capture it again with the answer changed."
|
|
15471
|
+
)
|
|
15472
|
+
}
|
|
15473
|
+
)
|
|
15474
|
+
) : /* @__PURE__ */ jsx54("span", { className: "flex-1", children: i.last_error ?? "Waiting for a signal." }),
|
|
14907
15475
|
i.state === "refused" ? /* @__PURE__ */ jsx54(
|
|
14908
15476
|
Button46,
|
|
14909
15477
|
{
|
|
@@ -15023,7 +15591,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
15023
15591
|
}
|
|
15024
15592
|
}
|
|
15025
15593
|
),
|
|
15026
|
-
missing ? /* @__PURE__ */ jsx54("
|
|
15594
|
+
missing ? /* @__PURE__ */ jsx54("div", { "data-testid": "capture-missing", children: /* @__PURE__ */ jsx54(RefusalNotice, { error: missing, className: "text-sm" }) }) : null,
|
|
15027
15595
|
/* @__PURE__ */ jsxs50("div", { className: "flex items-center gap-2", children: [
|
|
15028
15596
|
at > 0 ? /* @__PURE__ */ jsx54(
|
|
15029
15597
|
Button46,
|
|
@@ -15120,7 +15688,7 @@ function AdHocCaptureSheet({
|
|
|
15120
15688
|
},
|
|
15121
15689
|
[host]
|
|
15122
15690
|
);
|
|
15123
|
-
|
|
15691
|
+
useEffect41(() => {
|
|
15124
15692
|
let cancelled = false;
|
|
15125
15693
|
void (async () => {
|
|
15126
15694
|
const held = host.captureQueue ? await host.captureQueue.load() : [];
|
|
@@ -15251,13 +15819,13 @@ function AdHocCaptureSheet({
|
|
|
15251
15819
|
setUploadError(null);
|
|
15252
15820
|
void host.upload?.(file).then((result) => {
|
|
15253
15821
|
if (result.ok) setFileId(result.fileId);
|
|
15254
|
-
else setUploadError(result.reason);
|
|
15822
|
+
else setUploadError(refusal("invalid_argument", result.reason, "Pick another file, or try this one again."));
|
|
15255
15823
|
});
|
|
15256
15824
|
}
|
|
15257
15825
|
}
|
|
15258
15826
|
),
|
|
15259
15827
|
fileId ? /* @__PURE__ */ jsx55("p", { className: "text-[11px] text-muted-foreground", children: "Attached." }) : null,
|
|
15260
|
-
uploadError ? /* @__PURE__ */ jsx55(
|
|
15828
|
+
uploadError ? /* @__PURE__ */ jsx55(RefusalLine, { error: uploadError }) : null
|
|
15261
15829
|
] }) : null,
|
|
15262
15830
|
/* @__PURE__ */ jsx55(
|
|
15263
15831
|
Textarea3,
|
|
@@ -15299,7 +15867,7 @@ function AdHocCaptureSheet({
|
|
|
15299
15867
|
}
|
|
15300
15868
|
|
|
15301
15869
|
// src/PortalShell.tsx
|
|
15302
|
-
import { useCallback as useCallback36, useEffect as
|
|
15870
|
+
import { useCallback as useCallback36, useEffect as useEffect42, useState as useState53 } from "react";
|
|
15303
15871
|
import { useRecordsClient as useRecordsClient39 } from "@ai-matrx/records/react";
|
|
15304
15872
|
import { Button as Button48, Skeleton as Skeleton32, cn as cn50 } from "@ai-matrx/design-system";
|
|
15305
15873
|
import { jsx as jsx56, jsxs as jsxs52 } from "react/jsx-runtime";
|
|
@@ -15325,7 +15893,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
|
15325
15893
|
}
|
|
15326
15894
|
setReach(reached.data.map((row) => row.resource_id));
|
|
15327
15895
|
}, [client, resourceType]);
|
|
15328
|
-
|
|
15896
|
+
useEffect42(() => {
|
|
15329
15897
|
void load();
|
|
15330
15898
|
}, [load]);
|
|
15331
15899
|
if (error) {
|
|
@@ -15385,7 +15953,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
|
15385
15953
|
function PortalRow({ tableId, recordId }) {
|
|
15386
15954
|
const client = useRecordsClient39();
|
|
15387
15955
|
const [title, setTitle] = useState53(null);
|
|
15388
|
-
|
|
15956
|
+
useEffect42(() => {
|
|
15389
15957
|
let cancelled = false;
|
|
15390
15958
|
void client.recordRead({ record_id: recordId }).then((answered) => {
|
|
15391
15959
|
if (cancelled) return;
|
|
@@ -15405,7 +15973,7 @@ function PortalRow({ tableId, recordId }) {
|
|
|
15405
15973
|
}
|
|
15406
15974
|
|
|
15407
15975
|
// src/PublicViewPage.tsx
|
|
15408
|
-
import { useCallback as useCallback37, useEffect as
|
|
15976
|
+
import { useCallback as useCallback37, useEffect as useEffect43, useState as useState54 } from "react";
|
|
15409
15977
|
import { useRecordsClient as useRecordsClient40 } from "@ai-matrx/records/react";
|
|
15410
15978
|
import { Skeleton as Skeleton33, cn as cn51 } from "@ai-matrx/design-system";
|
|
15411
15979
|
import { jsx as jsx57, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
@@ -15449,7 +16017,7 @@ function PublicViewPage({ slug, className }) {
|
|
|
15449
16017
|
}
|
|
15450
16018
|
setRows([{ id: found.resource_id, document: read.data.document, level: "viewer", hidden: read.data.hidden }]);
|
|
15451
16019
|
}, [client, slug]);
|
|
15452
|
-
|
|
16020
|
+
useEffect43(() => {
|
|
15453
16021
|
void load();
|
|
15454
16022
|
}, [load]);
|
|
15455
16023
|
if (error) return /* @__PURE__ */ jsx57(RefusalNotice, { error, className });
|
|
@@ -15486,7 +16054,7 @@ function PublicRow({ row, fields }) {
|
|
|
15486
16054
|
}
|
|
15487
16055
|
|
|
15488
16056
|
// src/EmbedFrame.tsx
|
|
15489
|
-
import { useCallback as useCallback38, useEffect as
|
|
16057
|
+
import { useCallback as useCallback38, useEffect as useEffect44, useState as useState55 } from "react";
|
|
15490
16058
|
import { useRecordsClient as useRecordsClient41 } from "@ai-matrx/records/react";
|
|
15491
16059
|
import { Button as Button49, Input as Input5, Skeleton as Skeleton34, Textarea as Textarea4, cn as cn52 } from "@ai-matrx/design-system";
|
|
15492
16060
|
import { useTable as useTable22 } from "@ai-matrx/records/react";
|
|
@@ -15599,7 +16167,7 @@ function useEmbedHandshake(args) {
|
|
|
15599
16167
|
const [loading, setLoading] = useState55(true);
|
|
15600
16168
|
const origin = args.origin ?? (typeof location === "undefined" ? "" : location.origin);
|
|
15601
16169
|
const { secret, requiredMode } = args;
|
|
15602
|
-
|
|
16170
|
+
useEffect44(() => {
|
|
15603
16171
|
let cancelled = false;
|
|
15604
16172
|
setLoading(true);
|
|
15605
16173
|
setError(null);
|
|
@@ -15654,7 +16222,7 @@ function recordsDataSource(client, fallbackSchema = "custom") {
|
|
|
15654
16222
|
}
|
|
15655
16223
|
|
|
15656
16224
|
// src/TablesHome.tsx
|
|
15657
|
-
import { useCallback as useCallback39, useEffect as
|
|
16225
|
+
import { useCallback as useCallback39, useEffect as useEffect45, useState as useState56 } from "react";
|
|
15658
16226
|
import { useRecordsClient as useRecordsClient42, useTables as useTables4 } from "@ai-matrx/records/react";
|
|
15659
16227
|
import { BasicInput as BasicInput18, Button as Button50, Skeleton as Skeleton35, cn as cn53 } from "@ai-matrx/design-system";
|
|
15660
16228
|
|
|
@@ -15720,17 +16288,17 @@ async function declareTable(client, spec) {
|
|
|
15720
16288
|
if (!written.ok) return undo(client, table.data, home.data, spec.name, written.error);
|
|
15721
16289
|
return { ok: true, data: table.data, homeId: home.data };
|
|
15722
16290
|
}
|
|
15723
|
-
async function undo(client, tableId, homeId, name,
|
|
16291
|
+
async function undo(client, tableId, homeId, name, refusal2) {
|
|
15724
16292
|
const removed = await client.recordDelete({ record_id: tableId });
|
|
15725
16293
|
if (removed.ok) {
|
|
15726
16294
|
await client.recordDelete({ record_id: homeId });
|
|
15727
|
-
return { ok: false, error:
|
|
16295
|
+
return { ok: false, error: refusal2 };
|
|
15728
16296
|
}
|
|
15729
16297
|
return {
|
|
15730
16298
|
ok: false,
|
|
15731
16299
|
error: {
|
|
15732
|
-
...
|
|
15733
|
-
hint: `${
|
|
16300
|
+
...refusal2,
|
|
16301
|
+
hint: `${refusal2.hint ? `${refusal2.hint} ` : ""}"${name}" was created before this happened and could not be removed again, so it is in your tables with no columns \u2014 open it and add the first column, or delete it from its own Settings.`
|
|
15734
16302
|
}
|
|
15735
16303
|
};
|
|
15736
16304
|
}
|
|
@@ -15783,13 +16351,31 @@ var LANE_EMPTY = {
|
|
|
15783
16351
|
community: "Nothing shared beyond your organization. A table published by link or to the world lands here.",
|
|
15784
16352
|
app: "The app has not needed a table of its own in this organization yet. Saving a view, writing a comment or building a form makes one, and it appears here rather than among your own data."
|
|
15785
16353
|
};
|
|
16354
|
+
function keptByTheApp(table) {
|
|
16355
|
+
return table.kept_by_the_app === true;
|
|
16356
|
+
}
|
|
15786
16357
|
function laneFor(table) {
|
|
15787
16358
|
if (table.is_kernel) return "system";
|
|
15788
|
-
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX)) return "app";
|
|
16359
|
+
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX) || keptByTheApp(table)) return "app";
|
|
15789
16360
|
if (table.visibility === "public" || table.visibility === "link") return "community";
|
|
15790
16361
|
if (table.visibility === "personal") return "mine";
|
|
15791
16362
|
return "organization";
|
|
15792
16363
|
}
|
|
16364
|
+
var VISIBILITY_LANES = ["mine", "organization", "community", "world"];
|
|
16365
|
+
var VISIBILITY_LANE_TITLE = {
|
|
16366
|
+
mine: "Mine",
|
|
16367
|
+
organization: "My organization",
|
|
16368
|
+
community: "Community",
|
|
16369
|
+
world: "World"
|
|
16370
|
+
};
|
|
16371
|
+
function visibilityLaneFor(table) {
|
|
16372
|
+
if (table.is_kernel) return null;
|
|
16373
|
+
if ((table.slug ?? "").startsWith(APP_TABLE_PREFIX) || keptByTheApp(table)) return null;
|
|
16374
|
+
if (table.visibility === "personal") return "mine";
|
|
16375
|
+
if (table.visibility === "link") return "community";
|
|
16376
|
+
if (table.visibility === "public") return "world";
|
|
16377
|
+
return "organization";
|
|
16378
|
+
}
|
|
15793
16379
|
function TablesHome({ onOpenTable, className }) {
|
|
15794
16380
|
const client = useRecordsClient42();
|
|
15795
16381
|
const tables = useTables4();
|
|
@@ -15799,7 +16385,7 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
15799
16385
|
const [error, setError] = useState56(null);
|
|
15800
16386
|
const [importInto, setImportInto] = useState56(null);
|
|
15801
16387
|
const [boards, setBoards] = useState56(null);
|
|
15802
|
-
|
|
16388
|
+
useEffect45(() => {
|
|
15803
16389
|
let cancelled = false;
|
|
15804
16390
|
void client.dashboards({}).then((result) => {
|
|
15805
16391
|
if (cancelled) return;
|
|
@@ -15933,10 +16519,47 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
15933
16519
|
}
|
|
15934
16520
|
|
|
15935
16521
|
// src/TablePage.tsx
|
|
15936
|
-
import { useCallback as useCallback40, useEffect as
|
|
16522
|
+
import { useCallback as useCallback40, useEffect as useEffect46, useState as useState57 } from "react";
|
|
15937
16523
|
import { useFields as useFields24, useRecordsClient as useRecordsClient43, useTable as useTable23 } from "@ai-matrx/records/react";
|
|
15938
|
-
import { Button as Button51, Separator as Separator12, Skeleton as Skeleton36, cn as cn54 } from "@ai-matrx/design-system";
|
|
16524
|
+
import { BottomSheet, Button as Button51, Separator as Separator12, Skeleton as Skeleton36, cn as cn54, useIsMobile } from "@ai-matrx/design-system";
|
|
15939
16525
|
import { Fragment as Fragment28, jsx as jsx61, jsxs as jsxs56 } from "react/jsx-runtime";
|
|
16526
|
+
var RAIL_TITLE = {
|
|
16527
|
+
settings: "Table settings",
|
|
16528
|
+
inbox: "Inbox",
|
|
16529
|
+
import: "Import",
|
|
16530
|
+
field: "Add a field",
|
|
16531
|
+
record: "Record",
|
|
16532
|
+
"new-record": "New record",
|
|
16533
|
+
forms: "Forms",
|
|
16534
|
+
bookings: "Bookings",
|
|
16535
|
+
checklists: "Checklists",
|
|
16536
|
+
notifications: "Notifications",
|
|
16537
|
+
portals: "Portals",
|
|
16538
|
+
"who-changed": "Who changed this"
|
|
16539
|
+
};
|
|
16540
|
+
function RailFrame({
|
|
16541
|
+
phone,
|
|
16542
|
+
title,
|
|
16543
|
+
onClose,
|
|
16544
|
+
children
|
|
16545
|
+
}) {
|
|
16546
|
+
if (phone) {
|
|
16547
|
+
return /* @__PURE__ */ jsx61(
|
|
16548
|
+
BottomSheet,
|
|
16549
|
+
{
|
|
16550
|
+
open: true,
|
|
16551
|
+
onOpenChange: (open) => {
|
|
16552
|
+
if (!open) onClose();
|
|
16553
|
+
},
|
|
16554
|
+
title,
|
|
16555
|
+
size: "full",
|
|
16556
|
+
surface: "solid",
|
|
16557
|
+
children: /* @__PURE__ */ jsx61("div", { "data-rail-sheet": "", className: "min-h-0 flex-1 overflow-y-auto px-3 pb-6", children })
|
|
16558
|
+
}
|
|
16559
|
+
);
|
|
16560
|
+
}
|
|
16561
|
+
return /* @__PURE__ */ jsx61("aside", { "data-rail-column": "", className: "w-[26rem] shrink-0 overflow-y-auto rounded-md border p-3", children });
|
|
16562
|
+
}
|
|
15940
16563
|
function filterInWords(filter, fields) {
|
|
15941
16564
|
const nameOf = (key) => {
|
|
15942
16565
|
const field = fields.find((f) => f.key === key);
|
|
@@ -15984,9 +16607,43 @@ function chooseSurface(current, pressed) {
|
|
|
15984
16607
|
function surfaceChosen(current, asking) {
|
|
15985
16608
|
return asking.main !== void 0 ? current.main === asking.main : current.rail === asking.rail;
|
|
15986
16609
|
}
|
|
15987
|
-
function openingRail(activeRecordId) {
|
|
15988
|
-
|
|
16610
|
+
function openingRail(activeRecordId, activeRail) {
|
|
16611
|
+
if (activeRecordId) return { rail: "record", record: activeRecordId };
|
|
16612
|
+
const named = railFromParam(activeRail);
|
|
16613
|
+
if (named !== null && named !== "share") return { rail: named, record: null };
|
|
16614
|
+
return { rail: "none", record: null };
|
|
16615
|
+
}
|
|
16616
|
+
var RAIL_SPELLINGS = {
|
|
16617
|
+
forms: "forms",
|
|
16618
|
+
form: "forms",
|
|
16619
|
+
bookings: "bookings",
|
|
16620
|
+
booking: "bookings",
|
|
16621
|
+
checklists: "checklists",
|
|
16622
|
+
checklist: "checklists",
|
|
16623
|
+
notifications: "notifications",
|
|
16624
|
+
notification: "notifications",
|
|
16625
|
+
digests: "notifications",
|
|
16626
|
+
digest: "notifications",
|
|
16627
|
+
subscriptions: "notifications",
|
|
16628
|
+
subscription: "notifications",
|
|
16629
|
+
portals: "portals",
|
|
16630
|
+
portal: "portals",
|
|
16631
|
+
settings: "settings",
|
|
16632
|
+
import: "import",
|
|
16633
|
+
inbox: "inbox",
|
|
16634
|
+
share: "share",
|
|
16635
|
+
sharing: "share"
|
|
16636
|
+
};
|
|
16637
|
+
function railFromParam(raw) {
|
|
16638
|
+
if (typeof raw !== "string") return null;
|
|
16639
|
+
const token = raw.trim().toLowerCase();
|
|
16640
|
+
if (token === "") return null;
|
|
16641
|
+
return RAIL_SPELLINGS[token] ?? null;
|
|
16642
|
+
}
|
|
16643
|
+
function unknownRailLine(raw) {
|
|
16644
|
+
return `The link asked to open \u201C${raw}\u201D on this table, which is not something a table has, so you are looking at its records. A table opens Forms, Bookings, Checklists, Notifications, Portals, Settings, Import, Inbox and Share \u2014 each is a button above.`;
|
|
15989
16645
|
}
|
|
16646
|
+
var SHARE_NOT_YOURS_LINE = "The link asked to open this table's sharing, and only someone with Admin on this table can share it, so it did not open. Ask an admin of this table to share it or to give you Admin.";
|
|
15990
16647
|
function openingView(activeView, activeDashboardId) {
|
|
15991
16648
|
const named = pageViewFromParam(activeView);
|
|
15992
16649
|
if (named === null) {
|
|
@@ -16014,6 +16671,8 @@ function TablePage({
|
|
|
16014
16671
|
activeGroupField,
|
|
16015
16672
|
cameFrom,
|
|
16016
16673
|
filter,
|
|
16674
|
+
activeRail,
|
|
16675
|
+
activeItemId,
|
|
16017
16676
|
className
|
|
16018
16677
|
}) {
|
|
16019
16678
|
const client = useRecordsClient43();
|
|
@@ -16023,8 +16682,13 @@ function TablePage({
|
|
|
16023
16682
|
const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
|
|
16024
16683
|
const organizationId = useRecordsClient43().config.organizationId;
|
|
16025
16684
|
const [view, setView] = useState57(null);
|
|
16026
|
-
const opening = openingRail(activeRecordId);
|
|
16685
|
+
const opening = openingRail(activeRecordId, activeRail);
|
|
16027
16686
|
const opened = openingView(activeView, activeDashboardId);
|
|
16687
|
+
const railAsked = typeof activeRail === "string" ? activeRail.trim() : "";
|
|
16688
|
+
const railUnknown = railAsked !== "" && railFromParam(railAsked) === null ? railAsked : null;
|
|
16689
|
+
const shareFromLink = railFromParam(activeRail) === "share";
|
|
16690
|
+
const canShare = useCanShare();
|
|
16691
|
+
const itemFor = (which) => railFromParam(activeRail) === which ? activeItemId ?? null : null;
|
|
16028
16692
|
const [asking, setAsking] = useState57(null);
|
|
16029
16693
|
const [layoutFromLink, setLayoutFromLink] = useState57(opened.layout);
|
|
16030
16694
|
const [surface, setSurface] = useState57({
|
|
@@ -16035,6 +16699,7 @@ function TablePage({
|
|
|
16035
16699
|
const setRail = (next) => setSurface((now) => ({ ...now, rail: next }));
|
|
16036
16700
|
const [openRecord, setOpenRecord] = useState57(opening.record);
|
|
16037
16701
|
const viewVersion = useRecordVersion(view?.id ?? null);
|
|
16702
|
+
const phone = useIsMobile();
|
|
16038
16703
|
const shownLayout = layoutFromLink ?? view?.layout ?? "grid";
|
|
16039
16704
|
const press = (pressed) => {
|
|
16040
16705
|
const next = chooseSurface(surface, pressed);
|
|
@@ -16044,18 +16709,25 @@ function TablePage({
|
|
|
16044
16709
|
}
|
|
16045
16710
|
};
|
|
16046
16711
|
const show = (next) => press({ rail: next });
|
|
16047
|
-
|
|
16712
|
+
useEffect46(() => {
|
|
16048
16713
|
if (!activeRecordId) return;
|
|
16049
16714
|
setOpenRecord(activeRecordId);
|
|
16050
16715
|
setSurface((now) => ({ ...now, rail: "record" }));
|
|
16051
16716
|
}, [activeRecordId]);
|
|
16052
|
-
|
|
16717
|
+
useEffect46(() => {
|
|
16718
|
+
if (activeRecordId) return;
|
|
16719
|
+
const named = railFromParam(activeRail);
|
|
16720
|
+
if (named === null || named === "share") return;
|
|
16721
|
+
setSurface((now) => ({ ...now, rail: named }));
|
|
16722
|
+
}, [activeRail, activeRecordId]);
|
|
16723
|
+
useEffect46(() => {
|
|
16053
16724
|
const named = pageViewFromParam(activeView);
|
|
16054
16725
|
if (named === null) return;
|
|
16055
16726
|
const isLayout = named !== "dashboards" && named !== "archived";
|
|
16056
16727
|
setLayoutFromLink(isLayout ? named : null);
|
|
16057
16728
|
setSurface((now) => ({ ...now, main: isLayout ? "records" : named }));
|
|
16058
16729
|
}, [activeView]);
|
|
16730
|
+
const [viewSaveRefused, setViewSaveRefused] = useState57(null);
|
|
16059
16731
|
const patchView = useCallback40(
|
|
16060
16732
|
async (patch) => {
|
|
16061
16733
|
const current = view;
|
|
@@ -16063,12 +16735,13 @@ function TablePage({
|
|
|
16063
16735
|
setView({ ...current, ...patch });
|
|
16064
16736
|
const document2 = viewPatchDocument(patch);
|
|
16065
16737
|
if (Object.keys(document2).length === 0) return;
|
|
16066
|
-
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16738
|
+
setViewSaveRefused(null);
|
|
16739
|
+
const written = await saveViewPatch(client, current.id, document2, viewVersion.version);
|
|
16740
|
+
if (written.ok) {
|
|
16741
|
+
viewVersion.note(written.data);
|
|
16742
|
+
return;
|
|
16743
|
+
}
|
|
16744
|
+
setViewSaveRefused({ error: written.error, patch, before: current });
|
|
16072
16745
|
},
|
|
16073
16746
|
[client, view, viewVersion]
|
|
16074
16747
|
);
|
|
@@ -16213,11 +16886,33 @@ function TablePage({
|
|
|
16213
16886
|
organizationId,
|
|
16214
16887
|
subjectId: tableId,
|
|
16215
16888
|
name: table.data?.name,
|
|
16216
|
-
may: rights.share
|
|
16889
|
+
may: rights.share,
|
|
16890
|
+
initiallyOpen: shareFromLink
|
|
16217
16891
|
}
|
|
16218
16892
|
),
|
|
16219
16893
|
/* @__PURE__ */ jsx61(ExportMenu, { tableId })
|
|
16220
16894
|
] }),
|
|
16895
|
+
viewSaveRefused ? /* @__PURE__ */ jsx61(
|
|
16896
|
+
RefusalNotice,
|
|
16897
|
+
{
|
|
16898
|
+
error: viewSaveRefused.error,
|
|
16899
|
+
actions: /* @__PURE__ */ jsxs56("span", { className: "flex flex-wrap gap-1 pt-0.5", "data-view-save-refused": "", children: [
|
|
16900
|
+
/* @__PURE__ */ jsx61(Button51, { size: "sm", variant: "outline", onClick: () => void patchView(viewSaveRefused.patch), children: "Try again" }),
|
|
16901
|
+
/* @__PURE__ */ jsx61(
|
|
16902
|
+
Button51,
|
|
16903
|
+
{
|
|
16904
|
+
size: "sm",
|
|
16905
|
+
variant: "ghost",
|
|
16906
|
+
onClick: () => {
|
|
16907
|
+
setView(viewSaveRefused.before);
|
|
16908
|
+
setViewSaveRefused(null);
|
|
16909
|
+
},
|
|
16910
|
+
children: "Discard"
|
|
16911
|
+
}
|
|
16912
|
+
)
|
|
16913
|
+
] })
|
|
16914
|
+
}
|
|
16915
|
+
) : null,
|
|
16221
16916
|
main === "dashboards" ? /* @__PURE__ */ jsx61(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : main === "archived" ? (
|
|
16222
16917
|
/* THE ROWS THAT LEFT. Restoring one puts it back in the grid, so the
|
|
16223
16918
|
page returns to the records rather than leaving the person looking
|
|
@@ -16236,6 +16931,8 @@ function TablePage({
|
|
|
16236
16931
|
}
|
|
16237
16932
|
)
|
|
16238
16933
|
) : /* @__PURE__ */ jsxs56(Fragment28, { children: [
|
|
16934
|
+
railUnknown ? /* @__PURE__ */ jsx61("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: unknownRailLine(railUnknown) }) : null,
|
|
16935
|
+
shareFromLink && rights.known && (!rights.share || !canShare) ? /* @__PURE__ */ jsx61("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: canShare ? SHARE_NOT_YOURS_LINE : shareUnavailableReason() }) : null,
|
|
16239
16936
|
opened.unknown ? /* @__PURE__ */ jsx61("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: unknownViewLine(opened.unknown) }) : null,
|
|
16240
16937
|
cameFrom ? /* @__PURE__ */ jsx61(
|
|
16241
16938
|
"p",
|
|
@@ -16276,7 +16973,7 @@ function TablePage({
|
|
|
16276
16973
|
view ? null : /* @__PURE__ */ jsx61("p", { className: "text-xs text-muted-foreground", children: VIEW_NOT_SAVED_YET })
|
|
16277
16974
|
] })
|
|
16278
16975
|
] }),
|
|
16279
|
-
rail === "none" ? null : /* @__PURE__ */ jsxs56(
|
|
16976
|
+
rail === "none" ? null : /* @__PURE__ */ jsxs56(RailFrame, { phone, title: RAIL_TITLE[rail], onClose: () => setRail("none"), children: [
|
|
16280
16977
|
rail === "settings" ? /* @__PURE__ */ jsx61(
|
|
16281
16978
|
TableSettings,
|
|
16282
16979
|
{
|
|
@@ -16294,7 +16991,7 @@ function TablePage({
|
|
|
16294
16991
|
}
|
|
16295
16992
|
}
|
|
16296
16993
|
) : null,
|
|
16297
|
-
rail === "forms" ? /* @__PURE__ */ jsx61(FormsPanel, { tableId }) : null,
|
|
16994
|
+
rail === "forms" ? /* @__PURE__ */ jsx61(FormsPanel, { tableId, activeFormId: itemFor("forms") }) : null,
|
|
16298
16995
|
rail === "bookings" ? /* @__PURE__ */ jsx61(BookingSlots, { tableId }) : null,
|
|
16299
16996
|
rail === "checklists" ? /* @__PURE__ */ jsx61(
|
|
16300
16997
|
ChecklistsPanel,
|
|
@@ -16306,8 +17003,8 @@ function TablePage({
|
|
|
16306
17003
|
}
|
|
16307
17004
|
}
|
|
16308
17005
|
) : null,
|
|
16309
|
-
rail === "notifications" ? /* @__PURE__ */ jsx61(SubscriptionsPanel, { tableId }) : null,
|
|
16310
|
-
rail === "portals" ? /* @__PURE__ */ jsx61(PortalsPanel, { tableId }) : null,
|
|
17006
|
+
rail === "notifications" ? /* @__PURE__ */ jsx61(SubscriptionsPanel, { tableId, activeRuleId: itemFor("notifications") }) : null,
|
|
17007
|
+
rail === "portals" ? /* @__PURE__ */ jsx61(PortalsPanel, { tableId, activePortalId: itemFor("portals") }) : null,
|
|
16311
17008
|
rail === "import" ? /* @__PURE__ */ jsx61(ImportWizard, { tableId, onDone: () => setRail("none") }) : null,
|
|
16312
17009
|
rail === "field" ? /* @__PURE__ */ jsx61(FieldEditor, { tableId, onSaved: () => setRail("none"), onCancel: () => setRail("none") }) : null,
|
|
16313
17010
|
rail === "new-record" ? /* @__PURE__ */ jsx61(
|
|
@@ -16414,6 +17111,7 @@ export {
|
|
|
16414
17111
|
FORMULA_OP_LABEL,
|
|
16415
17112
|
FORMULA_OP_VALUES,
|
|
16416
17113
|
FORM_FLOWS,
|
|
17114
|
+
FORM_NOT_HERE_LINE,
|
|
16417
17115
|
FROZEN_COLUMN_WIDTH,
|
|
16418
17116
|
FieldControl,
|
|
16419
17117
|
FieldEditor,
|
|
@@ -16461,6 +17159,7 @@ export {
|
|
|
16461
17159
|
PAGE_VIEW_LABEL,
|
|
16462
17160
|
PARITY_LABEL,
|
|
16463
17161
|
PARITY_MADE_OF,
|
|
17162
|
+
PORTAL_NOT_HERE_LINE,
|
|
16464
17163
|
PROPOSED_CHANGE_ACT_LABEL,
|
|
16465
17164
|
PROPOSED_CHANGE_ACT_VALUES,
|
|
16466
17165
|
Peek,
|
|
@@ -16475,6 +17174,7 @@ export {
|
|
|
16475
17174
|
PublicViewPage,
|
|
16476
17175
|
ROLLUP_AGG_LABEL,
|
|
16477
17176
|
ROLLUP_AGG_VALUES,
|
|
17177
|
+
RULE_NOT_HERE_LINE,
|
|
16478
17178
|
RecordChat,
|
|
16479
17179
|
RecordChip,
|
|
16480
17180
|
RecordForm,
|
|
@@ -16489,6 +17189,7 @@ export {
|
|
|
16489
17189
|
SAVED_VIEWS_UNAVAILABLE,
|
|
16490
17190
|
SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS,
|
|
16491
17191
|
SERIES_COLORS,
|
|
17192
|
+
SHARE_NOT_YOURS_LINE,
|
|
16492
17193
|
SOMEBODY,
|
|
16493
17194
|
STAGE_RULE_ON_FAIL_LABEL,
|
|
16494
17195
|
STORE_ANSWERS_THESE,
|
|
@@ -16511,6 +17212,8 @@ export {
|
|
|
16511
17212
|
VIEW_LAYOUTS,
|
|
16512
17213
|
VIEW_NOT_SAVED_YET,
|
|
16513
17214
|
VIEW_TABLE,
|
|
17215
|
+
VISIBILITY_LANES,
|
|
17216
|
+
VISIBILITY_LANE_TITLE,
|
|
16514
17217
|
ViewBar,
|
|
16515
17218
|
ViewSwitcher,
|
|
16516
17219
|
WITHHELD_RECORD_LABEL,
|
|
@@ -16522,6 +17225,7 @@ export {
|
|
|
16522
17225
|
actorWords,
|
|
16523
17226
|
addFields,
|
|
16524
17227
|
archivedRowLine,
|
|
17228
|
+
asRefusal,
|
|
16525
17229
|
askableFields,
|
|
16526
17230
|
blockFromSpec,
|
|
16527
17231
|
bodyForReading,
|
|
@@ -16565,6 +17269,7 @@ export {
|
|
|
16565
17269
|
isPlainFieldType,
|
|
16566
17270
|
isRelationFieldType,
|
|
16567
17271
|
isSignatureField,
|
|
17272
|
+
keptByTheApp,
|
|
16568
17273
|
keyFor,
|
|
16569
17274
|
kindsWithNoChoice,
|
|
16570
17275
|
laneFor,
|
|
@@ -16587,15 +17292,20 @@ export {
|
|
|
16587
17292
|
previewLine,
|
|
16588
17293
|
previewWords,
|
|
16589
17294
|
publiclyAnswerable,
|
|
17295
|
+
railFromParam,
|
|
16590
17296
|
recordName,
|
|
16591
17297
|
recordNameIn,
|
|
16592
17298
|
recordsDataSource,
|
|
17299
|
+
refusal,
|
|
16593
17300
|
refusalForAPerson,
|
|
17301
|
+
refusalFromThrown,
|
|
16594
17302
|
refusalLineForAPerson,
|
|
17303
|
+
refusalOr,
|
|
16595
17304
|
renderValue,
|
|
16596
17305
|
revokeConsequence,
|
|
16597
17306
|
rowName,
|
|
16598
17307
|
rowNameIn,
|
|
17308
|
+
saveViewPatch,
|
|
16599
17309
|
scalarText,
|
|
16600
17310
|
shareUnavailableReason,
|
|
16601
17311
|
specFromBlock,
|
|
@@ -16605,6 +17315,7 @@ export {
|
|
|
16605
17315
|
tableName,
|
|
16606
17316
|
tableRightsAt,
|
|
16607
17317
|
tokenFor,
|
|
17318
|
+
unknownRailLine,
|
|
16608
17319
|
unknownViewLine,
|
|
16609
17320
|
useCanShare,
|
|
16610
17321
|
useEmbedHandshake,
|
|
@@ -16624,6 +17335,7 @@ export {
|
|
|
16624
17335
|
viewDocument,
|
|
16625
17336
|
viewFromRecord,
|
|
16626
17337
|
viewPatchDocument,
|
|
17338
|
+
visibilityLaneFor,
|
|
16627
17339
|
whatIsMissing,
|
|
16628
17340
|
whatYouMayDo,
|
|
16629
17341
|
whatYouMayDoWithTable,
|