@ai-matrx/records-ui 0.79.0 → 0.82.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 +96 -0
- package/dist/index.cjs +1097 -482
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +140 -11
- package/dist/index.d.ts +140 -11
- package/dist/index.js +1184 -569
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
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,32 +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),
|
|
966
|
+
className: cn3(
|
|
848
967
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
849
968
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
850
969
|
),
|
|
851
970
|
children: titleOf(row.id)
|
|
852
971
|
}
|
|
853
972
|
) }, row.id)) }),
|
|
854
|
-
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
|
|
855
974
|
] }),
|
|
856
|
-
allowCreate && !full ? /* @__PURE__ */
|
|
857
|
-
targetRefusal ? /* @__PURE__ */
|
|
858
|
-
/* @__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(
|
|
859
978
|
BasicInput2,
|
|
860
979
|
{
|
|
861
980
|
autoFocus: true,
|
|
@@ -872,7 +991,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
872
991
|
"aria-label": `Name the new ${targetWord}`
|
|
873
992
|
}
|
|
874
993
|
),
|
|
875
|
-
/* @__PURE__ */
|
|
994
|
+
/* @__PURE__ */ jsx4(
|
|
876
995
|
Button2,
|
|
877
996
|
{
|
|
878
997
|
type: "button",
|
|
@@ -883,12 +1002,13 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
883
1002
|
children: busy ? "Creating\u2026" : "Create"
|
|
884
1003
|
}
|
|
885
1004
|
)
|
|
886
|
-
] }) : /* @__PURE__ */
|
|
1005
|
+
] }) : /* @__PURE__ */ jsx4(
|
|
887
1006
|
Button2,
|
|
888
1007
|
{
|
|
889
1008
|
type: "button",
|
|
890
1009
|
size: "sm",
|
|
891
1010
|
variant: "ghost",
|
|
1011
|
+
title: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}`,
|
|
892
1012
|
className: "w-full justify-start text-xs",
|
|
893
1013
|
disabled: busy,
|
|
894
1014
|
onClick: () => {
|
|
@@ -898,10 +1018,25 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
898
1018
|
setCreating(true);
|
|
899
1019
|
}
|
|
900
1020
|
},
|
|
901
|
-
children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}`
|
|
1021
|
+
children: /* @__PURE__ */ jsx4("span", { className: "block w-full truncate text-left", children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}` })
|
|
902
1022
|
}
|
|
903
1023
|
),
|
|
904
|
-
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
|
|
905
1040
|
] }) : null
|
|
906
1041
|
] })
|
|
907
1042
|
] })
|
|
@@ -909,12 +1044,12 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
909
1044
|
}
|
|
910
1045
|
|
|
911
1046
|
// src/editors.tsx
|
|
912
|
-
import { Fragment as Fragment2, jsx as
|
|
1047
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
913
1048
|
function FieldLabel({ field, htmlFor, children }) {
|
|
914
|
-
return /* @__PURE__ */
|
|
915
|
-
/* @__PURE__ */
|
|
916
|
-
field.required ? /* @__PURE__ */
|
|
917
|
-
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,
|
|
918
1053
|
children
|
|
919
1054
|
] });
|
|
920
1055
|
}
|
|
@@ -925,17 +1060,17 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
925
1060
|
case "formula":
|
|
926
1061
|
case "lookup":
|
|
927
1062
|
case "rollup":
|
|
928
|
-
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: [
|
|
929
1064
|
value === null || value === void 0 ? "\u2014" : String(value),
|
|
930
|
-
/* @__PURE__ */
|
|
1065
|
+
/* @__PURE__ */ jsxs4("span", { className: "ml-2 italic", children: [
|
|
931
1066
|
fieldName(field),
|
|
932
1067
|
" is worked out by the system, so it cannot be typed in"
|
|
933
1068
|
] })
|
|
934
1069
|
] });
|
|
935
1070
|
case "json":
|
|
936
|
-
return /* @__PURE__ */
|
|
1071
|
+
return /* @__PURE__ */ jsx5(JsonControl, { field, value, onChange, disabled, id: inputId });
|
|
937
1072
|
case "long_text":
|
|
938
|
-
return /* @__PURE__ */
|
|
1073
|
+
return /* @__PURE__ */ jsx5(
|
|
939
1074
|
BasicTextarea,
|
|
940
1075
|
{
|
|
941
1076
|
id: inputId,
|
|
@@ -948,7 +1083,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
948
1083
|
case "number":
|
|
949
1084
|
case "currency":
|
|
950
1085
|
case "percent":
|
|
951
|
-
return /* @__PURE__ */
|
|
1086
|
+
return /* @__PURE__ */ jsx5(
|
|
952
1087
|
BasicInput3,
|
|
953
1088
|
{
|
|
954
1089
|
id: inputId,
|
|
@@ -961,7 +1096,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
961
1096
|
}
|
|
962
1097
|
);
|
|
963
1098
|
case "datetime":
|
|
964
|
-
return /* @__PURE__ */
|
|
1099
|
+
return /* @__PURE__ */ jsx5(
|
|
965
1100
|
BasicInput3,
|
|
966
1101
|
{
|
|
967
1102
|
id: inputId,
|
|
@@ -974,7 +1109,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
974
1109
|
case "email":
|
|
975
1110
|
case "phone":
|
|
976
1111
|
case "url":
|
|
977
|
-
return /* @__PURE__ */
|
|
1112
|
+
return /* @__PURE__ */ jsx5(
|
|
978
1113
|
BasicInput3,
|
|
979
1114
|
{
|
|
980
1115
|
id: inputId,
|
|
@@ -985,18 +1120,18 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
985
1120
|
}
|
|
986
1121
|
);
|
|
987
1122
|
case "checkbox":
|
|
988
|
-
return /* @__PURE__ */
|
|
1123
|
+
return /* @__PURE__ */ jsx5(CheckboxControl, { field, value, onChange, disabled, id: inputId });
|
|
989
1124
|
case "select":
|
|
990
1125
|
case "multi_select":
|
|
991
|
-
return /* @__PURE__ */
|
|
1126
|
+
return /* @__PURE__ */ jsx5(OptionControl, { field, value, onChange, disabled, id: inputId });
|
|
992
1127
|
case "member":
|
|
993
|
-
return /* @__PURE__ */
|
|
1128
|
+
return /* @__PURE__ */ jsx5(PersonPicker, { field, value, onChange, disabled, id: inputId });
|
|
994
1129
|
case "relation":
|
|
995
1130
|
case "attachment":
|
|
996
|
-
return /* @__PURE__ */
|
|
1131
|
+
return /* @__PURE__ */ jsx5(RelationPicker, { field, value, onChange, disabled, id: inputId });
|
|
997
1132
|
default:
|
|
998
1133
|
if (typeof value === "boolean" || field.config?.["kind"] === "boolean") {
|
|
999
|
-
return /* @__PURE__ */
|
|
1134
|
+
return /* @__PURE__ */ jsx5(
|
|
1000
1135
|
Checkbox2,
|
|
1001
1136
|
{
|
|
1002
1137
|
id: inputId,
|
|
@@ -1006,7 +1141,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1006
1141
|
}
|
|
1007
1142
|
);
|
|
1008
1143
|
}
|
|
1009
|
-
return /* @__PURE__ */
|
|
1144
|
+
return /* @__PURE__ */ jsx5(
|
|
1010
1145
|
BasicInput3,
|
|
1011
1146
|
{
|
|
1012
1147
|
id: inputId,
|
|
@@ -1019,8 +1154,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1019
1154
|
}
|
|
1020
1155
|
function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
1021
1156
|
const unanswered = value === null || value === void 0;
|
|
1022
|
-
return /* @__PURE__ */
|
|
1023
|
-
/* @__PURE__ */
|
|
1157
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
1158
|
+
/* @__PURE__ */ jsx5(
|
|
1024
1159
|
Checkbox2,
|
|
1025
1160
|
{
|
|
1026
1161
|
id,
|
|
@@ -1030,9 +1165,9 @@ function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
|
1030
1165
|
onCheckedChange: (next) => onChange(next === true)
|
|
1031
1166
|
}
|
|
1032
1167
|
),
|
|
1033
|
-
unanswered ? /* @__PURE__ */
|
|
1034
|
-
/* @__PURE__ */
|
|
1035
|
-
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(
|
|
1036
1171
|
Button3,
|
|
1037
1172
|
{
|
|
1038
1173
|
size: "sm",
|
|
@@ -1050,8 +1185,8 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1050
1185
|
const initial = value === null || value === void 0 ? "" : typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
1051
1186
|
const [text, setText] = useState3(initial);
|
|
1052
1187
|
const [broken, setBroken] = useState3(null);
|
|
1053
|
-
return /* @__PURE__ */
|
|
1054
|
-
/* @__PURE__ */
|
|
1188
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
|
|
1189
|
+
/* @__PURE__ */ jsx5(
|
|
1055
1190
|
BasicTextarea,
|
|
1056
1191
|
{
|
|
1057
1192
|
id,
|
|
@@ -1071,37 +1206,53 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1071
1206
|
onChange(JSON.parse(next));
|
|
1072
1207
|
setBroken(null);
|
|
1073
1208
|
} catch (thrown) {
|
|
1074
|
-
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
|
+
);
|
|
1075
1217
|
}
|
|
1076
1218
|
}
|
|
1077
1219
|
}
|
|
1078
1220
|
),
|
|
1079
|
-
broken ?
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
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
|
|
1084
1235
|
] });
|
|
1085
1236
|
}
|
|
1086
1237
|
function OptionControl({ field, value, onChange, disabled, id }) {
|
|
1087
1238
|
const client = useOptionalRecordsClient2();
|
|
1088
1239
|
const [options, setOptions] = useState3(null);
|
|
1089
|
-
const [
|
|
1240
|
+
const [refusal2, setRefusal] = useState3(null);
|
|
1090
1241
|
useEffect2(() => {
|
|
1091
1242
|
if (!client) return;
|
|
1092
1243
|
let cancelled = false;
|
|
1093
1244
|
void client.fieldOptions({ field_id: field.id }).then((result) => {
|
|
1094
1245
|
if (cancelled) return;
|
|
1095
1246
|
if (result.ok) setOptions(result.data ?? []);
|
|
1096
|
-
else setRefusal(
|
|
1247
|
+
else setRefusal(result.error);
|
|
1097
1248
|
});
|
|
1098
1249
|
return () => {
|
|
1099
1250
|
cancelled = true;
|
|
1100
1251
|
};
|
|
1101
1252
|
}, [client, field.id]);
|
|
1102
1253
|
if (!client) {
|
|
1103
|
-
return /* @__PURE__ */
|
|
1104
|
-
/* @__PURE__ */
|
|
1254
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
|
|
1255
|
+
/* @__PURE__ */ jsx5(
|
|
1105
1256
|
BasicInput3,
|
|
1106
1257
|
{
|
|
1107
1258
|
id,
|
|
@@ -1110,19 +1261,20 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1110
1261
|
onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
|
|
1111
1262
|
}
|
|
1112
1263
|
),
|
|
1113
|
-
/* @__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." })
|
|
1114
1265
|
] });
|
|
1115
1266
|
}
|
|
1116
|
-
if (
|
|
1117
|
-
|
|
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" });
|
|
1118
1270
|
if (options.length === 0) {
|
|
1119
|
-
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.` });
|
|
1120
1272
|
}
|
|
1121
1273
|
const titleOf = (option) => recordName(option.data, null, "Unnamed choice");
|
|
1122
1274
|
if (field.multi) {
|
|
1123
1275
|
const chosen = options.filter((option) => choiceValuesOf(value).some((v) => isTheChosen(option, v)));
|
|
1124
1276
|
const picked = new Set(chosen.map((option) => optionKey(option)));
|
|
1125
|
-
return /* @__PURE__ */
|
|
1277
|
+
return /* @__PURE__ */ jsx5("div", { id, className: "flex flex-wrap gap-1", children: options.map((option) => /* @__PURE__ */ jsx5(
|
|
1126
1278
|
Button3,
|
|
1127
1279
|
{
|
|
1128
1280
|
type: "button",
|
|
@@ -1141,15 +1293,15 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1141
1293
|
)) });
|
|
1142
1294
|
}
|
|
1143
1295
|
const current = options.find((option) => isTheChosen(option, value));
|
|
1144
|
-
return /* @__PURE__ */
|
|
1296
|
+
return /* @__PURE__ */ jsxs4(
|
|
1145
1297
|
Select2,
|
|
1146
1298
|
{
|
|
1147
1299
|
value: current ? optionKey(current) : "",
|
|
1148
1300
|
disabled: disabled ?? false,
|
|
1149
1301
|
onValueChange: (next) => onChange(next === "" ? null : next),
|
|
1150
1302
|
children: [
|
|
1151
|
-
/* @__PURE__ */
|
|
1152
|
-
/* @__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)) })
|
|
1153
1305
|
]
|
|
1154
1306
|
}
|
|
1155
1307
|
);
|
|
@@ -1159,14 +1311,21 @@ function RecordChip({
|
|
|
1159
1311
|
onRemove,
|
|
1160
1312
|
className
|
|
1161
1313
|
}) {
|
|
1162
|
-
return
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1314
|
+
return (
|
|
1315
|
+
// A chip carries a RECORD'S OWN TITLE, which nobody here chose the length of —
|
|
1316
|
+
// "Marisol Okonkwo — 418 Calle Puerto Vallarta, Camarillo" is an ordinary customer
|
|
1317
|
+
// name. So it caps at its container and ends in an ellipsis with the whole name in
|
|
1318
|
+
// its tooltip, rather than being cut mid-word by whatever box it landed in
|
|
1319
|
+
// (lane FIX-14, 2026-09-22).
|
|
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
|
|
1323
|
+
] })
|
|
1324
|
+
);
|
|
1166
1325
|
}
|
|
1167
1326
|
|
|
1168
1327
|
// src/PersonPicker.tsx
|
|
1169
|
-
import { jsx as
|
|
1328
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1170
1329
|
function personFromRow(row) {
|
|
1171
1330
|
const data = row.document ?? {};
|
|
1172
1331
|
const userId = data["user_id"];
|
|
@@ -1219,7 +1378,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1219
1378
|
]);
|
|
1220
1379
|
setMembers(roster);
|
|
1221
1380
|
if (records && !records.ok) {
|
|
1222
|
-
setError(
|
|
1381
|
+
setError(records.error);
|
|
1223
1382
|
return;
|
|
1224
1383
|
}
|
|
1225
1384
|
setPeople(records ? records.data.rows.map(personFromRow).filter((p) => p.userId !== null) : []);
|
|
@@ -1240,10 +1399,10 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1240
1399
|
};
|
|
1241
1400
|
}, [client, personTable, picked.length, people.length]);
|
|
1242
1401
|
if (!membersPort || !client) {
|
|
1243
|
-
return /* @__PURE__ */
|
|
1402
|
+
return /* @__PURE__ */ jsx6("p", { className: cn5("text-xs text-muted-foreground", className), children: NO_MEMBERS_REASON });
|
|
1244
1403
|
}
|
|
1245
1404
|
if (!personTable) {
|
|
1246
|
-
return /* @__PURE__ */
|
|
1405
|
+
return /* @__PURE__ */ jsxs5("p", { className: cn5("text-xs text-muted-foreground", className), children: [
|
|
1247
1406
|
fieldName(field),
|
|
1248
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."
|
|
1249
1408
|
] });
|
|
@@ -1261,7 +1420,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1261
1420
|
const resolved = await personRecordForMember(client, personTable, people, member);
|
|
1262
1421
|
setBusy(false);
|
|
1263
1422
|
if (!resolved.ok) {
|
|
1264
|
-
setError(
|
|
1423
|
+
setError(resolved.error);
|
|
1265
1424
|
return;
|
|
1266
1425
|
}
|
|
1267
1426
|
if (!people.some((p) => p.id === resolved.data)) {
|
|
@@ -1287,8 +1446,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1287
1446
|
const pickedUserIds = new Set(
|
|
1288
1447
|
picked.map((recordId) => people.find((p) => p.id === recordId)?.userId).filter(Boolean)
|
|
1289
1448
|
);
|
|
1290
|
-
return /* @__PURE__ */
|
|
1291
|
-
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(
|
|
1292
1451
|
RecordChip,
|
|
1293
1452
|
{
|
|
1294
1453
|
title: nameOfRecord(recordId),
|
|
@@ -1296,8 +1455,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1296
1455
|
},
|
|
1297
1456
|
recordId
|
|
1298
1457
|
)),
|
|
1299
|
-
disabled ? null : /* @__PURE__ */
|
|
1300
|
-
/* @__PURE__ */
|
|
1458
|
+
disabled ? null : /* @__PURE__ */ jsxs5(Popover2, { open, onOpenChange: setOpen, children: [
|
|
1459
|
+
/* @__PURE__ */ jsx6(PopoverTrigger2, { asChild: true, children: /* @__PURE__ */ jsx6(
|
|
1301
1460
|
Button4,
|
|
1302
1461
|
{
|
|
1303
1462
|
id,
|
|
@@ -1308,8 +1467,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1308
1467
|
children: picked.length === 0 ? "Pick a person" : "Change"
|
|
1309
1468
|
}
|
|
1310
1469
|
) }),
|
|
1311
|
-
/* @__PURE__ */
|
|
1312
|
-
/* @__PURE__ */
|
|
1470
|
+
/* @__PURE__ */ jsxs5(PopoverContent2, { sizing: "content", className: "p-1", align: "start", children: [
|
|
1471
|
+
/* @__PURE__ */ jsx6(
|
|
1313
1472
|
BasicInput4,
|
|
1314
1473
|
{
|
|
1315
1474
|
value: search,
|
|
@@ -1318,29 +1477,29 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1318
1477
|
className: "mb-1 h-7 text-xs"
|
|
1319
1478
|
}
|
|
1320
1479
|
),
|
|
1321
|
-
error ? /* @__PURE__ */
|
|
1322
|
-
members === null ? /* @__PURE__ */
|
|
1323
|
-
/* @__PURE__ */
|
|
1324
|
-
/* @__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(
|
|
1325
1484
|
"button",
|
|
1326
1485
|
{
|
|
1327
1486
|
type: "button",
|
|
1328
1487
|
disabled: busy,
|
|
1329
1488
|
onClick: () => void pick(member),
|
|
1330
|
-
className:
|
|
1489
|
+
className: cn5(
|
|
1331
1490
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1332
1491
|
pickedUserIds.has(member.userId) ? "bg-accent text-accent-foreground" : ""
|
|
1333
1492
|
),
|
|
1334
1493
|
children: [
|
|
1335
|
-
/* @__PURE__ */
|
|
1336
|
-
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: [
|
|
1337
1496
|
"\xB7 ",
|
|
1338
1497
|
member.email
|
|
1339
1498
|
] }) : null
|
|
1340
1499
|
]
|
|
1341
1500
|
}
|
|
1342
1501
|
) }, member.userId)) }),
|
|
1343
|
-
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
|
|
1344
1503
|
] })
|
|
1345
1504
|
] })
|
|
1346
1505
|
] })
|
|
@@ -1353,55 +1512,6 @@ import { asWriteConflict } from "@ai-matrx/records/core";
|
|
|
1353
1512
|
import { useRecordsClient as useRecordsClient3 } from "@ai-matrx/records/react";
|
|
1354
1513
|
import { Button as Button5, Checkbox as Checkbox3, cn as cn7 } from "@ai-matrx/design-system";
|
|
1355
1514
|
|
|
1356
|
-
// src/Refusal.tsx
|
|
1357
|
-
import { Alert, AlertDescription, AlertTitle, cn as cn5 } from "@ai-matrx/design-system";
|
|
1358
|
-
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1359
|
-
function hintIsMachineIdentity(hint) {
|
|
1360
|
-
return isMachineIdentity(hint);
|
|
1361
|
-
}
|
|
1362
|
-
function hintForAPerson(hint) {
|
|
1363
|
-
const said = (hint ?? "").trim();
|
|
1364
|
-
if (said === "") return null;
|
|
1365
|
-
return isMachineIdentity(said) ? null : said;
|
|
1366
|
-
}
|
|
1367
|
-
function RefusalNotice({
|
|
1368
|
-
error,
|
|
1369
|
-
className,
|
|
1370
|
-
actions
|
|
1371
|
-
}) {
|
|
1372
|
-
const plain = refusalForAPerson(error);
|
|
1373
|
-
return /* @__PURE__ */ jsxs5(
|
|
1374
|
-
Alert,
|
|
1375
|
-
{
|
|
1376
|
-
variant: "destructive",
|
|
1377
|
-
className: cn5("text-xs", className),
|
|
1378
|
-
...plain.forEngineers ? { title: plain.forEngineers } : {},
|
|
1379
|
-
children: [
|
|
1380
|
-
/* @__PURE__ */ jsx6(AlertTitle, { className: "text-xs font-medium", children: plain.title }),
|
|
1381
|
-
/* @__PURE__ */ jsxs5(AlertDescription, { className: "space-y-1 text-xs", children: [
|
|
1382
|
-
plain.sentence.replace(/\.$/, "") === plain.title ? null : /* @__PURE__ */ jsx6("p", { children: plain.sentence }),
|
|
1383
|
-
/* @__PURE__ */ jsx6("p", { className: "opacity-80", children: plain.remedy }),
|
|
1384
|
-
plain.forEngineers ? /* @__PURE__ */ jsx6(
|
|
1385
|
-
"p",
|
|
1386
|
-
{
|
|
1387
|
-
className: "sr-only",
|
|
1388
|
-
"aria-hidden": "true",
|
|
1389
|
-
"data-for-engineers": "",
|
|
1390
|
-
...error.sqlstate ? { "data-sqlstate": error.sqlstate } : {},
|
|
1391
|
-
children: plain.forEngineers
|
|
1392
|
-
}
|
|
1393
|
-
) : null,
|
|
1394
|
-
actions
|
|
1395
|
-
] })
|
|
1396
|
-
]
|
|
1397
|
-
}
|
|
1398
|
-
);
|
|
1399
|
-
}
|
|
1400
|
-
function RefusalLine({ error, className }) {
|
|
1401
|
-
const plain = refusalForAPerson(error);
|
|
1402
|
-
return /* @__PURE__ */ jsx6("p", { className: cn5("text-xs text-destructive", className), title: plain.forEngineers || void 0, children: refusalLineForAPerson(error) });
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
1515
|
// src/whoChangedSource.tsx
|
|
1406
1516
|
import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect4, useMemo as useMemo5, useRef, useState as useState5 } from "react";
|
|
1407
1517
|
import { useRecordsClient } from "@ai-matrx/records/react";
|
|
@@ -2240,7 +2350,7 @@ function GridCell({
|
|
|
2240
2350
|
);
|
|
2241
2351
|
const value = editing ? editing.valueFor(row, field) : row.document?.[field.key];
|
|
2242
2352
|
const state = editing ? editing.stateOf(row.id, field.key) : null;
|
|
2243
|
-
const
|
|
2353
|
+
const refusal2 = editing ? editing.refusalOf(row.id, field.key) : null;
|
|
2244
2354
|
const editable = Boolean(editing) && canWrite && fieldIsEditable(field);
|
|
2245
2355
|
if (open && editing) {
|
|
2246
2356
|
return /* @__PURE__ */ jsx10(
|
|
@@ -2269,8 +2379,8 @@ function GridCell({
|
|
|
2269
2379
|
}
|
|
2270
2380
|
),
|
|
2271
2381
|
answered ? null : /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", title: "nobody has answered this yet", children: "\u2014" }),
|
|
2272
|
-
|
|
2273
|
-
/* @__PURE__ */ jsx10(RefusalLine, { error:
|
|
2382
|
+
refusal2 ? /* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-1 rounded border border-destructive/40 px-1", children: [
|
|
2383
|
+
/* @__PURE__ */ jsx10(RefusalLine, { error: refusal2.error }),
|
|
2274
2384
|
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" })
|
|
2275
2385
|
] }) : null
|
|
2276
2386
|
] });
|
|
@@ -2345,11 +2455,11 @@ function GridCell({
|
|
|
2345
2455
|
),
|
|
2346
2456
|
onAskWhoChanged ? /* @__PURE__ */ jsx10(WhoBadge, { field, row, onAskWhoChanged }) : null,
|
|
2347
2457
|
state === "saving" ? /* @__PURE__ */ jsx10("span", { className: "px-1 text-[10px] text-muted-foreground", children: "Saving\u2026" }) : null,
|
|
2348
|
-
|
|
2349
|
-
/* @__PURE__ */ jsx10(RefusalLine, { error:
|
|
2350
|
-
|
|
2458
|
+
refusal2 ? /* @__PURE__ */ jsxs6("span", { className: "flex flex-col gap-0.5 rounded border border-destructive/40 p-1", children: [
|
|
2459
|
+
/* @__PURE__ */ jsx10(RefusalLine, { error: refusal2.error }),
|
|
2460
|
+
refusal2.conflict ? /* @__PURE__ */ jsxs6("span", { className: "text-[11px]", children: [
|
|
2351
2461
|
"Theirs: ",
|
|
2352
|
-
/* @__PURE__ */ jsx10("strong", { children: formatTheirs(
|
|
2462
|
+
/* @__PURE__ */ jsx10("strong", { children: formatTheirs(refusal2.conflict, field.key) })
|
|
2353
2463
|
] }) : null,
|
|
2354
2464
|
/* @__PURE__ */ jsxs6("span", { className: "flex gap-1", children: [
|
|
2355
2465
|
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" }),
|
|
@@ -2650,7 +2760,7 @@ var colorFromTheValue = (_fieldKey, value) => {
|
|
|
2650
2760
|
};
|
|
2651
2761
|
|
|
2652
2762
|
// src/Grid.tsx
|
|
2653
|
-
import { Button as Button8, Skeleton as Skeleton2, cn as cn9 } from "@ai-matrx/design-system";
|
|
2763
|
+
import { Button as Button8, ConfirmDialog, Skeleton as Skeleton2, cn as cn9 } from "@ai-matrx/design-system";
|
|
2654
2764
|
|
|
2655
2765
|
// src/Enrich.tsx
|
|
2656
2766
|
import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo8, useState as useState8 } from "react";
|
|
@@ -3072,7 +3182,20 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3072
3182
|
" in one go."
|
|
3073
3183
|
] }) : null
|
|
3074
3184
|
] }) : null,
|
|
3075
|
-
outcome ? /* @__PURE__ */ jsx11("p", { className:
|
|
3185
|
+
outcome ? outcome.ok ? /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: outcome.message }) : (
|
|
3186
|
+
// A run that did not happen is a refusal, with a heading and a remedy,
|
|
3187
|
+
// never the host's words in red (lane REFUSAL-SWEEP).
|
|
3188
|
+
/* @__PURE__ */ jsx11(
|
|
3189
|
+
RefusalLine,
|
|
3190
|
+
{
|
|
3191
|
+
error: refusal(
|
|
3192
|
+
"internal",
|
|
3193
|
+
outcome.message,
|
|
3194
|
+
"Nothing was filled in. Try the run again in a moment."
|
|
3195
|
+
)
|
|
3196
|
+
}
|
|
3197
|
+
)
|
|
3198
|
+
) : null,
|
|
3076
3199
|
field ? null : null
|
|
3077
3200
|
] }, row.field_id);
|
|
3078
3201
|
}) });
|
|
@@ -3117,15 +3240,15 @@ function PastePreview({
|
|
|
3117
3240
|
/* @__PURE__ */ jsx12(PlanTable, { plan }),
|
|
3118
3241
|
plan.refusals.length > 0 ? /* @__PURE__ */ jsxs8("div", { "data-matrx-paste-refusals": true, className: "flex flex-col gap-1", children: [
|
|
3119
3242
|
/* @__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:` }),
|
|
3120
|
-
/* @__PURE__ */ jsx12("ul", { className: "flex flex-col gap-1", children: plan.refusals.slice(0, 12).map((
|
|
3243
|
+
/* @__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: [
|
|
3121
3244
|
/* @__PURE__ */ jsxs8("span", { className: "font-medium", children: [
|
|
3122
3245
|
"Row ",
|
|
3123
|
-
|
|
3246
|
+
refusal2.fromLine,
|
|
3124
3247
|
", ",
|
|
3125
|
-
|
|
3248
|
+
refusal2.column
|
|
3126
3249
|
] }),
|
|
3127
3250
|
" \u2014 ",
|
|
3128
|
-
|
|
3251
|
+
refusal2.why
|
|
3129
3252
|
] }, index)) }),
|
|
3130
3253
|
plan.refusals.length > 12 ? /* @__PURE__ */ jsxs8("p", { className: "text-xs text-muted-foreground", children: [
|
|
3131
3254
|
plan.refusals.length - 12,
|
|
@@ -3153,7 +3276,13 @@ function PastePreview({
|
|
|
3153
3276
|
setAdding(group.fieldKey);
|
|
3154
3277
|
void onAddOptions(group.fieldKey, group.words).catch(
|
|
3155
3278
|
(err) => setAddFailed(
|
|
3156
|
-
|
|
3279
|
+
// The error's own text is an engineer's; the person
|
|
3280
|
+
// reads what did not happen and what to do.
|
|
3281
|
+
asRefusal(
|
|
3282
|
+
err,
|
|
3283
|
+
"Those options were not added, so the pasted words still do not match a choice.",
|
|
3284
|
+
"Try adding them again, or add them from the column's settings."
|
|
3285
|
+
)
|
|
3157
3286
|
)
|
|
3158
3287
|
).finally(() => setAdding(null));
|
|
3159
3288
|
},
|
|
@@ -3161,7 +3290,7 @@ function PastePreview({
|
|
|
3161
3290
|
}
|
|
3162
3291
|
)
|
|
3163
3292
|
] }, group.fieldKey)),
|
|
3164
|
-
addFailed ? /* @__PURE__ */ jsx12(
|
|
3293
|
+
addFailed ? /* @__PURE__ */ jsx12(RefusalLine, { error: addFailed }) : null
|
|
3165
3294
|
] }) : null,
|
|
3166
3295
|
plan.pastRightEdge > 0 ? /* @__PURE__ */ jsxs8("p", { className: "text-xs text-muted-foreground", children: [
|
|
3167
3296
|
plan.pastRightEdge,
|
|
@@ -3341,19 +3470,19 @@ function planPastedBlock(args) {
|
|
|
3341
3470
|
accepted[key] = judged.value;
|
|
3342
3471
|
}
|
|
3343
3472
|
const predicted = predictValueRefusals(args.fields, accepted);
|
|
3344
|
-
for (const
|
|
3345
|
-
const key =
|
|
3473
|
+
for (const refusal2 of predicted) {
|
|
3474
|
+
const key = refusal2.field_key;
|
|
3346
3475
|
if (key === void 0) continue;
|
|
3347
3476
|
const cell = built.find((b) => b.key === key && b.refusal === null);
|
|
3348
3477
|
if (!cell) continue;
|
|
3349
|
-
cell.refusal =
|
|
3478
|
+
cell.refusal = refusal2.message;
|
|
3350
3479
|
cell.value = null;
|
|
3351
3480
|
delete accepted[key];
|
|
3352
3481
|
refusals.push({
|
|
3353
3482
|
fromLine,
|
|
3354
3483
|
column: fieldName(byKey.get(key) ?? { key }),
|
|
3355
3484
|
raw: cell.raw,
|
|
3356
|
-
why:
|
|
3485
|
+
why: refusal2.message
|
|
3357
3486
|
});
|
|
3358
3487
|
}
|
|
3359
3488
|
cells += Object.keys(accepted).length;
|
|
@@ -3456,6 +3585,9 @@ function Grid({
|
|
|
3456
3585
|
[client, host, refreshEnrich]
|
|
3457
3586
|
);
|
|
3458
3587
|
const [pastePlan, setPastePlan] = useState10(null);
|
|
3588
|
+
const [deleting, setDeleting] = useState10(null);
|
|
3589
|
+
const [deleteBusy, setDeleteBusy] = useState10(false);
|
|
3590
|
+
const [archived, setArchived] = useState10(null);
|
|
3459
3591
|
const choiceOptions = useRef4(/* @__PURE__ */ new Map());
|
|
3460
3592
|
const loadChoiceOptions = useCallback6(
|
|
3461
3593
|
async (fieldList) => {
|
|
@@ -3743,6 +3875,21 @@ function Grid({
|
|
|
3743
3875
|
/* @__PURE__ */ jsx13("div", { className: "ml-auto flex items-center gap-1", children: toolbarActions })
|
|
3744
3876
|
] }),
|
|
3745
3877
|
records.error ? /* @__PURE__ */ jsx13(RefusalNotice, { error: records.error }) : null,
|
|
3878
|
+
archived ? /* @__PURE__ */ jsxs9(
|
|
3879
|
+
"p",
|
|
3880
|
+
{
|
|
3881
|
+
"data-records-archived-notice": "",
|
|
3882
|
+
role: "status",
|
|
3883
|
+
className: "flex items-center gap-2 rounded border border-border bg-muted/40 px-2 py-1 text-xs",
|
|
3884
|
+
children: [
|
|
3885
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
3886
|
+
archived,
|
|
3887
|
+
" is archived. Nothing was destroyed \u2014 it is in this table\u2019s Archived items, where Restore brings it back."
|
|
3888
|
+
] }),
|
|
3889
|
+
/* @__PURE__ */ jsx13(Button8, { size: "sm", variant: "ghost", className: "ml-auto h-6", onClick: () => setArchived(null), children: "Dismiss" })
|
|
3890
|
+
]
|
|
3891
|
+
}
|
|
3892
|
+
) : null,
|
|
3746
3893
|
editing.rowError ? /* @__PURE__ */ jsx13(
|
|
3747
3894
|
RefusalNotice,
|
|
3748
3895
|
{
|
|
@@ -3859,11 +4006,13 @@ function Grid({
|
|
|
3859
4006
|
}
|
|
3860
4007
|
) : null,
|
|
3861
4008
|
canWrite && mayWriteRow(row.id) ? /* @__PURE__ */ jsx13(
|
|
3862
|
-
|
|
4009
|
+
Button8,
|
|
3863
4010
|
{
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
4011
|
+
size: "sm",
|
|
4012
|
+
variant: "ghost",
|
|
4013
|
+
title: "Delete this record",
|
|
4014
|
+
onClick: () => setDeleting(row),
|
|
4015
|
+
children: "Delete"
|
|
3867
4016
|
}
|
|
3868
4017
|
) : null
|
|
3869
4018
|
] })
|
|
@@ -3906,7 +4055,32 @@ function Grid({
|
|
|
3906
4055
|
onPage: setPage
|
|
3907
4056
|
}
|
|
3908
4057
|
)
|
|
3909
|
-
] })
|
|
4058
|
+
] }),
|
|
4059
|
+
/* @__PURE__ */ jsx13(
|
|
4060
|
+
ConfirmDialog,
|
|
4061
|
+
{
|
|
4062
|
+
open: deleting !== null,
|
|
4063
|
+
onOpenChange: (next) => {
|
|
4064
|
+
if (!next && !deleteBusy) setDeleting(null);
|
|
4065
|
+
},
|
|
4066
|
+
variant: "destructive",
|
|
4067
|
+
title: `Delete ${deleting ? recordNameIn(table.data, deleting.document ?? {}) : "this record"}?`,
|
|
4068
|
+
description: table.data?.retention_days ? `It leaves the table and waits in Archived items. Nothing is destroyed: Restore brings it back, for ${table.data.retention_days} days.` : "It leaves the table and waits in Archived items. Nothing is destroyed: Restore brings it back.",
|
|
4069
|
+
confirmLabel: "Delete",
|
|
4070
|
+
cancelLabel: "Keep",
|
|
4071
|
+
busy: deleteBusy,
|
|
4072
|
+
onConfirm: async () => {
|
|
4073
|
+
const row = deleting;
|
|
4074
|
+
if (!row) return;
|
|
4075
|
+
const name = recordNameIn(table.data, row.document ?? {});
|
|
4076
|
+
setDeleteBusy(true);
|
|
4077
|
+
const failed = await editing.remove(row.id);
|
|
4078
|
+
setDeleteBusy(false);
|
|
4079
|
+
setDeleting(null);
|
|
4080
|
+
if (!failed) setArchived(name);
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
)
|
|
3910
4084
|
] }) });
|
|
3911
4085
|
}
|
|
3912
4086
|
function columnLabel(fields, columnId) {
|
|
@@ -3926,42 +4100,6 @@ function AddFieldButton({ onClick }) {
|
|
|
3926
4100
|
}
|
|
3927
4101
|
);
|
|
3928
4102
|
}
|
|
3929
|
-
function DeleteRecordButton({
|
|
3930
|
-
row,
|
|
3931
|
-
retentionDays,
|
|
3932
|
-
onDelete
|
|
3933
|
-
}) {
|
|
3934
|
-
const [asking, setAsking] = useState10(false);
|
|
3935
|
-
if (!asking) {
|
|
3936
|
-
return /* @__PURE__ */ jsx13(
|
|
3937
|
-
Button8,
|
|
3938
|
-
{
|
|
3939
|
-
size: "sm",
|
|
3940
|
-
variant: "ghost",
|
|
3941
|
-
title: "Delete this record",
|
|
3942
|
-
onClick: () => setAsking(true),
|
|
3943
|
-
children: "Delete"
|
|
3944
|
-
}
|
|
3945
|
-
);
|
|
3946
|
-
}
|
|
3947
|
-
return /* @__PURE__ */ jsxs9("span", { className: "flex items-center gap-1 text-[11px]", children: [
|
|
3948
|
-
/* @__PURE__ */ jsx13("span", { className: "text-muted-foreground", children: retentionDays ? `Removes it from the table. Restorable for ${retentionDays} days.` : "Removes it from the table. It can be restored from the record's history." }),
|
|
3949
|
-
/* @__PURE__ */ jsx13(
|
|
3950
|
-
Button8,
|
|
3951
|
-
{
|
|
3952
|
-
size: "sm",
|
|
3953
|
-
variant: "destructive",
|
|
3954
|
-
onClick: () => {
|
|
3955
|
-
setAsking(false);
|
|
3956
|
-
void onDelete();
|
|
3957
|
-
},
|
|
3958
|
-
children: "Delete"
|
|
3959
|
-
}
|
|
3960
|
-
),
|
|
3961
|
-
/* @__PURE__ */ jsx13(Button8, { size: "sm", variant: "ghost", onClick: () => setAsking(false), children: "Keep" }),
|
|
3962
|
-
/* @__PURE__ */ jsx13("span", { className: "sr-only", children: row.id })
|
|
3963
|
-
] });
|
|
3964
|
-
}
|
|
3965
4103
|
function Pager({
|
|
3966
4104
|
page,
|
|
3967
4105
|
pageSize,
|
|
@@ -4108,18 +4246,24 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4108
4246
|
size: "sm",
|
|
4109
4247
|
variant: "outline",
|
|
4110
4248
|
onClick: () => {
|
|
4249
|
+
const failed = (thrown) => setFailure(
|
|
4250
|
+
refusalFromThrown(
|
|
4251
|
+
thrown,
|
|
4252
|
+
"The spreadsheet could not be written.",
|
|
4253
|
+
"The CSV beside this button carries the same rows."
|
|
4254
|
+
)
|
|
4255
|
+
);
|
|
4256
|
+
setFailure(null);
|
|
4111
4257
|
try {
|
|
4112
|
-
|
|
4258
|
+
exportXlsx(fields.data ?? [], exported(), name).then(
|
|
4113
4259
|
(bytes) => download(
|
|
4114
4260
|
`${name}.xlsx`,
|
|
4115
4261
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
4116
4262
|
bytes
|
|
4117
4263
|
)
|
|
4118
|
-
);
|
|
4264
|
+
).catch(failed);
|
|
4119
4265
|
} catch (thrown) {
|
|
4120
|
-
|
|
4121
|
-
`The spreadsheet could not be written (${thrown instanceof Error ? thrown.message : String(thrown)}). The CSV beside this button carries the same rows.`
|
|
4122
|
-
);
|
|
4266
|
+
failed(thrown);
|
|
4123
4267
|
}
|
|
4124
4268
|
},
|
|
4125
4269
|
children: "XLSX"
|
|
@@ -4135,7 +4279,7 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4135
4279
|
human: () => exportCsv(fields.data ?? [], exported())
|
|
4136
4280
|
}
|
|
4137
4281
|
),
|
|
4138
|
-
failure ? /* @__PURE__ */ jsx14(
|
|
4282
|
+
failure ? /* @__PURE__ */ jsx14(RefusalLine, { error: failure }) : null
|
|
4139
4283
|
] });
|
|
4140
4284
|
}
|
|
4141
4285
|
|
|
@@ -4234,7 +4378,7 @@ function FilePicker({
|
|
|
4234
4378
|
}
|
|
4235
4379
|
|
|
4236
4380
|
// src/ImportWizard.tsx
|
|
4237
|
-
import { useCallback as useCallback8, useMemo as useMemo10, useState as useState13 } from "react";
|
|
4381
|
+
import { useCallback as useCallback8, useEffect as useEffect8, useMemo as useMemo10, useState as useState13 } from "react";
|
|
4238
4382
|
import { useFields as useFields5, useRecordsClient as useRecordsClient6, useTable as useTable3 } from "@ai-matrx/records/react";
|
|
4239
4383
|
import {
|
|
4240
4384
|
importCsv,
|
|
@@ -4251,6 +4395,45 @@ import {
|
|
|
4251
4395
|
Separator as Separator2,
|
|
4252
4396
|
cn as cn12
|
|
4253
4397
|
} from "@ai-matrx/design-system";
|
|
4398
|
+
|
|
4399
|
+
// src/importRuleCheck.ts
|
|
4400
|
+
import { predictValueRefusals as predictValueRefusals2 } from "@ai-matrx/records/core";
|
|
4401
|
+
function checkImportRules(fields, rows, mapping) {
|
|
4402
|
+
const byKey = new Map(fields.map((f) => [f.key, f]));
|
|
4403
|
+
const verdicts = [];
|
|
4404
|
+
for (const [header, key] of Object.entries(mapping)) {
|
|
4405
|
+
const field = byKey.get(key);
|
|
4406
|
+
if (!field) continue;
|
|
4407
|
+
let refused = 0;
|
|
4408
|
+
let judged = 0;
|
|
4409
|
+
let first = null;
|
|
4410
|
+
rows.forEach((row, i) => {
|
|
4411
|
+
const raw = String(row[header] ?? "").trim();
|
|
4412
|
+
if (raw === "") return;
|
|
4413
|
+
judged += 1;
|
|
4414
|
+
const meant = valueFromPastedText(field, raw);
|
|
4415
|
+
let why = null;
|
|
4416
|
+
if ("refusal" in meant) why = meant.refusal;
|
|
4417
|
+
else {
|
|
4418
|
+
const predicted = predictValueRefusals2([field], { [key]: meant.value }).filter(
|
|
4419
|
+
(p) => p.field_key === void 0 || p.field_key === key
|
|
4420
|
+
);
|
|
4421
|
+
if (predicted.length > 0) why = predicted[0].message;
|
|
4422
|
+
}
|
|
4423
|
+
if (why !== null) {
|
|
4424
|
+
refused += 1;
|
|
4425
|
+
if (!first) first = { why, line: i + 1, raw };
|
|
4426
|
+
}
|
|
4427
|
+
});
|
|
4428
|
+
if (refused > 0 && first) {
|
|
4429
|
+
const f = first;
|
|
4430
|
+
verdicts.push({ header, field, refused, judged, firstWhy: f.why, firstLine: f.line, firstRaw: f.raw });
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
return verdicts;
|
|
4434
|
+
}
|
|
4435
|
+
|
|
4436
|
+
// src/ImportWizard.tsx
|
|
4254
4437
|
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4255
4438
|
var BATCH = 250;
|
|
4256
4439
|
var SAMPLES = 40;
|
|
@@ -4273,7 +4456,7 @@ async function sha256(bytes) {
|
|
|
4273
4456
|
function typeWord(t) {
|
|
4274
4457
|
return fieldTypeChoice(t)?.label ?? (t === "relation" ? "Points at another table" : t);
|
|
4275
4458
|
}
|
|
4276
|
-
function ImportWizard({ tableId, onDone, className }) {
|
|
4459
|
+
function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
4277
4460
|
const client = useRecordsClient6();
|
|
4278
4461
|
const table = useTable3(tableId);
|
|
4279
4462
|
const fields = useFields5(tableId);
|
|
@@ -4299,6 +4482,50 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4299
4482
|
const [columnsAdded, setColumnsAdded] = useState13(null);
|
|
4300
4483
|
const [show, setShow] = useState13("all");
|
|
4301
4484
|
const [ledger, setLedger] = useState13(null);
|
|
4485
|
+
const [runs, setRuns] = useState13(null);
|
|
4486
|
+
const [openRun, setOpenRun] = useState13(null);
|
|
4487
|
+
const [runRows, setRunRows] = useState13(null);
|
|
4488
|
+
const [runProblem, setRunProblem] = useState13(null);
|
|
4489
|
+
const [runsProblem, setRunsProblem] = useState13(null);
|
|
4490
|
+
const loadRuns = useCallback8(async () => {
|
|
4491
|
+
const answered = await client.imports({ table_id: tableId, limit: 10 });
|
|
4492
|
+
if (answered.ok) {
|
|
4493
|
+
setRuns(answered.data);
|
|
4494
|
+
setRunsProblem(null);
|
|
4495
|
+
} else setRunsProblem(answered.error);
|
|
4496
|
+
}, [client, tableId]);
|
|
4497
|
+
useEffect8(() => {
|
|
4498
|
+
void loadRuns();
|
|
4499
|
+
}, [loadRuns]);
|
|
4500
|
+
const openRefusals = useCallback8(
|
|
4501
|
+
async (importId) => {
|
|
4502
|
+
setRunProblem(null);
|
|
4503
|
+
if (openRun === importId) {
|
|
4504
|
+
setOpenRun(null);
|
|
4505
|
+
setRunRows(null);
|
|
4506
|
+
return;
|
|
4507
|
+
}
|
|
4508
|
+
setOpenRun(importId);
|
|
4509
|
+
setRunRows(null);
|
|
4510
|
+
const answered = await client.importReport({
|
|
4511
|
+
import_id: importId,
|
|
4512
|
+
state: "refused",
|
|
4513
|
+
limit: 200
|
|
4514
|
+
});
|
|
4515
|
+
if (!answered.ok) {
|
|
4516
|
+
setRunProblem(answered.error);
|
|
4517
|
+
return;
|
|
4518
|
+
}
|
|
4519
|
+
setRunRows({
|
|
4520
|
+
import_id: answered.data.import_id,
|
|
4521
|
+
rows: answered.data.rows,
|
|
4522
|
+
kept: answered.data.kept,
|
|
4523
|
+
total: answered.data.total,
|
|
4524
|
+
note: answered.data.note
|
|
4525
|
+
});
|
|
4526
|
+
},
|
|
4527
|
+
[client, openRun]
|
|
4528
|
+
);
|
|
4302
4529
|
const declared = fields.data ?? [];
|
|
4303
4530
|
const take = useCallback8(
|
|
4304
4531
|
async (file) => {
|
|
@@ -4327,7 +4554,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4327
4554
|
}));
|
|
4328
4555
|
const answered = await client.importPlan({ table_id: tableId, columns });
|
|
4329
4556
|
if (!answered.ok) {
|
|
4330
|
-
setProblem(answered.error
|
|
4557
|
+
setProblem(answered.error);
|
|
4331
4558
|
setPhase("waiting");
|
|
4332
4559
|
return;
|
|
4333
4560
|
}
|
|
@@ -4344,7 +4571,14 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4344
4571
|
setMapping(next);
|
|
4345
4572
|
setPhase("ready");
|
|
4346
4573
|
} catch (thrown) {
|
|
4347
|
-
setProblem(
|
|
4574
|
+
setProblem(
|
|
4575
|
+
refusalFromThrown(
|
|
4576
|
+
thrown,
|
|
4577
|
+
"That file could not be read, so nothing was imported.",
|
|
4578
|
+
"Save it again as CSV or Excel and pick it again.",
|
|
4579
|
+
"invalid_argument"
|
|
4580
|
+
)
|
|
4581
|
+
);
|
|
4348
4582
|
setPhase("waiting");
|
|
4349
4583
|
}
|
|
4350
4584
|
},
|
|
@@ -4361,7 +4595,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4361
4595
|
setPhase("declaring");
|
|
4362
4596
|
const made = await client.importDeclareColumns({ table_id: tableId, rows, mapping });
|
|
4363
4597
|
if (!made.ok) {
|
|
4364
|
-
setProblem(made.error
|
|
4598
|
+
setProblem(made.error);
|
|
4365
4599
|
setPhase("ready");
|
|
4366
4600
|
return;
|
|
4367
4601
|
}
|
|
@@ -4380,7 +4614,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4380
4614
|
policy: { on_duplicate: onDuplicate, unmapped }
|
|
4381
4615
|
});
|
|
4382
4616
|
if (!opened.ok) {
|
|
4383
|
-
setProblem(opened.error
|
|
4617
|
+
setProblem(opened.error);
|
|
4384
4618
|
setPhase("ready");
|
|
4385
4619
|
return;
|
|
4386
4620
|
}
|
|
@@ -4399,7 +4633,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4399
4633
|
const batch = rows.slice(at, at + take2);
|
|
4400
4634
|
const answered = await client.importRows({ import_id: importId, rows: batch, mapping });
|
|
4401
4635
|
if (!answered.ok) {
|
|
4402
|
-
setProblem(answered.error
|
|
4636
|
+
setProblem(answered.error);
|
|
4403
4637
|
setOutcomes([...collected]);
|
|
4404
4638
|
setLedger({ ...told, final: false });
|
|
4405
4639
|
setPhase("ready");
|
|
@@ -4418,7 +4652,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4418
4652
|
}
|
|
4419
4653
|
const finished = await client.importFinish({ import_id: importId, unmapped });
|
|
4420
4654
|
if (!finished.ok) {
|
|
4421
|
-
setProblem(finished.error
|
|
4655
|
+
setProblem(finished.error);
|
|
4422
4656
|
setPhase("done");
|
|
4423
4657
|
return;
|
|
4424
4658
|
}
|
|
@@ -4432,8 +4666,9 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4432
4666
|
final: true
|
|
4433
4667
|
});
|
|
4434
4668
|
setPhase("done");
|
|
4435
|
-
|
|
4436
|
-
|
|
4669
|
+
onWrote?.(finished.data.rows_written);
|
|
4670
|
+
void loadRuns();
|
|
4671
|
+
}, [client, dedupeKey, fileBytes, fileHash, fileName, format, loadRuns, mapping, onDuplicate, onWrote, plan, rows, tableId, unmapped]);
|
|
4437
4672
|
const counts = useMemo10(() => {
|
|
4438
4673
|
let landed = 0;
|
|
4439
4674
|
let duplicate = 0;
|
|
@@ -4455,6 +4690,11 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4455
4690
|
const titleMissing = Boolean(titleKey) && titleMappedTo === null;
|
|
4456
4691
|
const titleFieldLabel = (titleKey ? plan?.fields.find((f) => f.key === titleKey)?.label : null) || (titleKey ? declared.find((f) => f.key === titleKey)?.label : null) || "the name column";
|
|
4457
4692
|
const busy = phase === "reading" || phase === "planning" || phase === "declaring" || phase === "writing";
|
|
4693
|
+
const ruleVerdicts = useMemo10(() => checkImportRules(declared, rows, mapping), [declared, rows, mapping]);
|
|
4694
|
+
const ruleVerdictOf = useMemo10(() => new Map(ruleVerdicts.map((v) => [v.header, v])), [ruleVerdicts]);
|
|
4695
|
+
const refusedByRules = ruleVerdicts.reduce((n, v) => n + v.refused, 0);
|
|
4696
|
+
const [goAheadAnyway, setGoAheadAnyway] = useState13(false);
|
|
4697
|
+
useEffect8(() => setGoAheadAnyway(false), [ruleVerdicts]);
|
|
4458
4698
|
return /* @__PURE__ */ jsxs12("div", { className: cn12("flex min-w-0 flex-col gap-2 text-xs", className), children: [
|
|
4459
4699
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
4460
4700
|
/* @__PURE__ */ jsx16("span", { className: "font-medium", children: "Import" }),
|
|
@@ -4482,7 +4722,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4482
4722
|
} : {}
|
|
4483
4723
|
}
|
|
4484
4724
|
) : null,
|
|
4485
|
-
problem ? /* @__PURE__ */ jsx16(
|
|
4725
|
+
problem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: problem }) : null,
|
|
4486
4726
|
phase === "reading" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
|
|
4487
4727
|
phase === "planning" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
|
|
4488
4728
|
plan && parsed ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
@@ -4579,7 +4819,28 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4579
4819
|
column.unit ? /* @__PURE__ */ jsx16("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
|
|
4580
4820
|
/* @__PURE__ */ jsx16("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
|
|
4581
4821
|
column.ambiguous ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
|
|
4582
|
-
column.collides_with ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null
|
|
4822
|
+
column.collides_with ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null,
|
|
4823
|
+
ruleVerdictOf.get(column.header) ? (() => {
|
|
4824
|
+
const v = ruleVerdictOf.get(column.header);
|
|
4825
|
+
return /* @__PURE__ */ jsxs12("div", { "data-import-rule-refusal": column.header, className: "mt-1", children: [
|
|
4826
|
+
/* @__PURE__ */ jsx16(
|
|
4827
|
+
RefusalLine,
|
|
4828
|
+
{
|
|
4829
|
+
error: refusal(
|
|
4830
|
+
"refused_by_rule",
|
|
4831
|
+
`${v.refused} of ${v.judged} row${v.judged === 1 ? "" : "s"} would be refused by ${fieldName(v.field)}'s rules. ${v.firstWhy}`,
|
|
4832
|
+
"Send this column somewhere else, fix those rows in your file, or import the rest and they are listed afterwards."
|
|
4833
|
+
)
|
|
4834
|
+
}
|
|
4835
|
+
),
|
|
4836
|
+
/* @__PURE__ */ jsxs12("p", { className: "text-muted-foreground", "data-import-rule-sample": "", children: [
|
|
4837
|
+
"Line ",
|
|
4838
|
+
v.firstLine,
|
|
4839
|
+
" in your file: ",
|
|
4840
|
+
v.firstRaw
|
|
4841
|
+
] })
|
|
4842
|
+
] });
|
|
4843
|
+
})() : null
|
|
4583
4844
|
] }),
|
|
4584
4845
|
/* @__PURE__ */ jsx16("span", { className: "truncate pt-1.5 text-muted-foreground", children: (column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ") })
|
|
4585
4846
|
]
|
|
@@ -4643,12 +4904,34 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4643
4904
|
)
|
|
4644
4905
|
] })
|
|
4645
4906
|
] }),
|
|
4907
|
+
ruleVerdicts.length > 0 ? /* @__PURE__ */ jsx16(
|
|
4908
|
+
RefusalNotice,
|
|
4909
|
+
{
|
|
4910
|
+
error: refusal(
|
|
4911
|
+
"refused_by_rule",
|
|
4912
|
+
`${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.`,
|
|
4913
|
+
"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."
|
|
4914
|
+
),
|
|
4915
|
+
actions: /* @__PURE__ */ jsxs12("label", { className: "mt-1 flex items-center gap-1.5", "data-import-go-ahead": "", children: [
|
|
4916
|
+
/* @__PURE__ */ jsx16(
|
|
4917
|
+
"input",
|
|
4918
|
+
{
|
|
4919
|
+
type: "checkbox",
|
|
4920
|
+
checked: goAheadAnyway,
|
|
4921
|
+
disabled: busy,
|
|
4922
|
+
onChange: (e) => setGoAheadAnyway(e.target.checked)
|
|
4923
|
+
}
|
|
4924
|
+
),
|
|
4925
|
+
"Import the rest anyway"
|
|
4926
|
+
] })
|
|
4927
|
+
}
|
|
4928
|
+
) : null,
|
|
4646
4929
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
4647
4930
|
/* @__PURE__ */ jsx16(
|
|
4648
4931
|
Button11,
|
|
4649
4932
|
{
|
|
4650
4933
|
size: "sm",
|
|
4651
|
-
disabled: busy || !rights.write || rows.length === 0 || titleMissing,
|
|
4934
|
+
disabled: busy || !rights.write || rows.length === 0 || titleMissing || ruleVerdicts.length > 0 && !goAheadAnyway,
|
|
4652
4935
|
onClick: () => void run(),
|
|
4653
4936
|
children: phase === "declaring" ? "Adding the columns\u2026" : phase === "writing" ? `Writing\u2026 ${progress} of ${rows.length}` : `Import ${rows.length} row${rows.length === 1 ? "" : "s"}`
|
|
4654
4937
|
}
|
|
@@ -4703,14 +4986,95 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4703
4986
|
] }) }),
|
|
4704
4987
|
/* @__PURE__ */ jsx16("tbody", { children: interesting.slice(0, 200).map((o) => /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
|
|
4705
4988
|
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: o.row }),
|
|
4706
|
-
/* @__PURE__ */ jsx16("td", { className:
|
|
4989
|
+
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: o.outcome === "refused" ? /* @__PURE__ */ jsx16(
|
|
4990
|
+
RefusalLine,
|
|
4991
|
+
{
|
|
4992
|
+
error: refusal("refused_by_rule", o.reason, "Fix this line in your file and import it again.")
|
|
4993
|
+
}
|
|
4994
|
+
) : o.outcome === "duplicate" ? /* @__PURE__ */ jsx16("span", { className: "text-muted-foreground", children: o.reason }) : "written" }),
|
|
4707
4995
|
/* @__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 ") : "" })
|
|
4708
4996
|
] }, `${o.outcome}-${o.row}`)) })
|
|
4709
4997
|
] }) }) : null,
|
|
4710
4998
|
proposals.length > 0 ? /* @__PURE__ */ jsx16("ul", { className: "flex flex-col gap-0.5", children: proposals.map((p) => /* @__PURE__ */ jsxs12("li", { className: "text-muted-foreground", children: [
|
|
4711
4999
|
/* @__PURE__ */ jsx16("span", { className: "font-medium text-foreground", children: p.column }),
|
|
4712
5000
|
p.state === "waiting" ? " is waiting in the approvals inbox." : p.state === "accepted" ? " was added as a column." : p.state === "ignored" ? " was left out." : p.state === "refused" ? ` could not be added: ${p.reason ?? ""}` : ` \u2014 ${p.state}`
|
|
4713
|
-
] }, p.column)) }) : null
|
|
5001
|
+
] }, p.column)) }) : null,
|
|
5002
|
+
phase === "done" ? /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5003
|
+
/* @__PURE__ */ jsx16(Button11, { size: "sm", onClick: () => onDone?.(ledger?.written ?? 0), children: "Done" }),
|
|
5004
|
+
(ledger?.refused ?? 0) > 0 ? /* @__PURE__ */ jsxs12("span", { className: "text-muted-foreground", children: [
|
|
5005
|
+
"The ",
|
|
5006
|
+
ledger?.refused,
|
|
5007
|
+
" row",
|
|
5008
|
+
ledger?.refused === 1 ? "" : "s",
|
|
5009
|
+
" that ",
|
|
5010
|
+
ledger?.refused === 1 ? "was" : "were",
|
|
5011
|
+
" refused",
|
|
5012
|
+
" ",
|
|
5013
|
+
"are kept with this import \u2014 open them again from the list of imports below, with the store's reason and the line from your file on each."
|
|
5014
|
+
] }) : null
|
|
5015
|
+
] }) : null
|
|
5016
|
+
] }) : null,
|
|
5017
|
+
runsProblem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: runsProblem }) : null,
|
|
5018
|
+
runs && runs.length > 0 ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
5019
|
+
/* @__PURE__ */ jsx16(Separator2, {}),
|
|
5020
|
+
/* @__PURE__ */ jsx16("p", { className: "font-medium", children: "Imports into this table" }),
|
|
5021
|
+
/* @__PURE__ */ jsx16("ul", { className: "flex flex-col gap-1", children: runs.map((r) => /* @__PURE__ */ jsxs12("li", { className: "flex flex-col gap-1", children: [
|
|
5022
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5023
|
+
/* @__PURE__ */ jsx16("span", { className: "font-medium", children: r.source_name ?? "a file" }),
|
|
5024
|
+
/* @__PURE__ */ jsxs12("span", { className: "text-muted-foreground", children: [
|
|
5025
|
+
new Date(r.opened_at).toLocaleString(),
|
|
5026
|
+
" \xB7 ",
|
|
5027
|
+
r.rows_written,
|
|
5028
|
+
" landed \xB7",
|
|
5029
|
+
" ",
|
|
5030
|
+
r.rows_duplicate,
|
|
5031
|
+
" already here \xB7 ",
|
|
5032
|
+
r.rows_refused,
|
|
5033
|
+
" refused, of ",
|
|
5034
|
+
r.rows_seen,
|
|
5035
|
+
" row",
|
|
5036
|
+
r.rows_seen === 1 ? "" : "s",
|
|
5037
|
+
" offered"
|
|
5038
|
+
] }),
|
|
5039
|
+
r.rows_refused > 0 ? /* @__PURE__ */ jsx16(
|
|
5040
|
+
"button",
|
|
5041
|
+
{
|
|
5042
|
+
type: "button",
|
|
5043
|
+
className: "underline underline-offset-2",
|
|
5044
|
+
"aria-expanded": openRun === r.import_id,
|
|
5045
|
+
onClick: () => void openRefusals(r.import_id),
|
|
5046
|
+
children: openRun === r.import_id ? "hide the rows it refused" : `see the ${r.rows_refused} row${r.rows_refused === 1 ? "" : "s"} it refused`
|
|
5047
|
+
}
|
|
5048
|
+
) : null
|
|
5049
|
+
] }),
|
|
5050
|
+
openRun === r.import_id ? runProblem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: runProblem }) : runRows ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
5051
|
+
runRows.note ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: runRows.note }) : null,
|
|
5052
|
+
/* @__PURE__ */ jsx16("div", { className: "max-h-64 overflow-auto rounded border", children: /* @__PURE__ */ jsxs12("table", { className: "w-full", children: [
|
|
5053
|
+
/* @__PURE__ */ jsx16("thead", { className: "sticky top-0 bg-muted", children: /* @__PURE__ */ jsxs12("tr", { children: [
|
|
5054
|
+
/* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "Line in your file" }),
|
|
5055
|
+
/* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "What happened" }),
|
|
5056
|
+
/* @__PURE__ */ jsx16("th", { className: "px-2 py-1 text-left font-medium", children: "The row in your file" })
|
|
5057
|
+
] }) }),
|
|
5058
|
+
/* @__PURE__ */ jsx16("tbody", { children: runRows.rows.map((row, i) => {
|
|
5059
|
+
const source = row.source ?? {};
|
|
5060
|
+
return /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
|
|
5061
|
+
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: String(row.row ?? "") }),
|
|
5062
|
+
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: /* @__PURE__ */ jsx16(
|
|
5063
|
+
RefusalLine,
|
|
5064
|
+
{
|
|
5065
|
+
error: refusal(
|
|
5066
|
+
"refused_by_rule",
|
|
5067
|
+
String(row.reason ?? "This row was refused."),
|
|
5068
|
+
"Fix this line in your file and import it again."
|
|
5069
|
+
)
|
|
5070
|
+
}
|
|
5071
|
+
) }),
|
|
5072
|
+
/* @__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 ") })
|
|
5073
|
+
] }, `${r.import_id}-${String(row.row ?? i)}`);
|
|
5074
|
+
}) })
|
|
5075
|
+
] }) })
|
|
5076
|
+
] }) : /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Reading the rows this import refused\u2026" }) : null
|
|
5077
|
+
] }, r.import_id)) })
|
|
4714
5078
|
] }) : null,
|
|
4715
5079
|
table.error ? /* @__PURE__ */ jsx16(RefusalNotice, { error: table.error }) : null
|
|
4716
5080
|
] });
|
|
@@ -4942,7 +5306,9 @@ function keyFor(label) {
|
|
|
4942
5306
|
if (token === "") return "";
|
|
4943
5307
|
return /^[a-z]/.test(token) ? token : `f_${token}`;
|
|
4944
5308
|
}
|
|
4945
|
-
var
|
|
5309
|
+
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.";
|
|
5310
|
+
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.";
|
|
5311
|
+
var COLUMN_DID_NOT_LAND = `${COLUMN_DID_NOT_LAND_SENTENCE} ${COLUMN_DID_NOT_LAND_REMEDY}`;
|
|
4946
5312
|
function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }) {
|
|
4947
5313
|
const table = useTable4(tableId);
|
|
4948
5314
|
const fields = useFields6(tableId);
|
|
@@ -5242,14 +5608,20 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5242
5608
|
] }, `${rule.kind}-${index}`))
|
|
5243
5609
|
] }),
|
|
5244
5610
|
mutation.error ? /* @__PURE__ */ jsx18(RefusalNotice, { error: mutation.error }) : null,
|
|
5245
|
-
missing ? /* @__PURE__ */ jsx18("p", { className: "text-[11px] text-
|
|
5246
|
-
notLanded ? /* @__PURE__ */ jsx18(
|
|
5611
|
+
missing ? /* @__PURE__ */ jsx18("p", { id: "field-editor-missing", className: "text-[11px] text-muted-foreground", "data-field-editor-missing": "", children: missing }) : null,
|
|
5612
|
+
notLanded ? /* @__PURE__ */ jsx18(
|
|
5613
|
+
RefusalNotice,
|
|
5614
|
+
{
|
|
5615
|
+
error: refusal("internal", COLUMN_DID_NOT_LAND_SENTENCE, COLUMN_DID_NOT_LAND_REMEDY)
|
|
5616
|
+
}
|
|
5617
|
+
) : null,
|
|
5247
5618
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5248
5619
|
/* @__PURE__ */ jsx18(
|
|
5249
5620
|
Button13,
|
|
5250
5621
|
{
|
|
5251
5622
|
size: "sm",
|
|
5252
5623
|
disabled: mutation.saving || missing !== null,
|
|
5624
|
+
...missing ? { "aria-describedby": "field-editor-missing" } : {},
|
|
5253
5625
|
onClick: () => void save(),
|
|
5254
5626
|
children: mutation.saving ? "Saving\u2026" : field ? EDIT_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
|
|
5255
5627
|
}
|
|
@@ -5529,7 +5901,7 @@ function ValueOnTheOtherSide({
|
|
|
5529
5901
|
}
|
|
5530
5902
|
|
|
5531
5903
|
// src/StageRules.tsx
|
|
5532
|
-
import { useCallback as useCallback9, useEffect as
|
|
5904
|
+
import { useCallback as useCallback9, useEffect as useEffect9, useMemo as useMemo13, useState as useState17 } from "react";
|
|
5533
5905
|
import { useFields as useFields7, useRecordsClient as useRecordsClient8, useTable as useTable5 } from "@ai-matrx/records/react";
|
|
5534
5906
|
import { BasicInput as BasicInput7, BasicTextarea as BasicTextarea3, Button as Button15, Separator as Separator5, Skeleton as Skeleton3, cn as cn15 } from "@ai-matrx/design-system";
|
|
5535
5907
|
|
|
@@ -5888,7 +6260,7 @@ function StageRulesSection({ tableId, stage, className }) {
|
|
|
5888
6260
|
if (mode.ok) setEnforcement(mode.data);
|
|
5889
6261
|
setAsked(true);
|
|
5890
6262
|
}, [client, tableId]);
|
|
5891
|
-
|
|
6263
|
+
useEffect9(() => {
|
|
5892
6264
|
void load();
|
|
5893
6265
|
}, [load]);
|
|
5894
6266
|
const stages = pipeline?.stages ?? [];
|
|
@@ -5899,7 +6271,7 @@ function StageRulesSection({ tableId, stage, className }) {
|
|
|
5899
6271
|
() => (fields.data ?? []).map((f) => ({ id: String(f.id), key: f.key, label: fieldName(f), field: f })),
|
|
5900
6272
|
[fields.data]
|
|
5901
6273
|
);
|
|
5902
|
-
|
|
6274
|
+
useEffect9(() => {
|
|
5903
6275
|
if (!draft || !stageKey || !draft.demands || Object.keys(draft.demands).length === 0) {
|
|
5904
6276
|
setPreview(null);
|
|
5905
6277
|
setPreviewError(null);
|
|
@@ -6338,7 +6710,7 @@ import { useFields as useFields10, useRecord as useRecord2, useRecordsClient as
|
|
|
6338
6710
|
import { AlchemyMenu as AlchemyMenu2 } from "@ai-matrx/alchemy/react";
|
|
6339
6711
|
|
|
6340
6712
|
// src/RecordChat.tsx
|
|
6341
|
-
import { useEffect as
|
|
6713
|
+
import { useEffect as useEffect10, useMemo as useMemo14, useState as useState19 } from "react";
|
|
6342
6714
|
import { useRecordsClient as useRecordsClient9 } from "@ai-matrx/records/react";
|
|
6343
6715
|
import { Skeleton as Skeleton4, cn as cn17 } from "@ai-matrx/design-system";
|
|
6344
6716
|
import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
@@ -6348,7 +6720,7 @@ function RecordChat({ tableId, recordId, className }) {
|
|
|
6348
6720
|
const host = useRecordsUi();
|
|
6349
6721
|
const [scope, setScope] = useState19(null);
|
|
6350
6722
|
const [error, setError] = useState19(null);
|
|
6351
|
-
|
|
6723
|
+
useEffect10(() => {
|
|
6352
6724
|
let cancelled = false;
|
|
6353
6725
|
setScope(null);
|
|
6354
6726
|
setError(null);
|
|
@@ -6465,7 +6837,7 @@ function entriesFor(scope) {
|
|
|
6465
6837
|
import { Button as Button19, Separator as Separator8, Skeleton as Skeleton5, cn as cn19 } from "@ai-matrx/design-system";
|
|
6466
6838
|
|
|
6467
6839
|
// src/RecordForm.tsx
|
|
6468
|
-
import { useEffect as
|
|
6840
|
+
import { useEffect as useEffect12, useMemo as useMemo16, useState as useState21 } from "react";
|
|
6469
6841
|
import {
|
|
6470
6842
|
useFields as useFields9,
|
|
6471
6843
|
useRecord,
|
|
@@ -6475,7 +6847,7 @@ import { predictWriteRefusals } from "@ai-matrx/records/core";
|
|
|
6475
6847
|
import { Button as Button17, Separator as Separator7, cn as cn18 } from "@ai-matrx/design-system";
|
|
6476
6848
|
|
|
6477
6849
|
// src/systemTable.ts
|
|
6478
|
-
import { useEffect as
|
|
6850
|
+
import { useEffect as useEffect11, useMemo as useMemo15, useState as useState20 } from "react";
|
|
6479
6851
|
import { useRecordsClient as useRecordsClient10 } from "@ai-matrx/records/react";
|
|
6480
6852
|
var inFlight = /* @__PURE__ */ new Map();
|
|
6481
6853
|
async function ensureSystemTable(client, spec) {
|
|
@@ -6608,7 +6980,7 @@ function useSystemTable(spec) {
|
|
|
6608
6980
|
const [state, setState] = useState20({ tableId: null, loading: true, error: null });
|
|
6609
6981
|
const slug = spec.slug;
|
|
6610
6982
|
const stable = useMemo15(() => spec, [slug]);
|
|
6611
|
-
|
|
6983
|
+
useEffect11(() => {
|
|
6612
6984
|
let cancelled = false;
|
|
6613
6985
|
setState({ tableId: null, loading: true, error: null });
|
|
6614
6986
|
void ensureSystemTable(client, stable).then((result) => {
|
|
@@ -6626,7 +6998,7 @@ function useSystemTable(spec) {
|
|
|
6626
6998
|
function useRecordVersion(recordId) {
|
|
6627
6999
|
const client = useRecordsClient10();
|
|
6628
7000
|
const [version, setVersion] = useState20(null);
|
|
6629
|
-
|
|
7001
|
+
useEffect11(() => {
|
|
6630
7002
|
let cancelled = false;
|
|
6631
7003
|
setVersion(null);
|
|
6632
7004
|
if (!recordId) return;
|
|
@@ -6663,7 +7035,7 @@ function RecordForm({
|
|
|
6663
7035
|
const [touched, setTouched] = useState21(false);
|
|
6664
7036
|
const loaded = useRecordVersion(recordId ?? null);
|
|
6665
7037
|
const loadedVersion = loaded.version;
|
|
6666
|
-
|
|
7038
|
+
useEffect12(() => {
|
|
6667
7039
|
const document3 = existing.data?.document;
|
|
6668
7040
|
if (document3) setDraft({ ...document3 });
|
|
6669
7041
|
}, [existing.data?.record_id, loadedVersion]);
|
|
@@ -6724,7 +7096,13 @@ function RecordForm({
|
|
|
6724
7096
|
},
|
|
6725
7097
|
field.id
|
|
6726
7098
|
)) }),
|
|
6727
|
-
touched ? predicted.filter((p) => !p.field_key).map((p) => /* @__PURE__ */ jsx23(
|
|
7099
|
+
touched ? predicted.filter((p) => !p.field_key).map((p) => /* @__PURE__ */ jsx23(
|
|
7100
|
+
RefusalNotice,
|
|
7101
|
+
{
|
|
7102
|
+
error: { code: "refused_by_rule", message: p.message }
|
|
7103
|
+
},
|
|
7104
|
+
p.message
|
|
7105
|
+
)) : null,
|
|
6728
7106
|
mutation.error ? /* @__PURE__ */ jsx23(
|
|
6729
7107
|
RefusalNotice,
|
|
6730
7108
|
{
|
|
@@ -6779,7 +7157,14 @@ function FieldRow({
|
|
|
6779
7157
|
return /* @__PURE__ */ jsxs19("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
6780
7158
|
/* @__PURE__ */ jsx23(FieldLabel, { field, htmlFor: id, children: /* @__PURE__ */ jsx23(ProvenanceBadge, { document: document2, fieldKey: field.key }) }),
|
|
6781
7159
|
/* @__PURE__ */ jsx23(FieldControl, { field, value, onChange, id }),
|
|
6782
|
-
problems.map((problem) => /* @__PURE__ */ jsx23(
|
|
7160
|
+
problems.map((problem) => /* @__PURE__ */ jsx23(
|
|
7161
|
+
RefusalNotice,
|
|
7162
|
+
{
|
|
7163
|
+
error: { code: "refused_by_rule", message: problem.message },
|
|
7164
|
+
className: "border-0 p-0"
|
|
7165
|
+
},
|
|
7166
|
+
problem.message
|
|
7167
|
+
))
|
|
6783
7168
|
] });
|
|
6784
7169
|
}
|
|
6785
7170
|
function asWords(value) {
|
|
@@ -6790,7 +7175,7 @@ function asWords(value) {
|
|
|
6790
7175
|
}
|
|
6791
7176
|
|
|
6792
7177
|
// src/ShareControl.tsx
|
|
6793
|
-
import { useState as useState22 } from "react";
|
|
7178
|
+
import { useEffect as useEffect13, useState as useState22 } from "react";
|
|
6794
7179
|
import { Button as Button18 } from "@ai-matrx/design-system";
|
|
6795
7180
|
import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
6796
7181
|
function useCanShare() {
|
|
@@ -6805,12 +7190,16 @@ function ShareControl({
|
|
|
6805
7190
|
subjectId,
|
|
6806
7191
|
name,
|
|
6807
7192
|
may,
|
|
7193
|
+
initiallyOpen = false,
|
|
6808
7194
|
size = "sm",
|
|
6809
7195
|
variant = "ghost",
|
|
6810
7196
|
className
|
|
6811
7197
|
}) {
|
|
6812
7198
|
const host = useRecordsUi();
|
|
6813
|
-
const [open, setOpen] = useState22(
|
|
7199
|
+
const [open, setOpen] = useState22(initiallyOpen);
|
|
7200
|
+
useEffect13(() => {
|
|
7201
|
+
if (initiallyOpen) setOpen(true);
|
|
7202
|
+
}, [initiallyOpen]);
|
|
6814
7203
|
const asked = useRecordRights(may === void 0 ? subjectId : null);
|
|
6815
7204
|
const mayShare = may ?? asked.share;
|
|
6816
7205
|
if (!host.share) return null;
|
|
@@ -7092,12 +7481,12 @@ function parseSorts(raw) {
|
|
|
7092
7481
|
}
|
|
7093
7482
|
|
|
7094
7483
|
// src/ViewSwitcher.tsx
|
|
7095
|
-
import { useEffect as
|
|
7484
|
+
import { useEffect as useEffect15, useMemo as useMemo18, useState as useState25 } from "react";
|
|
7096
7485
|
import { useFields as useFields12, useRecords as useRecords5, useRecordsClient as useRecordsClient13 } from "@ai-matrx/records/react";
|
|
7097
7486
|
import { Button as Button21, Skeleton as Skeleton7, cn as cn21 } from "@ai-matrx/design-system";
|
|
7098
7487
|
|
|
7099
7488
|
// src/Pipeline.tsx
|
|
7100
|
-
import { useCallback as useCallback10, useEffect as
|
|
7489
|
+
import { useCallback as useCallback10, useEffect as useEffect14, useMemo as useMemo17, useRef as useRef6, useState as useState24 } from "react";
|
|
7101
7490
|
import {
|
|
7102
7491
|
mayDrag,
|
|
7103
7492
|
useFields as useFields11,
|
|
@@ -7146,7 +7535,7 @@ function PipelineBoard({
|
|
|
7146
7535
|
const [dragging, setDragging] = useState24(null);
|
|
7147
7536
|
const [over, setOver] = useState24(null);
|
|
7148
7537
|
const alive = useRef6(true);
|
|
7149
|
-
|
|
7538
|
+
useEffect14(() => {
|
|
7150
7539
|
alive.current = true;
|
|
7151
7540
|
return () => {
|
|
7152
7541
|
alive.current = false;
|
|
@@ -7170,7 +7559,7 @@ function PipelineBoard({
|
|
|
7170
7559
|
setWaiting(held.ok ? new Map((held.data ?? []).map((p) => [p.record_id, p])) : /* @__PURE__ */ new Map());
|
|
7171
7560
|
setColumns(board.ok ? board.data ?? [] : []);
|
|
7172
7561
|
}, [client, tableId, measure]);
|
|
7173
|
-
|
|
7562
|
+
useEffect14(() => {
|
|
7174
7563
|
void reload();
|
|
7175
7564
|
}, [reload]);
|
|
7176
7565
|
const stageKey = definition?.stage_field ?? null;
|
|
@@ -7202,8 +7591,8 @@ function PipelineBoard({
|
|
|
7202
7591
|
const asked = await client.pipelineTransitionRefusal({ record_id: recordId, to });
|
|
7203
7592
|
if (!alive.current) return;
|
|
7204
7593
|
if (!asked.ok) {
|
|
7205
|
-
|
|
7206
|
-
|
|
7594
|
+
setPending({ kind: "clash", recordId, to, error: asked.error });
|
|
7595
|
+
onMoved?.();
|
|
7207
7596
|
return;
|
|
7208
7597
|
}
|
|
7209
7598
|
const verdict = asked.data;
|
|
@@ -7216,8 +7605,8 @@ function PipelineBoard({
|
|
|
7216
7605
|
const moved = await client.pipelineMove({ record_id: recordId, to });
|
|
7217
7606
|
if (!alive.current) return;
|
|
7218
7607
|
if (!moved.ok) {
|
|
7219
|
-
setPending({ kind: "
|
|
7220
|
-
|
|
7608
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7609
|
+
onMoved?.();
|
|
7221
7610
|
return;
|
|
7222
7611
|
}
|
|
7223
7612
|
const result = moved.data;
|
|
@@ -7238,21 +7627,22 @@ function PipelineBoard({
|
|
|
7238
7627
|
const moved = await client.pipelineMove({ record_id: recordId, to });
|
|
7239
7628
|
if (!alive.current) return;
|
|
7240
7629
|
if (!moved.ok) {
|
|
7241
|
-
setPending({ kind: "
|
|
7242
|
-
|
|
7630
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7631
|
+
onMoved?.();
|
|
7243
7632
|
return;
|
|
7244
7633
|
}
|
|
7245
7634
|
setPending({ kind: "filed", recordId, to, result: moved.data });
|
|
7246
7635
|
void reload();
|
|
7247
7636
|
},
|
|
7248
|
-
[client, reload]
|
|
7637
|
+
[client, onMoved, reload]
|
|
7249
7638
|
);
|
|
7250
7639
|
const moveWith = useCallback10(
|
|
7251
7640
|
async (recordId, to, also) => {
|
|
7252
7641
|
const moved = await client.pipelineMove({ record_id: recordId, to, also });
|
|
7253
7642
|
if (!alive.current) return;
|
|
7254
7643
|
if (!moved.ok) {
|
|
7255
|
-
|
|
7644
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7645
|
+
onMoved?.();
|
|
7256
7646
|
return;
|
|
7257
7647
|
}
|
|
7258
7648
|
setPending({ kind: "none" });
|
|
@@ -7380,11 +7770,33 @@ function Held({
|
|
|
7380
7770
|
if (pending.kind === "asking") {
|
|
7381
7771
|
return /* @__PURE__ */ jsx26("p", { className: "px-2 py-1 text-xs text-muted-foreground", children: "Asking\u2026" });
|
|
7382
7772
|
}
|
|
7773
|
+
if (pending.kind === "clash") {
|
|
7774
|
+
return /* @__PURE__ */ jsx26(
|
|
7775
|
+
RefusalNotice,
|
|
7776
|
+
{
|
|
7777
|
+
className: "mt-1",
|
|
7778
|
+
error: pending.error,
|
|
7779
|
+
actions: /* @__PURE__ */ jsx26("div", { className: "mt-1", children: /* @__PURE__ */ jsx26(Button20, { size: "sm", variant: "ghost", className: "h-6", onClick: onCancel, children: "Close" }) })
|
|
7780
|
+
}
|
|
7781
|
+
);
|
|
7782
|
+
}
|
|
7383
7783
|
if (pending.kind === "refused") {
|
|
7384
|
-
return
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7784
|
+
return (
|
|
7785
|
+
// THE STAGE GATE SAID NO: the one refusal surface, heading and remedy both,
|
|
7786
|
+
// never its sentence alone in a red box (lane REFUSAL-SWEEP).
|
|
7787
|
+
/* @__PURE__ */ jsx26(
|
|
7788
|
+
RefusalNotice,
|
|
7789
|
+
{
|
|
7790
|
+
className: "mt-1",
|
|
7791
|
+
error: refusal(
|
|
7792
|
+
"refused_by_rule",
|
|
7793
|
+
pending.verdict.why,
|
|
7794
|
+
"The card stays where it was. Fill in what this stage asks for, then move it again."
|
|
7795
|
+
),
|
|
7796
|
+
actions: /* @__PURE__ */ jsx26(Button20, { size: "sm", variant: "ghost", className: "mt-1 h-6", onClick: onCancel, children: "Close" })
|
|
7797
|
+
}
|
|
7798
|
+
)
|
|
7799
|
+
);
|
|
7388
7800
|
}
|
|
7389
7801
|
if (pending.kind === "approval") {
|
|
7390
7802
|
return /* @__PURE__ */ jsxs22("div", { className: "mt-1 flex flex-col gap-1 rounded border p-2 text-xs", children: [
|
|
@@ -7517,7 +7929,7 @@ function useViewRecords(view, pageSize = 200, filter) {
|
|
|
7517
7929
|
error: null
|
|
7518
7930
|
});
|
|
7519
7931
|
const ruleId = view.ruleId ?? null;
|
|
7520
|
-
|
|
7932
|
+
useEffect15(() => {
|
|
7521
7933
|
if (!ruleId) return;
|
|
7522
7934
|
let cancelled = false;
|
|
7523
7935
|
setRuled({ rows: [], loading: true, error: null });
|
|
@@ -7581,7 +7993,7 @@ function ViewSwitcher({
|
|
|
7581
7993
|
}) {
|
|
7582
7994
|
const [layout, setLayout] = useState25(view.layout);
|
|
7583
7995
|
const [local, setLocal] = useState25({});
|
|
7584
|
-
|
|
7996
|
+
useEffect15(() => {
|
|
7585
7997
|
setLayout(view.layout);
|
|
7586
7998
|
setLocal({});
|
|
7587
7999
|
}, [view.layout, view.name, view.subject]);
|
|
@@ -7649,7 +8061,7 @@ function useStageField(tableId) {
|
|
|
7649
8061
|
asked: false,
|
|
7650
8062
|
key: null
|
|
7651
8063
|
});
|
|
7652
|
-
|
|
8064
|
+
useEffect15(() => {
|
|
7653
8065
|
let cancelled = false;
|
|
7654
8066
|
setStage({ asked: false, key: null });
|
|
7655
8067
|
void client.tableStageField({ table_id: tableId }).then((r) => {
|
|
@@ -7918,7 +8330,7 @@ function Card2({
|
|
|
7918
8330
|
}
|
|
7919
8331
|
|
|
7920
8332
|
// src/ArchivedView.tsx
|
|
7921
|
-
import { useCallback as useCallback11, useEffect as
|
|
8333
|
+
import { useCallback as useCallback11, useEffect as useEffect16, useState as useState26 } from "react";
|
|
7922
8334
|
import {
|
|
7923
8335
|
ARCHIVE_LANES,
|
|
7924
8336
|
ARCHIVE_LANE_LABEL,
|
|
@@ -7955,8 +8367,8 @@ function ArchivedView({
|
|
|
7955
8367
|
const [loading, setLoading] = useState26(true);
|
|
7956
8368
|
const [error, setError] = useState26(null);
|
|
7957
8369
|
const [restoring, setRestoring] = useState26(null);
|
|
7958
|
-
const [
|
|
7959
|
-
|
|
8370
|
+
const [refusal2, setRefusal] = useState26(null);
|
|
8371
|
+
useEffect16(() => {
|
|
7960
8372
|
if (laneFromHost) setLane(laneFromHost);
|
|
7961
8373
|
}, [laneFromHost]);
|
|
7962
8374
|
const read = useCallback11(async () => {
|
|
@@ -7971,7 +8383,7 @@ function ArchivedView({
|
|
|
7971
8383
|
}
|
|
7972
8384
|
setLoading(false);
|
|
7973
8385
|
}, [client, tableId, lane, pageSize]);
|
|
7974
|
-
|
|
8386
|
+
useEffect16(() => {
|
|
7975
8387
|
void read();
|
|
7976
8388
|
}, [read]);
|
|
7977
8389
|
const pick = (next) => {
|
|
@@ -8032,7 +8444,7 @@ function ArchivedView({
|
|
|
8032
8444
|
/* @__PURE__ */ jsxs24("div", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
8033
8445
|
/* @__PURE__ */ jsx28("span", { className: "truncate text-sm", children: recordName(row.document, titleKey) }),
|
|
8034
8446
|
/* @__PURE__ */ jsx28("span", { className: "truncate text-xs text-muted-foreground", "data-testid": "archived-who", children: archivedByLine(row) }),
|
|
8035
|
-
|
|
8447
|
+
refusal2 && refusal2.recordId === row.id ? /* @__PURE__ */ jsx28(RefusalNotice, { error: refusal2.error, className: "mt-2" }) : null
|
|
8036
8448
|
] }),
|
|
8037
8449
|
mayRestore ? /* @__PURE__ */ jsx28(
|
|
8038
8450
|
Button22,
|
|
@@ -8053,7 +8465,7 @@ function ArchivedView({
|
|
|
8053
8465
|
}
|
|
8054
8466
|
|
|
8055
8467
|
// src/ArchivedDisclosure.tsx
|
|
8056
|
-
import { useCallback as useCallback12, useEffect as
|
|
8468
|
+
import { useCallback as useCallback12, useEffect as useEffect17, useState as useState27 } from "react";
|
|
8057
8469
|
import {
|
|
8058
8470
|
emptyPortalArchiveLine,
|
|
8059
8471
|
portalConfirmLine,
|
|
@@ -8110,7 +8522,7 @@ function ArchivedPortals({
|
|
|
8110
8522
|
const [confirming, setConfirming] = useState27(null);
|
|
8111
8523
|
const [typed, setTyped] = useState27("");
|
|
8112
8524
|
const [restoring, setRestoring] = useState27(null);
|
|
8113
|
-
const [
|
|
8525
|
+
const [refusal2, setRefusal] = useState27(null);
|
|
8114
8526
|
const [said, setSaid] = useState27(null);
|
|
8115
8527
|
const read = useCallback12(async () => {
|
|
8116
8528
|
const answered = await client.listPortals({ archived: "archived" });
|
|
@@ -8122,7 +8534,7 @@ function ArchivedPortals({
|
|
|
8122
8534
|
setError(answered.error);
|
|
8123
8535
|
}
|
|
8124
8536
|
}, [client]);
|
|
8125
|
-
|
|
8537
|
+
useEffect17(() => {
|
|
8126
8538
|
void read();
|
|
8127
8539
|
}, [read, refreshToken]);
|
|
8128
8540
|
const restore = async (portal) => {
|
|
@@ -8226,7 +8638,7 @@ function ArchivedPortals({
|
|
|
8226
8638
|
)
|
|
8227
8639
|
] })
|
|
8228
8640
|
] }) : null,
|
|
8229
|
-
|
|
8641
|
+
refusal2 && refusal2.portalId === portal.portal_id ? /* @__PURE__ */ jsx29(RefusalNotice, { error: refusal2.error }) : null
|
|
8230
8642
|
]
|
|
8231
8643
|
},
|
|
8232
8644
|
portal.portal_id
|
|
@@ -8238,7 +8650,7 @@ function ArchivedPortals({
|
|
|
8238
8650
|
}
|
|
8239
8651
|
|
|
8240
8652
|
// src/ViewBar.tsx
|
|
8241
|
-
import { useCallback as useCallback14, useEffect as
|
|
8653
|
+
import { useCallback as useCallback14, useEffect as useEffect18, useState as useState28 } from "react";
|
|
8242
8654
|
import { useRecordsClient as useRecordsClient16 } from "@ai-matrx/records/react";
|
|
8243
8655
|
import { Button as Button24, Input as Input2, Skeleton as Skeleton10, cn as cn24 } from "@ai-matrx/design-system";
|
|
8244
8656
|
|
|
@@ -8301,10 +8713,10 @@ function ViewBar({ tableId, activeViewId, onActiveView, seed, className }) {
|
|
|
8301
8713
|
setError(null);
|
|
8302
8714
|
setViews(mine);
|
|
8303
8715
|
}, [client, viewTableId, tableId, seed]);
|
|
8304
|
-
|
|
8716
|
+
useEffect18(() => {
|
|
8305
8717
|
void load();
|
|
8306
8718
|
}, [load]);
|
|
8307
|
-
|
|
8719
|
+
useEffect18(() => {
|
|
8308
8720
|
if (!views || views.length === 0) return;
|
|
8309
8721
|
const chosen = views.find((v) => v.id === (activeViewId ?? active)) ?? views.find((v) => v.isDefault) ?? views[0];
|
|
8310
8722
|
if (chosen.id !== active) setActive(chosen.id);
|
|
@@ -8480,12 +8892,12 @@ function ProposalRow({
|
|
|
8480
8892
|
}
|
|
8481
8893
|
|
|
8482
8894
|
// src/ActionInbox.tsx
|
|
8483
|
-
import { useCallback as useCallback16, useEffect as
|
|
8895
|
+
import { useCallback as useCallback16, useEffect as useEffect20, useMemo as useMemo20, useRef as useRef8, useState as useState31 } from "react";
|
|
8484
8896
|
import { useRecordsClient as useRecordsClient19 } from "@ai-matrx/records/react";
|
|
8485
8897
|
import { Badge as Badge9, Button as Button27, Skeleton as Skeleton12, cn as cn27 } from "@ai-matrx/design-system";
|
|
8486
8898
|
|
|
8487
8899
|
// src/ChecklistRunner.tsx
|
|
8488
|
-
import { useCallback as useCallback15, useEffect as
|
|
8900
|
+
import { useCallback as useCallback15, useEffect as useEffect19, useMemo as useMemo19, useState as useState30 } from "react";
|
|
8489
8901
|
import { useRecordsClient as useRecordsClient18, useTable as useTable10 } from "@ai-matrx/records/react";
|
|
8490
8902
|
import { Badge as Badge8, BasicInput as BasicInput9, BasicTextarea as BasicTextarea4, Button as Button26, Skeleton as Skeleton11, cn as cn26 } from "@ai-matrx/design-system";
|
|
8491
8903
|
import { Fragment as Fragment13, jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
@@ -8533,10 +8945,10 @@ function ChecklistRunner({
|
|
|
8533
8945
|
(held) => held && answered.data.some((r) => r.run_id === held) ? held : answered.data[0]?.run_id ?? null
|
|
8534
8946
|
);
|
|
8535
8947
|
}, [client, includeClosed, recordId, runId, tableId]);
|
|
8536
|
-
|
|
8948
|
+
useEffect19(() => {
|
|
8537
8949
|
void loadRuns();
|
|
8538
8950
|
}, [loadRuns]);
|
|
8539
|
-
|
|
8951
|
+
useEffect19(() => {
|
|
8540
8952
|
if (runId) setActiveId(runId);
|
|
8541
8953
|
}, [runId]);
|
|
8542
8954
|
const loadSteps = useCallback15(async () => {
|
|
@@ -8553,10 +8965,10 @@ function ChecklistRunner({
|
|
|
8553
8965
|
setError(null);
|
|
8554
8966
|
setSteps(answered.data);
|
|
8555
8967
|
}, [client, activeId]);
|
|
8556
|
-
|
|
8968
|
+
useEffect19(() => {
|
|
8557
8969
|
void loadSteps();
|
|
8558
8970
|
}, [loadSteps]);
|
|
8559
|
-
|
|
8971
|
+
useEffect19(() => {
|
|
8560
8972
|
if (!mayStart || !tableId) return;
|
|
8561
8973
|
let cancelled = false;
|
|
8562
8974
|
void client.checklistTemplates({ about_table_id: tableId, limit: 50 }).then((answered) => {
|
|
@@ -8782,7 +9194,7 @@ function useMyChecklistSteps(limit = 25) {
|
|
|
8782
9194
|
setSteps(held);
|
|
8783
9195
|
setLoading(false);
|
|
8784
9196
|
}, [client, limit, me]);
|
|
8785
|
-
|
|
9197
|
+
useEffect19(() => {
|
|
8786
9198
|
void refresh();
|
|
8787
9199
|
}, [refresh]);
|
|
8788
9200
|
return { steps, loading, error, refresh };
|
|
@@ -8791,7 +9203,7 @@ function MyChecklistSteps({ className }) {
|
|
|
8791
9203
|
const client = useRecordsClient18();
|
|
8792
9204
|
const { steps, loading, error, refresh } = useMyChecklistSteps();
|
|
8793
9205
|
const [busy, setBusy] = useState30(null);
|
|
8794
|
-
const [
|
|
9206
|
+
const [refusal2, setRefusal] = useState30(null);
|
|
8795
9207
|
const complete = useCallback15(
|
|
8796
9208
|
async (step2, evidence) => {
|
|
8797
9209
|
setBusy(step2.step_id);
|
|
@@ -8814,7 +9226,7 @@ function MyChecklistSteps({ className }) {
|
|
|
8814
9226
|
/* @__PURE__ */ jsx32("span", { className: "tabular-nums", children: steps.length })
|
|
8815
9227
|
] }),
|
|
8816
9228
|
error ? /* @__PURE__ */ jsx32(RefusalNotice, { error }) : null,
|
|
8817
|
-
|
|
9229
|
+
refusal2 ? /* @__PURE__ */ jsx32(RefusalNotice, { error: refusal2 }) : null,
|
|
8818
9230
|
/* @__PURE__ */ jsx32("ol", { className: "flex flex-col gap-1.5", children: steps.map((step2) => /* @__PURE__ */ jsx32(
|
|
8819
9231
|
StepRow,
|
|
8820
9232
|
{
|
|
@@ -8849,7 +9261,7 @@ function ChecklistsPanel({ tableId, onOpenRecord, className }) {
|
|
|
8849
9261
|
if (!r.ok) setRuns([]);
|
|
8850
9262
|
else setRuns(r.data);
|
|
8851
9263
|
}, [client, tableId]);
|
|
8852
|
-
|
|
9264
|
+
useEffect19(() => {
|
|
8853
9265
|
void load();
|
|
8854
9266
|
}, [load]);
|
|
8855
9267
|
if (templates === null) return /* @__PURE__ */ jsx32(Skeleton11, { className: cn26("h-40 w-full", className) });
|
|
@@ -8991,11 +9403,11 @@ function ChecklistTemplateEditor({
|
|
|
8991
9403
|
const client = useRecordsClient18();
|
|
8992
9404
|
const [name, setName] = useState30("");
|
|
8993
9405
|
const [rows, setRows] = useState30([{ ...EMPTY_ROW }]);
|
|
8994
|
-
const [
|
|
9406
|
+
const [refusal2, setRefusal] = useState30(null);
|
|
8995
9407
|
const [error, setError] = useState30(null);
|
|
8996
9408
|
const [busy, setBusy] = useState30(false);
|
|
8997
9409
|
const [loading, setLoading] = useState30(Boolean(templateId));
|
|
8998
|
-
|
|
9410
|
+
useEffect19(() => {
|
|
8999
9411
|
if (!templateId) return;
|
|
9000
9412
|
let cancelled = false;
|
|
9001
9413
|
void client.checklistTemplateShape({ template_id: templateId }).then((answered) => {
|
|
@@ -9032,7 +9444,7 @@ function ChecklistTemplateEditor({
|
|
|
9032
9444
|
}),
|
|
9033
9445
|
[aboutTableId, name, rows]
|
|
9034
9446
|
);
|
|
9035
|
-
|
|
9447
|
+
useEffect19(() => {
|
|
9036
9448
|
if (spec.steps.length === 0 || spec.name.length === 0) {
|
|
9037
9449
|
setRefusal(null);
|
|
9038
9450
|
return;
|
|
@@ -9159,9 +9571,9 @@ function ChecklistTemplateEditor({
|
|
|
9159
9571
|
] }, index)) }),
|
|
9160
9572
|
/* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-1.5", children: [
|
|
9161
9573
|
/* @__PURE__ */ jsx32(Button26, { size: "sm", variant: "outline", onClick: () => setRows((held) => [...held, { ...EMPTY_ROW }]), children: "Add a step" }),
|
|
9162
|
-
/* @__PURE__ */ jsx32(Button26, { size: "sm", disabled: busy ||
|
|
9574
|
+
/* @__PURE__ */ jsx32(Button26, { size: "sm", disabled: busy || refusal2 !== null || spec.steps.length === 0, onClick: () => void save(), children: busy ? "\u2026" : "Save" })
|
|
9163
9575
|
] }),
|
|
9164
|
-
|
|
9576
|
+
refusal2 ? /* @__PURE__ */ jsx32("p", { className: "text-xs text-muted-foreground", "data-testid": "checklist-editor-refusal", children: refusal2 }) : null
|
|
9165
9577
|
] });
|
|
9166
9578
|
}
|
|
9167
9579
|
function toStepSpec(row, index) {
|
|
@@ -9218,7 +9630,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9218
9630
|
setError(null);
|
|
9219
9631
|
setItems(result.data);
|
|
9220
9632
|
}, [client, includeSettled]);
|
|
9221
|
-
|
|
9633
|
+
useEffect20(() => {
|
|
9222
9634
|
void load();
|
|
9223
9635
|
}, [load]);
|
|
9224
9636
|
const shown = useMemo20(() => {
|
|
@@ -9226,10 +9638,10 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9226
9638
|
if (!tableId) return all;
|
|
9227
9639
|
return all.filter((i) => i.kind !== "assignment" || i.subject_kind !== "record" || true);
|
|
9228
9640
|
}, [items, tableId]);
|
|
9229
|
-
|
|
9641
|
+
useEffect20(() => {
|
|
9230
9642
|
if (cursor >= shown.length) setCursor(Math.max(0, shown.length - 1));
|
|
9231
9643
|
}, [shown.length, cursor]);
|
|
9232
|
-
|
|
9644
|
+
useEffect20(() => {
|
|
9233
9645
|
const el = listRef.current?.querySelector(`[data-row="${cursor}"]`);
|
|
9234
9646
|
el?.scrollIntoView({ block: "nearest" });
|
|
9235
9647
|
}, [cursor, shown.length]);
|
|
@@ -9355,7 +9767,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9355
9767
|
}
|
|
9356
9768
|
|
|
9357
9769
|
// src/HistoryPanel.tsx
|
|
9358
|
-
import { useCallback as useCallback17, useEffect as
|
|
9770
|
+
import { useCallback as useCallback17, useEffect as useEffect21, useRef as useRef9, useState as useState32 } from "react";
|
|
9359
9771
|
import {
|
|
9360
9772
|
useFields as useFields14,
|
|
9361
9773
|
useRecordsClient as useRecordsClient20,
|
|
@@ -9383,7 +9795,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
9383
9795
|
setError(null);
|
|
9384
9796
|
setEntries(answered.data);
|
|
9385
9797
|
}, [client, recordId]);
|
|
9386
|
-
|
|
9798
|
+
useEffect21(() => {
|
|
9387
9799
|
void load();
|
|
9388
9800
|
}, [load]);
|
|
9389
9801
|
if (error) return /* @__PURE__ */ jsx34(RefusalNotice, { error, className });
|
|
@@ -9664,7 +10076,7 @@ function say(value, field) {
|
|
|
9664
10076
|
}
|
|
9665
10077
|
|
|
9666
10078
|
// src/CommentThread.tsx
|
|
9667
|
-
import { useCallback as useCallback18, useEffect as
|
|
10079
|
+
import { useCallback as useCallback18, useEffect as useEffect22, useMemo as useMemo21, useRef as useRef10, useState as useState33 } from "react";
|
|
9668
10080
|
import {
|
|
9669
10081
|
useFields as useFields15,
|
|
9670
10082
|
useRecordsClient as useRecordsClient21,
|
|
@@ -9701,10 +10113,10 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
9701
10113
|
mayResolve: answered.data.may_resolve
|
|
9702
10114
|
});
|
|
9703
10115
|
}, [client, recordId, showResolved]);
|
|
9704
|
-
|
|
10116
|
+
useEffect22(() => {
|
|
9705
10117
|
void load();
|
|
9706
10118
|
}, [load]);
|
|
9707
|
-
|
|
10119
|
+
useEffect22(() => {
|
|
9708
10120
|
let alive = true;
|
|
9709
10121
|
if (!host.members) return;
|
|
9710
10122
|
void host.members().then((roster) => {
|
|
@@ -9907,7 +10319,7 @@ function Line({
|
|
|
9907
10319
|
}
|
|
9908
10320
|
|
|
9909
10321
|
// src/FieldHistoryPanel.tsx
|
|
9910
|
-
import { useCallback as useCallback19, useEffect as
|
|
10322
|
+
import { useCallback as useCallback19, useEffect as useEffect23, useState as useState34 } from "react";
|
|
9911
10323
|
import {
|
|
9912
10324
|
useFields as useFields16,
|
|
9913
10325
|
useRecordsClient as useRecordsClient22,
|
|
@@ -9942,7 +10354,7 @@ function FieldHistoryPanel({
|
|
|
9942
10354
|
setError(null);
|
|
9943
10355
|
setRows(answered.data);
|
|
9944
10356
|
}, [client, tableId, fieldKey, recordId]);
|
|
9945
|
-
|
|
10357
|
+
useEffect23(() => {
|
|
9946
10358
|
void load();
|
|
9947
10359
|
}, [load]);
|
|
9948
10360
|
const label = (fields.data ?? []).find((f) => f.key === fieldKey)?.label || humanize(fieldKey);
|
|
@@ -10135,7 +10547,7 @@ function submissionStamp(args) {
|
|
|
10135
10547
|
}
|
|
10136
10548
|
|
|
10137
10549
|
// src/PortalBuilder.tsx
|
|
10138
|
-
import { useCallback as useCallback20, useEffect as
|
|
10550
|
+
import { useCallback as useCallback20, useEffect as useEffect24, useMemo as useMemo22, useState as useState35 } from "react";
|
|
10139
10551
|
import { useRecordsClient as useRecordsClient23, useTables as useTables3 } from "@ai-matrx/records/react";
|
|
10140
10552
|
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";
|
|
10141
10553
|
import { Fragment as Fragment15, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
@@ -10168,7 +10580,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10168
10580
|
},
|
|
10169
10581
|
[client, fieldsByTable]
|
|
10170
10582
|
);
|
|
10171
|
-
|
|
10583
|
+
useEffect24(() => {
|
|
10172
10584
|
if (!portalId) return;
|
|
10173
10585
|
void (async () => {
|
|
10174
10586
|
const answered = await client.portalCard({ portal_id: portalId });
|
|
@@ -10182,7 +10594,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10182
10594
|
setClientTableId(answered.data.client_table_id);
|
|
10183
10595
|
})();
|
|
10184
10596
|
}, [client, portalId]);
|
|
10185
|
-
|
|
10597
|
+
useEffect24(() => {
|
|
10186
10598
|
if (!tableId) return;
|
|
10187
10599
|
setExposures(
|
|
10188
10600
|
(prev) => prev[tableId] ? prev : {
|
|
@@ -10430,7 +10842,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10430
10842
|
}
|
|
10431
10843
|
|
|
10432
10844
|
// src/PortalsPanel.tsx
|
|
10433
|
-
import { useCallback as useCallback21, useEffect as
|
|
10845
|
+
import { useCallback as useCallback21, useEffect as useEffect25, useMemo as useMemo23, useState as useState36 } from "react";
|
|
10434
10846
|
import { useRecordsClient as useRecordsClient24 } from "@ai-matrx/records/react";
|
|
10435
10847
|
import {
|
|
10436
10848
|
portalArchiveConsequence,
|
|
@@ -10471,6 +10883,7 @@ function BuildOrAsk({
|
|
|
10471
10883
|
|
|
10472
10884
|
// src/PortalsPanel.tsx
|
|
10473
10885
|
import { Fragment as Fragment16, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
10886
|
+
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.";
|
|
10474
10887
|
function revokeConsequence(person, portalTitle) {
|
|
10475
10888
|
const who = person.client ? `${person.email} (${person.client})` : person.email;
|
|
10476
10889
|
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.`;
|
|
@@ -10500,13 +10913,16 @@ function stateWords(person) {
|
|
|
10500
10913
|
};
|
|
10501
10914
|
}
|
|
10502
10915
|
var PORTAL_SUGGESTION = "Let each of my customers sign in and see their own jobs and invoices, and nothing else.";
|
|
10503
|
-
function PortalsPanel({ tableId, className }) {
|
|
10916
|
+
function PortalsPanel({ tableId, activePortalId, className }) {
|
|
10504
10917
|
const client = useRecordsClient24();
|
|
10505
10918
|
const host = useRecordsUi();
|
|
10506
10919
|
const [portals, setPortals] = useState36(null);
|
|
10507
10920
|
const [exposures, setExposures] = useState36([]);
|
|
10508
10921
|
const [listError, setListError] = useState36(null);
|
|
10509
|
-
const [openId, setOpenId] = useState36(null);
|
|
10922
|
+
const [openId, setOpenId] = useState36(activePortalId ?? null);
|
|
10923
|
+
useEffect25(() => {
|
|
10924
|
+
if (activePortalId) setOpenId(activePortalId);
|
|
10925
|
+
}, [activePortalId]);
|
|
10510
10926
|
const [building, setBuilding] = useState36(false);
|
|
10511
10927
|
const [adding, setAdding] = useState36(null);
|
|
10512
10928
|
const [archiveToken, setArchiveToken] = useState36(0);
|
|
@@ -10522,7 +10938,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
10522
10938
|
setPortals(answered.data);
|
|
10523
10939
|
setExposures(mapped.ok ? mapped.data : []);
|
|
10524
10940
|
}, [client]);
|
|
10525
|
-
|
|
10941
|
+
useEffect25(() => {
|
|
10526
10942
|
void load();
|
|
10527
10943
|
}, [load]);
|
|
10528
10944
|
if (portals === null) return /* @__PURE__ */ jsx39(Skeleton17, { className: cn33("h-32 w-full", className) });
|
|
@@ -10603,59 +11019,73 @@ function PortalsPanel({ tableId, className }) {
|
|
|
10603
11019
|
portal.portal_id
|
|
10604
11020
|
)) })
|
|
10605
11021
|
] }) : null,
|
|
11022
|
+
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,
|
|
10606
11023
|
/* @__PURE__ */ jsx39("ul", { className: "flex flex-col gap-2", children: shown.map((portal) => {
|
|
10607
11024
|
const url = `${origin}${portalPath(portal.slug)}`;
|
|
10608
11025
|
const open = openId === portal.portal_id;
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
"."
|
|
10619
|
-
] }),
|
|
10620
|
-
/* @__PURE__ */ jsxs35("p", { className: "mt-1.5 text-xs", children: [
|
|
10621
|
-
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.signed_in }),
|
|
10622
|
-
" signed in",
|
|
10623
|
-
" \xB7 ",
|
|
10624
|
-
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.invited - portal.signed_in }),
|
|
10625
|
-
" invited and waiting"
|
|
10626
|
-
] }),
|
|
10627
|
-
/* @__PURE__ */ jsxs35("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
10628
|
-
/* @__PURE__ */ jsx39(CopyLink, { url }),
|
|
10629
|
-
/* @__PURE__ */ jsx39(
|
|
10630
|
-
Button33,
|
|
10631
|
-
{
|
|
10632
|
-
size: "sm",
|
|
10633
|
-
variant: "ghost",
|
|
10634
|
-
onClick: () => setOpenId(open ? null : portal.portal_id),
|
|
10635
|
-
children: open ? "Close" : "Open it"
|
|
10636
|
-
}
|
|
11026
|
+
const linked = portal.portal_id === activePortalId;
|
|
11027
|
+
return /* @__PURE__ */ jsxs35(
|
|
11028
|
+
"li",
|
|
11029
|
+
{
|
|
11030
|
+
"data-linked": linked ? "true" : void 0,
|
|
11031
|
+
ref: linked ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
11032
|
+
className: cn33(
|
|
11033
|
+
"rounded-md border border-border bg-card p-2.5",
|
|
11034
|
+
linked && "border-primary ring-1 ring-primary"
|
|
10637
11035
|
),
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
portal,
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
11036
|
+
children: [
|
|
11037
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-2", children: [
|
|
11038
|
+
/* @__PURE__ */ jsx39("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: portal.title }),
|
|
11039
|
+
portal.is_active ? null : /* @__PURE__ */ jsx39(Badge13, { variant: "outline", children: "closed" })
|
|
11040
|
+
] }),
|
|
11041
|
+
/* @__PURE__ */ jsxs35("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
11042
|
+
"Clients come from ",
|
|
11043
|
+
portal.client_table,
|
|
11044
|
+
portal.tables === 1 ? ", and see 1 Table" : `, and see ${portal.tables} Tables`,
|
|
11045
|
+
"."
|
|
11046
|
+
] }),
|
|
11047
|
+
/* @__PURE__ */ jsxs35("p", { className: "mt-1.5 text-xs", children: [
|
|
11048
|
+
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.signed_in }),
|
|
11049
|
+
" signed in",
|
|
11050
|
+
" \xB7 ",
|
|
11051
|
+
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.invited - portal.signed_in }),
|
|
11052
|
+
" invited and waiting"
|
|
11053
|
+
] }),
|
|
11054
|
+
/* @__PURE__ */ jsxs35("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
11055
|
+
/* @__PURE__ */ jsx39(CopyLink, { url }),
|
|
11056
|
+
/* @__PURE__ */ jsx39(
|
|
11057
|
+
Button33,
|
|
11058
|
+
{
|
|
11059
|
+
size: "sm",
|
|
11060
|
+
variant: "ghost",
|
|
11061
|
+
onClick: () => setOpenId(open ? null : portal.portal_id),
|
|
11062
|
+
children: open ? "Close" : "Open it"
|
|
11063
|
+
}
|
|
11064
|
+
),
|
|
11065
|
+
/* @__PURE__ */ jsx39(
|
|
11066
|
+
ArchivePortalControl,
|
|
11067
|
+
{
|
|
11068
|
+
portal,
|
|
11069
|
+
onArchived: () => {
|
|
11070
|
+
setArchiveToken((t) => t + 1);
|
|
11071
|
+
void load();
|
|
11072
|
+
}
|
|
11073
|
+
}
|
|
11074
|
+
)
|
|
11075
|
+
] }),
|
|
11076
|
+
open ? /* @__PURE__ */ jsx39(
|
|
11077
|
+
PortalDetail,
|
|
11078
|
+
{
|
|
11079
|
+
portalId: portal.portal_id,
|
|
11080
|
+
...tableId ? { tableId } : {},
|
|
11081
|
+
origin,
|
|
11082
|
+
onChanged: () => void load()
|
|
10645
11083
|
}
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
{
|
|
10652
|
-
portalId: portal.portal_id,
|
|
10653
|
-
...tableId ? { tableId } : {},
|
|
10654
|
-
origin,
|
|
10655
|
-
onChanged: () => void load()
|
|
10656
|
-
}
|
|
10657
|
-
) : null
|
|
10658
|
-
] }, portal.portal_id);
|
|
11084
|
+
) : null
|
|
11085
|
+
]
|
|
11086
|
+
},
|
|
11087
|
+
portal.portal_id
|
|
11088
|
+
);
|
|
10659
11089
|
}) }),
|
|
10660
11090
|
/* @__PURE__ */ jsx39(ArchivedPortals, { refreshToken: archiveToken, onRestored: () => void load() })
|
|
10661
11091
|
] });
|
|
@@ -10784,7 +11214,7 @@ function PortalDetail({
|
|
|
10784
11214
|
setError(null);
|
|
10785
11215
|
setCard(answered.data);
|
|
10786
11216
|
}, [client, portalId]);
|
|
10787
|
-
|
|
11217
|
+
useEffect25(() => {
|
|
10788
11218
|
void load();
|
|
10789
11219
|
}, [load]);
|
|
10790
11220
|
const revoke = useCallback21(
|
|
@@ -10977,7 +11407,7 @@ function Preview({
|
|
|
10977
11407
|
const [which, setWhich] = useState36(first?.table_id ?? null);
|
|
10978
11408
|
const [rows, setRows] = useState36(null);
|
|
10979
11409
|
const [error, setError] = useState36(null);
|
|
10980
|
-
|
|
11410
|
+
useEffect25(() => {
|
|
10981
11411
|
if (!which) return;
|
|
10982
11412
|
let cancelled = false;
|
|
10983
11413
|
setRows(null);
|
|
@@ -11023,7 +11453,7 @@ function Invite({ card, onInvited }) {
|
|
|
11023
11453
|
const [busy, setBusy] = useState36(false);
|
|
11024
11454
|
const [said, setSaid] = useState36(null);
|
|
11025
11455
|
const [error, setError] = useState36(null);
|
|
11026
|
-
|
|
11456
|
+
useEffect25(() => {
|
|
11027
11457
|
let cancelled = false;
|
|
11028
11458
|
void client.list({ table_id: card.client_table_id, limit: 200 }).then((answered) => {
|
|
11029
11459
|
if (cancelled) return;
|
|
@@ -11125,7 +11555,7 @@ function Invite({ card, onInvited }) {
|
|
|
11125
11555
|
}
|
|
11126
11556
|
|
|
11127
11557
|
// src/DigestScheduler.tsx
|
|
11128
|
-
import { useCallback as useCallback22, useEffect as
|
|
11558
|
+
import { useCallback as useCallback22, useEffect as useEffect26, useMemo as useMemo24, useState as useState37 } from "react";
|
|
11129
11559
|
import { useRecordsClient as useRecordsClient25 } from "@ai-matrx/records/react";
|
|
11130
11560
|
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";
|
|
11131
11561
|
import { Fragment as Fragment17, jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
@@ -11189,7 +11619,7 @@ function DigestScheduler({
|
|
|
11189
11619
|
setMembers([]);
|
|
11190
11620
|
}
|
|
11191
11621
|
}, [client, tableId, host]);
|
|
11192
|
-
|
|
11622
|
+
useEffect26(() => {
|
|
11193
11623
|
void load();
|
|
11194
11624
|
}, [load]);
|
|
11195
11625
|
const schedule = useMemo24(() => {
|
|
@@ -11413,7 +11843,17 @@ function DigestScheduler({
|
|
|
11413
11843
|
preview ? /* @__PURE__ */ jsxs36("div", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
11414
11844
|
/* @__PURE__ */ jsx40("p", { className: "font-medium", children: preview.subject }),
|
|
11415
11845
|
/* @__PURE__ */ jsx40("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
11416
|
-
preview.incomplete ?
|
|
11846
|
+
preview.incomplete ? (
|
|
11847
|
+
// The summary could not be finished: a refusal line naming the missing
|
|
11848
|
+
// piece and what to do, never the bare sentence in red (lane REFUSAL-SWEEP).
|
|
11849
|
+
/* @__PURE__ */ jsx40(
|
|
11850
|
+
RefusalLine,
|
|
11851
|
+
{
|
|
11852
|
+
className: "mt-1",
|
|
11853
|
+
error: refusal("not_supported", preview.incomplete, "Fill in the missing piece above, then preview again.")
|
|
11854
|
+
}
|
|
11855
|
+
)
|
|
11856
|
+
) : null,
|
|
11417
11857
|
preview.entered.length > 0 ? /* @__PURE__ */ jsxs36("p", { className: "mt-1", children: [
|
|
11418
11858
|
/* @__PURE__ */ jsx40("span", { className: "font-medium", children: "Arrived:" }),
|
|
11419
11859
|
" ",
|
|
@@ -11431,10 +11871,11 @@ function DigestScheduler({
|
|
|
11431
11871
|
}
|
|
11432
11872
|
|
|
11433
11873
|
// src/SubscriptionsPanel.tsx
|
|
11434
|
-
import { useCallback as useCallback23, useEffect as
|
|
11874
|
+
import { useCallback as useCallback23, useEffect as useEffect27, useState as useState38 } from "react";
|
|
11435
11875
|
import { useRecordsClient as useRecordsClient26 } from "@ai-matrx/records/react";
|
|
11436
11876
|
import { Badge as Badge14, Button as Button35, Skeleton as Skeleton19, Switch as Switch2, cn as cn35 } from "@ai-matrx/design-system";
|
|
11437
11877
|
import { jsx as jsx41, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
11878
|
+
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.";
|
|
11438
11879
|
function whenItFires(subscription) {
|
|
11439
11880
|
if (subscription.cadence === "instant") return "as it happens";
|
|
11440
11881
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
@@ -11442,7 +11883,7 @@ function whenItFires(subscription) {
|
|
|
11442
11883
|
}
|
|
11443
11884
|
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
11444
11885
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
11445
|
-
function SubscriptionsPanel({ tableId, className }) {
|
|
11886
|
+
function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
11446
11887
|
const client = useRecordsClient26();
|
|
11447
11888
|
const [rows, setRows] = useState38(null);
|
|
11448
11889
|
const [error, setError] = useState38(null);
|
|
@@ -11460,7 +11901,7 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
11460
11901
|
setError(null);
|
|
11461
11902
|
setRows(answered.data);
|
|
11462
11903
|
}, [client, tableId]);
|
|
11463
|
-
|
|
11904
|
+
useEffect27(() => {
|
|
11464
11905
|
void load();
|
|
11465
11906
|
}, [load]);
|
|
11466
11907
|
const flip = useCallback23(
|
|
@@ -11533,96 +11974,119 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
11533
11974
|
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."
|
|
11534
11975
|
}
|
|
11535
11976
|
) : null,
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
"
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
" ",
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11977
|
+
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,
|
|
11978
|
+
/* @__PURE__ */ jsx41("ul", { className: "flex flex-col gap-2", children: rows.map((subscription) => /* @__PURE__ */ jsxs37(
|
|
11979
|
+
"li",
|
|
11980
|
+
{
|
|
11981
|
+
"data-linked": subscription.rule_id === activeRuleId ? "true" : void 0,
|
|
11982
|
+
ref: subscription.rule_id === activeRuleId ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
11983
|
+
className: cn35(
|
|
11984
|
+
"rounded-md border p-2.5",
|
|
11985
|
+
subscription.rule_id === activeRuleId && "border-primary ring-1 ring-primary"
|
|
11986
|
+
),
|
|
11987
|
+
children: [
|
|
11988
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-start gap-2", children: [
|
|
11989
|
+
/* @__PURE__ */ jsxs37("div", { className: "min-w-0 flex-1", children: [
|
|
11990
|
+
/* @__PURE__ */ jsx41("p", { className: "truncate text-sm font-medium", children: subscription.name }),
|
|
11991
|
+
/* @__PURE__ */ jsxs37("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
11992
|
+
CHANNEL_WORDS2[subscription.channel] ?? `on the ${subscription.channel} channel`,
|
|
11993
|
+
" \xB7 ",
|
|
11994
|
+
whenItFires(subscription)
|
|
11995
|
+
] })
|
|
11996
|
+
] }),
|
|
11997
|
+
subscription.i_may_mute ? /* @__PURE__ */ jsx41(
|
|
11998
|
+
Switch2,
|
|
11999
|
+
{
|
|
12000
|
+
checked: !subscription.muted,
|
|
12001
|
+
disabled: busy === subscription.rule_id,
|
|
12002
|
+
"aria-label": `Tell me about ${subscription.name}`,
|
|
12003
|
+
onCheckedChange: (on) => void flip(subscription, on)
|
|
12004
|
+
}
|
|
12005
|
+
) : /* @__PURE__ */ jsx41(Badge14, { variant: "secondary", children: "someone else's" })
|
|
12006
|
+
] }),
|
|
12007
|
+
/* @__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." }),
|
|
12008
|
+
/* @__PURE__ */ jsxs37("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
12009
|
+
subscription.next_digest_at ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
12010
|
+
"Next summary ",
|
|
12011
|
+
new Date(subscription.next_digest_at).toLocaleString()
|
|
12012
|
+
] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */ jsx41("span", { children: "No next summary while it is off." }) : null,
|
|
12013
|
+
subscription.last_sent_at ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
12014
|
+
"Last told you ",
|
|
12015
|
+
new Date(subscription.last_sent_at).toLocaleString()
|
|
12016
|
+
] }) : /* @__PURE__ */ jsx41("span", { children: "It has not told you anything yet." }),
|
|
12017
|
+
subscription.quiet_hours ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
12018
|
+
"Not between ",
|
|
12019
|
+
subscription.quiet_hours.start,
|
|
12020
|
+
" and ",
|
|
12021
|
+
subscription.quiet_hours.end,
|
|
12022
|
+
" \u2014 a send inside those hours waits until they end."
|
|
12023
|
+
] }) : null,
|
|
12024
|
+
subscription.cadence === "instant" ? null : /* @__PURE__ */ jsx41(
|
|
12025
|
+
Button35,
|
|
12026
|
+
{
|
|
12027
|
+
size: "sm",
|
|
12028
|
+
variant: "ghost",
|
|
12029
|
+
disabled: busy === subscription.rule_id,
|
|
12030
|
+
onClick: () => void showOne(subscription),
|
|
12031
|
+
children: "Send me a preview now"
|
|
12032
|
+
}
|
|
12033
|
+
)
|
|
12034
|
+
] }),
|
|
12035
|
+
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: [
|
|
12036
|
+
/* @__PURE__ */ jsx41("p", { className: "font-medium", children: preview.subject }),
|
|
12037
|
+
/* @__PURE__ */ jsx41("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
12038
|
+
preview.incomplete ? (
|
|
12039
|
+
// Absent or honest: a subscription that cannot produce a summary
|
|
12040
|
+
// says which piece is missing instead of showing an empty one.
|
|
12041
|
+
/* @__PURE__ */ jsx41(
|
|
12042
|
+
RefusalLine,
|
|
12043
|
+
{
|
|
12044
|
+
className: "mt-1",
|
|
12045
|
+
error: refusal(
|
|
12046
|
+
"not_supported",
|
|
12047
|
+
preview.incomplete,
|
|
12048
|
+
"Fill in the missing piece, then preview again."
|
|
12049
|
+
)
|
|
12050
|
+
}
|
|
12051
|
+
)
|
|
12052
|
+
) : null,
|
|
12053
|
+
preview.entered.length > 0 ? /* @__PURE__ */ jsxs37("p", { className: "mt-1", children: [
|
|
12054
|
+
/* @__PURE__ */ jsx41("span", { className: "font-medium", children: "Arrived:" }),
|
|
12055
|
+
" ",
|
|
12056
|
+
preview.entered.map((e) => e.name).join(", ")
|
|
12057
|
+
] }) : null,
|
|
12058
|
+
preview.left.length > 0 ? /* @__PURE__ */ jsxs37("p", { className: "mt-1", children: [
|
|
12059
|
+
/* @__PURE__ */ jsx41("span", { className: "font-medium", children: "Left:" }),
|
|
12060
|
+
" ",
|
|
12061
|
+
preview.left.map((e) => e.name).join(", ")
|
|
12062
|
+
] }) : null,
|
|
12063
|
+
preview.changed.length > 0 ? /* @__PURE__ */ jsxs37("p", { className: "mt-1", children: [
|
|
12064
|
+
/* @__PURE__ */ jsx41("span", { className: "font-medium", children: "Changed:" }),
|
|
12065
|
+
" ",
|
|
12066
|
+
preview.changed.map((e) => e.name).join(", ")
|
|
12067
|
+
] }) : null,
|
|
12068
|
+
/* @__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." }),
|
|
12069
|
+
/* @__PURE__ */ jsx41(Button35, { size: "sm", variant: "ghost", className: "mt-1", onClick: () => setPreview(null), children: "Close" })
|
|
12070
|
+
] }) : null
|
|
12071
|
+
]
|
|
12072
|
+
},
|
|
12073
|
+
subscription.rule_id
|
|
12074
|
+
)) })
|
|
11611
12075
|
] });
|
|
11612
12076
|
}
|
|
11613
12077
|
|
|
11614
12078
|
// src/FormBuilder.tsx
|
|
11615
|
-
import { useCallback as useCallback25, useEffect as
|
|
12079
|
+
import { useCallback as useCallback25, useEffect as useEffect30, useMemo as useMemo26, useState as useState41 } from "react";
|
|
11616
12080
|
import { useFields as useFields18, useRecordsClient as useRecordsClient28, useTable as useTable14 } from "@ai-matrx/records/react";
|
|
11617
12081
|
|
|
11618
12082
|
// src/publish-gate.ts
|
|
11619
|
-
import { useEffect as
|
|
12083
|
+
import { useEffect as useEffect28, useState as useState39 } from "react";
|
|
11620
12084
|
import { useRecordsClient as useRecordsClient27 } from "@ai-matrx/records/react";
|
|
11621
12085
|
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.";
|
|
11622
12086
|
function usePublishGate() {
|
|
11623
12087
|
const client = useRecordsClient27();
|
|
11624
12088
|
const [storeOpen, setStoreOpen] = useState39(null);
|
|
11625
|
-
|
|
12089
|
+
useEffect28(() => {
|
|
11626
12090
|
let alive = true;
|
|
11627
12091
|
void (async () => {
|
|
11628
12092
|
const answered = await client.storeIsOpen();
|
|
@@ -11653,7 +12117,7 @@ import {
|
|
|
11653
12117
|
} from "@ai-matrx/design-system";
|
|
11654
12118
|
|
|
11655
12119
|
// src/FormRunner.tsx
|
|
11656
|
-
import { useCallback as useCallback24, useEffect as
|
|
12120
|
+
import { useCallback as useCallback24, useEffect as useEffect29, useMemo as useMemo25, useRef as useRef11, useState as useState40 } from "react";
|
|
11657
12121
|
import { useFields as useFields17, useOptionalRecordsClient as useOptionalRecordsClient4 } from "@ai-matrx/records/react";
|
|
11658
12122
|
import { Button as Button36, Progress, Skeleton as Skeleton20, cn as cn36 } from "@ai-matrx/design-system";
|
|
11659
12123
|
import { Fragment as Fragment18, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
@@ -11670,9 +12134,16 @@ function ConnectedFormRunner(props) {
|
|
|
11670
12134
|
const submit = useCallback24(
|
|
11671
12135
|
async (values) => {
|
|
11672
12136
|
if (!client) {
|
|
12137
|
+
const said = "This form cannot send answers from here, so nothing was sent.";
|
|
11673
12138
|
return {
|
|
11674
12139
|
ok: false,
|
|
11675
|
-
message:
|
|
12140
|
+
message: said,
|
|
12141
|
+
error: refusal(
|
|
12142
|
+
"not_supported",
|
|
12143
|
+
said,
|
|
12144
|
+
"Open the form from its own link and answer it there.",
|
|
12145
|
+
"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."
|
|
12146
|
+
)
|
|
11676
12147
|
};
|
|
11677
12148
|
}
|
|
11678
12149
|
const document2 = {
|
|
@@ -11685,7 +12156,7 @@ function ConnectedFormRunner(props) {
|
|
|
11685
12156
|
})
|
|
11686
12157
|
};
|
|
11687
12158
|
const written = await client.recordWrite({ table_id: form.subject, data: document2 });
|
|
11688
|
-
if (!written.ok) return { ok: false, message: written.error.message };
|
|
12159
|
+
if (!written.ok) return { ok: false, message: written.error.message, error: written.error };
|
|
11689
12160
|
return { ok: true, recordId: written.data };
|
|
11690
12161
|
},
|
|
11691
12162
|
[client, form]
|
|
@@ -11727,7 +12198,7 @@ function FormStage({
|
|
|
11727
12198
|
const [answers, setAnswers] = useState40({});
|
|
11728
12199
|
const [at, setAt] = useState40(0);
|
|
11729
12200
|
const [error, setError] = useState40(null);
|
|
11730
|
-
const [
|
|
12201
|
+
const [refusal2, setRefusal] = useState40(null);
|
|
11731
12202
|
const [writing, setWriting] = useState40(false);
|
|
11732
12203
|
const [done, setDone] = useState40(null);
|
|
11733
12204
|
const [hidden, setHidden] = useState40({});
|
|
@@ -11747,7 +12218,7 @@ function FormStage({
|
|
|
11747
12218
|
};
|
|
11748
12219
|
});
|
|
11749
12220
|
}, [fields, form.questions]);
|
|
11750
|
-
|
|
12221
|
+
useEffect29(() => {
|
|
11751
12222
|
let cancelled = false;
|
|
11752
12223
|
const conditional = questions.filter((q) => q.showIf);
|
|
11753
12224
|
if (conditional.length === 0 || !evaluate) return;
|
|
@@ -11796,12 +12267,21 @@ function FormStage({
|
|
|
11796
12267
|
const outcome = await onSubmit(values);
|
|
11797
12268
|
setWriting(false);
|
|
11798
12269
|
if (!outcome.ok) {
|
|
11799
|
-
|
|
12270
|
+
const inWords = (text) => questions.reduce(
|
|
12271
|
+
(said, q) => said.replace(new RegExp(`\\b${q.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g"), q.ask),
|
|
12272
|
+
text
|
|
12273
|
+
);
|
|
12274
|
+
const told = outcome.error ?? refusal("invalid_argument", outcome.message, "Check your answers, then send again.");
|
|
12275
|
+
setRefusal({
|
|
12276
|
+
...told,
|
|
12277
|
+
message: inWords(told.message),
|
|
12278
|
+
...told.hint ? { hint: inWords(told.hint) } : {}
|
|
12279
|
+
});
|
|
11800
12280
|
return;
|
|
11801
12281
|
}
|
|
11802
12282
|
setDone(outcome.message ?? "sent");
|
|
11803
12283
|
onSubmitted?.(outcome.recordId ?? null);
|
|
11804
|
-
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview]);
|
|
12284
|
+
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
11805
12285
|
function advance() {
|
|
11806
12286
|
if (!oneAtATime) return;
|
|
11807
12287
|
if (index < live.length - 1) setAt(index + 1);
|
|
@@ -11818,7 +12298,7 @@ function FormStage({
|
|
|
11818
12298
|
if (event.shiftKey) retreat();
|
|
11819
12299
|
else advance();
|
|
11820
12300
|
}
|
|
11821
|
-
|
|
12301
|
+
useEffect29(() => {
|
|
11822
12302
|
const input = stage.current?.querySelector(
|
|
11823
12303
|
"input:not([tabindex='-1']), textarea, select, [role='combobox']"
|
|
11824
12304
|
);
|
|
@@ -11860,7 +12340,22 @@ function FormStage({
|
|
|
11860
12340
|
] }) : null,
|
|
11861
12341
|
form.intro && index === 0 ? /* @__PURE__ */ jsx42("p", { className: "text-sm text-muted-foreground", children: form.intro }) : null,
|
|
11862
12342
|
error ? /* @__PURE__ */ jsx42(RefusalNotice, { error, className: "text-left" }) : null,
|
|
11863
|
-
|
|
12343
|
+
refusal2 ? /* @__PURE__ */ jsx42(
|
|
12344
|
+
RefusalNotice,
|
|
12345
|
+
{
|
|
12346
|
+
error: refusal2,
|
|
12347
|
+
className: "text-left",
|
|
12348
|
+
onKeepEditing: () => {
|
|
12349
|
+
setRefusal(null);
|
|
12350
|
+
stage.current?.querySelector("input, textarea, select, button")?.focus();
|
|
12351
|
+
},
|
|
12352
|
+
onDiscard: () => {
|
|
12353
|
+
setRefusal(null);
|
|
12354
|
+
setAnswers({});
|
|
12355
|
+
setAt(0);
|
|
12356
|
+
}
|
|
12357
|
+
}
|
|
12358
|
+
) : null,
|
|
11864
12359
|
/* @__PURE__ */ jsxs38("div", { ref: stage, className: "flex flex-col gap-4 text-left", children: [
|
|
11865
12360
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ jsx42(
|
|
11866
12361
|
Question,
|
|
@@ -11934,12 +12429,15 @@ function Question({
|
|
|
11934
12429
|
void upload?.(file).then((result) => {
|
|
11935
12430
|
if (!result) return;
|
|
11936
12431
|
if (result.ok) onChange(result.fileId);
|
|
11937
|
-
else
|
|
12432
|
+
else
|
|
12433
|
+
setUploadError(
|
|
12434
|
+
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
12435
|
+
);
|
|
11938
12436
|
});
|
|
11939
12437
|
}
|
|
11940
12438
|
}
|
|
11941
12439
|
),
|
|
11942
|
-
uploadError ? /* @__PURE__ */ jsx42(
|
|
12440
|
+
uploadError ? /* @__PURE__ */ jsx42(RefusalLine, { error: uploadError }) : null
|
|
11943
12441
|
] }) : /* @__PURE__ */ jsx42(FieldControl, { field, value, onChange, id })
|
|
11944
12442
|
] });
|
|
11945
12443
|
}
|
|
@@ -12013,10 +12511,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
12013
12511
|
setError(null);
|
|
12014
12512
|
setForms(mine);
|
|
12015
12513
|
}, [client, tableId, seed, claimSeed]);
|
|
12016
|
-
|
|
12514
|
+
useEffect30(() => {
|
|
12017
12515
|
void load();
|
|
12018
12516
|
}, [load]);
|
|
12019
|
-
|
|
12517
|
+
useEffect30(() => {
|
|
12020
12518
|
if (!forms || forms.length === 0) return;
|
|
12021
12519
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
12022
12520
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -12427,7 +12925,7 @@ function groupLabel(groups) {
|
|
|
12427
12925
|
|
|
12428
12926
|
// src/chartFrame.tsx
|
|
12429
12927
|
import {
|
|
12430
|
-
useEffect as
|
|
12928
|
+
useEffect as useEffect31,
|
|
12431
12929
|
useId as useId2,
|
|
12432
12930
|
useRef as useRef12,
|
|
12433
12931
|
useState as useState42
|
|
@@ -12437,7 +12935,7 @@ import { jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
|
12437
12935
|
function useMeasuredWidth(fallback = 480) {
|
|
12438
12936
|
const ref = useRef12(null);
|
|
12439
12937
|
const [width, setWidth] = useState42(fallback);
|
|
12440
|
-
|
|
12938
|
+
useEffect31(() => {
|
|
12441
12939
|
const node = ref.current;
|
|
12442
12940
|
if (!node) return;
|
|
12443
12941
|
const apply = () => {
|
|
@@ -12561,7 +13059,7 @@ function isSignatureField(field) {
|
|
|
12561
13059
|
}
|
|
12562
13060
|
|
|
12563
13061
|
// src/DocTemplate.tsx
|
|
12564
|
-
import { useCallback as useCallback26, useEffect as
|
|
13062
|
+
import { useCallback as useCallback26, useEffect as useEffect32, useState as useState43 } from "react";
|
|
12565
13063
|
import { useFields as useFields19, useRecordsClient as useRecordsClient29, useTable as useTable15 } from "@ai-matrx/records/react";
|
|
12566
13064
|
import { BasicInput as BasicInput14, BasicTextarea as BasicTextarea6, Button as Button38, Label as Label8, Skeleton as Skeleton22, cn as cn39 } from "@ai-matrx/design-system";
|
|
12567
13065
|
import { jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
@@ -12609,10 +13107,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12609
13107
|
setError(null);
|
|
12610
13108
|
setTemplates(rows);
|
|
12611
13109
|
}, [client, tableId, seed, fields.data]);
|
|
12612
|
-
|
|
13110
|
+
useEffect32(() => {
|
|
12613
13111
|
void load();
|
|
12614
13112
|
}, [load]);
|
|
12615
|
-
|
|
13113
|
+
useEffect32(() => {
|
|
12616
13114
|
if (!templates || templates.length === 0) return;
|
|
12617
13115
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
12618
13116
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -12620,7 +13118,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12620
13118
|
setDraftBody(chosen.body);
|
|
12621
13119
|
onActiveTemplate?.(chosen);
|
|
12622
13120
|
}, [templates, activeTemplateId]);
|
|
12623
|
-
|
|
13121
|
+
useEffect32(() => {
|
|
12624
13122
|
let cancelled = false;
|
|
12625
13123
|
if (draftBody.trim() === "") {
|
|
12626
13124
|
setUnresolved([]);
|
|
@@ -12731,16 +13229,21 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12731
13229
|
onChange: (e) => setDraftBody(e.target.value)
|
|
12732
13230
|
}
|
|
12733
13231
|
),
|
|
12734
|
-
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: "
|
|
12735
|
-
/* @__PURE__ */ jsx45("code", { children: token.raw }),
|
|
12736
|
-
" \u2014
|
|
12737
|
-
|
|
13232
|
+
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: [
|
|
13233
|
+
/* @__PURE__ */ jsx45("code", { className: "text-destructive", children: token.raw }),
|
|
13234
|
+
/* @__PURE__ */ jsx45("span", { "aria-hidden": "true", children: "\u2014" }),
|
|
13235
|
+
/* @__PURE__ */ jsx45(
|
|
13236
|
+
RefusalLine,
|
|
13237
|
+
{
|
|
13238
|
+
error: refusal("invalid_argument", token.why, "Change it to a Field this table has, or take it out.")
|
|
13239
|
+
}
|
|
13240
|
+
)
|
|
12738
13241
|
] }, 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
|
|
12739
13242
|
] });
|
|
12740
13243
|
}
|
|
12741
13244
|
|
|
12742
13245
|
// src/DocRender.tsx
|
|
12743
|
-
import { useCallback as useCallback27, useEffect as
|
|
13246
|
+
import { useCallback as useCallback27, useEffect as useEffect33, useRef as useRef13, useState as useState44 } from "react";
|
|
12744
13247
|
import { useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
|
|
12745
13248
|
import { Button as Button39, Skeleton as Skeleton23, cn as cn40 } from "@ai-matrx/design-system";
|
|
12746
13249
|
import { jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
@@ -12769,7 +13272,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
12769
13272
|
setPreview(body.data);
|
|
12770
13273
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
12771
13274
|
}, [client, templateId, recordId]);
|
|
12772
|
-
|
|
13275
|
+
useEffect33(() => {
|
|
12773
13276
|
void load();
|
|
12774
13277
|
}, [load]);
|
|
12775
13278
|
async function freeze() {
|
|
@@ -12853,7 +13356,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
12853
13356
|
}
|
|
12854
13357
|
|
|
12855
13358
|
// src/SignBlock.tsx
|
|
12856
|
-
import { useCallback as useCallback28, useEffect as
|
|
13359
|
+
import { useCallback as useCallback28, useEffect as useEffect34, useState as useState45 } from "react";
|
|
12857
13360
|
import { useFields as useFields20, useRecordsClient as useRecordsClient31 } from "@ai-matrx/records/react";
|
|
12858
13361
|
import { BasicInput as BasicInput15, Button as Button40, Skeleton as Skeleton24, cn as cn41 } from "@ai-matrx/design-system";
|
|
12859
13362
|
import { useTable as useTable16 } from "@ai-matrx/records/react";
|
|
@@ -12883,7 +13386,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
12883
13386
|
}
|
|
12884
13387
|
setVerdicts(answers);
|
|
12885
13388
|
}, [client, recordId]);
|
|
12886
|
-
|
|
13389
|
+
useEffect34(() => {
|
|
12887
13390
|
void load();
|
|
12888
13391
|
}, [load]);
|
|
12889
13392
|
async function sign(field) {
|
|
@@ -12939,7 +13442,21 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
12939
13442
|
signature.document_hash.slice(0, 12)
|
|
12940
13443
|
] })
|
|
12941
13444
|
] }),
|
|
12942
|
-
|
|
13445
|
+
intact === false ? (
|
|
13446
|
+
// THE STORE REFUSED TO CALL IT INTACT: its reason goes through the one
|
|
13447
|
+
// formatter, with what to do next, never raw in red (lane REFUSAL-SWEEP).
|
|
13448
|
+
/* @__PURE__ */ jsx47(
|
|
13449
|
+
RefusalLine,
|
|
13450
|
+
{
|
|
13451
|
+
className: "mt-0.5",
|
|
13452
|
+
error: refusal(
|
|
13453
|
+
"refused_by_rule",
|
|
13454
|
+
typeof verdict === "object" && verdict !== null && "reason" in verdict ? String(verdict.reason) : "The store says what is there no longer matches what was signed.",
|
|
13455
|
+
"Ask for it to be signed again on the version that is there now."
|
|
13456
|
+
)
|
|
13457
|
+
}
|
|
13458
|
+
)
|
|
13459
|
+
) : /* @__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 ?? "") })
|
|
12943
13460
|
] }, signature.id);
|
|
12944
13461
|
}) }) : null,
|
|
12945
13462
|
!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: [
|
|
@@ -12990,7 +13507,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
12990
13507
|
}
|
|
12991
13508
|
|
|
12992
13509
|
// src/NotifyRuleEditor.tsx
|
|
12993
|
-
import { useCallback as useCallback29, useEffect as
|
|
13510
|
+
import { useCallback as useCallback29, useEffect as useEffect35, useState as useState46 } from "react";
|
|
12994
13511
|
import { useRecordsClient as useRecordsClient32, useTable as useTable17 } from "@ai-matrx/records/react";
|
|
12995
13512
|
import { Button as Button41, Skeleton as Skeleton25, cn as cn42 } from "@ai-matrx/design-system";
|
|
12996
13513
|
import { jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
@@ -13026,7 +13543,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13026
13543
|
if (host.savedViews) setViews(await host.savedViews());
|
|
13027
13544
|
else setViews(null);
|
|
13028
13545
|
}, [client, host, tableId]);
|
|
13029
|
-
|
|
13546
|
+
useEffect35(() => {
|
|
13030
13547
|
void load();
|
|
13031
13548
|
}, [load]);
|
|
13032
13549
|
const write = useCallback29(
|
|
@@ -13054,7 +13571,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13054
13571
|
},
|
|
13055
13572
|
[client, tableId]
|
|
13056
13573
|
);
|
|
13057
|
-
|
|
13574
|
+
useEffect35(() => {
|
|
13058
13575
|
if (!subscriptions || !seed || seed.length === 0) return;
|
|
13059
13576
|
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
13060
13577
|
if (missing.length === 0) return;
|
|
@@ -13221,7 +13738,7 @@ import { Fragment as Fragment20, jsx as jsx49, jsxs as jsxs45 } from "react/jsx-
|
|
|
13221
13738
|
function pretty(n) {
|
|
13222
13739
|
return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
|
|
13223
13740
|
}
|
|
13224
|
-
function
|
|
13741
|
+
function asRefusal2(block) {
|
|
13225
13742
|
return mapPgError(
|
|
13226
13743
|
{
|
|
13227
13744
|
message: block.refused ?? "",
|
|
@@ -13282,7 +13799,7 @@ function ChartBlock({ block, subject, className }) {
|
|
|
13282
13799
|
" ms"
|
|
13283
13800
|
] }) : null
|
|
13284
13801
|
] }),
|
|
13285
|
-
block.refused ? /* @__PURE__ */ jsx49(RefusalNotice, { error:
|
|
13802
|
+
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: [
|
|
13286
13803
|
/* @__PURE__ */ jsx49(Drawing, { kind, points, series, config, onDrill: drill }),
|
|
13287
13804
|
/* @__PURE__ */ jsx49(Values, { points, measure: primary, config, onDrill: drill })
|
|
13288
13805
|
] }) })
|
|
@@ -13510,7 +14027,7 @@ function Drawing({
|
|
|
13510
14027
|
}
|
|
13511
14028
|
|
|
13512
14029
|
// src/DashboardCanvas.tsx
|
|
13513
|
-
import { useCallback as useCallback30, useEffect as
|
|
14030
|
+
import { useCallback as useCallback30, useEffect as useEffect36, useMemo as useMemo28, useState as useState47 } from "react";
|
|
13514
14031
|
import { useFields as useFields21, useRecordsClient as useRecordsClient33, useTable as useTable18 } from "@ai-matrx/records/react";
|
|
13515
14032
|
import {
|
|
13516
14033
|
BasicInput as BasicInput16,
|
|
@@ -13547,17 +14064,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13547
14064
|
setError(null);
|
|
13548
14065
|
setBoards(answered.data.map(dashboardFromSummary));
|
|
13549
14066
|
}, [client, tableId]);
|
|
13550
|
-
|
|
14067
|
+
useEffect36(() => {
|
|
13551
14068
|
void load();
|
|
13552
14069
|
}, [load]);
|
|
13553
|
-
|
|
14070
|
+
useEffect36(() => {
|
|
13554
14071
|
if (!boards || boards.length === 0) return;
|
|
13555
14072
|
const chosen = boards.find((d) => d.id === (activeDashboardId ?? activeId)) ?? boards[0];
|
|
13556
14073
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
13557
14074
|
}, [boards, activeDashboardId]);
|
|
13558
14075
|
const board = useMemo28(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
|
|
13559
14076
|
const filterKey = JSON.stringify(filter ?? {});
|
|
13560
|
-
|
|
14077
|
+
useEffect36(() => {
|
|
13561
14078
|
if (!activeId) {
|
|
13562
14079
|
setRun(null);
|
|
13563
14080
|
return;
|
|
@@ -13853,18 +14370,19 @@ function GroupingPicker({
|
|
|
13853
14370
|
}
|
|
13854
14371
|
|
|
13855
14372
|
// src/FormsPanel.tsx
|
|
13856
|
-
import { useCallback as useCallback31, useEffect as
|
|
14373
|
+
import { useCallback as useCallback31, useEffect as useEffect37, useState as useState48 } from "react";
|
|
13857
14374
|
import { useRecordsClient as useRecordsClient34, useTable as useTable19 } from "@ai-matrx/records/react";
|
|
13858
14375
|
import { publicFormPath as publicFormPath2 } from "@ai-matrx/records";
|
|
13859
14376
|
import { Badge as Badge15, Button as Button43, Skeleton as Skeleton27, cn as cn45 } from "@ai-matrx/design-system";
|
|
13860
14377
|
import { Fragment as Fragment22, jsx as jsx51, jsxs as jsxs47 } from "react/jsx-runtime";
|
|
14378
|
+
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.";
|
|
13861
14379
|
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.";
|
|
13862
14380
|
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.";
|
|
13863
14381
|
function formSuggestion(tableName2) {
|
|
13864
14382
|
const subject = tableName2?.trim() ? tableName2.trim() : "this table";
|
|
13865
14383
|
return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
|
|
13866
14384
|
}
|
|
13867
|
-
function FormsPanel({ tableId, className }) {
|
|
14385
|
+
function FormsPanel({ tableId, activeFormId, className }) {
|
|
13868
14386
|
const client = useRecordsClient34();
|
|
13869
14387
|
const publishGate = usePublishGate();
|
|
13870
14388
|
const host = useRecordsUi();
|
|
@@ -13875,6 +14393,11 @@ function FormsPanel({ tableId, className }) {
|
|
|
13875
14393
|
const [busy, setBusy] = useState48(null);
|
|
13876
14394
|
const [copied, setCopied] = useState48(null);
|
|
13877
14395
|
const [building, setBuilding] = useState48(false);
|
|
14396
|
+
const linked = activeFormId && forms !== null ? forms.find((f) => f.form_id === activeFormId) ?? null : null;
|
|
14397
|
+
const linkedMissing = Boolean(activeFormId) && forms !== null && linked === null;
|
|
14398
|
+
useEffect37(() => {
|
|
14399
|
+
if (linked && rights.structure) setBuilding(true);
|
|
14400
|
+
}, [linked, rights.structure]);
|
|
13878
14401
|
const load = useCallback31(async () => {
|
|
13879
14402
|
const answered = await client.forms({ table_id: tableId });
|
|
13880
14403
|
if (!answered.ok) {
|
|
@@ -13885,7 +14408,7 @@ function FormsPanel({ tableId, className }) {
|
|
|
13885
14408
|
setError(null);
|
|
13886
14409
|
setForms(answered.data);
|
|
13887
14410
|
}, [client, tableId]);
|
|
13888
|
-
|
|
14411
|
+
useEffect37(() => {
|
|
13889
14412
|
void load();
|
|
13890
14413
|
}, [load]);
|
|
13891
14414
|
const toggle = useCallback31(
|
|
@@ -13923,10 +14446,12 @@ function FormsPanel({ tableId, className }) {
|
|
|
13923
14446
|
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
|
|
13924
14447
|
] }),
|
|
13925
14448
|
error ? /* @__PURE__ */ jsx51(RefusalNotice, { error }) : null,
|
|
14449
|
+
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,
|
|
13926
14450
|
building ? /* @__PURE__ */ jsx51(
|
|
13927
14451
|
FormBuilder,
|
|
13928
14452
|
{
|
|
13929
14453
|
tableId,
|
|
14454
|
+
activeFormId: linked ? linked.form_id : null,
|
|
13930
14455
|
onActiveForm: () => {
|
|
13931
14456
|
void load();
|
|
13932
14457
|
}
|
|
@@ -13952,57 +14477,69 @@ function FormsPanel({ tableId, className }) {
|
|
|
13952
14477
|
) : null,
|
|
13953
14478
|
/* @__PURE__ */ jsx51("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
|
|
13954
14479
|
const url = `${origin}${publicFormPath2(form.form_id)}`;
|
|
13955
|
-
return /* @__PURE__ */ jsxs47(
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
"
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
"
|
|
13971
|
-
|
|
13972
|
-
|
|
13973
|
-
|
|
13974
|
-
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
|
|
13985
|
-
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
|
|
13996
|
-
|
|
13997
|
-
|
|
13998
|
-
|
|
14480
|
+
return /* @__PURE__ */ jsxs47(
|
|
14481
|
+
"li",
|
|
14482
|
+
{
|
|
14483
|
+
"data-linked": form.form_id === activeFormId ? "true" : void 0,
|
|
14484
|
+
ref: form.form_id === activeFormId ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
14485
|
+
className: cn45(
|
|
14486
|
+
"rounded-md border p-2.5",
|
|
14487
|
+
form.form_id === activeFormId && "border-primary ring-1 ring-primary"
|
|
14488
|
+
),
|
|
14489
|
+
children: [
|
|
14490
|
+
/* @__PURE__ */ jsxs47("div", { className: "flex items-center gap-2", children: [
|
|
14491
|
+
/* @__PURE__ */ jsx51("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: form.title ?? form.slug }),
|
|
14492
|
+
/* @__PURE__ */ jsx51(Badge15, { variant: form.state === "open" ? "default" : "secondary", children: form.state })
|
|
14493
|
+
] }),
|
|
14494
|
+
/* @__PURE__ */ jsx51("p", { className: "mt-0.5 text-xs text-muted-foreground", children: FORM_STATE_WORDS[form.state] }),
|
|
14495
|
+
/* @__PURE__ */ jsxs47("p", { className: "mt-1.5 text-xs", children: [
|
|
14496
|
+
/* @__PURE__ */ jsx51("span", { className: "font-medium", children: form.in_table }),
|
|
14497
|
+
" in the table",
|
|
14498
|
+
form.held > 0 ? /* @__PURE__ */ jsxs47(Fragment22, { children: [
|
|
14499
|
+
" \xB7 ",
|
|
14500
|
+
/* @__PURE__ */ jsx51("span", { className: "font-medium", children: form.held }),
|
|
14501
|
+
" waiting for someone"
|
|
14502
|
+
] }) : null,
|
|
14503
|
+
form.rejected > 0 ? /* @__PURE__ */ jsxs47(Fragment22, { children: [
|
|
14504
|
+
" \xB7 ",
|
|
14505
|
+
/* @__PURE__ */ jsx51("span", { className: "font-medium", children: form.rejected }),
|
|
14506
|
+
" turned away"
|
|
14507
|
+
] }) : null,
|
|
14508
|
+
form.submission_cap !== null ? /* @__PURE__ */ jsxs47("span", { className: "text-muted-foreground", children: [
|
|
14509
|
+
" \xB7 stops at ",
|
|
14510
|
+
form.submission_cap
|
|
14511
|
+
] }) : null
|
|
14512
|
+
] }),
|
|
14513
|
+
/* @__PURE__ */ jsxs47("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
14514
|
+
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,
|
|
14515
|
+
rights.structure && !publishGate.blocked ? /* @__PURE__ */ jsx51(
|
|
14516
|
+
Button43,
|
|
14517
|
+
{
|
|
14518
|
+
size: "sm",
|
|
14519
|
+
variant: "ghost",
|
|
14520
|
+
disabled: busy === form.form_id,
|
|
14521
|
+
onClick: () => void toggle(form),
|
|
14522
|
+
children: busy === form.form_id ? "\u2026" : form.published_at && !form.closed_at ? "Unpublish" : "Publish"
|
|
14523
|
+
}
|
|
14524
|
+
) : null
|
|
14525
|
+
] }),
|
|
14526
|
+
rights.structure && publishGate.why ? /* @__PURE__ */ jsx51("p", { className: "mt-1.5 text-xs text-muted-foreground", children: publishGate.why }) : null,
|
|
14527
|
+
shown && shown === url ? /* @__PURE__ */ jsxs47("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
14528
|
+
"This browser would not let the page copy for you, so here it is to copy by hand:",
|
|
14529
|
+
" ",
|
|
14530
|
+
url
|
|
14531
|
+
] }) : null
|
|
14532
|
+
]
|
|
14533
|
+
},
|
|
14534
|
+
form.form_id
|
|
14535
|
+
);
|
|
13999
14536
|
}) }),
|
|
14000
14537
|
!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
|
|
14001
14538
|
] });
|
|
14002
14539
|
}
|
|
14003
14540
|
|
|
14004
14541
|
// src/BookingBuilder.tsx
|
|
14005
|
-
import { useCallback as useCallback32, useEffect as
|
|
14542
|
+
import { useCallback as useCallback32, useEffect as useEffect38, useMemo as useMemo29, useState as useState49 } from "react";
|
|
14006
14543
|
import { useFields as useFields22, useRecordsClient as useRecordsClient35, useTable as useTable20 } from "@ai-matrx/records/react";
|
|
14007
14544
|
import {
|
|
14008
14545
|
bookingPath
|
|
@@ -14077,18 +14614,18 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
14077
14614
|
setLoaded(true);
|
|
14078
14615
|
if (mine) setFormId(mine.form_id);
|
|
14079
14616
|
}, [client, tableId, bookingId]);
|
|
14080
|
-
|
|
14617
|
+
useEffect38(() => {
|
|
14081
14618
|
void load();
|
|
14082
14619
|
}, [load]);
|
|
14083
|
-
|
|
14620
|
+
useEffect38(() => {
|
|
14084
14621
|
if (title !== "" || !table.data) return;
|
|
14085
14622
|
setTitle(existing?.title ?? `Book a ${minutes}-minute ${table.data.name} appointment`);
|
|
14086
14623
|
}, [table.data, existing]);
|
|
14087
|
-
|
|
14624
|
+
useEffect38(() => {
|
|
14088
14625
|
if (!existing) return;
|
|
14089
14626
|
setMinutes(existing.slot_minutes);
|
|
14090
14627
|
}, [existing]);
|
|
14091
|
-
|
|
14628
|
+
useEffect38(() => {
|
|
14092
14629
|
if (!offer) return;
|
|
14093
14630
|
setWindows(draftWindows(offer));
|
|
14094
14631
|
setMinutes(offer.slot_minutes);
|
|
@@ -14350,7 +14887,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
14350
14887
|
}
|
|
14351
14888
|
|
|
14352
14889
|
// src/BookingSlots.tsx
|
|
14353
|
-
import { useCallback as useCallback33, useEffect as
|
|
14890
|
+
import { useCallback as useCallback33, useEffect as useEffect39, useMemo as useMemo30, useState as useState50 } from "react";
|
|
14354
14891
|
import { useRecordsClient as useRecordsClient36, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
|
|
14355
14892
|
import { bookingPath as bookingPath2 } from "@ai-matrx/records";
|
|
14356
14893
|
import { Badge as Badge16, Button as Button45, Skeleton as Skeleton29, cn as cn47 } from "@ai-matrx/design-system";
|
|
@@ -14381,7 +14918,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
14381
14918
|
setError(null);
|
|
14382
14919
|
setPages(answered.data);
|
|
14383
14920
|
}, [client, tableId]);
|
|
14384
|
-
|
|
14921
|
+
useEffect39(() => {
|
|
14385
14922
|
void load();
|
|
14386
14923
|
}, [load]);
|
|
14387
14924
|
const subjectIds = useMemo30(
|
|
@@ -14550,12 +15087,12 @@ function nextInWords(page) {
|
|
|
14550
15087
|
}
|
|
14551
15088
|
|
|
14552
15089
|
// src/CaptureSheet.tsx
|
|
14553
|
-
import { useCallback as useCallback35, useEffect as
|
|
15090
|
+
import { useCallback as useCallback35, useEffect as useEffect41, useRef as useRef15, useState as useState52 } from "react";
|
|
14554
15091
|
import { useFields as useFields23, useRecordsClient as useRecordsClient38, useTable as useTable21 } from "@ai-matrx/records/react";
|
|
14555
15092
|
import { Button as Button47, Input as Input4, Skeleton as Skeleton31, Textarea as Textarea3, cn as cn49 } from "@ai-matrx/design-system";
|
|
14556
15093
|
|
|
14557
15094
|
// src/CaptureRun.tsx
|
|
14558
|
-
import { useCallback as useCallback34, useEffect as
|
|
15095
|
+
import { useCallback as useCallback34, useEffect as useEffect40, useMemo as useMemo31, useRef as useRef14, useState as useState51 } from "react";
|
|
14559
15096
|
import {
|
|
14560
15097
|
coerceTypedAnswer as coerceTypedAnswer2,
|
|
14561
15098
|
coercionLabel,
|
|
@@ -14614,7 +15151,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14614
15151
|
const [lastSynced, setLastSynced] = useState51(null);
|
|
14615
15152
|
const [sending, setSending] = useState51(false);
|
|
14616
15153
|
const queueRef = useRef14(null);
|
|
14617
|
-
|
|
15154
|
+
useEffect40(() => {
|
|
14618
15155
|
const q2 = openCaptureQueue({
|
|
14619
15156
|
onChange: (c, all) => {
|
|
14620
15157
|
setCounts(c);
|
|
@@ -14652,13 +15189,13 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14652
15189
|
void q2.sync().then(() => void q2.lastSyncedAt().then(setLastSynced));
|
|
14653
15190
|
return () => q2.dispose();
|
|
14654
15191
|
}, [client, host]);
|
|
14655
|
-
|
|
15192
|
+
useEffect40(() => {
|
|
14656
15193
|
if (given !== void 0) return;
|
|
14657
15194
|
let cancelled = false;
|
|
14658
15195
|
void client.captureOpen({ sheet_id: sheetId }).then((res) => {
|
|
14659
15196
|
if (cancelled) return;
|
|
14660
15197
|
if (res.ok) setFace(res.data);
|
|
14661
|
-
else setLoadFailed(res.error
|
|
15198
|
+
else setLoadFailed(refusalOr(res.error, "unreachable", "This sheet could not be opened."));
|
|
14662
15199
|
});
|
|
14663
15200
|
return () => {
|
|
14664
15201
|
cancelled = true;
|
|
@@ -14705,7 +15242,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14705
15242
|
const needed = questions.filter((x) => x.required && !answered(x.field));
|
|
14706
15243
|
if (needed.length > 0) {
|
|
14707
15244
|
setMissing(
|
|
14708
|
-
|
|
15245
|
+
refusal(
|
|
15246
|
+
"invalid_argument",
|
|
15247
|
+
`${needed.map((x) => (x.ask ?? x.field).replace(/\?$/, "")).join(", ")} still ${needed.length === 1 ? "needs" : "need"} an answer.`,
|
|
15248
|
+
"Answer it, then capture again. Nothing has been queued yet."
|
|
15249
|
+
)
|
|
14709
15250
|
);
|
|
14710
15251
|
setAt(questions.findIndex((x) => x.field === needed[0].field));
|
|
14711
15252
|
return;
|
|
@@ -14736,7 +15277,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14736
15277
|
if (online) await sync();
|
|
14737
15278
|
}
|
|
14738
15279
|
if (loadFailed) {
|
|
14739
|
-
return /* @__PURE__ */ jsx54("section", { className: cn48("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx54("
|
|
15280
|
+
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" }) }) });
|
|
14740
15281
|
}
|
|
14741
15282
|
if (face === void 0) {
|
|
14742
15283
|
return /* @__PURE__ */ jsx54(Skeleton30, { className: cn48("mx-auto h-64 w-full max-w-sm", className) });
|
|
@@ -14767,7 +15308,21 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14767
15308
|
] });
|
|
14768
15309
|
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: [
|
|
14769
15310
|
/* @__PURE__ */ jsx54("span", { className: "tabular-nums text-muted-foreground", children: new Date(i.captured_at).toLocaleTimeString() }),
|
|
14770
|
-
|
|
15311
|
+
i.state === "refused" ? (
|
|
15312
|
+
// THE STORE REFUSED THIS ONE: a refusal line, heading and remedy
|
|
15313
|
+
// both — never its raw words in red (lane REFUSAL-SWEEP).
|
|
15314
|
+
/* @__PURE__ */ jsx54(
|
|
15315
|
+
RefusalLine,
|
|
15316
|
+
{
|
|
15317
|
+
className: "flex-1",
|
|
15318
|
+
error: refusal(
|
|
15319
|
+
"refused_by_rule",
|
|
15320
|
+
i.last_error ?? "This one was refused.",
|
|
15321
|
+
"Throw it away and capture it again with the answer changed."
|
|
15322
|
+
)
|
|
15323
|
+
}
|
|
15324
|
+
)
|
|
15325
|
+
) : /* @__PURE__ */ jsx54("span", { className: "flex-1", children: i.last_error ?? "Waiting for a signal." }),
|
|
14771
15326
|
i.state === "refused" ? /* @__PURE__ */ jsx54(
|
|
14772
15327
|
Button46,
|
|
14773
15328
|
{
|
|
@@ -14887,7 +15442,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14887
15442
|
}
|
|
14888
15443
|
}
|
|
14889
15444
|
),
|
|
14890
|
-
missing ? /* @__PURE__ */ jsx54("
|
|
15445
|
+
missing ? /* @__PURE__ */ jsx54("div", { "data-testid": "capture-missing", children: /* @__PURE__ */ jsx54(RefusalNotice, { error: missing, className: "text-sm" }) }) : null,
|
|
14891
15446
|
/* @__PURE__ */ jsxs50("div", { className: "flex items-center gap-2", children: [
|
|
14892
15447
|
at > 0 ? /* @__PURE__ */ jsx54(
|
|
14893
15448
|
Button46,
|
|
@@ -14984,7 +15539,7 @@ function AdHocCaptureSheet({
|
|
|
14984
15539
|
},
|
|
14985
15540
|
[host]
|
|
14986
15541
|
);
|
|
14987
|
-
|
|
15542
|
+
useEffect41(() => {
|
|
14988
15543
|
let cancelled = false;
|
|
14989
15544
|
void (async () => {
|
|
14990
15545
|
const held = host.captureQueue ? await host.captureQueue.load() : [];
|
|
@@ -15115,13 +15670,13 @@ function AdHocCaptureSheet({
|
|
|
15115
15670
|
setUploadError(null);
|
|
15116
15671
|
void host.upload?.(file).then((result) => {
|
|
15117
15672
|
if (result.ok) setFileId(result.fileId);
|
|
15118
|
-
else setUploadError(result.reason);
|
|
15673
|
+
else setUploadError(refusal("invalid_argument", result.reason, "Pick another file, or try this one again."));
|
|
15119
15674
|
});
|
|
15120
15675
|
}
|
|
15121
15676
|
}
|
|
15122
15677
|
),
|
|
15123
15678
|
fileId ? /* @__PURE__ */ jsx55("p", { className: "text-[11px] text-muted-foreground", children: "Attached." }) : null,
|
|
15124
|
-
uploadError ? /* @__PURE__ */ jsx55(
|
|
15679
|
+
uploadError ? /* @__PURE__ */ jsx55(RefusalLine, { error: uploadError }) : null
|
|
15125
15680
|
] }) : null,
|
|
15126
15681
|
/* @__PURE__ */ jsx55(
|
|
15127
15682
|
Textarea3,
|
|
@@ -15163,7 +15718,7 @@ function AdHocCaptureSheet({
|
|
|
15163
15718
|
}
|
|
15164
15719
|
|
|
15165
15720
|
// src/PortalShell.tsx
|
|
15166
|
-
import { useCallback as useCallback36, useEffect as
|
|
15721
|
+
import { useCallback as useCallback36, useEffect as useEffect42, useState as useState53 } from "react";
|
|
15167
15722
|
import { useRecordsClient as useRecordsClient39 } from "@ai-matrx/records/react";
|
|
15168
15723
|
import { Button as Button48, Skeleton as Skeleton32, cn as cn50 } from "@ai-matrx/design-system";
|
|
15169
15724
|
import { jsx as jsx56, jsxs as jsxs52 } from "react/jsx-runtime";
|
|
@@ -15189,7 +15744,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
|
15189
15744
|
}
|
|
15190
15745
|
setReach(reached.data.map((row) => row.resource_id));
|
|
15191
15746
|
}, [client, resourceType]);
|
|
15192
|
-
|
|
15747
|
+
useEffect42(() => {
|
|
15193
15748
|
void load();
|
|
15194
15749
|
}, [load]);
|
|
15195
15750
|
if (error) {
|
|
@@ -15249,7 +15804,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
|
15249
15804
|
function PortalRow({ tableId, recordId }) {
|
|
15250
15805
|
const client = useRecordsClient39();
|
|
15251
15806
|
const [title, setTitle] = useState53(null);
|
|
15252
|
-
|
|
15807
|
+
useEffect42(() => {
|
|
15253
15808
|
let cancelled = false;
|
|
15254
15809
|
void client.recordRead({ record_id: recordId }).then((answered) => {
|
|
15255
15810
|
if (cancelled) return;
|
|
@@ -15269,7 +15824,7 @@ function PortalRow({ tableId, recordId }) {
|
|
|
15269
15824
|
}
|
|
15270
15825
|
|
|
15271
15826
|
// src/PublicViewPage.tsx
|
|
15272
|
-
import { useCallback as useCallback37, useEffect as
|
|
15827
|
+
import { useCallback as useCallback37, useEffect as useEffect43, useState as useState54 } from "react";
|
|
15273
15828
|
import { useRecordsClient as useRecordsClient40 } from "@ai-matrx/records/react";
|
|
15274
15829
|
import { Skeleton as Skeleton33, cn as cn51 } from "@ai-matrx/design-system";
|
|
15275
15830
|
import { jsx as jsx57, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
@@ -15313,7 +15868,7 @@ function PublicViewPage({ slug, className }) {
|
|
|
15313
15868
|
}
|
|
15314
15869
|
setRows([{ id: found.resource_id, document: read.data.document, level: "viewer", hidden: read.data.hidden }]);
|
|
15315
15870
|
}, [client, slug]);
|
|
15316
|
-
|
|
15871
|
+
useEffect43(() => {
|
|
15317
15872
|
void load();
|
|
15318
15873
|
}, [load]);
|
|
15319
15874
|
if (error) return /* @__PURE__ */ jsx57(RefusalNotice, { error, className });
|
|
@@ -15350,7 +15905,7 @@ function PublicRow({ row, fields }) {
|
|
|
15350
15905
|
}
|
|
15351
15906
|
|
|
15352
15907
|
// src/EmbedFrame.tsx
|
|
15353
|
-
import { useCallback as useCallback38, useEffect as
|
|
15908
|
+
import { useCallback as useCallback38, useEffect as useEffect44, useState as useState55 } from "react";
|
|
15354
15909
|
import { useRecordsClient as useRecordsClient41 } from "@ai-matrx/records/react";
|
|
15355
15910
|
import { Button as Button49, Input as Input5, Skeleton as Skeleton34, Textarea as Textarea4, cn as cn52 } from "@ai-matrx/design-system";
|
|
15356
15911
|
import { useTable as useTable22 } from "@ai-matrx/records/react";
|
|
@@ -15463,7 +16018,7 @@ function useEmbedHandshake(args) {
|
|
|
15463
16018
|
const [loading, setLoading] = useState55(true);
|
|
15464
16019
|
const origin = args.origin ?? (typeof location === "undefined" ? "" : location.origin);
|
|
15465
16020
|
const { secret, requiredMode } = args;
|
|
15466
|
-
|
|
16021
|
+
useEffect44(() => {
|
|
15467
16022
|
let cancelled = false;
|
|
15468
16023
|
setLoading(true);
|
|
15469
16024
|
setError(null);
|
|
@@ -15518,7 +16073,7 @@ function recordsDataSource(client, fallbackSchema = "custom") {
|
|
|
15518
16073
|
}
|
|
15519
16074
|
|
|
15520
16075
|
// src/TablesHome.tsx
|
|
15521
|
-
import { useCallback as useCallback39, useEffect as
|
|
16076
|
+
import { useCallback as useCallback39, useEffect as useEffect45, useState as useState56 } from "react";
|
|
15522
16077
|
import { useRecordsClient as useRecordsClient42, useTables as useTables4 } from "@ai-matrx/records/react";
|
|
15523
16078
|
import { BasicInput as BasicInput18, Button as Button50, Skeleton as Skeleton35, cn as cn53 } from "@ai-matrx/design-system";
|
|
15524
16079
|
|
|
@@ -15584,17 +16139,17 @@ async function declareTable(client, spec) {
|
|
|
15584
16139
|
if (!written.ok) return undo(client, table.data, home.data, spec.name, written.error);
|
|
15585
16140
|
return { ok: true, data: table.data, homeId: home.data };
|
|
15586
16141
|
}
|
|
15587
|
-
async function undo(client, tableId, homeId, name,
|
|
16142
|
+
async function undo(client, tableId, homeId, name, refusal2) {
|
|
15588
16143
|
const removed = await client.recordDelete({ record_id: tableId });
|
|
15589
16144
|
if (removed.ok) {
|
|
15590
16145
|
await client.recordDelete({ record_id: homeId });
|
|
15591
|
-
return { ok: false, error:
|
|
16146
|
+
return { ok: false, error: refusal2 };
|
|
15592
16147
|
}
|
|
15593
16148
|
return {
|
|
15594
16149
|
ok: false,
|
|
15595
16150
|
error: {
|
|
15596
|
-
...
|
|
15597
|
-
hint: `${
|
|
16151
|
+
...refusal2,
|
|
16152
|
+
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.`
|
|
15598
16153
|
}
|
|
15599
16154
|
};
|
|
15600
16155
|
}
|
|
@@ -15663,7 +16218,7 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
15663
16218
|
const [error, setError] = useState56(null);
|
|
15664
16219
|
const [importInto, setImportInto] = useState56(null);
|
|
15665
16220
|
const [boards, setBoards] = useState56(null);
|
|
15666
|
-
|
|
16221
|
+
useEffect45(() => {
|
|
15667
16222
|
let cancelled = false;
|
|
15668
16223
|
void client.dashboards({}).then((result) => {
|
|
15669
16224
|
if (cancelled) return;
|
|
@@ -15797,7 +16352,7 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
15797
16352
|
}
|
|
15798
16353
|
|
|
15799
16354
|
// src/TablePage.tsx
|
|
15800
|
-
import { useCallback as useCallback40, useEffect as
|
|
16355
|
+
import { useCallback as useCallback40, useEffect as useEffect46, useState as useState57 } from "react";
|
|
15801
16356
|
import { useFields as useFields24, useRecordsClient as useRecordsClient43, useTable as useTable23 } from "@ai-matrx/records/react";
|
|
15802
16357
|
import { Button as Button51, Separator as Separator12, Skeleton as Skeleton36, cn as cn54 } from "@ai-matrx/design-system";
|
|
15803
16358
|
import { Fragment as Fragment28, jsx as jsx61, jsxs as jsxs56 } from "react/jsx-runtime";
|
|
@@ -15848,9 +16403,43 @@ function chooseSurface(current, pressed) {
|
|
|
15848
16403
|
function surfaceChosen(current, asking) {
|
|
15849
16404
|
return asking.main !== void 0 ? current.main === asking.main : current.rail === asking.rail;
|
|
15850
16405
|
}
|
|
15851
|
-
function openingRail(activeRecordId) {
|
|
15852
|
-
|
|
16406
|
+
function openingRail(activeRecordId, activeRail) {
|
|
16407
|
+
if (activeRecordId) return { rail: "record", record: activeRecordId };
|
|
16408
|
+
const named = railFromParam(activeRail);
|
|
16409
|
+
if (named !== null && named !== "share") return { rail: named, record: null };
|
|
16410
|
+
return { rail: "none", record: null };
|
|
16411
|
+
}
|
|
16412
|
+
var RAIL_SPELLINGS = {
|
|
16413
|
+
forms: "forms",
|
|
16414
|
+
form: "forms",
|
|
16415
|
+
bookings: "bookings",
|
|
16416
|
+
booking: "bookings",
|
|
16417
|
+
checklists: "checklists",
|
|
16418
|
+
checklist: "checklists",
|
|
16419
|
+
notifications: "notifications",
|
|
16420
|
+
notification: "notifications",
|
|
16421
|
+
digests: "notifications",
|
|
16422
|
+
digest: "notifications",
|
|
16423
|
+
subscriptions: "notifications",
|
|
16424
|
+
subscription: "notifications",
|
|
16425
|
+
portals: "portals",
|
|
16426
|
+
portal: "portals",
|
|
16427
|
+
settings: "settings",
|
|
16428
|
+
import: "import",
|
|
16429
|
+
inbox: "inbox",
|
|
16430
|
+
share: "share",
|
|
16431
|
+
sharing: "share"
|
|
16432
|
+
};
|
|
16433
|
+
function railFromParam(raw) {
|
|
16434
|
+
if (typeof raw !== "string") return null;
|
|
16435
|
+
const token = raw.trim().toLowerCase();
|
|
16436
|
+
if (token === "") return null;
|
|
16437
|
+
return RAIL_SPELLINGS[token] ?? null;
|
|
16438
|
+
}
|
|
16439
|
+
function unknownRailLine(raw) {
|
|
16440
|
+
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.`;
|
|
15853
16441
|
}
|
|
16442
|
+
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.";
|
|
15854
16443
|
function openingView(activeView, activeDashboardId) {
|
|
15855
16444
|
const named = pageViewFromParam(activeView);
|
|
15856
16445
|
if (named === null) {
|
|
@@ -15878,6 +16467,8 @@ function TablePage({
|
|
|
15878
16467
|
activeGroupField,
|
|
15879
16468
|
cameFrom,
|
|
15880
16469
|
filter,
|
|
16470
|
+
activeRail,
|
|
16471
|
+
activeItemId,
|
|
15881
16472
|
className
|
|
15882
16473
|
}) {
|
|
15883
16474
|
const client = useRecordsClient43();
|
|
@@ -15887,8 +16478,13 @@ function TablePage({
|
|
|
15887
16478
|
const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
|
|
15888
16479
|
const organizationId = useRecordsClient43().config.organizationId;
|
|
15889
16480
|
const [view, setView] = useState57(null);
|
|
15890
|
-
const opening = openingRail(activeRecordId);
|
|
16481
|
+
const opening = openingRail(activeRecordId, activeRail);
|
|
15891
16482
|
const opened = openingView(activeView, activeDashboardId);
|
|
16483
|
+
const railAsked = typeof activeRail === "string" ? activeRail.trim() : "";
|
|
16484
|
+
const railUnknown = railAsked !== "" && railFromParam(railAsked) === null ? railAsked : null;
|
|
16485
|
+
const shareFromLink = railFromParam(activeRail) === "share";
|
|
16486
|
+
const canShare = useCanShare();
|
|
16487
|
+
const itemFor = (which) => railFromParam(activeRail) === which ? activeItemId ?? null : null;
|
|
15892
16488
|
const [asking, setAsking] = useState57(null);
|
|
15893
16489
|
const [layoutFromLink, setLayoutFromLink] = useState57(opened.layout);
|
|
15894
16490
|
const [surface, setSurface] = useState57({
|
|
@@ -15908,12 +16504,18 @@ function TablePage({
|
|
|
15908
16504
|
}
|
|
15909
16505
|
};
|
|
15910
16506
|
const show = (next) => press({ rail: next });
|
|
15911
|
-
|
|
16507
|
+
useEffect46(() => {
|
|
15912
16508
|
if (!activeRecordId) return;
|
|
15913
16509
|
setOpenRecord(activeRecordId);
|
|
15914
16510
|
setSurface((now) => ({ ...now, rail: "record" }));
|
|
15915
16511
|
}, [activeRecordId]);
|
|
15916
|
-
|
|
16512
|
+
useEffect46(() => {
|
|
16513
|
+
if (activeRecordId) return;
|
|
16514
|
+
const named = railFromParam(activeRail);
|
|
16515
|
+
if (named === null || named === "share") return;
|
|
16516
|
+
setSurface((now) => ({ ...now, rail: named }));
|
|
16517
|
+
}, [activeRail, activeRecordId]);
|
|
16518
|
+
useEffect46(() => {
|
|
15917
16519
|
const named = pageViewFromParam(activeView);
|
|
15918
16520
|
if (named === null) return;
|
|
15919
16521
|
const isLayout = named !== "dashboards" && named !== "archived";
|
|
@@ -16077,7 +16679,8 @@ function TablePage({
|
|
|
16077
16679
|
organizationId,
|
|
16078
16680
|
subjectId: tableId,
|
|
16079
16681
|
name: table.data?.name,
|
|
16080
|
-
may: rights.share
|
|
16682
|
+
may: rights.share,
|
|
16683
|
+
initiallyOpen: shareFromLink
|
|
16081
16684
|
}
|
|
16082
16685
|
),
|
|
16083
16686
|
/* @__PURE__ */ jsx61(ExportMenu, { tableId })
|
|
@@ -16100,6 +16703,8 @@ function TablePage({
|
|
|
16100
16703
|
}
|
|
16101
16704
|
)
|
|
16102
16705
|
) : /* @__PURE__ */ jsxs56(Fragment28, { children: [
|
|
16706
|
+
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,
|
|
16707
|
+
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,
|
|
16103
16708
|
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,
|
|
16104
16709
|
cameFrom ? /* @__PURE__ */ jsx61(
|
|
16105
16710
|
"p",
|
|
@@ -16158,7 +16763,7 @@ function TablePage({
|
|
|
16158
16763
|
}
|
|
16159
16764
|
}
|
|
16160
16765
|
) : null,
|
|
16161
|
-
rail === "forms" ? /* @__PURE__ */ jsx61(FormsPanel, { tableId }) : null,
|
|
16766
|
+
rail === "forms" ? /* @__PURE__ */ jsx61(FormsPanel, { tableId, activeFormId: itemFor("forms") }) : null,
|
|
16162
16767
|
rail === "bookings" ? /* @__PURE__ */ jsx61(BookingSlots, { tableId }) : null,
|
|
16163
16768
|
rail === "checklists" ? /* @__PURE__ */ jsx61(
|
|
16164
16769
|
ChecklistsPanel,
|
|
@@ -16170,8 +16775,8 @@ function TablePage({
|
|
|
16170
16775
|
}
|
|
16171
16776
|
}
|
|
16172
16777
|
) : null,
|
|
16173
|
-
rail === "notifications" ? /* @__PURE__ */ jsx61(SubscriptionsPanel, { tableId }) : null,
|
|
16174
|
-
rail === "portals" ? /* @__PURE__ */ jsx61(PortalsPanel, { tableId }) : null,
|
|
16778
|
+
rail === "notifications" ? /* @__PURE__ */ jsx61(SubscriptionsPanel, { tableId, activeRuleId: itemFor("notifications") }) : null,
|
|
16779
|
+
rail === "portals" ? /* @__PURE__ */ jsx61(PortalsPanel, { tableId, activePortalId: itemFor("portals") }) : null,
|
|
16175
16780
|
rail === "import" ? /* @__PURE__ */ jsx61(ImportWizard, { tableId, onDone: () => setRail("none") }) : null,
|
|
16176
16781
|
rail === "field" ? /* @__PURE__ */ jsx61(FieldEditor, { tableId, onSaved: () => setRail("none"), onCancel: () => setRail("none") }) : null,
|
|
16177
16782
|
rail === "new-record" ? /* @__PURE__ */ jsx61(
|
|
@@ -16278,6 +16883,7 @@ export {
|
|
|
16278
16883
|
FORMULA_OP_LABEL,
|
|
16279
16884
|
FORMULA_OP_VALUES,
|
|
16280
16885
|
FORM_FLOWS,
|
|
16886
|
+
FORM_NOT_HERE_LINE,
|
|
16281
16887
|
FROZEN_COLUMN_WIDTH,
|
|
16282
16888
|
FieldControl,
|
|
16283
16889
|
FieldEditor,
|
|
@@ -16325,6 +16931,7 @@ export {
|
|
|
16325
16931
|
PAGE_VIEW_LABEL,
|
|
16326
16932
|
PARITY_LABEL,
|
|
16327
16933
|
PARITY_MADE_OF,
|
|
16934
|
+
PORTAL_NOT_HERE_LINE,
|
|
16328
16935
|
PROPOSED_CHANGE_ACT_LABEL,
|
|
16329
16936
|
PROPOSED_CHANGE_ACT_VALUES,
|
|
16330
16937
|
Peek,
|
|
@@ -16339,6 +16946,7 @@ export {
|
|
|
16339
16946
|
PublicViewPage,
|
|
16340
16947
|
ROLLUP_AGG_LABEL,
|
|
16341
16948
|
ROLLUP_AGG_VALUES,
|
|
16949
|
+
RULE_NOT_HERE_LINE,
|
|
16342
16950
|
RecordChat,
|
|
16343
16951
|
RecordChip,
|
|
16344
16952
|
RecordForm,
|
|
@@ -16353,6 +16961,7 @@ export {
|
|
|
16353
16961
|
SAVED_VIEWS_UNAVAILABLE,
|
|
16354
16962
|
SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS,
|
|
16355
16963
|
SERIES_COLORS,
|
|
16964
|
+
SHARE_NOT_YOURS_LINE,
|
|
16356
16965
|
SOMEBODY,
|
|
16357
16966
|
STAGE_RULE_ON_FAIL_LABEL,
|
|
16358
16967
|
STORE_ANSWERS_THESE,
|
|
@@ -16386,6 +16995,7 @@ export {
|
|
|
16386
16995
|
actorWords,
|
|
16387
16996
|
addFields,
|
|
16388
16997
|
archivedRowLine,
|
|
16998
|
+
asRefusal,
|
|
16389
16999
|
askableFields,
|
|
16390
17000
|
blockFromSpec,
|
|
16391
17001
|
bodyForReading,
|
|
@@ -16451,11 +17061,15 @@ export {
|
|
|
16451
17061
|
previewLine,
|
|
16452
17062
|
previewWords,
|
|
16453
17063
|
publiclyAnswerable,
|
|
17064
|
+
railFromParam,
|
|
16454
17065
|
recordName,
|
|
16455
17066
|
recordNameIn,
|
|
16456
17067
|
recordsDataSource,
|
|
17068
|
+
refusal,
|
|
16457
17069
|
refusalForAPerson,
|
|
17070
|
+
refusalFromThrown,
|
|
16458
17071
|
refusalLineForAPerson,
|
|
17072
|
+
refusalOr,
|
|
16459
17073
|
renderValue,
|
|
16460
17074
|
revokeConsequence,
|
|
16461
17075
|
rowName,
|
|
@@ -16469,6 +17083,7 @@ export {
|
|
|
16469
17083
|
tableName,
|
|
16470
17084
|
tableRightsAt,
|
|
16471
17085
|
tokenFor,
|
|
17086
|
+
unknownRailLine,
|
|
16472
17087
|
unknownViewLine,
|
|
16473
17088
|
useCanShare,
|
|
16474
17089
|
useEmbedHandshake,
|