@ai-matrx/records-ui 0.81.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 +34 -0
- package/dist/index.cjs +911 -432
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -10
- package/dist/index.d.ts +119 -10
- package/dist/index.js +986 -507
- 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,33 +948,33 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
830
948
|
className: "mb-1 h-7 text-xs"
|
|
831
949
|
}
|
|
832
950
|
),
|
|
833
|
-
|
|
834
|
-
!rows && !
|
|
835
|
-
full ? /* @__PURE__ */
|
|
951
|
+
refusal2 ? /* @__PURE__ */ jsx4(RefusalLine, { error: refusal2, className: "p-2" }) : null,
|
|
952
|
+
!rows && !refusal2 ? /* @__PURE__ */ jsx4("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading\u2026" }) : null,
|
|
953
|
+
full ? /* @__PURE__ */ jsxs3("p", { className: "p-2 text-xs text-muted-foreground", children: [
|
|
836
954
|
fieldName(field),
|
|
837
955
|
" can point at ",
|
|
838
956
|
field.relation_max,
|
|
839
957
|
" at most. Remove one to add another."
|
|
840
958
|
] }) : null,
|
|
841
|
-
/* @__PURE__ */
|
|
842
|
-
/* @__PURE__ */
|
|
959
|
+
/* @__PURE__ */ jsxs3(ScrollArea, { className: "max-h-56", children: [
|
|
960
|
+
/* @__PURE__ */ jsx4("ul", { children: visible.map((row) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
|
|
843
961
|
"button",
|
|
844
962
|
{
|
|
845
963
|
type: "button",
|
|
846
964
|
onClick: () => toggle(row.id),
|
|
847
965
|
title: titleOf(row.id),
|
|
848
|
-
className:
|
|
966
|
+
className: cn3(
|
|
849
967
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
850
968
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
851
969
|
),
|
|
852
970
|
children: titleOf(row.id)
|
|
853
971
|
}
|
|
854
972
|
) }, row.id)) }),
|
|
855
|
-
rows && visible.length === 0 ? /* @__PURE__ */
|
|
973
|
+
rows && visible.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "p-2 text-xs text-muted-foreground", children: "Nothing in that table matches." }) : null
|
|
856
974
|
] }),
|
|
857
|
-
allowCreate && !full ? /* @__PURE__ */
|
|
858
|
-
targetRefusal ? /* @__PURE__ */
|
|
859
|
-
/* @__PURE__ */
|
|
975
|
+
allowCreate && !full ? /* @__PURE__ */ jsxs3("div", { className: "mt-1 border-t pt-1", children: [
|
|
976
|
+
targetRefusal ? /* @__PURE__ */ jsx4(RefusalLine, { error: targetRefusal, className: "p-2" }) : !target ? /* @__PURE__ */ jsx4("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading that table\u2026" }) : creating ? /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 p-1", children: [
|
|
977
|
+
/* @__PURE__ */ jsx4(
|
|
860
978
|
BasicInput2,
|
|
861
979
|
{
|
|
862
980
|
autoFocus: true,
|
|
@@ -873,7 +991,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
873
991
|
"aria-label": `Name the new ${targetWord}`
|
|
874
992
|
}
|
|
875
993
|
),
|
|
876
|
-
/* @__PURE__ */
|
|
994
|
+
/* @__PURE__ */ jsx4(
|
|
877
995
|
Button2,
|
|
878
996
|
{
|
|
879
997
|
type: "button",
|
|
@@ -884,7 +1002,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
884
1002
|
children: busy ? "Creating\u2026" : "Create"
|
|
885
1003
|
}
|
|
886
1004
|
)
|
|
887
|
-
] }) : /* @__PURE__ */
|
|
1005
|
+
] }) : /* @__PURE__ */ jsx4(
|
|
888
1006
|
Button2,
|
|
889
1007
|
{
|
|
890
1008
|
type: "button",
|
|
@@ -900,10 +1018,25 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
900
1018
|
setCreating(true);
|
|
901
1019
|
}
|
|
902
1020
|
},
|
|
903
|
-
children: /* @__PURE__ */
|
|
1021
|
+
children: /* @__PURE__ */ jsx4("span", { className: "block w-full truncate text-left", children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}` })
|
|
904
1022
|
}
|
|
905
1023
|
),
|
|
906
|
-
createRefusal ?
|
|
1024
|
+
createRefusal ? (
|
|
1025
|
+
// The typed title is still in the search box: the notice owns the
|
|
1026
|
+
// two doors out (lane REFUSAL-SWEEP).
|
|
1027
|
+
/* @__PURE__ */ jsx4(
|
|
1028
|
+
RefusalNotice,
|
|
1029
|
+
{
|
|
1030
|
+
error: createRefusal,
|
|
1031
|
+
className: "mt-1 max-w-72",
|
|
1032
|
+
onKeepEditing: () => setCreateRefusal(null),
|
|
1033
|
+
onDiscard: () => {
|
|
1034
|
+
setCreateRefusal(null);
|
|
1035
|
+
setSearch("");
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
)
|
|
1039
|
+
) : null
|
|
907
1040
|
] }) : null
|
|
908
1041
|
] })
|
|
909
1042
|
] })
|
|
@@ -911,12 +1044,12 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
911
1044
|
}
|
|
912
1045
|
|
|
913
1046
|
// src/editors.tsx
|
|
914
|
-
import { Fragment as Fragment2, jsx as
|
|
1047
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
915
1048
|
function FieldLabel({ field, htmlFor, children }) {
|
|
916
|
-
return /* @__PURE__ */
|
|
917
|
-
/* @__PURE__ */
|
|
918
|
-
field.required ? /* @__PURE__ */
|
|
919
|
-
field.unit ? /* @__PURE__ */
|
|
1049
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1050
|
+
/* @__PURE__ */ jsx5(Label2, { htmlFor, className: "text-xs font-medium", children: fieldName(field) }),
|
|
1051
|
+
field.required ? /* @__PURE__ */ jsx5("span", { className: "text-xs text-destructive", children: "required" }) : null,
|
|
1052
|
+
field.unit ? /* @__PURE__ */ jsx5("span", { className: "text-xs text-muted-foreground", children: field.unit }) : null,
|
|
920
1053
|
children
|
|
921
1054
|
] });
|
|
922
1055
|
}
|
|
@@ -927,17 +1060,17 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
927
1060
|
case "formula":
|
|
928
1061
|
case "lookup":
|
|
929
1062
|
case "rollup":
|
|
930
|
-
return /* @__PURE__ */
|
|
1063
|
+
return /* @__PURE__ */ jsxs4("div", { id: inputId, className: "rounded border border-dashed px-2 py-1.5 text-xs text-muted-foreground", children: [
|
|
931
1064
|
value === null || value === void 0 ? "\u2014" : String(value),
|
|
932
|
-
/* @__PURE__ */
|
|
1065
|
+
/* @__PURE__ */ jsxs4("span", { className: "ml-2 italic", children: [
|
|
933
1066
|
fieldName(field),
|
|
934
1067
|
" is worked out by the system, so it cannot be typed in"
|
|
935
1068
|
] })
|
|
936
1069
|
] });
|
|
937
1070
|
case "json":
|
|
938
|
-
return /* @__PURE__ */
|
|
1071
|
+
return /* @__PURE__ */ jsx5(JsonControl, { field, value, onChange, disabled, id: inputId });
|
|
939
1072
|
case "long_text":
|
|
940
|
-
return /* @__PURE__ */
|
|
1073
|
+
return /* @__PURE__ */ jsx5(
|
|
941
1074
|
BasicTextarea,
|
|
942
1075
|
{
|
|
943
1076
|
id: inputId,
|
|
@@ -950,7 +1083,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
950
1083
|
case "number":
|
|
951
1084
|
case "currency":
|
|
952
1085
|
case "percent":
|
|
953
|
-
return /* @__PURE__ */
|
|
1086
|
+
return /* @__PURE__ */ jsx5(
|
|
954
1087
|
BasicInput3,
|
|
955
1088
|
{
|
|
956
1089
|
id: inputId,
|
|
@@ -963,7 +1096,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
963
1096
|
}
|
|
964
1097
|
);
|
|
965
1098
|
case "datetime":
|
|
966
|
-
return /* @__PURE__ */
|
|
1099
|
+
return /* @__PURE__ */ jsx5(
|
|
967
1100
|
BasicInput3,
|
|
968
1101
|
{
|
|
969
1102
|
id: inputId,
|
|
@@ -976,7 +1109,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
976
1109
|
case "email":
|
|
977
1110
|
case "phone":
|
|
978
1111
|
case "url":
|
|
979
|
-
return /* @__PURE__ */
|
|
1112
|
+
return /* @__PURE__ */ jsx5(
|
|
980
1113
|
BasicInput3,
|
|
981
1114
|
{
|
|
982
1115
|
id: inputId,
|
|
@@ -987,18 +1120,18 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
987
1120
|
}
|
|
988
1121
|
);
|
|
989
1122
|
case "checkbox":
|
|
990
|
-
return /* @__PURE__ */
|
|
1123
|
+
return /* @__PURE__ */ jsx5(CheckboxControl, { field, value, onChange, disabled, id: inputId });
|
|
991
1124
|
case "select":
|
|
992
1125
|
case "multi_select":
|
|
993
|
-
return /* @__PURE__ */
|
|
1126
|
+
return /* @__PURE__ */ jsx5(OptionControl, { field, value, onChange, disabled, id: inputId });
|
|
994
1127
|
case "member":
|
|
995
|
-
return /* @__PURE__ */
|
|
1128
|
+
return /* @__PURE__ */ jsx5(PersonPicker, { field, value, onChange, disabled, id: inputId });
|
|
996
1129
|
case "relation":
|
|
997
1130
|
case "attachment":
|
|
998
|
-
return /* @__PURE__ */
|
|
1131
|
+
return /* @__PURE__ */ jsx5(RelationPicker, { field, value, onChange, disabled, id: inputId });
|
|
999
1132
|
default:
|
|
1000
1133
|
if (typeof value === "boolean" || field.config?.["kind"] === "boolean") {
|
|
1001
|
-
return /* @__PURE__ */
|
|
1134
|
+
return /* @__PURE__ */ jsx5(
|
|
1002
1135
|
Checkbox2,
|
|
1003
1136
|
{
|
|
1004
1137
|
id: inputId,
|
|
@@ -1008,7 +1141,7 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1008
1141
|
}
|
|
1009
1142
|
);
|
|
1010
1143
|
}
|
|
1011
|
-
return /* @__PURE__ */
|
|
1144
|
+
return /* @__PURE__ */ jsx5(
|
|
1012
1145
|
BasicInput3,
|
|
1013
1146
|
{
|
|
1014
1147
|
id: inputId,
|
|
@@ -1021,8 +1154,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1021
1154
|
}
|
|
1022
1155
|
function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
1023
1156
|
const unanswered = value === null || value === void 0;
|
|
1024
|
-
return /* @__PURE__ */
|
|
1025
|
-
/* @__PURE__ */
|
|
1157
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
1158
|
+
/* @__PURE__ */ jsx5(
|
|
1026
1159
|
Checkbox2,
|
|
1027
1160
|
{
|
|
1028
1161
|
id,
|
|
@@ -1032,9 +1165,9 @@ function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
|
1032
1165
|
onCheckedChange: (next) => onChange(next === true)
|
|
1033
1166
|
}
|
|
1034
1167
|
),
|
|
1035
|
-
unanswered ? /* @__PURE__ */
|
|
1036
|
-
/* @__PURE__ */
|
|
1037
|
-
field.required ? null : /* @__PURE__ */
|
|
1168
|
+
unanswered ? /* @__PURE__ */ jsx5("span", { className: "text-xs italic text-muted-foreground", children: "not answered yet" }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1169
|
+
/* @__PURE__ */ jsx5("span", { className: "text-xs text-muted-foreground", children: value === true ? "Yes" : "No" }),
|
|
1170
|
+
field.required ? null : /* @__PURE__ */ jsx5(
|
|
1038
1171
|
Button3,
|
|
1039
1172
|
{
|
|
1040
1173
|
size: "sm",
|
|
@@ -1052,8 +1185,8 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1052
1185
|
const initial = value === null || value === void 0 ? "" : typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
1053
1186
|
const [text, setText] = useState3(initial);
|
|
1054
1187
|
const [broken, setBroken] = useState3(null);
|
|
1055
|
-
return /* @__PURE__ */
|
|
1056
|
-
/* @__PURE__ */
|
|
1188
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
|
|
1189
|
+
/* @__PURE__ */ jsx5(
|
|
1057
1190
|
BasicTextarea,
|
|
1058
1191
|
{
|
|
1059
1192
|
id,
|
|
@@ -1073,37 +1206,53 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1073
1206
|
onChange(JSON.parse(next));
|
|
1074
1207
|
setBroken(null);
|
|
1075
1208
|
} catch (thrown) {
|
|
1076
|
-
setBroken(
|
|
1209
|
+
setBroken(
|
|
1210
|
+
refusalFromThrown(
|
|
1211
|
+
thrown,
|
|
1212
|
+
`${fieldName(field)} is not readable as structured data yet.`,
|
|
1213
|
+
"Fix the text so it reads as structured data, or discard it to put back what was there.",
|
|
1214
|
+
"invalid_argument"
|
|
1215
|
+
)
|
|
1216
|
+
);
|
|
1077
1217
|
}
|
|
1078
1218
|
}
|
|
1079
1219
|
}
|
|
1080
1220
|
),
|
|
1081
|
-
broken ?
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1221
|
+
broken ? (
|
|
1222
|
+
// The typed text is still in the box: the notice owns the two doors out.
|
|
1223
|
+
/* @__PURE__ */ jsx5(
|
|
1224
|
+
RefusalNotice,
|
|
1225
|
+
{
|
|
1226
|
+
error: broken,
|
|
1227
|
+
onKeepEditing: () => id ? document.getElementById(id)?.focus() : void 0,
|
|
1228
|
+
onDiscard: () => {
|
|
1229
|
+
setText(initial);
|
|
1230
|
+
setBroken(null);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
)
|
|
1234
|
+
) : null
|
|
1086
1235
|
] });
|
|
1087
1236
|
}
|
|
1088
1237
|
function OptionControl({ field, value, onChange, disabled, id }) {
|
|
1089
1238
|
const client = useOptionalRecordsClient2();
|
|
1090
1239
|
const [options, setOptions] = useState3(null);
|
|
1091
|
-
const [
|
|
1240
|
+
const [refusal2, setRefusal] = useState3(null);
|
|
1092
1241
|
useEffect2(() => {
|
|
1093
1242
|
if (!client) return;
|
|
1094
1243
|
let cancelled = false;
|
|
1095
1244
|
void client.fieldOptions({ field_id: field.id }).then((result) => {
|
|
1096
1245
|
if (cancelled) return;
|
|
1097
1246
|
if (result.ok) setOptions(result.data ?? []);
|
|
1098
|
-
else setRefusal(
|
|
1247
|
+
else setRefusal(result.error);
|
|
1099
1248
|
});
|
|
1100
1249
|
return () => {
|
|
1101
1250
|
cancelled = true;
|
|
1102
1251
|
};
|
|
1103
1252
|
}, [client, field.id]);
|
|
1104
1253
|
if (!client) {
|
|
1105
|
-
return /* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */
|
|
1254
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
|
|
1255
|
+
/* @__PURE__ */ jsx5(
|
|
1107
1256
|
BasicInput3,
|
|
1108
1257
|
{
|
|
1109
1258
|
id,
|
|
@@ -1112,19 +1261,20 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1112
1261
|
onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
|
|
1113
1262
|
}
|
|
1114
1263
|
),
|
|
1115
|
-
/* @__PURE__ */
|
|
1264
|
+
/* @__PURE__ */ jsx5("p", { className: "text-xs text-muted-foreground", children: "This is normally picked from a list. A page answered without an account cannot read that list, so type your answer and whoever receives it will match it up." })
|
|
1116
1265
|
] });
|
|
1117
1266
|
}
|
|
1118
|
-
if (
|
|
1119
|
-
|
|
1267
|
+
if (refusal2)
|
|
1268
|
+
return /* @__PURE__ */ jsx5("div", { id, children: /* @__PURE__ */ jsx5(RefusalLine, { error: refusal2 }) });
|
|
1269
|
+
if (!options) return /* @__PURE__ */ jsx5("p", { id, className: "text-xs text-muted-foreground", children: "Reading this field's choices\u2026" });
|
|
1120
1270
|
if (options.length === 0) {
|
|
1121
|
-
return /* @__PURE__ */
|
|
1271
|
+
return /* @__PURE__ */ jsx5("p", { id, className: "text-xs text-muted-foreground", children: field.options_table_id ? `${fieldName(field)} takes its choices from a Table that has no records yet \u2014 add the choices there and they appear here.` : `${fieldName(field)} does not name the Table its choices come from yet, so there is nothing to choose. A table admin sets it in the field editor.` });
|
|
1122
1272
|
}
|
|
1123
1273
|
const titleOf = (option) => recordName(option.data, null, "Unnamed choice");
|
|
1124
1274
|
if (field.multi) {
|
|
1125
1275
|
const chosen = options.filter((option) => choiceValuesOf(value).some((v) => isTheChosen(option, v)));
|
|
1126
1276
|
const picked = new Set(chosen.map((option) => optionKey(option)));
|
|
1127
|
-
return /* @__PURE__ */
|
|
1277
|
+
return /* @__PURE__ */ jsx5("div", { id, className: "flex flex-wrap gap-1", children: options.map((option) => /* @__PURE__ */ jsx5(
|
|
1128
1278
|
Button3,
|
|
1129
1279
|
{
|
|
1130
1280
|
type: "button",
|
|
@@ -1143,15 +1293,15 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1143
1293
|
)) });
|
|
1144
1294
|
}
|
|
1145
1295
|
const current = options.find((option) => isTheChosen(option, value));
|
|
1146
|
-
return /* @__PURE__ */
|
|
1296
|
+
return /* @__PURE__ */ jsxs4(
|
|
1147
1297
|
Select2,
|
|
1148
1298
|
{
|
|
1149
1299
|
value: current ? optionKey(current) : "",
|
|
1150
1300
|
disabled: disabled ?? false,
|
|
1151
1301
|
onValueChange: (next) => onChange(next === "" ? null : next),
|
|
1152
1302
|
children: [
|
|
1153
|
-
/* @__PURE__ */
|
|
1154
|
-
/* @__PURE__ */
|
|
1303
|
+
/* @__PURE__ */ jsx5(SelectTrigger2, { id, size: "sm", children: /* @__PURE__ */ jsx5(SelectValue2, { placeholder: "Choose" }) }),
|
|
1304
|
+
/* @__PURE__ */ jsx5(SelectContent2, { children: options.map((option) => /* @__PURE__ */ jsx5(SelectItem2, { value: optionKey(option), children: titleOf(option) }, option.id)) })
|
|
1155
1305
|
]
|
|
1156
1306
|
}
|
|
1157
1307
|
);
|
|
@@ -1167,15 +1317,15 @@ function RecordChip({
|
|
|
1167
1317
|
// name. So it caps at its container and ends in an ellipsis with the whole name in
|
|
1168
1318
|
// its tooltip, rather than being cut mid-word by whatever box it landed in
|
|
1169
1319
|
// (lane FIX-14, 2026-09-22).
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
onRemove ? /* @__PURE__ */
|
|
1320
|
+
/* @__PURE__ */ jsxs4(Badge, { variant: "secondary", className: cn4("max-w-full gap-1 text-[11px]", className), title, children: [
|
|
1321
|
+
/* @__PURE__ */ jsx5("span", { className: "min-w-0 truncate", children: title }),
|
|
1322
|
+
onRemove ? /* @__PURE__ */ jsx5("button", { type: "button", onClick: onRemove, "aria-label": `Remove ${title}`, className: "shrink-0 opacity-60 hover:opacity-100", children: "\xD7" }) : null
|
|
1173
1323
|
] })
|
|
1174
1324
|
);
|
|
1175
1325
|
}
|
|
1176
1326
|
|
|
1177
1327
|
// src/PersonPicker.tsx
|
|
1178
|
-
import { jsx as
|
|
1328
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1179
1329
|
function personFromRow(row) {
|
|
1180
1330
|
const data = row.document ?? {};
|
|
1181
1331
|
const userId = data["user_id"];
|
|
@@ -1228,7 +1378,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1228
1378
|
]);
|
|
1229
1379
|
setMembers(roster);
|
|
1230
1380
|
if (records && !records.ok) {
|
|
1231
|
-
setError(
|
|
1381
|
+
setError(records.error);
|
|
1232
1382
|
return;
|
|
1233
1383
|
}
|
|
1234
1384
|
setPeople(records ? records.data.rows.map(personFromRow).filter((p) => p.userId !== null) : []);
|
|
@@ -1249,10 +1399,10 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1249
1399
|
};
|
|
1250
1400
|
}, [client, personTable, picked.length, people.length]);
|
|
1251
1401
|
if (!membersPort || !client) {
|
|
1252
|
-
return /* @__PURE__ */
|
|
1402
|
+
return /* @__PURE__ */ jsx6("p", { className: cn5("text-xs text-muted-foreground", className), children: NO_MEMBERS_REASON });
|
|
1253
1403
|
}
|
|
1254
1404
|
if (!personTable) {
|
|
1255
|
-
return /* @__PURE__ */
|
|
1405
|
+
return /* @__PURE__ */ jsxs5("p", { className: cn5("text-xs text-muted-foreground", className), children: [
|
|
1256
1406
|
fieldName(field),
|
|
1257
1407
|
" is a person field, but it does not say which Table the people are records of yet, so there is nothing to pick from. A table admin sets its target in the field editor."
|
|
1258
1408
|
] });
|
|
@@ -1270,7 +1420,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1270
1420
|
const resolved = await personRecordForMember(client, personTable, people, member);
|
|
1271
1421
|
setBusy(false);
|
|
1272
1422
|
if (!resolved.ok) {
|
|
1273
|
-
setError(
|
|
1423
|
+
setError(resolved.error);
|
|
1274
1424
|
return;
|
|
1275
1425
|
}
|
|
1276
1426
|
if (!people.some((p) => p.id === resolved.data)) {
|
|
@@ -1296,8 +1446,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1296
1446
|
const pickedUserIds = new Set(
|
|
1297
1447
|
picked.map((recordId) => people.find((p) => p.id === recordId)?.userId).filter(Boolean)
|
|
1298
1448
|
);
|
|
1299
|
-
return /* @__PURE__ */
|
|
1300
|
-
picked.map((recordId) => /* @__PURE__ */
|
|
1449
|
+
return /* @__PURE__ */ jsxs5("div", { className: cn5("flex flex-wrap items-center gap-1", className), children: [
|
|
1450
|
+
picked.map((recordId) => /* @__PURE__ */ jsx6(
|
|
1301
1451
|
RecordChip,
|
|
1302
1452
|
{
|
|
1303
1453
|
title: nameOfRecord(recordId),
|
|
@@ -1305,8 +1455,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1305
1455
|
},
|
|
1306
1456
|
recordId
|
|
1307
1457
|
)),
|
|
1308
|
-
disabled ? null : /* @__PURE__ */
|
|
1309
|
-
/* @__PURE__ */
|
|
1458
|
+
disabled ? null : /* @__PURE__ */ jsxs5(Popover2, { open, onOpenChange: setOpen, children: [
|
|
1459
|
+
/* @__PURE__ */ jsx6(PopoverTrigger2, { asChild: true, children: /* @__PURE__ */ jsx6(
|
|
1310
1460
|
Button4,
|
|
1311
1461
|
{
|
|
1312
1462
|
id,
|
|
@@ -1317,8 +1467,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1317
1467
|
children: picked.length === 0 ? "Pick a person" : "Change"
|
|
1318
1468
|
}
|
|
1319
1469
|
) }),
|
|
1320
|
-
/* @__PURE__ */
|
|
1321
|
-
/* @__PURE__ */
|
|
1470
|
+
/* @__PURE__ */ jsxs5(PopoverContent2, { sizing: "content", className: "p-1", align: "start", children: [
|
|
1471
|
+
/* @__PURE__ */ jsx6(
|
|
1322
1472
|
BasicInput4,
|
|
1323
1473
|
{
|
|
1324
1474
|
value: search,
|
|
@@ -1327,29 +1477,29 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1327
1477
|
className: "mb-1 h-7 text-xs"
|
|
1328
1478
|
}
|
|
1329
1479
|
),
|
|
1330
|
-
error ? /* @__PURE__ */
|
|
1331
|
-
members === null ? /* @__PURE__ */
|
|
1332
|
-
/* @__PURE__ */
|
|
1333
|
-
/* @__PURE__ */
|
|
1480
|
+
error ? /* @__PURE__ */ jsx6(RefusalLine, { error, className: "p-2" }) : null,
|
|
1481
|
+
members === null ? /* @__PURE__ */ jsx6("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading the members\u2026" }) : null,
|
|
1482
|
+
/* @__PURE__ */ jsxs5(ScrollArea2, { className: "max-h-56", children: [
|
|
1483
|
+
/* @__PURE__ */ jsx6("ul", { children: visible.map((member) => /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsxs5(
|
|
1334
1484
|
"button",
|
|
1335
1485
|
{
|
|
1336
1486
|
type: "button",
|
|
1337
1487
|
disabled: busy,
|
|
1338
1488
|
onClick: () => void pick(member),
|
|
1339
|
-
className:
|
|
1489
|
+
className: cn5(
|
|
1340
1490
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1341
1491
|
pickedUserIds.has(member.userId) ? "bg-accent text-accent-foreground" : ""
|
|
1342
1492
|
),
|
|
1343
1493
|
children: [
|
|
1344
|
-
/* @__PURE__ */
|
|
1345
|
-
member.email && member.email !== memberName(member) ? /* @__PURE__ */
|
|
1494
|
+
/* @__PURE__ */ jsx6("span", { children: memberName(member) }),
|
|
1495
|
+
member.email && member.email !== memberName(member) ? /* @__PURE__ */ jsxs5("span", { className: "ml-1.5 opacity-60", children: [
|
|
1346
1496
|
"\xB7 ",
|
|
1347
1497
|
member.email
|
|
1348
1498
|
] }) : null
|
|
1349
1499
|
]
|
|
1350
1500
|
}
|
|
1351
1501
|
) }, member.userId)) }),
|
|
1352
|
-
members !== null && visible.length === 0 ? /* @__PURE__ */
|
|
1502
|
+
members !== null && visible.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "p-2 text-xs text-muted-foreground", children: members.length === 0 ? "This organization has no other members yet, so there is nobody to assign." : "Nobody in this organization matches." }) : null
|
|
1353
1503
|
] })
|
|
1354
1504
|
] })
|
|
1355
1505
|
] })
|
|
@@ -1362,55 +1512,6 @@ import { asWriteConflict } from "@ai-matrx/records/core";
|
|
|
1362
1512
|
import { useRecordsClient as useRecordsClient3 } from "@ai-matrx/records/react";
|
|
1363
1513
|
import { Button as Button5, Checkbox as Checkbox3, cn as cn7 } from "@ai-matrx/design-system";
|
|
1364
1514
|
|
|
1365
|
-
// src/Refusal.tsx
|
|
1366
|
-
import { Alert, AlertDescription, AlertTitle, cn as cn5 } from "@ai-matrx/design-system";
|
|
1367
|
-
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1368
|
-
function hintIsMachineIdentity(hint) {
|
|
1369
|
-
return isMachineIdentity(hint);
|
|
1370
|
-
}
|
|
1371
|
-
function hintForAPerson(hint) {
|
|
1372
|
-
const said = (hint ?? "").trim();
|
|
1373
|
-
if (said === "") return null;
|
|
1374
|
-
return isMachineIdentity(said) ? null : said;
|
|
1375
|
-
}
|
|
1376
|
-
function RefusalNotice({
|
|
1377
|
-
error,
|
|
1378
|
-
className,
|
|
1379
|
-
actions
|
|
1380
|
-
}) {
|
|
1381
|
-
const plain = refusalForAPerson(error);
|
|
1382
|
-
return /* @__PURE__ */ jsxs5(
|
|
1383
|
-
Alert,
|
|
1384
|
-
{
|
|
1385
|
-
variant: "destructive",
|
|
1386
|
-
className: cn5("text-xs", className),
|
|
1387
|
-
...plain.forEngineers ? { title: plain.forEngineers } : {},
|
|
1388
|
-
children: [
|
|
1389
|
-
/* @__PURE__ */ jsx6(AlertTitle, { className: "text-xs font-medium", children: plain.title }),
|
|
1390
|
-
/* @__PURE__ */ jsxs5(AlertDescription, { className: "space-y-1 text-xs", children: [
|
|
1391
|
-
plain.sentence.replace(/\.$/, "") === plain.title ? null : /* @__PURE__ */ jsx6("p", { children: plain.sentence }),
|
|
1392
|
-
/* @__PURE__ */ jsx6("p", { className: "opacity-80", children: plain.remedy }),
|
|
1393
|
-
plain.forEngineers ? /* @__PURE__ */ jsx6(
|
|
1394
|
-
"p",
|
|
1395
|
-
{
|
|
1396
|
-
className: "sr-only",
|
|
1397
|
-
"aria-hidden": "true",
|
|
1398
|
-
"data-for-engineers": "",
|
|
1399
|
-
...error.sqlstate ? { "data-sqlstate": error.sqlstate } : {},
|
|
1400
|
-
children: plain.forEngineers
|
|
1401
|
-
}
|
|
1402
|
-
) : null,
|
|
1403
|
-
actions
|
|
1404
|
-
] })
|
|
1405
|
-
]
|
|
1406
|
-
}
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
function RefusalLine({ error, className }) {
|
|
1410
|
-
const plain = refusalForAPerson(error);
|
|
1411
|
-
return /* @__PURE__ */ jsx6("p", { className: cn5("text-xs text-destructive", className), title: plain.forEngineers || void 0, children: refusalLineForAPerson(error) });
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
1515
|
// src/whoChangedSource.tsx
|
|
1415
1516
|
import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect4, useMemo as useMemo5, useRef, useState as useState5 } from "react";
|
|
1416
1517
|
import { useRecordsClient } from "@ai-matrx/records/react";
|
|
@@ -2249,7 +2350,7 @@ function GridCell({
|
|
|
2249
2350
|
);
|
|
2250
2351
|
const value = editing ? editing.valueFor(row, field) : row.document?.[field.key];
|
|
2251
2352
|
const state = editing ? editing.stateOf(row.id, field.key) : null;
|
|
2252
|
-
const
|
|
2353
|
+
const refusal2 = editing ? editing.refusalOf(row.id, field.key) : null;
|
|
2253
2354
|
const editable = Boolean(editing) && canWrite && fieldIsEditable(field);
|
|
2254
2355
|
if (open && editing) {
|
|
2255
2356
|
return /* @__PURE__ */ jsx10(
|
|
@@ -2278,8 +2379,8 @@ function GridCell({
|
|
|
2278
2379
|
}
|
|
2279
2380
|
),
|
|
2280
2381
|
answered ? null : /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", title: "nobody has answered this yet", children: "\u2014" }),
|
|
2281
|
-
|
|
2282
|
-
/* @__PURE__ */ jsx10(RefusalLine, { error:
|
|
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 }),
|
|
2283
2384
|
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" })
|
|
2284
2385
|
] }) : null
|
|
2285
2386
|
] });
|
|
@@ -2354,11 +2455,11 @@ function GridCell({
|
|
|
2354
2455
|
),
|
|
2355
2456
|
onAskWhoChanged ? /* @__PURE__ */ jsx10(WhoBadge, { field, row, onAskWhoChanged }) : null,
|
|
2356
2457
|
state === "saving" ? /* @__PURE__ */ jsx10("span", { className: "px-1 text-[10px] text-muted-foreground", children: "Saving\u2026" }) : null,
|
|
2357
|
-
|
|
2358
|
-
/* @__PURE__ */ jsx10(RefusalLine, { error:
|
|
2359
|
-
|
|
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: [
|
|
2360
2461
|
"Theirs: ",
|
|
2361
|
-
/* @__PURE__ */ jsx10("strong", { children: formatTheirs(
|
|
2462
|
+
/* @__PURE__ */ jsx10("strong", { children: formatTheirs(refusal2.conflict, field.key) })
|
|
2362
2463
|
] }) : null,
|
|
2363
2464
|
/* @__PURE__ */ jsxs6("span", { className: "flex gap-1", children: [
|
|
2364
2465
|
/* @__PURE__ */ jsx10(Button5, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" }),
|
|
@@ -3081,7 +3182,20 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3081
3182
|
" in one go."
|
|
3082
3183
|
] }) : null
|
|
3083
3184
|
] }) : null,
|
|
3084
|
-
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,
|
|
3085
3199
|
field ? null : null
|
|
3086
3200
|
] }, row.field_id);
|
|
3087
3201
|
}) });
|
|
@@ -3126,15 +3240,15 @@ function PastePreview({
|
|
|
3126
3240
|
/* @__PURE__ */ jsx12(PlanTable, { plan }),
|
|
3127
3241
|
plan.refusals.length > 0 ? /* @__PURE__ */ jsxs8("div", { "data-matrx-paste-refusals": true, className: "flex flex-col gap-1", children: [
|
|
3128
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:` }),
|
|
3129
|
-
/* @__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: [
|
|
3130
3244
|
/* @__PURE__ */ jsxs8("span", { className: "font-medium", children: [
|
|
3131
3245
|
"Row ",
|
|
3132
|
-
|
|
3246
|
+
refusal2.fromLine,
|
|
3133
3247
|
", ",
|
|
3134
|
-
|
|
3248
|
+
refusal2.column
|
|
3135
3249
|
] }),
|
|
3136
3250
|
" \u2014 ",
|
|
3137
|
-
|
|
3251
|
+
refusal2.why
|
|
3138
3252
|
] }, index)) }),
|
|
3139
3253
|
plan.refusals.length > 12 ? /* @__PURE__ */ jsxs8("p", { className: "text-xs text-muted-foreground", children: [
|
|
3140
3254
|
plan.refusals.length - 12,
|
|
@@ -3162,7 +3276,13 @@ function PastePreview({
|
|
|
3162
3276
|
setAdding(group.fieldKey);
|
|
3163
3277
|
void onAddOptions(group.fieldKey, group.words).catch(
|
|
3164
3278
|
(err) => setAddFailed(
|
|
3165
|
-
|
|
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
|
+
)
|
|
3166
3286
|
)
|
|
3167
3287
|
).finally(() => setAdding(null));
|
|
3168
3288
|
},
|
|
@@ -3170,7 +3290,7 @@ function PastePreview({
|
|
|
3170
3290
|
}
|
|
3171
3291
|
)
|
|
3172
3292
|
] }, group.fieldKey)),
|
|
3173
|
-
addFailed ? /* @__PURE__ */ jsx12(
|
|
3293
|
+
addFailed ? /* @__PURE__ */ jsx12(RefusalLine, { error: addFailed }) : null
|
|
3174
3294
|
] }) : null,
|
|
3175
3295
|
plan.pastRightEdge > 0 ? /* @__PURE__ */ jsxs8("p", { className: "text-xs text-muted-foreground", children: [
|
|
3176
3296
|
plan.pastRightEdge,
|
|
@@ -3350,19 +3470,19 @@ function planPastedBlock(args) {
|
|
|
3350
3470
|
accepted[key] = judged.value;
|
|
3351
3471
|
}
|
|
3352
3472
|
const predicted = predictValueRefusals(args.fields, accepted);
|
|
3353
|
-
for (const
|
|
3354
|
-
const key =
|
|
3473
|
+
for (const refusal2 of predicted) {
|
|
3474
|
+
const key = refusal2.field_key;
|
|
3355
3475
|
if (key === void 0) continue;
|
|
3356
3476
|
const cell = built.find((b) => b.key === key && b.refusal === null);
|
|
3357
3477
|
if (!cell) continue;
|
|
3358
|
-
cell.refusal =
|
|
3478
|
+
cell.refusal = refusal2.message;
|
|
3359
3479
|
cell.value = null;
|
|
3360
3480
|
delete accepted[key];
|
|
3361
3481
|
refusals.push({
|
|
3362
3482
|
fromLine,
|
|
3363
3483
|
column: fieldName(byKey.get(key) ?? { key }),
|
|
3364
3484
|
raw: cell.raw,
|
|
3365
|
-
why:
|
|
3485
|
+
why: refusal2.message
|
|
3366
3486
|
});
|
|
3367
3487
|
}
|
|
3368
3488
|
cells += Object.keys(accepted).length;
|
|
@@ -4126,18 +4246,24 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4126
4246
|
size: "sm",
|
|
4127
4247
|
variant: "outline",
|
|
4128
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);
|
|
4129
4257
|
try {
|
|
4130
|
-
|
|
4258
|
+
exportXlsx(fields.data ?? [], exported(), name).then(
|
|
4131
4259
|
(bytes) => download(
|
|
4132
4260
|
`${name}.xlsx`,
|
|
4133
4261
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
4134
4262
|
bytes
|
|
4135
4263
|
)
|
|
4136
|
-
);
|
|
4264
|
+
).catch(failed);
|
|
4137
4265
|
} catch (thrown) {
|
|
4138
|
-
|
|
4139
|
-
`The spreadsheet could not be written (${thrown instanceof Error ? thrown.message : String(thrown)}). The CSV beside this button carries the same rows.`
|
|
4140
|
-
);
|
|
4266
|
+
failed(thrown);
|
|
4141
4267
|
}
|
|
4142
4268
|
},
|
|
4143
4269
|
children: "XLSX"
|
|
@@ -4153,7 +4279,7 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4153
4279
|
human: () => exportCsv(fields.data ?? [], exported())
|
|
4154
4280
|
}
|
|
4155
4281
|
),
|
|
4156
|
-
failure ? /* @__PURE__ */ jsx14(
|
|
4282
|
+
failure ? /* @__PURE__ */ jsx14(RefusalLine, { error: failure }) : null
|
|
4157
4283
|
] });
|
|
4158
4284
|
}
|
|
4159
4285
|
|
|
@@ -4269,6 +4395,45 @@ import {
|
|
|
4269
4395
|
Separator as Separator2,
|
|
4270
4396
|
cn as cn12
|
|
4271
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
|
|
4272
4437
|
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4273
4438
|
var BATCH = 250;
|
|
4274
4439
|
var SAMPLES = 40;
|
|
@@ -4321,9 +4486,13 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4321
4486
|
const [openRun, setOpenRun] = useState13(null);
|
|
4322
4487
|
const [runRows, setRunRows] = useState13(null);
|
|
4323
4488
|
const [runProblem, setRunProblem] = useState13(null);
|
|
4489
|
+
const [runsProblem, setRunsProblem] = useState13(null);
|
|
4324
4490
|
const loadRuns = useCallback8(async () => {
|
|
4325
4491
|
const answered = await client.imports({ table_id: tableId, limit: 10 });
|
|
4326
|
-
if (answered.ok)
|
|
4492
|
+
if (answered.ok) {
|
|
4493
|
+
setRuns(answered.data);
|
|
4494
|
+
setRunsProblem(null);
|
|
4495
|
+
} else setRunsProblem(answered.error);
|
|
4327
4496
|
}, [client, tableId]);
|
|
4328
4497
|
useEffect8(() => {
|
|
4329
4498
|
void loadRuns();
|
|
@@ -4344,7 +4513,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4344
4513
|
limit: 200
|
|
4345
4514
|
});
|
|
4346
4515
|
if (!answered.ok) {
|
|
4347
|
-
setRunProblem(answered.error
|
|
4516
|
+
setRunProblem(answered.error);
|
|
4348
4517
|
return;
|
|
4349
4518
|
}
|
|
4350
4519
|
setRunRows({
|
|
@@ -4385,7 +4554,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4385
4554
|
}));
|
|
4386
4555
|
const answered = await client.importPlan({ table_id: tableId, columns });
|
|
4387
4556
|
if (!answered.ok) {
|
|
4388
|
-
setProblem(answered.error
|
|
4557
|
+
setProblem(answered.error);
|
|
4389
4558
|
setPhase("waiting");
|
|
4390
4559
|
return;
|
|
4391
4560
|
}
|
|
@@ -4402,7 +4571,14 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4402
4571
|
setMapping(next);
|
|
4403
4572
|
setPhase("ready");
|
|
4404
4573
|
} catch (thrown) {
|
|
4405
|
-
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
|
+
);
|
|
4406
4582
|
setPhase("waiting");
|
|
4407
4583
|
}
|
|
4408
4584
|
},
|
|
@@ -4419,7 +4595,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4419
4595
|
setPhase("declaring");
|
|
4420
4596
|
const made = await client.importDeclareColumns({ table_id: tableId, rows, mapping });
|
|
4421
4597
|
if (!made.ok) {
|
|
4422
|
-
setProblem(made.error
|
|
4598
|
+
setProblem(made.error);
|
|
4423
4599
|
setPhase("ready");
|
|
4424
4600
|
return;
|
|
4425
4601
|
}
|
|
@@ -4438,7 +4614,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4438
4614
|
policy: { on_duplicate: onDuplicate, unmapped }
|
|
4439
4615
|
});
|
|
4440
4616
|
if (!opened.ok) {
|
|
4441
|
-
setProblem(opened.error
|
|
4617
|
+
setProblem(opened.error);
|
|
4442
4618
|
setPhase("ready");
|
|
4443
4619
|
return;
|
|
4444
4620
|
}
|
|
@@ -4457,7 +4633,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4457
4633
|
const batch = rows.slice(at, at + take2);
|
|
4458
4634
|
const answered = await client.importRows({ import_id: importId, rows: batch, mapping });
|
|
4459
4635
|
if (!answered.ok) {
|
|
4460
|
-
setProblem(answered.error
|
|
4636
|
+
setProblem(answered.error);
|
|
4461
4637
|
setOutcomes([...collected]);
|
|
4462
4638
|
setLedger({ ...told, final: false });
|
|
4463
4639
|
setPhase("ready");
|
|
@@ -4476,7 +4652,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4476
4652
|
}
|
|
4477
4653
|
const finished = await client.importFinish({ import_id: importId, unmapped });
|
|
4478
4654
|
if (!finished.ok) {
|
|
4479
|
-
setProblem(finished.error
|
|
4655
|
+
setProblem(finished.error);
|
|
4480
4656
|
setPhase("done");
|
|
4481
4657
|
return;
|
|
4482
4658
|
}
|
|
@@ -4514,6 +4690,11 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4514
4690
|
const titleMissing = Boolean(titleKey) && titleMappedTo === null;
|
|
4515
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";
|
|
4516
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]);
|
|
4517
4698
|
return /* @__PURE__ */ jsxs12("div", { className: cn12("flex min-w-0 flex-col gap-2 text-xs", className), children: [
|
|
4518
4699
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
4519
4700
|
/* @__PURE__ */ jsx16("span", { className: "font-medium", children: "Import" }),
|
|
@@ -4541,7 +4722,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4541
4722
|
} : {}
|
|
4542
4723
|
}
|
|
4543
4724
|
) : null,
|
|
4544
|
-
problem ? /* @__PURE__ */ jsx16(
|
|
4725
|
+
problem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: problem }) : null,
|
|
4545
4726
|
phase === "reading" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
|
|
4546
4727
|
phase === "planning" ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
|
|
4547
4728
|
plan && parsed ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
@@ -4638,7 +4819,28 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4638
4819
|
column.unit ? /* @__PURE__ */ jsx16("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
|
|
4639
4820
|
/* @__PURE__ */ jsx16("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
|
|
4640
4821
|
column.ambiguous ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
|
|
4641
|
-
column.collides_with ? /* @__PURE__ */ jsx16("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null
|
|
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
|
|
4642
4844
|
] }),
|
|
4643
4845
|
/* @__PURE__ */ jsx16("span", { className: "truncate pt-1.5 text-muted-foreground", children: (column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ") })
|
|
4644
4846
|
]
|
|
@@ -4702,12 +4904,34 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4702
4904
|
)
|
|
4703
4905
|
] })
|
|
4704
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,
|
|
4705
4929
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
4706
4930
|
/* @__PURE__ */ jsx16(
|
|
4707
4931
|
Button11,
|
|
4708
4932
|
{
|
|
4709
4933
|
size: "sm",
|
|
4710
|
-
disabled: busy || !rights.write || rows.length === 0 || titleMissing,
|
|
4934
|
+
disabled: busy || !rights.write || rows.length === 0 || titleMissing || ruleVerdicts.length > 0 && !goAheadAnyway,
|
|
4711
4935
|
onClick: () => void run(),
|
|
4712
4936
|
children: phase === "declaring" ? "Adding the columns\u2026" : phase === "writing" ? `Writing\u2026 ${progress} of ${rows.length}` : `Import ${rows.length} row${rows.length === 1 ? "" : "s"}`
|
|
4713
4937
|
}
|
|
@@ -4762,7 +4986,12 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4762
4986
|
] }) }),
|
|
4763
4987
|
/* @__PURE__ */ jsx16("tbody", { children: interesting.slice(0, 200).map((o) => /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
|
|
4764
4988
|
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: o.row }),
|
|
4765
|
-
/* @__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" }),
|
|
4766
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 ") : "" })
|
|
4767
4996
|
] }, `${o.outcome}-${o.row}`)) })
|
|
4768
4997
|
] }) }) : null,
|
|
@@ -4785,6 +5014,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4785
5014
|
] }) : null
|
|
4786
5015
|
] }) : null
|
|
4787
5016
|
] }) : null,
|
|
5017
|
+
runsProblem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: runsProblem }) : null,
|
|
4788
5018
|
runs && runs.length > 0 ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
4789
5019
|
/* @__PURE__ */ jsx16(Separator2, {}),
|
|
4790
5020
|
/* @__PURE__ */ jsx16("p", { className: "font-medium", children: "Imports into this table" }),
|
|
@@ -4817,7 +5047,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4817
5047
|
}
|
|
4818
5048
|
) : null
|
|
4819
5049
|
] }),
|
|
4820
|
-
openRun === r.import_id ? runProblem ? /* @__PURE__ */ jsx16(
|
|
5050
|
+
openRun === r.import_id ? runProblem ? /* @__PURE__ */ jsx16(RefusalNotice, { error: runProblem }) : runRows ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
4821
5051
|
runRows.note ? /* @__PURE__ */ jsx16("p", { className: "text-muted-foreground", children: runRows.note }) : null,
|
|
4822
5052
|
/* @__PURE__ */ jsx16("div", { className: "max-h-64 overflow-auto rounded border", children: /* @__PURE__ */ jsxs12("table", { className: "w-full", children: [
|
|
4823
5053
|
/* @__PURE__ */ jsx16("thead", { className: "sticky top-0 bg-muted", children: /* @__PURE__ */ jsxs12("tr", { children: [
|
|
@@ -4829,7 +5059,16 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4829
5059
|
const source = row.source ?? {};
|
|
4830
5060
|
return /* @__PURE__ */ jsxs12("tr", { className: "border-t align-top", children: [
|
|
4831
5061
|
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1", children: String(row.row ?? "") }),
|
|
4832
|
-
/* @__PURE__ */ jsx16("td", { className: "px-2 py-1
|
|
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
|
+
) }),
|
|
4833
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 ") })
|
|
4834
5073
|
] }, `${r.import_id}-${String(row.row ?? i)}`);
|
|
4835
5074
|
}) })
|
|
@@ -5067,7 +5306,9 @@ function keyFor(label) {
|
|
|
5067
5306
|
if (token === "") return "";
|
|
5068
5307
|
return /^[a-z]/.test(token) ? token : `f_${token}`;
|
|
5069
5308
|
}
|
|
5070
|
-
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}`;
|
|
5071
5312
|
function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }) {
|
|
5072
5313
|
const table = useTable4(tableId);
|
|
5073
5314
|
const fields = useFields6(tableId);
|
|
@@ -5367,14 +5608,20 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5367
5608
|
] }, `${rule.kind}-${index}`))
|
|
5368
5609
|
] }),
|
|
5369
5610
|
mutation.error ? /* @__PURE__ */ jsx18(RefusalNotice, { error: mutation.error }) : null,
|
|
5370
|
-
missing ? /* @__PURE__ */ jsx18("p", { className: "text-[11px] text-
|
|
5371
|
-
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,
|
|
5372
5618
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5373
5619
|
/* @__PURE__ */ jsx18(
|
|
5374
5620
|
Button13,
|
|
5375
5621
|
{
|
|
5376
5622
|
size: "sm",
|
|
5377
5623
|
disabled: mutation.saving || missing !== null,
|
|
5624
|
+
...missing ? { "aria-describedby": "field-editor-missing" } : {},
|
|
5378
5625
|
onClick: () => void save(),
|
|
5379
5626
|
children: mutation.saving ? "Saving\u2026" : field ? EDIT_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
|
|
5380
5627
|
}
|
|
@@ -6849,7 +7096,13 @@ function RecordForm({
|
|
|
6849
7096
|
},
|
|
6850
7097
|
field.id
|
|
6851
7098
|
)) }),
|
|
6852
|
-
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,
|
|
6853
7106
|
mutation.error ? /* @__PURE__ */ jsx23(
|
|
6854
7107
|
RefusalNotice,
|
|
6855
7108
|
{
|
|
@@ -6904,7 +7157,14 @@ function FieldRow({
|
|
|
6904
7157
|
return /* @__PURE__ */ jsxs19("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
6905
7158
|
/* @__PURE__ */ jsx23(FieldLabel, { field, htmlFor: id, children: /* @__PURE__ */ jsx23(ProvenanceBadge, { document: document2, fieldKey: field.key }) }),
|
|
6906
7159
|
/* @__PURE__ */ jsx23(FieldControl, { field, value, onChange, id }),
|
|
6907
|
-
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
|
+
))
|
|
6908
7168
|
] });
|
|
6909
7169
|
}
|
|
6910
7170
|
function asWords(value) {
|
|
@@ -6915,7 +7175,7 @@ function asWords(value) {
|
|
|
6915
7175
|
}
|
|
6916
7176
|
|
|
6917
7177
|
// src/ShareControl.tsx
|
|
6918
|
-
import { useState as useState22 } from "react";
|
|
7178
|
+
import { useEffect as useEffect13, useState as useState22 } from "react";
|
|
6919
7179
|
import { Button as Button18 } from "@ai-matrx/design-system";
|
|
6920
7180
|
import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
6921
7181
|
function useCanShare() {
|
|
@@ -6930,12 +7190,16 @@ function ShareControl({
|
|
|
6930
7190
|
subjectId,
|
|
6931
7191
|
name,
|
|
6932
7192
|
may,
|
|
7193
|
+
initiallyOpen = false,
|
|
6933
7194
|
size = "sm",
|
|
6934
7195
|
variant = "ghost",
|
|
6935
7196
|
className
|
|
6936
7197
|
}) {
|
|
6937
7198
|
const host = useRecordsUi();
|
|
6938
|
-
const [open, setOpen] = useState22(
|
|
7199
|
+
const [open, setOpen] = useState22(initiallyOpen);
|
|
7200
|
+
useEffect13(() => {
|
|
7201
|
+
if (initiallyOpen) setOpen(true);
|
|
7202
|
+
}, [initiallyOpen]);
|
|
6939
7203
|
const asked = useRecordRights(may === void 0 ? subjectId : null);
|
|
6940
7204
|
const mayShare = may ?? asked.share;
|
|
6941
7205
|
if (!host.share) return null;
|
|
@@ -7217,12 +7481,12 @@ function parseSorts(raw) {
|
|
|
7217
7481
|
}
|
|
7218
7482
|
|
|
7219
7483
|
// src/ViewSwitcher.tsx
|
|
7220
|
-
import { useEffect as
|
|
7484
|
+
import { useEffect as useEffect15, useMemo as useMemo18, useState as useState25 } from "react";
|
|
7221
7485
|
import { useFields as useFields12, useRecords as useRecords5, useRecordsClient as useRecordsClient13 } from "@ai-matrx/records/react";
|
|
7222
7486
|
import { Button as Button21, Skeleton as Skeleton7, cn as cn21 } from "@ai-matrx/design-system";
|
|
7223
7487
|
|
|
7224
7488
|
// src/Pipeline.tsx
|
|
7225
|
-
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";
|
|
7226
7490
|
import {
|
|
7227
7491
|
mayDrag,
|
|
7228
7492
|
useFields as useFields11,
|
|
@@ -7271,7 +7535,7 @@ function PipelineBoard({
|
|
|
7271
7535
|
const [dragging, setDragging] = useState24(null);
|
|
7272
7536
|
const [over, setOver] = useState24(null);
|
|
7273
7537
|
const alive = useRef6(true);
|
|
7274
|
-
|
|
7538
|
+
useEffect14(() => {
|
|
7275
7539
|
alive.current = true;
|
|
7276
7540
|
return () => {
|
|
7277
7541
|
alive.current = false;
|
|
@@ -7295,7 +7559,7 @@ function PipelineBoard({
|
|
|
7295
7559
|
setWaiting(held.ok ? new Map((held.data ?? []).map((p) => [p.record_id, p])) : /* @__PURE__ */ new Map());
|
|
7296
7560
|
setColumns(board.ok ? board.data ?? [] : []);
|
|
7297
7561
|
}, [client, tableId, measure]);
|
|
7298
|
-
|
|
7562
|
+
useEffect14(() => {
|
|
7299
7563
|
void reload();
|
|
7300
7564
|
}, [reload]);
|
|
7301
7565
|
const stageKey = definition?.stage_field ?? null;
|
|
@@ -7517,10 +7781,22 @@ function Held({
|
|
|
7517
7781
|
);
|
|
7518
7782
|
}
|
|
7519
7783
|
if (pending.kind === "refused") {
|
|
7520
|
-
return
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
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
|
+
);
|
|
7524
7800
|
}
|
|
7525
7801
|
if (pending.kind === "approval") {
|
|
7526
7802
|
return /* @__PURE__ */ jsxs22("div", { className: "mt-1 flex flex-col gap-1 rounded border p-2 text-xs", children: [
|
|
@@ -7653,7 +7929,7 @@ function useViewRecords(view, pageSize = 200, filter) {
|
|
|
7653
7929
|
error: null
|
|
7654
7930
|
});
|
|
7655
7931
|
const ruleId = view.ruleId ?? null;
|
|
7656
|
-
|
|
7932
|
+
useEffect15(() => {
|
|
7657
7933
|
if (!ruleId) return;
|
|
7658
7934
|
let cancelled = false;
|
|
7659
7935
|
setRuled({ rows: [], loading: true, error: null });
|
|
@@ -7717,7 +7993,7 @@ function ViewSwitcher({
|
|
|
7717
7993
|
}) {
|
|
7718
7994
|
const [layout, setLayout] = useState25(view.layout);
|
|
7719
7995
|
const [local, setLocal] = useState25({});
|
|
7720
|
-
|
|
7996
|
+
useEffect15(() => {
|
|
7721
7997
|
setLayout(view.layout);
|
|
7722
7998
|
setLocal({});
|
|
7723
7999
|
}, [view.layout, view.name, view.subject]);
|
|
@@ -7785,7 +8061,7 @@ function useStageField(tableId) {
|
|
|
7785
8061
|
asked: false,
|
|
7786
8062
|
key: null
|
|
7787
8063
|
});
|
|
7788
|
-
|
|
8064
|
+
useEffect15(() => {
|
|
7789
8065
|
let cancelled = false;
|
|
7790
8066
|
setStage({ asked: false, key: null });
|
|
7791
8067
|
void client.tableStageField({ table_id: tableId }).then((r) => {
|
|
@@ -8054,7 +8330,7 @@ function Card2({
|
|
|
8054
8330
|
}
|
|
8055
8331
|
|
|
8056
8332
|
// src/ArchivedView.tsx
|
|
8057
|
-
import { useCallback as useCallback11, useEffect as
|
|
8333
|
+
import { useCallback as useCallback11, useEffect as useEffect16, useState as useState26 } from "react";
|
|
8058
8334
|
import {
|
|
8059
8335
|
ARCHIVE_LANES,
|
|
8060
8336
|
ARCHIVE_LANE_LABEL,
|
|
@@ -8091,8 +8367,8 @@ function ArchivedView({
|
|
|
8091
8367
|
const [loading, setLoading] = useState26(true);
|
|
8092
8368
|
const [error, setError] = useState26(null);
|
|
8093
8369
|
const [restoring, setRestoring] = useState26(null);
|
|
8094
|
-
const [
|
|
8095
|
-
|
|
8370
|
+
const [refusal2, setRefusal] = useState26(null);
|
|
8371
|
+
useEffect16(() => {
|
|
8096
8372
|
if (laneFromHost) setLane(laneFromHost);
|
|
8097
8373
|
}, [laneFromHost]);
|
|
8098
8374
|
const read = useCallback11(async () => {
|
|
@@ -8107,7 +8383,7 @@ function ArchivedView({
|
|
|
8107
8383
|
}
|
|
8108
8384
|
setLoading(false);
|
|
8109
8385
|
}, [client, tableId, lane, pageSize]);
|
|
8110
|
-
|
|
8386
|
+
useEffect16(() => {
|
|
8111
8387
|
void read();
|
|
8112
8388
|
}, [read]);
|
|
8113
8389
|
const pick = (next) => {
|
|
@@ -8168,7 +8444,7 @@ function ArchivedView({
|
|
|
8168
8444
|
/* @__PURE__ */ jsxs24("div", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
8169
8445
|
/* @__PURE__ */ jsx28("span", { className: "truncate text-sm", children: recordName(row.document, titleKey) }),
|
|
8170
8446
|
/* @__PURE__ */ jsx28("span", { className: "truncate text-xs text-muted-foreground", "data-testid": "archived-who", children: archivedByLine(row) }),
|
|
8171
|
-
|
|
8447
|
+
refusal2 && refusal2.recordId === row.id ? /* @__PURE__ */ jsx28(RefusalNotice, { error: refusal2.error, className: "mt-2" }) : null
|
|
8172
8448
|
] }),
|
|
8173
8449
|
mayRestore ? /* @__PURE__ */ jsx28(
|
|
8174
8450
|
Button22,
|
|
@@ -8189,7 +8465,7 @@ function ArchivedView({
|
|
|
8189
8465
|
}
|
|
8190
8466
|
|
|
8191
8467
|
// src/ArchivedDisclosure.tsx
|
|
8192
|
-
import { useCallback as useCallback12, useEffect as
|
|
8468
|
+
import { useCallback as useCallback12, useEffect as useEffect17, useState as useState27 } from "react";
|
|
8193
8469
|
import {
|
|
8194
8470
|
emptyPortalArchiveLine,
|
|
8195
8471
|
portalConfirmLine,
|
|
@@ -8246,7 +8522,7 @@ function ArchivedPortals({
|
|
|
8246
8522
|
const [confirming, setConfirming] = useState27(null);
|
|
8247
8523
|
const [typed, setTyped] = useState27("");
|
|
8248
8524
|
const [restoring, setRestoring] = useState27(null);
|
|
8249
|
-
const [
|
|
8525
|
+
const [refusal2, setRefusal] = useState27(null);
|
|
8250
8526
|
const [said, setSaid] = useState27(null);
|
|
8251
8527
|
const read = useCallback12(async () => {
|
|
8252
8528
|
const answered = await client.listPortals({ archived: "archived" });
|
|
@@ -8258,7 +8534,7 @@ function ArchivedPortals({
|
|
|
8258
8534
|
setError(answered.error);
|
|
8259
8535
|
}
|
|
8260
8536
|
}, [client]);
|
|
8261
|
-
|
|
8537
|
+
useEffect17(() => {
|
|
8262
8538
|
void read();
|
|
8263
8539
|
}, [read, refreshToken]);
|
|
8264
8540
|
const restore = async (portal) => {
|
|
@@ -8362,7 +8638,7 @@ function ArchivedPortals({
|
|
|
8362
8638
|
)
|
|
8363
8639
|
] })
|
|
8364
8640
|
] }) : null,
|
|
8365
|
-
|
|
8641
|
+
refusal2 && refusal2.portalId === portal.portal_id ? /* @__PURE__ */ jsx29(RefusalNotice, { error: refusal2.error }) : null
|
|
8366
8642
|
]
|
|
8367
8643
|
},
|
|
8368
8644
|
portal.portal_id
|
|
@@ -8374,7 +8650,7 @@ function ArchivedPortals({
|
|
|
8374
8650
|
}
|
|
8375
8651
|
|
|
8376
8652
|
// src/ViewBar.tsx
|
|
8377
|
-
import { useCallback as useCallback14, useEffect as
|
|
8653
|
+
import { useCallback as useCallback14, useEffect as useEffect18, useState as useState28 } from "react";
|
|
8378
8654
|
import { useRecordsClient as useRecordsClient16 } from "@ai-matrx/records/react";
|
|
8379
8655
|
import { Button as Button24, Input as Input2, Skeleton as Skeleton10, cn as cn24 } from "@ai-matrx/design-system";
|
|
8380
8656
|
|
|
@@ -8437,10 +8713,10 @@ function ViewBar({ tableId, activeViewId, onActiveView, seed, className }) {
|
|
|
8437
8713
|
setError(null);
|
|
8438
8714
|
setViews(mine);
|
|
8439
8715
|
}, [client, viewTableId, tableId, seed]);
|
|
8440
|
-
|
|
8716
|
+
useEffect18(() => {
|
|
8441
8717
|
void load();
|
|
8442
8718
|
}, [load]);
|
|
8443
|
-
|
|
8719
|
+
useEffect18(() => {
|
|
8444
8720
|
if (!views || views.length === 0) return;
|
|
8445
8721
|
const chosen = views.find((v) => v.id === (activeViewId ?? active)) ?? views.find((v) => v.isDefault) ?? views[0];
|
|
8446
8722
|
if (chosen.id !== active) setActive(chosen.id);
|
|
@@ -8616,12 +8892,12 @@ function ProposalRow({
|
|
|
8616
8892
|
}
|
|
8617
8893
|
|
|
8618
8894
|
// src/ActionInbox.tsx
|
|
8619
|
-
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";
|
|
8620
8896
|
import { useRecordsClient as useRecordsClient19 } from "@ai-matrx/records/react";
|
|
8621
8897
|
import { Badge as Badge9, Button as Button27, Skeleton as Skeleton12, cn as cn27 } from "@ai-matrx/design-system";
|
|
8622
8898
|
|
|
8623
8899
|
// src/ChecklistRunner.tsx
|
|
8624
|
-
import { useCallback as useCallback15, useEffect as
|
|
8900
|
+
import { useCallback as useCallback15, useEffect as useEffect19, useMemo as useMemo19, useState as useState30 } from "react";
|
|
8625
8901
|
import { useRecordsClient as useRecordsClient18, useTable as useTable10 } from "@ai-matrx/records/react";
|
|
8626
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";
|
|
8627
8903
|
import { Fragment as Fragment13, jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
@@ -8669,10 +8945,10 @@ function ChecklistRunner({
|
|
|
8669
8945
|
(held) => held && answered.data.some((r) => r.run_id === held) ? held : answered.data[0]?.run_id ?? null
|
|
8670
8946
|
);
|
|
8671
8947
|
}, [client, includeClosed, recordId, runId, tableId]);
|
|
8672
|
-
|
|
8948
|
+
useEffect19(() => {
|
|
8673
8949
|
void loadRuns();
|
|
8674
8950
|
}, [loadRuns]);
|
|
8675
|
-
|
|
8951
|
+
useEffect19(() => {
|
|
8676
8952
|
if (runId) setActiveId(runId);
|
|
8677
8953
|
}, [runId]);
|
|
8678
8954
|
const loadSteps = useCallback15(async () => {
|
|
@@ -8689,10 +8965,10 @@ function ChecklistRunner({
|
|
|
8689
8965
|
setError(null);
|
|
8690
8966
|
setSteps(answered.data);
|
|
8691
8967
|
}, [client, activeId]);
|
|
8692
|
-
|
|
8968
|
+
useEffect19(() => {
|
|
8693
8969
|
void loadSteps();
|
|
8694
8970
|
}, [loadSteps]);
|
|
8695
|
-
|
|
8971
|
+
useEffect19(() => {
|
|
8696
8972
|
if (!mayStart || !tableId) return;
|
|
8697
8973
|
let cancelled = false;
|
|
8698
8974
|
void client.checklistTemplates({ about_table_id: tableId, limit: 50 }).then((answered) => {
|
|
@@ -8918,7 +9194,7 @@ function useMyChecklistSteps(limit = 25) {
|
|
|
8918
9194
|
setSteps(held);
|
|
8919
9195
|
setLoading(false);
|
|
8920
9196
|
}, [client, limit, me]);
|
|
8921
|
-
|
|
9197
|
+
useEffect19(() => {
|
|
8922
9198
|
void refresh();
|
|
8923
9199
|
}, [refresh]);
|
|
8924
9200
|
return { steps, loading, error, refresh };
|
|
@@ -8927,7 +9203,7 @@ function MyChecklistSteps({ className }) {
|
|
|
8927
9203
|
const client = useRecordsClient18();
|
|
8928
9204
|
const { steps, loading, error, refresh } = useMyChecklistSteps();
|
|
8929
9205
|
const [busy, setBusy] = useState30(null);
|
|
8930
|
-
const [
|
|
9206
|
+
const [refusal2, setRefusal] = useState30(null);
|
|
8931
9207
|
const complete = useCallback15(
|
|
8932
9208
|
async (step2, evidence) => {
|
|
8933
9209
|
setBusy(step2.step_id);
|
|
@@ -8950,7 +9226,7 @@ function MyChecklistSteps({ className }) {
|
|
|
8950
9226
|
/* @__PURE__ */ jsx32("span", { className: "tabular-nums", children: steps.length })
|
|
8951
9227
|
] }),
|
|
8952
9228
|
error ? /* @__PURE__ */ jsx32(RefusalNotice, { error }) : null,
|
|
8953
|
-
|
|
9229
|
+
refusal2 ? /* @__PURE__ */ jsx32(RefusalNotice, { error: refusal2 }) : null,
|
|
8954
9230
|
/* @__PURE__ */ jsx32("ol", { className: "flex flex-col gap-1.5", children: steps.map((step2) => /* @__PURE__ */ jsx32(
|
|
8955
9231
|
StepRow,
|
|
8956
9232
|
{
|
|
@@ -8985,7 +9261,7 @@ function ChecklistsPanel({ tableId, onOpenRecord, className }) {
|
|
|
8985
9261
|
if (!r.ok) setRuns([]);
|
|
8986
9262
|
else setRuns(r.data);
|
|
8987
9263
|
}, [client, tableId]);
|
|
8988
|
-
|
|
9264
|
+
useEffect19(() => {
|
|
8989
9265
|
void load();
|
|
8990
9266
|
}, [load]);
|
|
8991
9267
|
if (templates === null) return /* @__PURE__ */ jsx32(Skeleton11, { className: cn26("h-40 w-full", className) });
|
|
@@ -9127,11 +9403,11 @@ function ChecklistTemplateEditor({
|
|
|
9127
9403
|
const client = useRecordsClient18();
|
|
9128
9404
|
const [name, setName] = useState30("");
|
|
9129
9405
|
const [rows, setRows] = useState30([{ ...EMPTY_ROW }]);
|
|
9130
|
-
const [
|
|
9406
|
+
const [refusal2, setRefusal] = useState30(null);
|
|
9131
9407
|
const [error, setError] = useState30(null);
|
|
9132
9408
|
const [busy, setBusy] = useState30(false);
|
|
9133
9409
|
const [loading, setLoading] = useState30(Boolean(templateId));
|
|
9134
|
-
|
|
9410
|
+
useEffect19(() => {
|
|
9135
9411
|
if (!templateId) return;
|
|
9136
9412
|
let cancelled = false;
|
|
9137
9413
|
void client.checklistTemplateShape({ template_id: templateId }).then((answered) => {
|
|
@@ -9168,7 +9444,7 @@ function ChecklistTemplateEditor({
|
|
|
9168
9444
|
}),
|
|
9169
9445
|
[aboutTableId, name, rows]
|
|
9170
9446
|
);
|
|
9171
|
-
|
|
9447
|
+
useEffect19(() => {
|
|
9172
9448
|
if (spec.steps.length === 0 || spec.name.length === 0) {
|
|
9173
9449
|
setRefusal(null);
|
|
9174
9450
|
return;
|
|
@@ -9295,9 +9571,9 @@ function ChecklistTemplateEditor({
|
|
|
9295
9571
|
] }, index)) }),
|
|
9296
9572
|
/* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-1.5", children: [
|
|
9297
9573
|
/* @__PURE__ */ jsx32(Button26, { size: "sm", variant: "outline", onClick: () => setRows((held) => [...held, { ...EMPTY_ROW }]), children: "Add a step" }),
|
|
9298
|
-
/* @__PURE__ */ jsx32(Button26, { size: "sm", disabled: busy ||
|
|
9574
|
+
/* @__PURE__ */ jsx32(Button26, { size: "sm", disabled: busy || refusal2 !== null || spec.steps.length === 0, onClick: () => void save(), children: busy ? "\u2026" : "Save" })
|
|
9299
9575
|
] }),
|
|
9300
|
-
|
|
9576
|
+
refusal2 ? /* @__PURE__ */ jsx32("p", { className: "text-xs text-muted-foreground", "data-testid": "checklist-editor-refusal", children: refusal2 }) : null
|
|
9301
9577
|
] });
|
|
9302
9578
|
}
|
|
9303
9579
|
function toStepSpec(row, index) {
|
|
@@ -9354,7 +9630,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9354
9630
|
setError(null);
|
|
9355
9631
|
setItems(result.data);
|
|
9356
9632
|
}, [client, includeSettled]);
|
|
9357
|
-
|
|
9633
|
+
useEffect20(() => {
|
|
9358
9634
|
void load();
|
|
9359
9635
|
}, [load]);
|
|
9360
9636
|
const shown = useMemo20(() => {
|
|
@@ -9362,10 +9638,10 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9362
9638
|
if (!tableId) return all;
|
|
9363
9639
|
return all.filter((i) => i.kind !== "assignment" || i.subject_kind !== "record" || true);
|
|
9364
9640
|
}, [items, tableId]);
|
|
9365
|
-
|
|
9641
|
+
useEffect20(() => {
|
|
9366
9642
|
if (cursor >= shown.length) setCursor(Math.max(0, shown.length - 1));
|
|
9367
9643
|
}, [shown.length, cursor]);
|
|
9368
|
-
|
|
9644
|
+
useEffect20(() => {
|
|
9369
9645
|
const el = listRef.current?.querySelector(`[data-row="${cursor}"]`);
|
|
9370
9646
|
el?.scrollIntoView({ block: "nearest" });
|
|
9371
9647
|
}, [cursor, shown.length]);
|
|
@@ -9491,7 +9767,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
|
|
|
9491
9767
|
}
|
|
9492
9768
|
|
|
9493
9769
|
// src/HistoryPanel.tsx
|
|
9494
|
-
import { useCallback as useCallback17, useEffect as
|
|
9770
|
+
import { useCallback as useCallback17, useEffect as useEffect21, useRef as useRef9, useState as useState32 } from "react";
|
|
9495
9771
|
import {
|
|
9496
9772
|
useFields as useFields14,
|
|
9497
9773
|
useRecordsClient as useRecordsClient20,
|
|
@@ -9519,7 +9795,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
|
|
|
9519
9795
|
setError(null);
|
|
9520
9796
|
setEntries(answered.data);
|
|
9521
9797
|
}, [client, recordId]);
|
|
9522
|
-
|
|
9798
|
+
useEffect21(() => {
|
|
9523
9799
|
void load();
|
|
9524
9800
|
}, [load]);
|
|
9525
9801
|
if (error) return /* @__PURE__ */ jsx34(RefusalNotice, { error, className });
|
|
@@ -9800,7 +10076,7 @@ function say(value, field) {
|
|
|
9800
10076
|
}
|
|
9801
10077
|
|
|
9802
10078
|
// src/CommentThread.tsx
|
|
9803
|
-
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";
|
|
9804
10080
|
import {
|
|
9805
10081
|
useFields as useFields15,
|
|
9806
10082
|
useRecordsClient as useRecordsClient21,
|
|
@@ -9837,10 +10113,10 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
|
|
|
9837
10113
|
mayResolve: answered.data.may_resolve
|
|
9838
10114
|
});
|
|
9839
10115
|
}, [client, recordId, showResolved]);
|
|
9840
|
-
|
|
10116
|
+
useEffect22(() => {
|
|
9841
10117
|
void load();
|
|
9842
10118
|
}, [load]);
|
|
9843
|
-
|
|
10119
|
+
useEffect22(() => {
|
|
9844
10120
|
let alive = true;
|
|
9845
10121
|
if (!host.members) return;
|
|
9846
10122
|
void host.members().then((roster) => {
|
|
@@ -10043,7 +10319,7 @@ function Line({
|
|
|
10043
10319
|
}
|
|
10044
10320
|
|
|
10045
10321
|
// src/FieldHistoryPanel.tsx
|
|
10046
|
-
import { useCallback as useCallback19, useEffect as
|
|
10322
|
+
import { useCallback as useCallback19, useEffect as useEffect23, useState as useState34 } from "react";
|
|
10047
10323
|
import {
|
|
10048
10324
|
useFields as useFields16,
|
|
10049
10325
|
useRecordsClient as useRecordsClient22,
|
|
@@ -10078,7 +10354,7 @@ function FieldHistoryPanel({
|
|
|
10078
10354
|
setError(null);
|
|
10079
10355
|
setRows(answered.data);
|
|
10080
10356
|
}, [client, tableId, fieldKey, recordId]);
|
|
10081
|
-
|
|
10357
|
+
useEffect23(() => {
|
|
10082
10358
|
void load();
|
|
10083
10359
|
}, [load]);
|
|
10084
10360
|
const label = (fields.data ?? []).find((f) => f.key === fieldKey)?.label || humanize(fieldKey);
|
|
@@ -10271,7 +10547,7 @@ function submissionStamp(args) {
|
|
|
10271
10547
|
}
|
|
10272
10548
|
|
|
10273
10549
|
// src/PortalBuilder.tsx
|
|
10274
|
-
import { useCallback as useCallback20, useEffect as
|
|
10550
|
+
import { useCallback as useCallback20, useEffect as useEffect24, useMemo as useMemo22, useState as useState35 } from "react";
|
|
10275
10551
|
import { useRecordsClient as useRecordsClient23, useTables as useTables3 } from "@ai-matrx/records/react";
|
|
10276
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";
|
|
10277
10553
|
import { Fragment as Fragment15, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
@@ -10304,7 +10580,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10304
10580
|
},
|
|
10305
10581
|
[client, fieldsByTable]
|
|
10306
10582
|
);
|
|
10307
|
-
|
|
10583
|
+
useEffect24(() => {
|
|
10308
10584
|
if (!portalId) return;
|
|
10309
10585
|
void (async () => {
|
|
10310
10586
|
const answered = await client.portalCard({ portal_id: portalId });
|
|
@@ -10318,7 +10594,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10318
10594
|
setClientTableId(answered.data.client_table_id);
|
|
10319
10595
|
})();
|
|
10320
10596
|
}, [client, portalId]);
|
|
10321
|
-
|
|
10597
|
+
useEffect24(() => {
|
|
10322
10598
|
if (!tableId) return;
|
|
10323
10599
|
setExposures(
|
|
10324
10600
|
(prev) => prev[tableId] ? prev : {
|
|
@@ -10566,7 +10842,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
10566
10842
|
}
|
|
10567
10843
|
|
|
10568
10844
|
// src/PortalsPanel.tsx
|
|
10569
|
-
import { useCallback as useCallback21, useEffect as
|
|
10845
|
+
import { useCallback as useCallback21, useEffect as useEffect25, useMemo as useMemo23, useState as useState36 } from "react";
|
|
10570
10846
|
import { useRecordsClient as useRecordsClient24 } from "@ai-matrx/records/react";
|
|
10571
10847
|
import {
|
|
10572
10848
|
portalArchiveConsequence,
|
|
@@ -10607,6 +10883,7 @@ function BuildOrAsk({
|
|
|
10607
10883
|
|
|
10608
10884
|
// src/PortalsPanel.tsx
|
|
10609
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.";
|
|
10610
10887
|
function revokeConsequence(person, portalTitle) {
|
|
10611
10888
|
const who = person.client ? `${person.email} (${person.client})` : person.email;
|
|
10612
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.`;
|
|
@@ -10636,13 +10913,16 @@ function stateWords(person) {
|
|
|
10636
10913
|
};
|
|
10637
10914
|
}
|
|
10638
10915
|
var PORTAL_SUGGESTION = "Let each of my customers sign in and see their own jobs and invoices, and nothing else.";
|
|
10639
|
-
function PortalsPanel({ tableId, className }) {
|
|
10916
|
+
function PortalsPanel({ tableId, activePortalId, className }) {
|
|
10640
10917
|
const client = useRecordsClient24();
|
|
10641
10918
|
const host = useRecordsUi();
|
|
10642
10919
|
const [portals, setPortals] = useState36(null);
|
|
10643
10920
|
const [exposures, setExposures] = useState36([]);
|
|
10644
10921
|
const [listError, setListError] = useState36(null);
|
|
10645
|
-
const [openId, setOpenId] = useState36(null);
|
|
10922
|
+
const [openId, setOpenId] = useState36(activePortalId ?? null);
|
|
10923
|
+
useEffect25(() => {
|
|
10924
|
+
if (activePortalId) setOpenId(activePortalId);
|
|
10925
|
+
}, [activePortalId]);
|
|
10646
10926
|
const [building, setBuilding] = useState36(false);
|
|
10647
10927
|
const [adding, setAdding] = useState36(null);
|
|
10648
10928
|
const [archiveToken, setArchiveToken] = useState36(0);
|
|
@@ -10658,7 +10938,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
10658
10938
|
setPortals(answered.data);
|
|
10659
10939
|
setExposures(mapped.ok ? mapped.data : []);
|
|
10660
10940
|
}, [client]);
|
|
10661
|
-
|
|
10941
|
+
useEffect25(() => {
|
|
10662
10942
|
void load();
|
|
10663
10943
|
}, [load]);
|
|
10664
10944
|
if (portals === null) return /* @__PURE__ */ jsx39(Skeleton17, { className: cn33("h-32 w-full", className) });
|
|
@@ -10739,59 +11019,73 @@ function PortalsPanel({ tableId, className }) {
|
|
|
10739
11019
|
portal.portal_id
|
|
10740
11020
|
)) })
|
|
10741
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,
|
|
10742
11023
|
/* @__PURE__ */ jsx39("ul", { className: "flex flex-col gap-2", children: shown.map((portal) => {
|
|
10743
11024
|
const url = `${origin}${portalPath(portal.slug)}`;
|
|
10744
11025
|
const open = openId === portal.portal_id;
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
"."
|
|
10755
|
-
] }),
|
|
10756
|
-
/* @__PURE__ */ jsxs35("p", { className: "mt-1.5 text-xs", children: [
|
|
10757
|
-
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.signed_in }),
|
|
10758
|
-
" signed in",
|
|
10759
|
-
" \xB7 ",
|
|
10760
|
-
/* @__PURE__ */ jsx39("span", { className: "font-medium", children: portal.invited - portal.signed_in }),
|
|
10761
|
-
" invited and waiting"
|
|
10762
|
-
] }),
|
|
10763
|
-
/* @__PURE__ */ jsxs35("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
10764
|
-
/* @__PURE__ */ jsx39(CopyLink, { url }),
|
|
10765
|
-
/* @__PURE__ */ jsx39(
|
|
10766
|
-
Button33,
|
|
10767
|
-
{
|
|
10768
|
-
size: "sm",
|
|
10769
|
-
variant: "ghost",
|
|
10770
|
-
onClick: () => setOpenId(open ? null : portal.portal_id),
|
|
10771
|
-
children: open ? "Close" : "Open it"
|
|
10772
|
-
}
|
|
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"
|
|
10773
11035
|
),
|
|
10774
|
-
|
|
10775
|
-
|
|
10776
|
-
|
|
10777
|
-
portal,
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
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()
|
|
10781
11083
|
}
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
{
|
|
10788
|
-
portalId: portal.portal_id,
|
|
10789
|
-
...tableId ? { tableId } : {},
|
|
10790
|
-
origin,
|
|
10791
|
-
onChanged: () => void load()
|
|
10792
|
-
}
|
|
10793
|
-
) : null
|
|
10794
|
-
] }, portal.portal_id);
|
|
11084
|
+
) : null
|
|
11085
|
+
]
|
|
11086
|
+
},
|
|
11087
|
+
portal.portal_id
|
|
11088
|
+
);
|
|
10795
11089
|
}) }),
|
|
10796
11090
|
/* @__PURE__ */ jsx39(ArchivedPortals, { refreshToken: archiveToken, onRestored: () => void load() })
|
|
10797
11091
|
] });
|
|
@@ -10920,7 +11214,7 @@ function PortalDetail({
|
|
|
10920
11214
|
setError(null);
|
|
10921
11215
|
setCard(answered.data);
|
|
10922
11216
|
}, [client, portalId]);
|
|
10923
|
-
|
|
11217
|
+
useEffect25(() => {
|
|
10924
11218
|
void load();
|
|
10925
11219
|
}, [load]);
|
|
10926
11220
|
const revoke = useCallback21(
|
|
@@ -11113,7 +11407,7 @@ function Preview({
|
|
|
11113
11407
|
const [which, setWhich] = useState36(first?.table_id ?? null);
|
|
11114
11408
|
const [rows, setRows] = useState36(null);
|
|
11115
11409
|
const [error, setError] = useState36(null);
|
|
11116
|
-
|
|
11410
|
+
useEffect25(() => {
|
|
11117
11411
|
if (!which) return;
|
|
11118
11412
|
let cancelled = false;
|
|
11119
11413
|
setRows(null);
|
|
@@ -11159,7 +11453,7 @@ function Invite({ card, onInvited }) {
|
|
|
11159
11453
|
const [busy, setBusy] = useState36(false);
|
|
11160
11454
|
const [said, setSaid] = useState36(null);
|
|
11161
11455
|
const [error, setError] = useState36(null);
|
|
11162
|
-
|
|
11456
|
+
useEffect25(() => {
|
|
11163
11457
|
let cancelled = false;
|
|
11164
11458
|
void client.list({ table_id: card.client_table_id, limit: 200 }).then((answered) => {
|
|
11165
11459
|
if (cancelled) return;
|
|
@@ -11261,7 +11555,7 @@ function Invite({ card, onInvited }) {
|
|
|
11261
11555
|
}
|
|
11262
11556
|
|
|
11263
11557
|
// src/DigestScheduler.tsx
|
|
11264
|
-
import { useCallback as useCallback22, useEffect as
|
|
11558
|
+
import { useCallback as useCallback22, useEffect as useEffect26, useMemo as useMemo24, useState as useState37 } from "react";
|
|
11265
11559
|
import { useRecordsClient as useRecordsClient25 } from "@ai-matrx/records/react";
|
|
11266
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";
|
|
11267
11561
|
import { Fragment as Fragment17, jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
@@ -11325,7 +11619,7 @@ function DigestScheduler({
|
|
|
11325
11619
|
setMembers([]);
|
|
11326
11620
|
}
|
|
11327
11621
|
}, [client, tableId, host]);
|
|
11328
|
-
|
|
11622
|
+
useEffect26(() => {
|
|
11329
11623
|
void load();
|
|
11330
11624
|
}, [load]);
|
|
11331
11625
|
const schedule = useMemo24(() => {
|
|
@@ -11549,7 +11843,17 @@ function DigestScheduler({
|
|
|
11549
11843
|
preview ? /* @__PURE__ */ jsxs36("div", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
11550
11844
|
/* @__PURE__ */ jsx40("p", { className: "font-medium", children: preview.subject }),
|
|
11551
11845
|
/* @__PURE__ */ jsx40("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
11552
|
-
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,
|
|
11553
11857
|
preview.entered.length > 0 ? /* @__PURE__ */ jsxs36("p", { className: "mt-1", children: [
|
|
11554
11858
|
/* @__PURE__ */ jsx40("span", { className: "font-medium", children: "Arrived:" }),
|
|
11555
11859
|
" ",
|
|
@@ -11567,10 +11871,11 @@ function DigestScheduler({
|
|
|
11567
11871
|
}
|
|
11568
11872
|
|
|
11569
11873
|
// src/SubscriptionsPanel.tsx
|
|
11570
|
-
import { useCallback as useCallback23, useEffect as
|
|
11874
|
+
import { useCallback as useCallback23, useEffect as useEffect27, useState as useState38 } from "react";
|
|
11571
11875
|
import { useRecordsClient as useRecordsClient26 } from "@ai-matrx/records/react";
|
|
11572
11876
|
import { Badge as Badge14, Button as Button35, Skeleton as Skeleton19, Switch as Switch2, cn as cn35 } from "@ai-matrx/design-system";
|
|
11573
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.";
|
|
11574
11879
|
function whenItFires(subscription) {
|
|
11575
11880
|
if (subscription.cadence === "instant") return "as it happens";
|
|
11576
11881
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
@@ -11578,7 +11883,7 @@ function whenItFires(subscription) {
|
|
|
11578
11883
|
}
|
|
11579
11884
|
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
11580
11885
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
11581
|
-
function SubscriptionsPanel({ tableId, className }) {
|
|
11886
|
+
function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
11582
11887
|
const client = useRecordsClient26();
|
|
11583
11888
|
const [rows, setRows] = useState38(null);
|
|
11584
11889
|
const [error, setError] = useState38(null);
|
|
@@ -11596,7 +11901,7 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
11596
11901
|
setError(null);
|
|
11597
11902
|
setRows(answered.data);
|
|
11598
11903
|
}, [client, tableId]);
|
|
11599
|
-
|
|
11904
|
+
useEffect27(() => {
|
|
11600
11905
|
void load();
|
|
11601
11906
|
}, [load]);
|
|
11602
11907
|
const flip = useCallback23(
|
|
@@ -11669,96 +11974,119 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
11669
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."
|
|
11670
11975
|
}
|
|
11671
11976
|
) : null,
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
"
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
" ",
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
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
|
+
)) })
|
|
11747
12075
|
] });
|
|
11748
12076
|
}
|
|
11749
12077
|
|
|
11750
12078
|
// src/FormBuilder.tsx
|
|
11751
|
-
import { useCallback as useCallback25, useEffect as
|
|
12079
|
+
import { useCallback as useCallback25, useEffect as useEffect30, useMemo as useMemo26, useState as useState41 } from "react";
|
|
11752
12080
|
import { useFields as useFields18, useRecordsClient as useRecordsClient28, useTable as useTable14 } from "@ai-matrx/records/react";
|
|
11753
12081
|
|
|
11754
12082
|
// src/publish-gate.ts
|
|
11755
|
-
import { useEffect as
|
|
12083
|
+
import { useEffect as useEffect28, useState as useState39 } from "react";
|
|
11756
12084
|
import { useRecordsClient as useRecordsClient27 } from "@ai-matrx/records/react";
|
|
11757
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.";
|
|
11758
12086
|
function usePublishGate() {
|
|
11759
12087
|
const client = useRecordsClient27();
|
|
11760
12088
|
const [storeOpen, setStoreOpen] = useState39(null);
|
|
11761
|
-
|
|
12089
|
+
useEffect28(() => {
|
|
11762
12090
|
let alive = true;
|
|
11763
12091
|
void (async () => {
|
|
11764
12092
|
const answered = await client.storeIsOpen();
|
|
@@ -11789,7 +12117,7 @@ import {
|
|
|
11789
12117
|
} from "@ai-matrx/design-system";
|
|
11790
12118
|
|
|
11791
12119
|
// src/FormRunner.tsx
|
|
11792
|
-
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";
|
|
11793
12121
|
import { useFields as useFields17, useOptionalRecordsClient as useOptionalRecordsClient4 } from "@ai-matrx/records/react";
|
|
11794
12122
|
import { Button as Button36, Progress, Skeleton as Skeleton20, cn as cn36 } from "@ai-matrx/design-system";
|
|
11795
12123
|
import { Fragment as Fragment18, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
@@ -11806,9 +12134,16 @@ function ConnectedFormRunner(props) {
|
|
|
11806
12134
|
const submit = useCallback24(
|
|
11807
12135
|
async (values) => {
|
|
11808
12136
|
if (!client) {
|
|
12137
|
+
const said = "This form cannot send answers from here, so nothing was sent.";
|
|
11809
12138
|
return {
|
|
11810
12139
|
ok: false,
|
|
11811
|
-
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
|
+
)
|
|
11812
12147
|
};
|
|
11813
12148
|
}
|
|
11814
12149
|
const document2 = {
|
|
@@ -11821,7 +12156,7 @@ function ConnectedFormRunner(props) {
|
|
|
11821
12156
|
})
|
|
11822
12157
|
};
|
|
11823
12158
|
const written = await client.recordWrite({ table_id: form.subject, data: document2 });
|
|
11824
|
-
if (!written.ok) return { ok: false, message: written.error.message };
|
|
12159
|
+
if (!written.ok) return { ok: false, message: written.error.message, error: written.error };
|
|
11825
12160
|
return { ok: true, recordId: written.data };
|
|
11826
12161
|
},
|
|
11827
12162
|
[client, form]
|
|
@@ -11863,7 +12198,7 @@ function FormStage({
|
|
|
11863
12198
|
const [answers, setAnswers] = useState40({});
|
|
11864
12199
|
const [at, setAt] = useState40(0);
|
|
11865
12200
|
const [error, setError] = useState40(null);
|
|
11866
|
-
const [
|
|
12201
|
+
const [refusal2, setRefusal] = useState40(null);
|
|
11867
12202
|
const [writing, setWriting] = useState40(false);
|
|
11868
12203
|
const [done, setDone] = useState40(null);
|
|
11869
12204
|
const [hidden, setHidden] = useState40({});
|
|
@@ -11883,7 +12218,7 @@ function FormStage({
|
|
|
11883
12218
|
};
|
|
11884
12219
|
});
|
|
11885
12220
|
}, [fields, form.questions]);
|
|
11886
|
-
|
|
12221
|
+
useEffect29(() => {
|
|
11887
12222
|
let cancelled = false;
|
|
11888
12223
|
const conditional = questions.filter((q) => q.showIf);
|
|
11889
12224
|
if (conditional.length === 0 || !evaluate) return;
|
|
@@ -11932,12 +12267,21 @@ function FormStage({
|
|
|
11932
12267
|
const outcome = await onSubmit(values);
|
|
11933
12268
|
setWriting(false);
|
|
11934
12269
|
if (!outcome.ok) {
|
|
11935
|
-
|
|
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
|
+
});
|
|
11936
12280
|
return;
|
|
11937
12281
|
}
|
|
11938
12282
|
setDone(outcome.message ?? "sent");
|
|
11939
12283
|
onSubmitted?.(outcome.recordId ?? null);
|
|
11940
|
-
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview]);
|
|
12284
|
+
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
11941
12285
|
function advance() {
|
|
11942
12286
|
if (!oneAtATime) return;
|
|
11943
12287
|
if (index < live.length - 1) setAt(index + 1);
|
|
@@ -11954,7 +12298,7 @@ function FormStage({
|
|
|
11954
12298
|
if (event.shiftKey) retreat();
|
|
11955
12299
|
else advance();
|
|
11956
12300
|
}
|
|
11957
|
-
|
|
12301
|
+
useEffect29(() => {
|
|
11958
12302
|
const input = stage.current?.querySelector(
|
|
11959
12303
|
"input:not([tabindex='-1']), textarea, select, [role='combobox']"
|
|
11960
12304
|
);
|
|
@@ -11996,7 +12340,22 @@ function FormStage({
|
|
|
11996
12340
|
] }) : null,
|
|
11997
12341
|
form.intro && index === 0 ? /* @__PURE__ */ jsx42("p", { className: "text-sm text-muted-foreground", children: form.intro }) : null,
|
|
11998
12342
|
error ? /* @__PURE__ */ jsx42(RefusalNotice, { error, className: "text-left" }) : null,
|
|
11999
|
-
|
|
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,
|
|
12000
12359
|
/* @__PURE__ */ jsxs38("div", { ref: stage, className: "flex flex-col gap-4 text-left", children: [
|
|
12001
12360
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ jsx42(
|
|
12002
12361
|
Question,
|
|
@@ -12070,12 +12429,15 @@ function Question({
|
|
|
12070
12429
|
void upload?.(file).then((result) => {
|
|
12071
12430
|
if (!result) return;
|
|
12072
12431
|
if (result.ok) onChange(result.fileId);
|
|
12073
|
-
else
|
|
12432
|
+
else
|
|
12433
|
+
setUploadError(
|
|
12434
|
+
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
12435
|
+
);
|
|
12074
12436
|
});
|
|
12075
12437
|
}
|
|
12076
12438
|
}
|
|
12077
12439
|
),
|
|
12078
|
-
uploadError ? /* @__PURE__ */ jsx42(
|
|
12440
|
+
uploadError ? /* @__PURE__ */ jsx42(RefusalLine, { error: uploadError }) : null
|
|
12079
12441
|
] }) : /* @__PURE__ */ jsx42(FieldControl, { field, value, onChange, id })
|
|
12080
12442
|
] });
|
|
12081
12443
|
}
|
|
@@ -12149,10 +12511,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
12149
12511
|
setError(null);
|
|
12150
12512
|
setForms(mine);
|
|
12151
12513
|
}, [client, tableId, seed, claimSeed]);
|
|
12152
|
-
|
|
12514
|
+
useEffect30(() => {
|
|
12153
12515
|
void load();
|
|
12154
12516
|
}, [load]);
|
|
12155
|
-
|
|
12517
|
+
useEffect30(() => {
|
|
12156
12518
|
if (!forms || forms.length === 0) return;
|
|
12157
12519
|
const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
|
|
12158
12520
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -12563,7 +12925,7 @@ function groupLabel(groups) {
|
|
|
12563
12925
|
|
|
12564
12926
|
// src/chartFrame.tsx
|
|
12565
12927
|
import {
|
|
12566
|
-
useEffect as
|
|
12928
|
+
useEffect as useEffect31,
|
|
12567
12929
|
useId as useId2,
|
|
12568
12930
|
useRef as useRef12,
|
|
12569
12931
|
useState as useState42
|
|
@@ -12573,7 +12935,7 @@ import { jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
|
12573
12935
|
function useMeasuredWidth(fallback = 480) {
|
|
12574
12936
|
const ref = useRef12(null);
|
|
12575
12937
|
const [width, setWidth] = useState42(fallback);
|
|
12576
|
-
|
|
12938
|
+
useEffect31(() => {
|
|
12577
12939
|
const node = ref.current;
|
|
12578
12940
|
if (!node) return;
|
|
12579
12941
|
const apply = () => {
|
|
@@ -12697,7 +13059,7 @@ function isSignatureField(field) {
|
|
|
12697
13059
|
}
|
|
12698
13060
|
|
|
12699
13061
|
// src/DocTemplate.tsx
|
|
12700
|
-
import { useCallback as useCallback26, useEffect as
|
|
13062
|
+
import { useCallback as useCallback26, useEffect as useEffect32, useState as useState43 } from "react";
|
|
12701
13063
|
import { useFields as useFields19, useRecordsClient as useRecordsClient29, useTable as useTable15 } from "@ai-matrx/records/react";
|
|
12702
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";
|
|
12703
13065
|
import { jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
@@ -12745,10 +13107,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12745
13107
|
setError(null);
|
|
12746
13108
|
setTemplates(rows);
|
|
12747
13109
|
}, [client, tableId, seed, fields.data]);
|
|
12748
|
-
|
|
13110
|
+
useEffect32(() => {
|
|
12749
13111
|
void load();
|
|
12750
13112
|
}, [load]);
|
|
12751
|
-
|
|
13113
|
+
useEffect32(() => {
|
|
12752
13114
|
if (!templates || templates.length === 0) return;
|
|
12753
13115
|
const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
|
|
12754
13116
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
@@ -12756,7 +13118,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12756
13118
|
setDraftBody(chosen.body);
|
|
12757
13119
|
onActiveTemplate?.(chosen);
|
|
12758
13120
|
}, [templates, activeTemplateId]);
|
|
12759
|
-
|
|
13121
|
+
useEffect32(() => {
|
|
12760
13122
|
let cancelled = false;
|
|
12761
13123
|
if (draftBody.trim() === "") {
|
|
12762
13124
|
setUnresolved([]);
|
|
@@ -12867,16 +13229,21 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12867
13229
|
onChange: (e) => setDraftBody(e.target.value)
|
|
12868
13230
|
}
|
|
12869
13231
|
),
|
|
12870
|
-
unresolved.length > 0 ? /* @__PURE__ */ jsx45("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ jsxs41("li", { className: "
|
|
12871
|
-
/* @__PURE__ */ jsx45("code", { children: token.raw }),
|
|
12872
|
-
" \u2014
|
|
12873
|
-
|
|
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
|
+
)
|
|
12874
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
|
|
12875
13242
|
] });
|
|
12876
13243
|
}
|
|
12877
13244
|
|
|
12878
13245
|
// src/DocRender.tsx
|
|
12879
|
-
import { useCallback as useCallback27, useEffect as
|
|
13246
|
+
import { useCallback as useCallback27, useEffect as useEffect33, useRef as useRef13, useState as useState44 } from "react";
|
|
12880
13247
|
import { useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
|
|
12881
13248
|
import { Button as Button39, Skeleton as Skeleton23, cn as cn40 } from "@ai-matrx/design-system";
|
|
12882
13249
|
import { jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
@@ -12905,7 +13272,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
12905
13272
|
setPreview(body.data);
|
|
12906
13273
|
setRenders(held.data.filter((r) => r.template_id === templateId));
|
|
12907
13274
|
}, [client, templateId, recordId]);
|
|
12908
|
-
|
|
13275
|
+
useEffect33(() => {
|
|
12909
13276
|
void load();
|
|
12910
13277
|
}, [load]);
|
|
12911
13278
|
async function freeze() {
|
|
@@ -12989,7 +13356,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
|
|
|
12989
13356
|
}
|
|
12990
13357
|
|
|
12991
13358
|
// src/SignBlock.tsx
|
|
12992
|
-
import { useCallback as useCallback28, useEffect as
|
|
13359
|
+
import { useCallback as useCallback28, useEffect as useEffect34, useState as useState45 } from "react";
|
|
12993
13360
|
import { useFields as useFields20, useRecordsClient as useRecordsClient31 } from "@ai-matrx/records/react";
|
|
12994
13361
|
import { BasicInput as BasicInput15, Button as Button40, Skeleton as Skeleton24, cn as cn41 } from "@ai-matrx/design-system";
|
|
12995
13362
|
import { useTable as useTable16 } from "@ai-matrx/records/react";
|
|
@@ -13019,7 +13386,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
13019
13386
|
}
|
|
13020
13387
|
setVerdicts(answers);
|
|
13021
13388
|
}, [client, recordId]);
|
|
13022
|
-
|
|
13389
|
+
useEffect34(() => {
|
|
13023
13390
|
void load();
|
|
13024
13391
|
}, [load]);
|
|
13025
13392
|
async function sign(field) {
|
|
@@ -13075,7 +13442,21 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
13075
13442
|
signature.document_hash.slice(0, 12)
|
|
13076
13443
|
] })
|
|
13077
13444
|
] }),
|
|
13078
|
-
|
|
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 ?? "") })
|
|
13079
13460
|
] }, signature.id);
|
|
13080
13461
|
}) }) : null,
|
|
13081
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: [
|
|
@@ -13126,7 +13507,7 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
13126
13507
|
}
|
|
13127
13508
|
|
|
13128
13509
|
// src/NotifyRuleEditor.tsx
|
|
13129
|
-
import { useCallback as useCallback29, useEffect as
|
|
13510
|
+
import { useCallback as useCallback29, useEffect as useEffect35, useState as useState46 } from "react";
|
|
13130
13511
|
import { useRecordsClient as useRecordsClient32, useTable as useTable17 } from "@ai-matrx/records/react";
|
|
13131
13512
|
import { Button as Button41, Skeleton as Skeleton25, cn as cn42 } from "@ai-matrx/design-system";
|
|
13132
13513
|
import { jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
@@ -13162,7 +13543,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13162
13543
|
if (host.savedViews) setViews(await host.savedViews());
|
|
13163
13544
|
else setViews(null);
|
|
13164
13545
|
}, [client, host, tableId]);
|
|
13165
|
-
|
|
13546
|
+
useEffect35(() => {
|
|
13166
13547
|
void load();
|
|
13167
13548
|
}, [load]);
|
|
13168
13549
|
const write = useCallback29(
|
|
@@ -13190,7 +13571,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13190
13571
|
},
|
|
13191
13572
|
[client, tableId]
|
|
13192
13573
|
);
|
|
13193
|
-
|
|
13574
|
+
useEffect35(() => {
|
|
13194
13575
|
if (!subscriptions || !seed || seed.length === 0) return;
|
|
13195
13576
|
const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
|
|
13196
13577
|
if (missing.length === 0) return;
|
|
@@ -13357,7 +13738,7 @@ import { Fragment as Fragment20, jsx as jsx49, jsxs as jsxs45 } from "react/jsx-
|
|
|
13357
13738
|
function pretty(n) {
|
|
13358
13739
|
return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
|
|
13359
13740
|
}
|
|
13360
|
-
function
|
|
13741
|
+
function asRefusal2(block) {
|
|
13361
13742
|
return mapPgError(
|
|
13362
13743
|
{
|
|
13363
13744
|
message: block.refused ?? "",
|
|
@@ -13418,7 +13799,7 @@ function ChartBlock({ block, subject, className }) {
|
|
|
13418
13799
|
" ms"
|
|
13419
13800
|
] }) : null
|
|
13420
13801
|
] }),
|
|
13421
|
-
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: [
|
|
13422
13803
|
/* @__PURE__ */ jsx49(Drawing, { kind, points, series, config, onDrill: drill }),
|
|
13423
13804
|
/* @__PURE__ */ jsx49(Values, { points, measure: primary, config, onDrill: drill })
|
|
13424
13805
|
] }) })
|
|
@@ -13646,7 +14027,7 @@ function Drawing({
|
|
|
13646
14027
|
}
|
|
13647
14028
|
|
|
13648
14029
|
// src/DashboardCanvas.tsx
|
|
13649
|
-
import { useCallback as useCallback30, useEffect as
|
|
14030
|
+
import { useCallback as useCallback30, useEffect as useEffect36, useMemo as useMemo28, useState as useState47 } from "react";
|
|
13650
14031
|
import { useFields as useFields21, useRecordsClient as useRecordsClient33, useTable as useTable18 } from "@ai-matrx/records/react";
|
|
13651
14032
|
import {
|
|
13652
14033
|
BasicInput as BasicInput16,
|
|
@@ -13683,17 +14064,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
13683
14064
|
setError(null);
|
|
13684
14065
|
setBoards(answered.data.map(dashboardFromSummary));
|
|
13685
14066
|
}, [client, tableId]);
|
|
13686
|
-
|
|
14067
|
+
useEffect36(() => {
|
|
13687
14068
|
void load();
|
|
13688
14069
|
}, [load]);
|
|
13689
|
-
|
|
14070
|
+
useEffect36(() => {
|
|
13690
14071
|
if (!boards || boards.length === 0) return;
|
|
13691
14072
|
const chosen = boards.find((d) => d.id === (activeDashboardId ?? activeId)) ?? boards[0];
|
|
13692
14073
|
if (chosen.id !== activeId) setActiveId(chosen.id);
|
|
13693
14074
|
}, [boards, activeDashboardId]);
|
|
13694
14075
|
const board = useMemo28(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
|
|
13695
14076
|
const filterKey = JSON.stringify(filter ?? {});
|
|
13696
|
-
|
|
14077
|
+
useEffect36(() => {
|
|
13697
14078
|
if (!activeId) {
|
|
13698
14079
|
setRun(null);
|
|
13699
14080
|
return;
|
|
@@ -13989,18 +14370,19 @@ function GroupingPicker({
|
|
|
13989
14370
|
}
|
|
13990
14371
|
|
|
13991
14372
|
// src/FormsPanel.tsx
|
|
13992
|
-
import { useCallback as useCallback31, useEffect as
|
|
14373
|
+
import { useCallback as useCallback31, useEffect as useEffect37, useState as useState48 } from "react";
|
|
13993
14374
|
import { useRecordsClient as useRecordsClient34, useTable as useTable19 } from "@ai-matrx/records/react";
|
|
13994
14375
|
import { publicFormPath as publicFormPath2 } from "@ai-matrx/records";
|
|
13995
14376
|
import { Badge as Badge15, Button as Button43, Skeleton as Skeleton27, cn as cn45 } from "@ai-matrx/design-system";
|
|
13996
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.";
|
|
13997
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.";
|
|
13998
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.";
|
|
13999
14381
|
function formSuggestion(tableName2) {
|
|
14000
14382
|
const subject = tableName2?.trim() ? tableName2.trim() : "this table";
|
|
14001
14383
|
return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
|
|
14002
14384
|
}
|
|
14003
|
-
function FormsPanel({ tableId, className }) {
|
|
14385
|
+
function FormsPanel({ tableId, activeFormId, className }) {
|
|
14004
14386
|
const client = useRecordsClient34();
|
|
14005
14387
|
const publishGate = usePublishGate();
|
|
14006
14388
|
const host = useRecordsUi();
|
|
@@ -14011,6 +14393,11 @@ function FormsPanel({ tableId, className }) {
|
|
|
14011
14393
|
const [busy, setBusy] = useState48(null);
|
|
14012
14394
|
const [copied, setCopied] = useState48(null);
|
|
14013
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]);
|
|
14014
14401
|
const load = useCallback31(async () => {
|
|
14015
14402
|
const answered = await client.forms({ table_id: tableId });
|
|
14016
14403
|
if (!answered.ok) {
|
|
@@ -14021,7 +14408,7 @@ function FormsPanel({ tableId, className }) {
|
|
|
14021
14408
|
setError(null);
|
|
14022
14409
|
setForms(answered.data);
|
|
14023
14410
|
}, [client, tableId]);
|
|
14024
|
-
|
|
14411
|
+
useEffect37(() => {
|
|
14025
14412
|
void load();
|
|
14026
14413
|
}, [load]);
|
|
14027
14414
|
const toggle = useCallback31(
|
|
@@ -14059,10 +14446,12 @@ function FormsPanel({ tableId, className }) {
|
|
|
14059
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
|
|
14060
14447
|
] }),
|
|
14061
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,
|
|
14062
14450
|
building ? /* @__PURE__ */ jsx51(
|
|
14063
14451
|
FormBuilder,
|
|
14064
14452
|
{
|
|
14065
14453
|
tableId,
|
|
14454
|
+
activeFormId: linked ? linked.form_id : null,
|
|
14066
14455
|
onActiveForm: () => {
|
|
14067
14456
|
void load();
|
|
14068
14457
|
}
|
|
@@ -14088,57 +14477,69 @@ function FormsPanel({ tableId, className }) {
|
|
|
14088
14477
|
) : null,
|
|
14089
14478
|
/* @__PURE__ */ jsx51("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
|
|
14090
14479
|
const url = `${origin}${publicFormPath2(form.form_id)}`;
|
|
14091
|
-
return /* @__PURE__ */ jsxs47(
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
|
|
14101
|
-
"
|
|
14102
|
-
|
|
14103
|
-
|
|
14104
|
-
|
|
14105
|
-
|
|
14106
|
-
"
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
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
|
+
);
|
|
14135
14536
|
}) }),
|
|
14136
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
|
|
14137
14538
|
] });
|
|
14138
14539
|
}
|
|
14139
14540
|
|
|
14140
14541
|
// src/BookingBuilder.tsx
|
|
14141
|
-
import { useCallback as useCallback32, useEffect as
|
|
14542
|
+
import { useCallback as useCallback32, useEffect as useEffect38, useMemo as useMemo29, useState as useState49 } from "react";
|
|
14142
14543
|
import { useFields as useFields22, useRecordsClient as useRecordsClient35, useTable as useTable20 } from "@ai-matrx/records/react";
|
|
14143
14544
|
import {
|
|
14144
14545
|
bookingPath
|
|
@@ -14213,18 +14614,18 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
14213
14614
|
setLoaded(true);
|
|
14214
14615
|
if (mine) setFormId(mine.form_id);
|
|
14215
14616
|
}, [client, tableId, bookingId]);
|
|
14216
|
-
|
|
14617
|
+
useEffect38(() => {
|
|
14217
14618
|
void load();
|
|
14218
14619
|
}, [load]);
|
|
14219
|
-
|
|
14620
|
+
useEffect38(() => {
|
|
14220
14621
|
if (title !== "" || !table.data) return;
|
|
14221
14622
|
setTitle(existing?.title ?? `Book a ${minutes}-minute ${table.data.name} appointment`);
|
|
14222
14623
|
}, [table.data, existing]);
|
|
14223
|
-
|
|
14624
|
+
useEffect38(() => {
|
|
14224
14625
|
if (!existing) return;
|
|
14225
14626
|
setMinutes(existing.slot_minutes);
|
|
14226
14627
|
}, [existing]);
|
|
14227
|
-
|
|
14628
|
+
useEffect38(() => {
|
|
14228
14629
|
if (!offer) return;
|
|
14229
14630
|
setWindows(draftWindows(offer));
|
|
14230
14631
|
setMinutes(offer.slot_minutes);
|
|
@@ -14486,7 +14887,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
14486
14887
|
}
|
|
14487
14888
|
|
|
14488
14889
|
// src/BookingSlots.tsx
|
|
14489
|
-
import { useCallback as useCallback33, useEffect as
|
|
14890
|
+
import { useCallback as useCallback33, useEffect as useEffect39, useMemo as useMemo30, useState as useState50 } from "react";
|
|
14490
14891
|
import { useRecordsClient as useRecordsClient36, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
|
|
14491
14892
|
import { bookingPath as bookingPath2 } from "@ai-matrx/records";
|
|
14492
14893
|
import { Badge as Badge16, Button as Button45, Skeleton as Skeleton29, cn as cn47 } from "@ai-matrx/design-system";
|
|
@@ -14517,7 +14918,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
14517
14918
|
setError(null);
|
|
14518
14919
|
setPages(answered.data);
|
|
14519
14920
|
}, [client, tableId]);
|
|
14520
|
-
|
|
14921
|
+
useEffect39(() => {
|
|
14521
14922
|
void load();
|
|
14522
14923
|
}, [load]);
|
|
14523
14924
|
const subjectIds = useMemo30(
|
|
@@ -14686,12 +15087,12 @@ function nextInWords(page) {
|
|
|
14686
15087
|
}
|
|
14687
15088
|
|
|
14688
15089
|
// src/CaptureSheet.tsx
|
|
14689
|
-
import { useCallback as useCallback35, useEffect as
|
|
15090
|
+
import { useCallback as useCallback35, useEffect as useEffect41, useRef as useRef15, useState as useState52 } from "react";
|
|
14690
15091
|
import { useFields as useFields23, useRecordsClient as useRecordsClient38, useTable as useTable21 } from "@ai-matrx/records/react";
|
|
14691
15092
|
import { Button as Button47, Input as Input4, Skeleton as Skeleton31, Textarea as Textarea3, cn as cn49 } from "@ai-matrx/design-system";
|
|
14692
15093
|
|
|
14693
15094
|
// src/CaptureRun.tsx
|
|
14694
|
-
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";
|
|
14695
15096
|
import {
|
|
14696
15097
|
coerceTypedAnswer as coerceTypedAnswer2,
|
|
14697
15098
|
coercionLabel,
|
|
@@ -14750,7 +15151,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14750
15151
|
const [lastSynced, setLastSynced] = useState51(null);
|
|
14751
15152
|
const [sending, setSending] = useState51(false);
|
|
14752
15153
|
const queueRef = useRef14(null);
|
|
14753
|
-
|
|
15154
|
+
useEffect40(() => {
|
|
14754
15155
|
const q2 = openCaptureQueue({
|
|
14755
15156
|
onChange: (c, all) => {
|
|
14756
15157
|
setCounts(c);
|
|
@@ -14788,13 +15189,13 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14788
15189
|
void q2.sync().then(() => void q2.lastSyncedAt().then(setLastSynced));
|
|
14789
15190
|
return () => q2.dispose();
|
|
14790
15191
|
}, [client, host]);
|
|
14791
|
-
|
|
15192
|
+
useEffect40(() => {
|
|
14792
15193
|
if (given !== void 0) return;
|
|
14793
15194
|
let cancelled = false;
|
|
14794
15195
|
void client.captureOpen({ sheet_id: sheetId }).then((res) => {
|
|
14795
15196
|
if (cancelled) return;
|
|
14796
15197
|
if (res.ok) setFace(res.data);
|
|
14797
|
-
else setLoadFailed(res.error
|
|
15198
|
+
else setLoadFailed(refusalOr(res.error, "unreachable", "This sheet could not be opened."));
|
|
14798
15199
|
});
|
|
14799
15200
|
return () => {
|
|
14800
15201
|
cancelled = true;
|
|
@@ -14841,7 +15242,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14841
15242
|
const needed = questions.filter((x) => x.required && !answered(x.field));
|
|
14842
15243
|
if (needed.length > 0) {
|
|
14843
15244
|
setMissing(
|
|
14844
|
-
|
|
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
|
+
)
|
|
14845
15250
|
);
|
|
14846
15251
|
setAt(questions.findIndex((x) => x.field === needed[0].field));
|
|
14847
15252
|
return;
|
|
@@ -14872,7 +15277,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14872
15277
|
if (online) await sync();
|
|
14873
15278
|
}
|
|
14874
15279
|
if (loadFailed) {
|
|
14875
|
-
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" }) }) });
|
|
14876
15281
|
}
|
|
14877
15282
|
if (face === void 0) {
|
|
14878
15283
|
return /* @__PURE__ */ jsx54(Skeleton30, { className: cn48("mx-auto h-64 w-full max-w-sm", className) });
|
|
@@ -14903,7 +15308,21 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14903
15308
|
] });
|
|
14904
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: [
|
|
14905
15310
|
/* @__PURE__ */ jsx54("span", { className: "tabular-nums text-muted-foreground", children: new Date(i.captured_at).toLocaleTimeString() }),
|
|
14906
|
-
|
|
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." }),
|
|
14907
15326
|
i.state === "refused" ? /* @__PURE__ */ jsx54(
|
|
14908
15327
|
Button46,
|
|
14909
15328
|
{
|
|
@@ -15023,7 +15442,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
15023
15442
|
}
|
|
15024
15443
|
}
|
|
15025
15444
|
),
|
|
15026
|
-
missing ? /* @__PURE__ */ jsx54("
|
|
15445
|
+
missing ? /* @__PURE__ */ jsx54("div", { "data-testid": "capture-missing", children: /* @__PURE__ */ jsx54(RefusalNotice, { error: missing, className: "text-sm" }) }) : null,
|
|
15027
15446
|
/* @__PURE__ */ jsxs50("div", { className: "flex items-center gap-2", children: [
|
|
15028
15447
|
at > 0 ? /* @__PURE__ */ jsx54(
|
|
15029
15448
|
Button46,
|
|
@@ -15120,7 +15539,7 @@ function AdHocCaptureSheet({
|
|
|
15120
15539
|
},
|
|
15121
15540
|
[host]
|
|
15122
15541
|
);
|
|
15123
|
-
|
|
15542
|
+
useEffect41(() => {
|
|
15124
15543
|
let cancelled = false;
|
|
15125
15544
|
void (async () => {
|
|
15126
15545
|
const held = host.captureQueue ? await host.captureQueue.load() : [];
|
|
@@ -15251,13 +15670,13 @@ function AdHocCaptureSheet({
|
|
|
15251
15670
|
setUploadError(null);
|
|
15252
15671
|
void host.upload?.(file).then((result) => {
|
|
15253
15672
|
if (result.ok) setFileId(result.fileId);
|
|
15254
|
-
else setUploadError(result.reason);
|
|
15673
|
+
else setUploadError(refusal("invalid_argument", result.reason, "Pick another file, or try this one again."));
|
|
15255
15674
|
});
|
|
15256
15675
|
}
|
|
15257
15676
|
}
|
|
15258
15677
|
),
|
|
15259
15678
|
fileId ? /* @__PURE__ */ jsx55("p", { className: "text-[11px] text-muted-foreground", children: "Attached." }) : null,
|
|
15260
|
-
uploadError ? /* @__PURE__ */ jsx55(
|
|
15679
|
+
uploadError ? /* @__PURE__ */ jsx55(RefusalLine, { error: uploadError }) : null
|
|
15261
15680
|
] }) : null,
|
|
15262
15681
|
/* @__PURE__ */ jsx55(
|
|
15263
15682
|
Textarea3,
|
|
@@ -15299,7 +15718,7 @@ function AdHocCaptureSheet({
|
|
|
15299
15718
|
}
|
|
15300
15719
|
|
|
15301
15720
|
// src/PortalShell.tsx
|
|
15302
|
-
import { useCallback as useCallback36, useEffect as
|
|
15721
|
+
import { useCallback as useCallback36, useEffect as useEffect42, useState as useState53 } from "react";
|
|
15303
15722
|
import { useRecordsClient as useRecordsClient39 } from "@ai-matrx/records/react";
|
|
15304
15723
|
import { Button as Button48, Skeleton as Skeleton32, cn as cn50 } from "@ai-matrx/design-system";
|
|
15305
15724
|
import { jsx as jsx56, jsxs as jsxs52 } from "react/jsx-runtime";
|
|
@@ -15325,7 +15744,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
|
15325
15744
|
}
|
|
15326
15745
|
setReach(reached.data.map((row) => row.resource_id));
|
|
15327
15746
|
}, [client, resourceType]);
|
|
15328
|
-
|
|
15747
|
+
useEffect42(() => {
|
|
15329
15748
|
void load();
|
|
15330
15749
|
}, [load]);
|
|
15331
15750
|
if (error) {
|
|
@@ -15385,7 +15804,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
|
|
|
15385
15804
|
function PortalRow({ tableId, recordId }) {
|
|
15386
15805
|
const client = useRecordsClient39();
|
|
15387
15806
|
const [title, setTitle] = useState53(null);
|
|
15388
|
-
|
|
15807
|
+
useEffect42(() => {
|
|
15389
15808
|
let cancelled = false;
|
|
15390
15809
|
void client.recordRead({ record_id: recordId }).then((answered) => {
|
|
15391
15810
|
if (cancelled) return;
|
|
@@ -15405,7 +15824,7 @@ function PortalRow({ tableId, recordId }) {
|
|
|
15405
15824
|
}
|
|
15406
15825
|
|
|
15407
15826
|
// src/PublicViewPage.tsx
|
|
15408
|
-
import { useCallback as useCallback37, useEffect as
|
|
15827
|
+
import { useCallback as useCallback37, useEffect as useEffect43, useState as useState54 } from "react";
|
|
15409
15828
|
import { useRecordsClient as useRecordsClient40 } from "@ai-matrx/records/react";
|
|
15410
15829
|
import { Skeleton as Skeleton33, cn as cn51 } from "@ai-matrx/design-system";
|
|
15411
15830
|
import { jsx as jsx57, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
@@ -15449,7 +15868,7 @@ function PublicViewPage({ slug, className }) {
|
|
|
15449
15868
|
}
|
|
15450
15869
|
setRows([{ id: found.resource_id, document: read.data.document, level: "viewer", hidden: read.data.hidden }]);
|
|
15451
15870
|
}, [client, slug]);
|
|
15452
|
-
|
|
15871
|
+
useEffect43(() => {
|
|
15453
15872
|
void load();
|
|
15454
15873
|
}, [load]);
|
|
15455
15874
|
if (error) return /* @__PURE__ */ jsx57(RefusalNotice, { error, className });
|
|
@@ -15486,7 +15905,7 @@ function PublicRow({ row, fields }) {
|
|
|
15486
15905
|
}
|
|
15487
15906
|
|
|
15488
15907
|
// src/EmbedFrame.tsx
|
|
15489
|
-
import { useCallback as useCallback38, useEffect as
|
|
15908
|
+
import { useCallback as useCallback38, useEffect as useEffect44, useState as useState55 } from "react";
|
|
15490
15909
|
import { useRecordsClient as useRecordsClient41 } from "@ai-matrx/records/react";
|
|
15491
15910
|
import { Button as Button49, Input as Input5, Skeleton as Skeleton34, Textarea as Textarea4, cn as cn52 } from "@ai-matrx/design-system";
|
|
15492
15911
|
import { useTable as useTable22 } from "@ai-matrx/records/react";
|
|
@@ -15599,7 +16018,7 @@ function useEmbedHandshake(args) {
|
|
|
15599
16018
|
const [loading, setLoading] = useState55(true);
|
|
15600
16019
|
const origin = args.origin ?? (typeof location === "undefined" ? "" : location.origin);
|
|
15601
16020
|
const { secret, requiredMode } = args;
|
|
15602
|
-
|
|
16021
|
+
useEffect44(() => {
|
|
15603
16022
|
let cancelled = false;
|
|
15604
16023
|
setLoading(true);
|
|
15605
16024
|
setError(null);
|
|
@@ -15654,7 +16073,7 @@ function recordsDataSource(client, fallbackSchema = "custom") {
|
|
|
15654
16073
|
}
|
|
15655
16074
|
|
|
15656
16075
|
// src/TablesHome.tsx
|
|
15657
|
-
import { useCallback as useCallback39, useEffect as
|
|
16076
|
+
import { useCallback as useCallback39, useEffect as useEffect45, useState as useState56 } from "react";
|
|
15658
16077
|
import { useRecordsClient as useRecordsClient42, useTables as useTables4 } from "@ai-matrx/records/react";
|
|
15659
16078
|
import { BasicInput as BasicInput18, Button as Button50, Skeleton as Skeleton35, cn as cn53 } from "@ai-matrx/design-system";
|
|
15660
16079
|
|
|
@@ -15720,17 +16139,17 @@ async function declareTable(client, spec) {
|
|
|
15720
16139
|
if (!written.ok) return undo(client, table.data, home.data, spec.name, written.error);
|
|
15721
16140
|
return { ok: true, data: table.data, homeId: home.data };
|
|
15722
16141
|
}
|
|
15723
|
-
async function undo(client, tableId, homeId, name,
|
|
16142
|
+
async function undo(client, tableId, homeId, name, refusal2) {
|
|
15724
16143
|
const removed = await client.recordDelete({ record_id: tableId });
|
|
15725
16144
|
if (removed.ok) {
|
|
15726
16145
|
await client.recordDelete({ record_id: homeId });
|
|
15727
|
-
return { ok: false, error:
|
|
16146
|
+
return { ok: false, error: refusal2 };
|
|
15728
16147
|
}
|
|
15729
16148
|
return {
|
|
15730
16149
|
ok: false,
|
|
15731
16150
|
error: {
|
|
15732
|
-
...
|
|
15733
|
-
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.`
|
|
15734
16153
|
}
|
|
15735
16154
|
};
|
|
15736
16155
|
}
|
|
@@ -15799,7 +16218,7 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
15799
16218
|
const [error, setError] = useState56(null);
|
|
15800
16219
|
const [importInto, setImportInto] = useState56(null);
|
|
15801
16220
|
const [boards, setBoards] = useState56(null);
|
|
15802
|
-
|
|
16221
|
+
useEffect45(() => {
|
|
15803
16222
|
let cancelled = false;
|
|
15804
16223
|
void client.dashboards({}).then((result) => {
|
|
15805
16224
|
if (cancelled) return;
|
|
@@ -15933,7 +16352,7 @@ function TablesHome({ onOpenTable, className }) {
|
|
|
15933
16352
|
}
|
|
15934
16353
|
|
|
15935
16354
|
// src/TablePage.tsx
|
|
15936
|
-
import { useCallback as useCallback40, useEffect as
|
|
16355
|
+
import { useCallback as useCallback40, useEffect as useEffect46, useState as useState57 } from "react";
|
|
15937
16356
|
import { useFields as useFields24, useRecordsClient as useRecordsClient43, useTable as useTable23 } from "@ai-matrx/records/react";
|
|
15938
16357
|
import { Button as Button51, Separator as Separator12, Skeleton as Skeleton36, cn as cn54 } from "@ai-matrx/design-system";
|
|
15939
16358
|
import { Fragment as Fragment28, jsx as jsx61, jsxs as jsxs56 } from "react/jsx-runtime";
|
|
@@ -15984,9 +16403,43 @@ function chooseSurface(current, pressed) {
|
|
|
15984
16403
|
function surfaceChosen(current, asking) {
|
|
15985
16404
|
return asking.main !== void 0 ? current.main === asking.main : current.rail === asking.rail;
|
|
15986
16405
|
}
|
|
15987
|
-
function openingRail(activeRecordId) {
|
|
15988
|
-
|
|
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;
|
|
15989
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.`;
|
|
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.";
|
|
15990
16443
|
function openingView(activeView, activeDashboardId) {
|
|
15991
16444
|
const named = pageViewFromParam(activeView);
|
|
15992
16445
|
if (named === null) {
|
|
@@ -16014,6 +16467,8 @@ function TablePage({
|
|
|
16014
16467
|
activeGroupField,
|
|
16015
16468
|
cameFrom,
|
|
16016
16469
|
filter,
|
|
16470
|
+
activeRail,
|
|
16471
|
+
activeItemId,
|
|
16017
16472
|
className
|
|
16018
16473
|
}) {
|
|
16019
16474
|
const client = useRecordsClient43();
|
|
@@ -16023,8 +16478,13 @@ function TablePage({
|
|
|
16023
16478
|
const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
|
|
16024
16479
|
const organizationId = useRecordsClient43().config.organizationId;
|
|
16025
16480
|
const [view, setView] = useState57(null);
|
|
16026
|
-
const opening = openingRail(activeRecordId);
|
|
16481
|
+
const opening = openingRail(activeRecordId, activeRail);
|
|
16027
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;
|
|
16028
16488
|
const [asking, setAsking] = useState57(null);
|
|
16029
16489
|
const [layoutFromLink, setLayoutFromLink] = useState57(opened.layout);
|
|
16030
16490
|
const [surface, setSurface] = useState57({
|
|
@@ -16044,12 +16504,18 @@ function TablePage({
|
|
|
16044
16504
|
}
|
|
16045
16505
|
};
|
|
16046
16506
|
const show = (next) => press({ rail: next });
|
|
16047
|
-
|
|
16507
|
+
useEffect46(() => {
|
|
16048
16508
|
if (!activeRecordId) return;
|
|
16049
16509
|
setOpenRecord(activeRecordId);
|
|
16050
16510
|
setSurface((now) => ({ ...now, rail: "record" }));
|
|
16051
16511
|
}, [activeRecordId]);
|
|
16052
|
-
|
|
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(() => {
|
|
16053
16519
|
const named = pageViewFromParam(activeView);
|
|
16054
16520
|
if (named === null) return;
|
|
16055
16521
|
const isLayout = named !== "dashboards" && named !== "archived";
|
|
@@ -16213,7 +16679,8 @@ function TablePage({
|
|
|
16213
16679
|
organizationId,
|
|
16214
16680
|
subjectId: tableId,
|
|
16215
16681
|
name: table.data?.name,
|
|
16216
|
-
may: rights.share
|
|
16682
|
+
may: rights.share,
|
|
16683
|
+
initiallyOpen: shareFromLink
|
|
16217
16684
|
}
|
|
16218
16685
|
),
|
|
16219
16686
|
/* @__PURE__ */ jsx61(ExportMenu, { tableId })
|
|
@@ -16236,6 +16703,8 @@ function TablePage({
|
|
|
16236
16703
|
}
|
|
16237
16704
|
)
|
|
16238
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,
|
|
16239
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,
|
|
16240
16709
|
cameFrom ? /* @__PURE__ */ jsx61(
|
|
16241
16710
|
"p",
|
|
@@ -16294,7 +16763,7 @@ function TablePage({
|
|
|
16294
16763
|
}
|
|
16295
16764
|
}
|
|
16296
16765
|
) : null,
|
|
16297
|
-
rail === "forms" ? /* @__PURE__ */ jsx61(FormsPanel, { tableId }) : null,
|
|
16766
|
+
rail === "forms" ? /* @__PURE__ */ jsx61(FormsPanel, { tableId, activeFormId: itemFor("forms") }) : null,
|
|
16298
16767
|
rail === "bookings" ? /* @__PURE__ */ jsx61(BookingSlots, { tableId }) : null,
|
|
16299
16768
|
rail === "checklists" ? /* @__PURE__ */ jsx61(
|
|
16300
16769
|
ChecklistsPanel,
|
|
@@ -16306,8 +16775,8 @@ function TablePage({
|
|
|
16306
16775
|
}
|
|
16307
16776
|
}
|
|
16308
16777
|
) : null,
|
|
16309
|
-
rail === "notifications" ? /* @__PURE__ */ jsx61(SubscriptionsPanel, { tableId }) : null,
|
|
16310
|
-
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,
|
|
16311
16780
|
rail === "import" ? /* @__PURE__ */ jsx61(ImportWizard, { tableId, onDone: () => setRail("none") }) : null,
|
|
16312
16781
|
rail === "field" ? /* @__PURE__ */ jsx61(FieldEditor, { tableId, onSaved: () => setRail("none"), onCancel: () => setRail("none") }) : null,
|
|
16313
16782
|
rail === "new-record" ? /* @__PURE__ */ jsx61(
|
|
@@ -16414,6 +16883,7 @@ export {
|
|
|
16414
16883
|
FORMULA_OP_LABEL,
|
|
16415
16884
|
FORMULA_OP_VALUES,
|
|
16416
16885
|
FORM_FLOWS,
|
|
16886
|
+
FORM_NOT_HERE_LINE,
|
|
16417
16887
|
FROZEN_COLUMN_WIDTH,
|
|
16418
16888
|
FieldControl,
|
|
16419
16889
|
FieldEditor,
|
|
@@ -16461,6 +16931,7 @@ export {
|
|
|
16461
16931
|
PAGE_VIEW_LABEL,
|
|
16462
16932
|
PARITY_LABEL,
|
|
16463
16933
|
PARITY_MADE_OF,
|
|
16934
|
+
PORTAL_NOT_HERE_LINE,
|
|
16464
16935
|
PROPOSED_CHANGE_ACT_LABEL,
|
|
16465
16936
|
PROPOSED_CHANGE_ACT_VALUES,
|
|
16466
16937
|
Peek,
|
|
@@ -16475,6 +16946,7 @@ export {
|
|
|
16475
16946
|
PublicViewPage,
|
|
16476
16947
|
ROLLUP_AGG_LABEL,
|
|
16477
16948
|
ROLLUP_AGG_VALUES,
|
|
16949
|
+
RULE_NOT_HERE_LINE,
|
|
16478
16950
|
RecordChat,
|
|
16479
16951
|
RecordChip,
|
|
16480
16952
|
RecordForm,
|
|
@@ -16489,6 +16961,7 @@ export {
|
|
|
16489
16961
|
SAVED_VIEWS_UNAVAILABLE,
|
|
16490
16962
|
SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS,
|
|
16491
16963
|
SERIES_COLORS,
|
|
16964
|
+
SHARE_NOT_YOURS_LINE,
|
|
16492
16965
|
SOMEBODY,
|
|
16493
16966
|
STAGE_RULE_ON_FAIL_LABEL,
|
|
16494
16967
|
STORE_ANSWERS_THESE,
|
|
@@ -16522,6 +16995,7 @@ export {
|
|
|
16522
16995
|
actorWords,
|
|
16523
16996
|
addFields,
|
|
16524
16997
|
archivedRowLine,
|
|
16998
|
+
asRefusal,
|
|
16525
16999
|
askableFields,
|
|
16526
17000
|
blockFromSpec,
|
|
16527
17001
|
bodyForReading,
|
|
@@ -16587,11 +17061,15 @@ export {
|
|
|
16587
17061
|
previewLine,
|
|
16588
17062
|
previewWords,
|
|
16589
17063
|
publiclyAnswerable,
|
|
17064
|
+
railFromParam,
|
|
16590
17065
|
recordName,
|
|
16591
17066
|
recordNameIn,
|
|
16592
17067
|
recordsDataSource,
|
|
17068
|
+
refusal,
|
|
16593
17069
|
refusalForAPerson,
|
|
17070
|
+
refusalFromThrown,
|
|
16594
17071
|
refusalLineForAPerson,
|
|
17072
|
+
refusalOr,
|
|
16595
17073
|
renderValue,
|
|
16596
17074
|
revokeConsequence,
|
|
16597
17075
|
rowName,
|
|
@@ -16605,6 +17083,7 @@ export {
|
|
|
16605
17083
|
tableName,
|
|
16606
17084
|
tableRightsAt,
|
|
16607
17085
|
tokenFor,
|
|
17086
|
+
unknownRailLine,
|
|
16608
17087
|
unknownViewLine,
|
|
16609
17088
|
useCanShare,
|
|
16610
17089
|
useEmbedHandshake,
|