@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.cjs
CHANGED
|
@@ -88,6 +88,7 @@ __export(src_exports, {
|
|
|
88
88
|
FORMULA_OP_LABEL: () => FORMULA_OP_LABEL,
|
|
89
89
|
FORMULA_OP_VALUES: () => FORMULA_OP_VALUES,
|
|
90
90
|
FORM_FLOWS: () => FORM_FLOWS,
|
|
91
|
+
FORM_NOT_HERE_LINE: () => FORM_NOT_HERE_LINE,
|
|
91
92
|
FROZEN_COLUMN_WIDTH: () => FROZEN_COLUMN_WIDTH,
|
|
92
93
|
FieldControl: () => FieldControl,
|
|
93
94
|
FieldEditor: () => FieldEditor,
|
|
@@ -135,6 +136,7 @@ __export(src_exports, {
|
|
|
135
136
|
PAGE_VIEW_LABEL: () => PAGE_VIEW_LABEL,
|
|
136
137
|
PARITY_LABEL: () => PARITY_LABEL,
|
|
137
138
|
PARITY_MADE_OF: () => PARITY_MADE_OF,
|
|
139
|
+
PORTAL_NOT_HERE_LINE: () => PORTAL_NOT_HERE_LINE,
|
|
138
140
|
PROPOSED_CHANGE_ACT_LABEL: () => PROPOSED_CHANGE_ACT_LABEL,
|
|
139
141
|
PROPOSED_CHANGE_ACT_VALUES: () => PROPOSED_CHANGE_ACT_VALUES,
|
|
140
142
|
Peek: () => Peek,
|
|
@@ -149,6 +151,7 @@ __export(src_exports, {
|
|
|
149
151
|
PublicViewPage: () => PublicViewPage,
|
|
150
152
|
ROLLUP_AGG_LABEL: () => ROLLUP_AGG_LABEL,
|
|
151
153
|
ROLLUP_AGG_VALUES: () => ROLLUP_AGG_VALUES,
|
|
154
|
+
RULE_NOT_HERE_LINE: () => RULE_NOT_HERE_LINE,
|
|
152
155
|
RecordChat: () => RecordChat,
|
|
153
156
|
RecordChip: () => RecordChip,
|
|
154
157
|
RecordForm: () => RecordForm,
|
|
@@ -163,6 +166,7 @@ __export(src_exports, {
|
|
|
163
166
|
SAVED_VIEWS_UNAVAILABLE: () => SAVED_VIEWS_UNAVAILABLE,
|
|
164
167
|
SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS: () => SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS,
|
|
165
168
|
SERIES_COLORS: () => SERIES_COLORS,
|
|
169
|
+
SHARE_NOT_YOURS_LINE: () => SHARE_NOT_YOURS_LINE,
|
|
166
170
|
SOMEBODY: () => SOMEBODY,
|
|
167
171
|
STAGE_RULE_ON_FAIL_LABEL: () => STAGE_RULE_ON_FAIL_LABEL,
|
|
168
172
|
STORE_ANSWERS_THESE: () => STORE_ANSWERS_THESE,
|
|
@@ -196,6 +200,7 @@ __export(src_exports, {
|
|
|
196
200
|
actorWords: () => actorWords,
|
|
197
201
|
addFields: () => addFields,
|
|
198
202
|
archivedRowLine: () => archivedRowLine,
|
|
203
|
+
asRefusal: () => asRefusal,
|
|
199
204
|
askableFields: () => askableFields,
|
|
200
205
|
blockFromSpec: () => blockFromSpec,
|
|
201
206
|
bodyForReading: () => bodyForReading,
|
|
@@ -261,11 +266,15 @@ __export(src_exports, {
|
|
|
261
266
|
previewLine: () => previewLine,
|
|
262
267
|
previewWords: () => previewWords,
|
|
263
268
|
publiclyAnswerable: () => publiclyAnswerable,
|
|
269
|
+
railFromParam: () => railFromParam,
|
|
264
270
|
recordName: () => recordName,
|
|
265
271
|
recordNameIn: () => recordNameIn,
|
|
266
272
|
recordsDataSource: () => recordsDataSource,
|
|
273
|
+
refusal: () => refusal,
|
|
267
274
|
refusalForAPerson: () => refusalForAPerson,
|
|
275
|
+
refusalFromThrown: () => refusalFromThrown,
|
|
268
276
|
refusalLineForAPerson: () => refusalLineForAPerson,
|
|
277
|
+
refusalOr: () => refusalOr,
|
|
269
278
|
renderValue: () => renderValue,
|
|
270
279
|
revokeConsequence: () => revokeConsequence,
|
|
271
280
|
rowName: () => rowName,
|
|
@@ -279,6 +288,7 @@ __export(src_exports, {
|
|
|
279
288
|
tableName: () => tableName,
|
|
280
289
|
tableRightsAt: () => tableRightsAt,
|
|
281
290
|
tokenFor: () => tokenFor,
|
|
291
|
+
unknownRailLine: () => unknownRailLine,
|
|
282
292
|
unknownViewLine: () => unknownViewLine,
|
|
283
293
|
useCanShare: () => useCanShare,
|
|
284
294
|
useEmbedHandshake: () => useEmbedHandshake,
|
|
@@ -826,13 +836,13 @@ function ReferenceBuilder({
|
|
|
826
836
|
// src/PersonPicker.tsx
|
|
827
837
|
var import_react9 = require("react");
|
|
828
838
|
var import_react10 = require("@ai-matrx/records/react");
|
|
829
|
-
var
|
|
839
|
+
var import_design_system5 = require("@ai-matrx/design-system");
|
|
830
840
|
|
|
831
841
|
// src/editors.tsx
|
|
832
842
|
var import_react7 = require("react");
|
|
833
843
|
var import_records4 = require("@ai-matrx/records");
|
|
834
844
|
var import_react8 = require("@ai-matrx/records/react");
|
|
835
|
-
var
|
|
845
|
+
var import_design_system4 = require("@ai-matrx/design-system");
|
|
836
846
|
|
|
837
847
|
// src/parity.ts
|
|
838
848
|
var import_records3 = require("@ai-matrx/records");
|
|
@@ -934,8 +944,119 @@ function fieldTypeLabel(field) {
|
|
|
934
944
|
// src/RelationPicker.tsx
|
|
935
945
|
var import_react5 = require("react");
|
|
936
946
|
var import_react6 = require("@ai-matrx/records/react");
|
|
947
|
+
var import_design_system3 = require("@ai-matrx/design-system");
|
|
948
|
+
|
|
949
|
+
// src/Refusal.tsx
|
|
937
950
|
var import_design_system2 = require("@ai-matrx/design-system");
|
|
938
|
-
var import_jsx_runtime3 = (
|
|
951
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
952
|
+
function hintIsMachineIdentity(hint) {
|
|
953
|
+
return isMachineIdentity(hint);
|
|
954
|
+
}
|
|
955
|
+
function hintForAPerson(hint) {
|
|
956
|
+
const said = (hint ?? "").trim();
|
|
957
|
+
if (said === "") return null;
|
|
958
|
+
return isMachineIdentity(said) ? null : said;
|
|
959
|
+
}
|
|
960
|
+
function RefusalNotice({
|
|
961
|
+
error,
|
|
962
|
+
className,
|
|
963
|
+
actions,
|
|
964
|
+
onKeepEditing,
|
|
965
|
+
onDiscard
|
|
966
|
+
}) {
|
|
967
|
+
const plain = refusalForAPerson(error);
|
|
968
|
+
const doors = onKeepEditing || onDiscard ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-wrap items-center gap-1 pt-0.5", "data-refusal-doors": "", children: [
|
|
969
|
+
onKeepEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
970
|
+
"button",
|
|
971
|
+
{
|
|
972
|
+
type: "button",
|
|
973
|
+
"data-refusal-keep-editing": "",
|
|
974
|
+
className: "rounded border px-2 py-0.5 text-xs hover:bg-muted",
|
|
975
|
+
onClick: onKeepEditing,
|
|
976
|
+
children: "Keep editing"
|
|
977
|
+
}
|
|
978
|
+
) : null,
|
|
979
|
+
onDiscard ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
980
|
+
"button",
|
|
981
|
+
{
|
|
982
|
+
type: "button",
|
|
983
|
+
"data-refusal-discard": "",
|
|
984
|
+
className: "rounded border px-2 py-0.5 text-xs hover:bg-muted",
|
|
985
|
+
onClick: onDiscard,
|
|
986
|
+
children: "Discard"
|
|
987
|
+
}
|
|
988
|
+
) : null
|
|
989
|
+
] }) : null;
|
|
990
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
991
|
+
import_design_system2.Alert,
|
|
992
|
+
{
|
|
993
|
+
variant: "destructive",
|
|
994
|
+
role: "alert",
|
|
995
|
+
"data-refusal-notice": "",
|
|
996
|
+
className: (0, import_design_system2.cn)("text-xs", className),
|
|
997
|
+
...plain.forEngineers ? { title: plain.forEngineers } : {},
|
|
998
|
+
children: [
|
|
999
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system2.AlertTitle, { className: "text-xs font-medium", children: plain.title }),
|
|
1000
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system2.AlertDescription, { className: "space-y-1 text-xs", children: [
|
|
1001
|
+
plain.sentence.replace(/\.$/, "") === plain.title ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: plain.sentence }),
|
|
1002
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "opacity-80", children: plain.remedy }),
|
|
1003
|
+
plain.forEngineers ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1004
|
+
"p",
|
|
1005
|
+
{
|
|
1006
|
+
className: "sr-only",
|
|
1007
|
+
"aria-hidden": "true",
|
|
1008
|
+
"data-for-engineers": "",
|
|
1009
|
+
...error.sqlstate ? { "data-sqlstate": error.sqlstate } : {},
|
|
1010
|
+
children: plain.forEngineers
|
|
1011
|
+
}
|
|
1012
|
+
) : null,
|
|
1013
|
+
actions,
|
|
1014
|
+
doors
|
|
1015
|
+
] })
|
|
1016
|
+
]
|
|
1017
|
+
}
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
function RefusalLine({ error, className }) {
|
|
1021
|
+
const plain = refusalForAPerson(error);
|
|
1022
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1023
|
+
"p",
|
|
1024
|
+
{
|
|
1025
|
+
role: "alert",
|
|
1026
|
+
"data-refusal-line": "",
|
|
1027
|
+
className: (0, import_design_system2.cn)("text-xs text-destructive", className),
|
|
1028
|
+
title: plain.forEngineers || void 0,
|
|
1029
|
+
children: refusalLineForAPerson(error)
|
|
1030
|
+
}
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// src/refusals.ts
|
|
1035
|
+
function refusal(code, sentence2, remedy, diagnostic) {
|
|
1036
|
+
return {
|
|
1037
|
+
code,
|
|
1038
|
+
message: sentence2,
|
|
1039
|
+
...remedy ? { hint: remedy } : {},
|
|
1040
|
+
...diagnostic ? { diagnostic } : {}
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
function refusalFromThrown(thrown, sentence2, remedy, code = "internal") {
|
|
1044
|
+
const said = thrown instanceof Error ? thrown.message : String(thrown ?? "");
|
|
1045
|
+
return refusal(code, sentence2, remedy, said ? `Thrown: ${said}` : null);
|
|
1046
|
+
}
|
|
1047
|
+
function refusalOr(error, code, sentence2) {
|
|
1048
|
+
return error ?? refusal(code, sentence2);
|
|
1049
|
+
}
|
|
1050
|
+
function asRefusal(thrown, sentence2, remedy, code = "internal") {
|
|
1051
|
+
const t = thrown;
|
|
1052
|
+
if (t && typeof t === "object" && typeof t.code === "string" && typeof t.message === "string" && !(thrown instanceof Error)) {
|
|
1053
|
+
return t;
|
|
1054
|
+
}
|
|
1055
|
+
return refusalFromThrown(thrown, sentence2, remedy, code);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// src/RelationPicker.tsx
|
|
1059
|
+
var import_jsx_runtime4 = (
|
|
939
1060
|
// The Field's own name is already printed above this control by
|
|
940
1061
|
// `FieldLabel`, so the sentence does not say it a second time.
|
|
941
1062
|
require("react/jsx-runtime")
|
|
@@ -945,7 +1066,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
945
1066
|
const client = (0, import_react6.useOptionalRecordsClient)();
|
|
946
1067
|
const [rows, setRows] = (0, import_react5.useState)(null);
|
|
947
1068
|
const [words, setWords] = (0, import_react5.useState)({});
|
|
948
|
-
const [
|
|
1069
|
+
const [refusal2, setRefusal] = (0, import_react5.useState)(null);
|
|
949
1070
|
const [search, setSearch] = (0, import_react5.useState)("");
|
|
950
1071
|
const [open, setOpen] = (0, import_react5.useState)(false);
|
|
951
1072
|
const [target, setTarget] = (0, import_react5.useState)(null);
|
|
@@ -965,7 +1086,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
965
1086
|
void client.list({ table_id: target2, limit: 200 }).then((result) => {
|
|
966
1087
|
if (cancelled) return;
|
|
967
1088
|
if (result.ok) setRows(result.data.rows);
|
|
968
|
-
else setRefusal(
|
|
1089
|
+
else setRefusal(result.error);
|
|
969
1090
|
});
|
|
970
1091
|
return () => {
|
|
971
1092
|
cancelled = true;
|
|
@@ -999,14 +1120,17 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
999
1120
|
void client.tableList().then((result) => {
|
|
1000
1121
|
if (cancelled) return;
|
|
1001
1122
|
if (!result.ok) {
|
|
1002
|
-
setTargetRefusal(
|
|
1123
|
+
setTargetRefusal(result.error);
|
|
1003
1124
|
return;
|
|
1004
1125
|
}
|
|
1005
1126
|
const found = result.data.find((t) => t.id === id2) ?? null;
|
|
1006
1127
|
if (found) setTarget(found);
|
|
1007
1128
|
else
|
|
1008
1129
|
setTargetRefusal(
|
|
1009
|
-
|
|
1130
|
+
refusal(
|
|
1131
|
+
"door",
|
|
1132
|
+
"That Table is not one you can open, so a new record cannot be added to it from here."
|
|
1133
|
+
)
|
|
1010
1134
|
);
|
|
1011
1135
|
});
|
|
1012
1136
|
return () => {
|
|
@@ -1014,10 +1138,10 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1014
1138
|
};
|
|
1015
1139
|
}, [client, open, allowCreate, target, targetRefusal, field.relation_target]);
|
|
1016
1140
|
if (!client) {
|
|
1017
|
-
return /* @__PURE__ */ (0,
|
|
1141
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { id, className: (0, import_design_system3.cn)("text-xs text-muted-foreground", className), children: NO_STORE_ON_THIS_SCREEN_REASON });
|
|
1018
1142
|
}
|
|
1019
1143
|
if (!field.relation_target) {
|
|
1020
|
-
return /* @__PURE__ */ (0,
|
|
1144
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
1021
1145
|
fieldName(field),
|
|
1022
1146
|
" 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."
|
|
1023
1147
|
] });
|
|
@@ -1041,13 +1165,17 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1041
1165
|
const tableId = field.relation_target;
|
|
1042
1166
|
if (!tableId || title === "" || busy) return;
|
|
1043
1167
|
if (!target) {
|
|
1044
|
-
setCreateRefusal("Still reading that Table. Try again in a moment.");
|
|
1168
|
+
setCreateRefusal(refusal("unreachable", "Still reading that Table.", "Try again in a moment."));
|
|
1045
1169
|
return;
|
|
1046
1170
|
}
|
|
1047
1171
|
const titleField = target.title_field;
|
|
1048
1172
|
if (!titleField) {
|
|
1049
1173
|
setCreateRefusal(
|
|
1050
|
-
|
|
1174
|
+
refusal(
|
|
1175
|
+
"not_supported",
|
|
1176
|
+
`${target.name} has no column that names a record yet, so a new one would have nothing to show here.`,
|
|
1177
|
+
"A table admin picks its title column in that table's settings."
|
|
1178
|
+
)
|
|
1051
1179
|
);
|
|
1052
1180
|
return;
|
|
1053
1181
|
}
|
|
@@ -1056,7 +1184,7 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1056
1184
|
const written = await client.recordWrite({ table_id: tableId, data: { [titleField]: title } });
|
|
1057
1185
|
if (!written.ok) {
|
|
1058
1186
|
setBusy(false);
|
|
1059
|
-
setCreateRefusal(
|
|
1187
|
+
setCreateRefusal(written.error);
|
|
1060
1188
|
return;
|
|
1061
1189
|
}
|
|
1062
1190
|
const createdId = String(written.data);
|
|
@@ -1071,8 +1199,8 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1071
1199
|
const visible = (rows ?? []).filter(
|
|
1072
1200
|
(row) => search.trim() === "" ? true : titleOf(row.id).toLowerCase().includes(search.trim().toLowerCase())
|
|
1073
1201
|
);
|
|
1074
|
-
return /* @__PURE__ */ (0,
|
|
1075
|
-
picked.map((recordId) => /* @__PURE__ */ (0,
|
|
1202
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: (0, import_design_system3.cn)("flex flex-wrap items-center gap-1", className), children: [
|
|
1203
|
+
picked.map((recordId) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1076
1204
|
RecordChip,
|
|
1077
1205
|
{
|
|
1078
1206
|
title: titleOf(recordId),
|
|
@@ -1080,11 +1208,11 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1080
1208
|
},
|
|
1081
1209
|
recordId
|
|
1082
1210
|
)),
|
|
1083
|
-
disabled ? null : /* @__PURE__ */ (0,
|
|
1084
|
-
/* @__PURE__ */ (0,
|
|
1085
|
-
/* @__PURE__ */ (0,
|
|
1086
|
-
/* @__PURE__ */ (0,
|
|
1087
|
-
|
|
1211
|
+
disabled ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.Popover, { open, onOpenChange: setOpen, children: [
|
|
1212
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_design_system3.Button, { id, type: "button", size: "sm", variant: "outline", "aria-label": `Pick for ${fieldName(field)}`, children: picked.length === 0 ? "Pick" : "Change" }) }),
|
|
1213
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.PopoverContent, { sizing: "content", className: "p-1", align: "start", children: [
|
|
1214
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1215
|
+
import_design_system3.BasicInput,
|
|
1088
1216
|
{
|
|
1089
1217
|
value: search,
|
|
1090
1218
|
onChange: (e) => setSearch(e.target.value),
|
|
@@ -1092,34 +1220,34 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1092
1220
|
className: "mb-1 h-7 text-xs"
|
|
1093
1221
|
}
|
|
1094
1222
|
),
|
|
1095
|
-
|
|
1096
|
-
!rows && !
|
|
1097
|
-
full ? /* @__PURE__ */ (0,
|
|
1223
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RefusalLine, { error: refusal2, className: "p-2" }) : null,
|
|
1224
|
+
!rows && !refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading\u2026" }) : null,
|
|
1225
|
+
full ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "p-2 text-xs text-muted-foreground", children: [
|
|
1098
1226
|
fieldName(field),
|
|
1099
1227
|
" can point at ",
|
|
1100
1228
|
field.relation_max,
|
|
1101
1229
|
" at most. Remove one to add another."
|
|
1102
1230
|
] }) : null,
|
|
1103
|
-
/* @__PURE__ */ (0,
|
|
1104
|
-
/* @__PURE__ */ (0,
|
|
1231
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system3.ScrollArea, { className: "max-h-56", children: [
|
|
1232
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { children: visible.map((row) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1105
1233
|
"button",
|
|
1106
1234
|
{
|
|
1107
1235
|
type: "button",
|
|
1108
1236
|
onClick: () => toggle(row.id),
|
|
1109
1237
|
title: titleOf(row.id),
|
|
1110
|
-
className: (0,
|
|
1238
|
+
className: (0, import_design_system3.cn)(
|
|
1111
1239
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1112
1240
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
1113
1241
|
),
|
|
1114
1242
|
children: titleOf(row.id)
|
|
1115
1243
|
}
|
|
1116
1244
|
) }, row.id)) }),
|
|
1117
|
-
rows && visible.length === 0 ? /* @__PURE__ */ (0,
|
|
1245
|
+
rows && visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Nothing in that table matches." }) : null
|
|
1118
1246
|
] }),
|
|
1119
|
-
allowCreate && !full ? /* @__PURE__ */ (0,
|
|
1120
|
-
targetRefusal ? /* @__PURE__ */ (0,
|
|
1121
|
-
/* @__PURE__ */ (0,
|
|
1122
|
-
|
|
1247
|
+
allowCreate && !full ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mt-1 border-t pt-1", children: [
|
|
1248
|
+
targetRefusal ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RefusalLine, { error: targetRefusal, className: "p-2" }) : !target ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading that table\u2026" }) : creating ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1 p-1", children: [
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1250
|
+
import_design_system3.BasicInput,
|
|
1123
1251
|
{
|
|
1124
1252
|
autoFocus: true,
|
|
1125
1253
|
value: newTitle,
|
|
@@ -1135,8 +1263,8 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1135
1263
|
"aria-label": `Name the new ${targetWord}`
|
|
1136
1264
|
}
|
|
1137
1265
|
),
|
|
1138
|
-
/* @__PURE__ */ (0,
|
|
1139
|
-
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1267
|
+
import_design_system3.Button,
|
|
1140
1268
|
{
|
|
1141
1269
|
type: "button",
|
|
1142
1270
|
size: "sm",
|
|
@@ -1146,8 +1274,8 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1146
1274
|
children: busy ? "Creating\u2026" : "Create"
|
|
1147
1275
|
}
|
|
1148
1276
|
)
|
|
1149
|
-
] }) : /* @__PURE__ */ (0,
|
|
1150
|
-
|
|
1277
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1278
|
+
import_design_system3.Button,
|
|
1151
1279
|
{
|
|
1152
1280
|
type: "button",
|
|
1153
1281
|
size: "sm",
|
|
@@ -1162,10 +1290,25 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1162
1290
|
setCreating(true);
|
|
1163
1291
|
}
|
|
1164
1292
|
},
|
|
1165
|
-
children: /* @__PURE__ */ (0,
|
|
1293
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "block w-full truncate text-left", children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}` })
|
|
1166
1294
|
}
|
|
1167
1295
|
),
|
|
1168
|
-
createRefusal ?
|
|
1296
|
+
createRefusal ? (
|
|
1297
|
+
// The typed title is still in the search box: the notice owns the
|
|
1298
|
+
// two doors out (lane REFUSAL-SWEEP).
|
|
1299
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1300
|
+
RefusalNotice,
|
|
1301
|
+
{
|
|
1302
|
+
error: createRefusal,
|
|
1303
|
+
className: "mt-1 max-w-72",
|
|
1304
|
+
onKeepEditing: () => setCreateRefusal(null),
|
|
1305
|
+
onDiscard: () => {
|
|
1306
|
+
setCreateRefusal(null);
|
|
1307
|
+
setSearch("");
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
)
|
|
1311
|
+
) : null
|
|
1169
1312
|
] }) : null
|
|
1170
1313
|
] })
|
|
1171
1314
|
] })
|
|
@@ -1173,12 +1316,12 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1173
1316
|
}
|
|
1174
1317
|
|
|
1175
1318
|
// src/editors.tsx
|
|
1176
|
-
var
|
|
1319
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1177
1320
|
function FieldLabel({ field, htmlFor, children }) {
|
|
1178
|
-
return /* @__PURE__ */ (0,
|
|
1179
|
-
/* @__PURE__ */ (0,
|
|
1180
|
-
field.required ? /* @__PURE__ */ (0,
|
|
1181
|
-
field.unit ? /* @__PURE__ */ (0,
|
|
1321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system4.Label, { htmlFor, className: "text-xs font-medium", children: fieldName(field) }),
|
|
1323
|
+
field.required ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs text-destructive", children: "required" }) : null,
|
|
1324
|
+
field.unit ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs text-muted-foreground", children: field.unit }) : null,
|
|
1182
1325
|
children
|
|
1183
1326
|
] });
|
|
1184
1327
|
}
|
|
@@ -1189,18 +1332,18 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1189
1332
|
case "formula":
|
|
1190
1333
|
case "lookup":
|
|
1191
1334
|
case "rollup":
|
|
1192
|
-
return /* @__PURE__ */ (0,
|
|
1335
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { id: inputId, className: "rounded border border-dashed px-2 py-1.5 text-xs text-muted-foreground", children: [
|
|
1193
1336
|
value === null || value === void 0 ? "\u2014" : String(value),
|
|
1194
|
-
/* @__PURE__ */ (0,
|
|
1337
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ml-2 italic", children: [
|
|
1195
1338
|
fieldName(field),
|
|
1196
1339
|
" is worked out by the system, so it cannot be typed in"
|
|
1197
1340
|
] })
|
|
1198
1341
|
] });
|
|
1199
1342
|
case "json":
|
|
1200
|
-
return /* @__PURE__ */ (0,
|
|
1343
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(JsonControl, { field, value, onChange, disabled, id: inputId });
|
|
1201
1344
|
case "long_text":
|
|
1202
|
-
return /* @__PURE__ */ (0,
|
|
1203
|
-
|
|
1345
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1346
|
+
import_design_system4.BasicTextarea,
|
|
1204
1347
|
{
|
|
1205
1348
|
id: inputId,
|
|
1206
1349
|
rows: 3,
|
|
@@ -1212,8 +1355,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1212
1355
|
case "number":
|
|
1213
1356
|
case "currency":
|
|
1214
1357
|
case "percent":
|
|
1215
|
-
return /* @__PURE__ */ (0,
|
|
1216
|
-
|
|
1358
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1359
|
+
import_design_system4.BasicInput,
|
|
1217
1360
|
{
|
|
1218
1361
|
id: inputId,
|
|
1219
1362
|
type: "number",
|
|
@@ -1225,8 +1368,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1225
1368
|
}
|
|
1226
1369
|
);
|
|
1227
1370
|
case "datetime":
|
|
1228
|
-
return /* @__PURE__ */ (0,
|
|
1229
|
-
|
|
1371
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1372
|
+
import_design_system4.BasicInput,
|
|
1230
1373
|
{
|
|
1231
1374
|
id: inputId,
|
|
1232
1375
|
type: field.format === "datetime" ? "datetime-local" : "date",
|
|
@@ -1238,8 +1381,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1238
1381
|
case "email":
|
|
1239
1382
|
case "phone":
|
|
1240
1383
|
case "url":
|
|
1241
|
-
return /* @__PURE__ */ (0,
|
|
1242
|
-
|
|
1384
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1385
|
+
import_design_system4.BasicInput,
|
|
1243
1386
|
{
|
|
1244
1387
|
id: inputId,
|
|
1245
1388
|
type: kind === "email" ? "email" : kind === "phone" ? "tel" : "url",
|
|
@@ -1249,19 +1392,19 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1249
1392
|
}
|
|
1250
1393
|
);
|
|
1251
1394
|
case "checkbox":
|
|
1252
|
-
return /* @__PURE__ */ (0,
|
|
1395
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CheckboxControl, { field, value, onChange, disabled, id: inputId });
|
|
1253
1396
|
case "select":
|
|
1254
1397
|
case "multi_select":
|
|
1255
|
-
return /* @__PURE__ */ (0,
|
|
1398
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OptionControl, { field, value, onChange, disabled, id: inputId });
|
|
1256
1399
|
case "member":
|
|
1257
|
-
return /* @__PURE__ */ (0,
|
|
1400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PersonPicker, { field, value, onChange, disabled, id: inputId });
|
|
1258
1401
|
case "relation":
|
|
1259
1402
|
case "attachment":
|
|
1260
|
-
return /* @__PURE__ */ (0,
|
|
1403
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RelationPicker, { field, value, onChange, disabled, id: inputId });
|
|
1261
1404
|
default:
|
|
1262
1405
|
if (typeof value === "boolean" || field.config?.["kind"] === "boolean") {
|
|
1263
|
-
return /* @__PURE__ */ (0,
|
|
1264
|
-
|
|
1406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1407
|
+
import_design_system4.Checkbox,
|
|
1265
1408
|
{
|
|
1266
1409
|
id: inputId,
|
|
1267
1410
|
checked: value === true,
|
|
@@ -1270,8 +1413,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1270
1413
|
}
|
|
1271
1414
|
);
|
|
1272
1415
|
}
|
|
1273
|
-
return /* @__PURE__ */ (0,
|
|
1274
|
-
|
|
1416
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1417
|
+
import_design_system4.BasicInput,
|
|
1275
1418
|
{
|
|
1276
1419
|
id: inputId,
|
|
1277
1420
|
value: value === null || value === void 0 ? "" : String(value),
|
|
@@ -1283,9 +1426,9 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1283
1426
|
}
|
|
1284
1427
|
function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
1285
1428
|
const unanswered = value === null || value === void 0;
|
|
1286
|
-
return /* @__PURE__ */ (0,
|
|
1287
|
-
/* @__PURE__ */ (0,
|
|
1288
|
-
|
|
1429
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1431
|
+
import_design_system4.Checkbox,
|
|
1289
1432
|
{
|
|
1290
1433
|
id,
|
|
1291
1434
|
checked: value === true,
|
|
@@ -1294,10 +1437,10 @@ function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
|
1294
1437
|
onCheckedChange: (next) => onChange(next === true)
|
|
1295
1438
|
}
|
|
1296
1439
|
),
|
|
1297
|
-
unanswered ? /* @__PURE__ */ (0,
|
|
1298
|
-
/* @__PURE__ */ (0,
|
|
1299
|
-
field.required ? null : /* @__PURE__ */ (0,
|
|
1300
|
-
|
|
1440
|
+
unanswered ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs italic text-muted-foreground", children: "not answered yet" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs text-muted-foreground", children: value === true ? "Yes" : "No" }),
|
|
1442
|
+
field.required ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1443
|
+
import_design_system4.Button,
|
|
1301
1444
|
{
|
|
1302
1445
|
size: "sm",
|
|
1303
1446
|
variant: "ghost",
|
|
@@ -1314,9 +1457,9 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1314
1457
|
const initial = value === null || value === void 0 ? "" : typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
1315
1458
|
const [text, setText] = (0, import_react7.useState)(initial);
|
|
1316
1459
|
const [broken, setBroken] = (0, import_react7.useState)(null);
|
|
1317
|
-
return /* @__PURE__ */ (0,
|
|
1318
|
-
/* @__PURE__ */ (0,
|
|
1319
|
-
|
|
1460
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
1461
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1462
|
+
import_design_system4.BasicTextarea,
|
|
1320
1463
|
{
|
|
1321
1464
|
id,
|
|
1322
1465
|
rows: 4,
|
|
@@ -1335,38 +1478,54 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1335
1478
|
onChange(JSON.parse(next));
|
|
1336
1479
|
setBroken(null);
|
|
1337
1480
|
} catch (thrown) {
|
|
1338
|
-
setBroken(
|
|
1481
|
+
setBroken(
|
|
1482
|
+
refusalFromThrown(
|
|
1483
|
+
thrown,
|
|
1484
|
+
`${fieldName(field)} is not readable as structured data yet.`,
|
|
1485
|
+
"Fix the text so it reads as structured data, or discard it to put back what was there.",
|
|
1486
|
+
"invalid_argument"
|
|
1487
|
+
)
|
|
1488
|
+
);
|
|
1339
1489
|
}
|
|
1340
1490
|
}
|
|
1341
1491
|
}
|
|
1342
1492
|
),
|
|
1343
|
-
broken ?
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1493
|
+
broken ? (
|
|
1494
|
+
// The typed text is still in the box: the notice owns the two doors out.
|
|
1495
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1496
|
+
RefusalNotice,
|
|
1497
|
+
{
|
|
1498
|
+
error: broken,
|
|
1499
|
+
onKeepEditing: () => id ? document.getElementById(id)?.focus() : void 0,
|
|
1500
|
+
onDiscard: () => {
|
|
1501
|
+
setText(initial);
|
|
1502
|
+
setBroken(null);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
)
|
|
1506
|
+
) : null
|
|
1348
1507
|
] });
|
|
1349
1508
|
}
|
|
1350
1509
|
function OptionControl({ field, value, onChange, disabled, id }) {
|
|
1351
1510
|
const client = (0, import_react8.useOptionalRecordsClient)();
|
|
1352
1511
|
const [options, setOptions] = (0, import_react7.useState)(null);
|
|
1353
|
-
const [
|
|
1512
|
+
const [refusal2, setRefusal] = (0, import_react7.useState)(null);
|
|
1354
1513
|
(0, import_react7.useEffect)(() => {
|
|
1355
1514
|
if (!client) return;
|
|
1356
1515
|
let cancelled = false;
|
|
1357
1516
|
void client.fieldOptions({ field_id: field.id }).then((result) => {
|
|
1358
1517
|
if (cancelled) return;
|
|
1359
1518
|
if (result.ok) setOptions(result.data ?? []);
|
|
1360
|
-
else setRefusal(
|
|
1519
|
+
else setRefusal(result.error);
|
|
1361
1520
|
});
|
|
1362
1521
|
return () => {
|
|
1363
1522
|
cancelled = true;
|
|
1364
1523
|
};
|
|
1365
1524
|
}, [client, field.id]);
|
|
1366
1525
|
if (!client) {
|
|
1367
|
-
return /* @__PURE__ */ (0,
|
|
1368
|
-
/* @__PURE__ */ (0,
|
|
1369
|
-
|
|
1526
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1528
|
+
import_design_system4.BasicInput,
|
|
1370
1529
|
{
|
|
1371
1530
|
id,
|
|
1372
1531
|
value: value === null || value === void 0 ? "" : String(value),
|
|
@@ -1374,20 +1533,21 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1374
1533
|
onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
|
|
1375
1534
|
}
|
|
1376
1535
|
),
|
|
1377
|
-
/* @__PURE__ */ (0,
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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." })
|
|
1378
1537
|
] });
|
|
1379
1538
|
}
|
|
1380
|
-
if (
|
|
1381
|
-
|
|
1539
|
+
if (refusal2)
|
|
1540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { id, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RefusalLine, { error: refusal2 }) });
|
|
1541
|
+
if (!options) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { id, className: "text-xs text-muted-foreground", children: "Reading this field's choices\u2026" });
|
|
1382
1542
|
if (options.length === 0) {
|
|
1383
|
-
return /* @__PURE__ */ (0,
|
|
1543
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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.` });
|
|
1384
1544
|
}
|
|
1385
1545
|
const titleOf = (option) => recordName(option.data, null, "Unnamed choice");
|
|
1386
1546
|
if (field.multi) {
|
|
1387
1547
|
const chosen = options.filter((option) => (0, import_records4.choiceValuesOf)(value).some((v) => (0, import_records4.isTheChosen)(option, v)));
|
|
1388
1548
|
const picked = new Set(chosen.map((option) => (0, import_records4.optionKey)(option)));
|
|
1389
|
-
return /* @__PURE__ */ (0,
|
|
1390
|
-
|
|
1549
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { id, className: "flex flex-wrap gap-1", children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1550
|
+
import_design_system4.Button,
|
|
1391
1551
|
{
|
|
1392
1552
|
type: "button",
|
|
1393
1553
|
size: "sm",
|
|
@@ -1405,15 +1565,15 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1405
1565
|
)) });
|
|
1406
1566
|
}
|
|
1407
1567
|
const current = options.find((option) => (0, import_records4.isTheChosen)(option, value));
|
|
1408
|
-
return /* @__PURE__ */ (0,
|
|
1409
|
-
|
|
1568
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1569
|
+
import_design_system4.Select,
|
|
1410
1570
|
{
|
|
1411
1571
|
value: current ? (0, import_records4.optionKey)(current) : "",
|
|
1412
1572
|
disabled: disabled ?? false,
|
|
1413
1573
|
onValueChange: (next) => onChange(next === "" ? null : next),
|
|
1414
1574
|
children: [
|
|
1415
|
-
/* @__PURE__ */ (0,
|
|
1416
|
-
/* @__PURE__ */ (0,
|
|
1575
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system4.SelectTrigger, { id, size: "sm", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system4.SelectValue, { placeholder: "Choose" }) }),
|
|
1576
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system4.SelectContent, { children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system4.SelectItem, { value: (0, import_records4.optionKey)(option), children: titleOf(option) }, option.id)) })
|
|
1417
1577
|
]
|
|
1418
1578
|
}
|
|
1419
1579
|
);
|
|
@@ -1429,15 +1589,15 @@ function RecordChip({
|
|
|
1429
1589
|
// name. So it caps at its container and ends in an ellipsis with the whole name in
|
|
1430
1590
|
// its tooltip, rather than being cut mid-word by whatever box it landed in
|
|
1431
1591
|
// (lane FIX-14, 2026-09-22).
|
|
1432
|
-
/* @__PURE__ */ (0,
|
|
1433
|
-
/* @__PURE__ */ (0,
|
|
1434
|
-
onRemove ? /* @__PURE__ */ (0,
|
|
1592
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_design_system4.Badge, { variant: "secondary", className: (0, import_design_system4.cn)("max-w-full gap-1 text-[11px]", className), title, children: [
|
|
1593
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "min-w-0 truncate", children: title }),
|
|
1594
|
+
onRemove ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: onRemove, "aria-label": `Remove ${title}`, className: "shrink-0 opacity-60 hover:opacity-100", children: "\xD7" }) : null
|
|
1435
1595
|
] })
|
|
1436
1596
|
);
|
|
1437
1597
|
}
|
|
1438
1598
|
|
|
1439
1599
|
// src/PersonPicker.tsx
|
|
1440
|
-
var
|
|
1600
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1441
1601
|
function personFromRow(row) {
|
|
1442
1602
|
const data = row.document ?? {};
|
|
1443
1603
|
const userId = data["user_id"];
|
|
@@ -1490,7 +1650,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1490
1650
|
]);
|
|
1491
1651
|
setMembers(roster);
|
|
1492
1652
|
if (records && !records.ok) {
|
|
1493
|
-
setError(
|
|
1653
|
+
setError(records.error);
|
|
1494
1654
|
return;
|
|
1495
1655
|
}
|
|
1496
1656
|
setPeople(records ? records.data.rows.map(personFromRow).filter((p) => p.userId !== null) : []);
|
|
@@ -1511,10 +1671,10 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1511
1671
|
};
|
|
1512
1672
|
}, [client, personTable, picked.length, people.length]);
|
|
1513
1673
|
if (!membersPort || !client) {
|
|
1514
|
-
return /* @__PURE__ */ (0,
|
|
1674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: (0, import_design_system5.cn)("text-xs text-muted-foreground", className), children: NO_MEMBERS_REASON });
|
|
1515
1675
|
}
|
|
1516
1676
|
if (!personTable) {
|
|
1517
|
-
return /* @__PURE__ */ (0,
|
|
1677
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: (0, import_design_system5.cn)("text-xs text-muted-foreground", className), children: [
|
|
1518
1678
|
fieldName(field),
|
|
1519
1679
|
" 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."
|
|
1520
1680
|
] });
|
|
@@ -1532,7 +1692,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1532
1692
|
const resolved = await personRecordForMember(client, personTable, people, member);
|
|
1533
1693
|
setBusy(false);
|
|
1534
1694
|
if (!resolved.ok) {
|
|
1535
|
-
setError(
|
|
1695
|
+
setError(resolved.error);
|
|
1536
1696
|
return;
|
|
1537
1697
|
}
|
|
1538
1698
|
if (!people.some((p) => p.id === resolved.data)) {
|
|
@@ -1558,8 +1718,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1558
1718
|
const pickedUserIds = new Set(
|
|
1559
1719
|
picked.map((recordId) => people.find((p) => p.id === recordId)?.userId).filter(Boolean)
|
|
1560
1720
|
);
|
|
1561
|
-
return /* @__PURE__ */ (0,
|
|
1562
|
-
picked.map((recordId) => /* @__PURE__ */ (0,
|
|
1721
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: (0, import_design_system5.cn)("flex flex-wrap items-center gap-1", className), children: [
|
|
1722
|
+
picked.map((recordId) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1563
1723
|
RecordChip,
|
|
1564
1724
|
{
|
|
1565
1725
|
title: nameOfRecord(recordId),
|
|
@@ -1567,9 +1727,9 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1567
1727
|
},
|
|
1568
1728
|
recordId
|
|
1569
1729
|
)),
|
|
1570
|
-
disabled ? null : /* @__PURE__ */ (0,
|
|
1571
|
-
/* @__PURE__ */ (0,
|
|
1572
|
-
|
|
1730
|
+
disabled ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system5.Popover, { open, onOpenChange: setOpen, children: [
|
|
1731
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1732
|
+
import_design_system5.Button,
|
|
1573
1733
|
{
|
|
1574
1734
|
id,
|
|
1575
1735
|
type: "button",
|
|
@@ -1579,9 +1739,9 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1579
1739
|
children: picked.length === 0 ? "Pick a person" : "Change"
|
|
1580
1740
|
}
|
|
1581
1741
|
) }),
|
|
1582
|
-
/* @__PURE__ */ (0,
|
|
1583
|
-
/* @__PURE__ */ (0,
|
|
1584
|
-
|
|
1742
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system5.PopoverContent, { sizing: "content", className: "p-1", align: "start", children: [
|
|
1743
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1744
|
+
import_design_system5.BasicInput,
|
|
1585
1745
|
{
|
|
1586
1746
|
value: search,
|
|
1587
1747
|
onChange: (e) => setSearch(e.target.value),
|
|
@@ -1589,29 +1749,29 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1589
1749
|
className: "mb-1 h-7 text-xs"
|
|
1590
1750
|
}
|
|
1591
1751
|
),
|
|
1592
|
-
error ? /* @__PURE__ */ (0,
|
|
1593
|
-
members === null ? /* @__PURE__ */ (0,
|
|
1594
|
-
/* @__PURE__ */ (0,
|
|
1595
|
-
/* @__PURE__ */ (0,
|
|
1752
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RefusalLine, { error, className: "p-2" }) : null,
|
|
1753
|
+
members === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading the members\u2026" }) : null,
|
|
1754
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system5.ScrollArea, { className: "max-h-56", children: [
|
|
1755
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { children: visible.map((member) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1596
1756
|
"button",
|
|
1597
1757
|
{
|
|
1598
1758
|
type: "button",
|
|
1599
1759
|
disabled: busy,
|
|
1600
1760
|
onClick: () => void pick(member),
|
|
1601
|
-
className: (0,
|
|
1761
|
+
className: (0, import_design_system5.cn)(
|
|
1602
1762
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1603
1763
|
pickedUserIds.has(member.userId) ? "bg-accent text-accent-foreground" : ""
|
|
1604
1764
|
),
|
|
1605
1765
|
children: [
|
|
1606
|
-
/* @__PURE__ */ (0,
|
|
1607
|
-
member.email && member.email !== memberName(member) ? /* @__PURE__ */ (0,
|
|
1766
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: memberName(member) }),
|
|
1767
|
+
member.email && member.email !== memberName(member) ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ml-1.5 opacity-60", children: [
|
|
1608
1768
|
"\xB7 ",
|
|
1609
1769
|
member.email
|
|
1610
1770
|
] }) : null
|
|
1611
1771
|
]
|
|
1612
1772
|
}
|
|
1613
1773
|
) }, member.userId)) }),
|
|
1614
|
-
members !== null && visible.length === 0 ? /* @__PURE__ */ (0,
|
|
1774
|
+
members !== null && visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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
|
|
1615
1775
|
] })
|
|
1616
1776
|
] })
|
|
1617
1777
|
] })
|
|
@@ -1624,55 +1784,6 @@ var import_core = require("@ai-matrx/records/core");
|
|
|
1624
1784
|
var import_react16 = require("@ai-matrx/records/react");
|
|
1625
1785
|
var import_design_system7 = require("@ai-matrx/design-system");
|
|
1626
1786
|
|
|
1627
|
-
// src/Refusal.tsx
|
|
1628
|
-
var import_design_system5 = require("@ai-matrx/design-system");
|
|
1629
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1630
|
-
function hintIsMachineIdentity(hint) {
|
|
1631
|
-
return isMachineIdentity(hint);
|
|
1632
|
-
}
|
|
1633
|
-
function hintForAPerson(hint) {
|
|
1634
|
-
const said = (hint ?? "").trim();
|
|
1635
|
-
if (said === "") return null;
|
|
1636
|
-
return isMachineIdentity(said) ? null : said;
|
|
1637
|
-
}
|
|
1638
|
-
function RefusalNotice({
|
|
1639
|
-
error,
|
|
1640
|
-
className,
|
|
1641
|
-
actions
|
|
1642
|
-
}) {
|
|
1643
|
-
const plain = refusalForAPerson(error);
|
|
1644
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1645
|
-
import_design_system5.Alert,
|
|
1646
|
-
{
|
|
1647
|
-
variant: "destructive",
|
|
1648
|
-
className: (0, import_design_system5.cn)("text-xs", className),
|
|
1649
|
-
...plain.forEngineers ? { title: plain.forEngineers } : {},
|
|
1650
|
-
children: [
|
|
1651
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.AlertTitle, { className: "text-xs font-medium", children: plain.title }),
|
|
1652
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system5.AlertDescription, { className: "space-y-1 text-xs", children: [
|
|
1653
|
-
plain.sentence.replace(/\.$/, "") === plain.title ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: plain.sentence }),
|
|
1654
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "opacity-80", children: plain.remedy }),
|
|
1655
|
-
plain.forEngineers ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1656
|
-
"p",
|
|
1657
|
-
{
|
|
1658
|
-
className: "sr-only",
|
|
1659
|
-
"aria-hidden": "true",
|
|
1660
|
-
"data-for-engineers": "",
|
|
1661
|
-
...error.sqlstate ? { "data-sqlstate": error.sqlstate } : {},
|
|
1662
|
-
children: plain.forEngineers
|
|
1663
|
-
}
|
|
1664
|
-
) : null,
|
|
1665
|
-
actions
|
|
1666
|
-
] })
|
|
1667
|
-
]
|
|
1668
|
-
}
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
function RefusalLine({ error, className }) {
|
|
1672
|
-
const plain = refusalForAPerson(error);
|
|
1673
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: (0, import_design_system5.cn)("text-xs text-destructive", className), title: plain.forEngineers || void 0, children: refusalLineForAPerson(error) });
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
1787
|
// src/whoChangedSource.tsx
|
|
1677
1788
|
var import_react11 = require("react");
|
|
1678
1789
|
var import_react12 = require("@ai-matrx/records/react");
|
|
@@ -2508,7 +2619,7 @@ function GridCell({
|
|
|
2508
2619
|
);
|
|
2509
2620
|
const value = editing ? editing.valueFor(row, field) : row.document?.[field.key];
|
|
2510
2621
|
const state = editing ? editing.stateOf(row.id, field.key) : null;
|
|
2511
|
-
const
|
|
2622
|
+
const refusal2 = editing ? editing.refusalOf(row.id, field.key) : null;
|
|
2512
2623
|
const editable = Boolean(editing) && canWrite && fieldIsEditable(field);
|
|
2513
2624
|
if (open && editing) {
|
|
2514
2625
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -2537,8 +2648,8 @@ function GridCell({
|
|
|
2537
2648
|
}
|
|
2538
2649
|
),
|
|
2539
2650
|
answered ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-xs text-muted-foreground", title: "nobody has answered this yet", children: "\u2014" }),
|
|
2540
|
-
|
|
2541
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RefusalLine, { error:
|
|
2651
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex items-center gap-1 rounded border border-destructive/40 px-1", children: [
|
|
2652
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RefusalLine, { error: refusal2.error }),
|
|
2542
2653
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" })
|
|
2543
2654
|
] }) : null
|
|
2544
2655
|
] });
|
|
@@ -2613,11 +2724,11 @@ function GridCell({
|
|
|
2613
2724
|
),
|
|
2614
2725
|
onAskWhoChanged ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WhoBadge, { field, row, onAskWhoChanged }) : null,
|
|
2615
2726
|
state === "saving" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "px-1 text-[10px] text-muted-foreground", children: "Saving\u2026" }) : null,
|
|
2616
|
-
|
|
2617
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RefusalLine, { error:
|
|
2618
|
-
|
|
2727
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex flex-col gap-0.5 rounded border border-destructive/40 p-1", children: [
|
|
2728
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RefusalLine, { error: refusal2.error }),
|
|
2729
|
+
refusal2.conflict ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "text-[11px]", children: [
|
|
2619
2730
|
"Theirs: ",
|
|
2620
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: formatTheirs(
|
|
2731
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: formatTheirs(refusal2.conflict, field.key) })
|
|
2621
2732
|
] }) : null,
|
|
2622
2733
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex gap-1", children: [
|
|
2623
2734
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system7.Button, { size: "sm", variant: "outline", onClick: () => editing?.retry(row.id, field.key), children: "Try again" }),
|
|
@@ -3316,7 +3427,20 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3316
3427
|
" in one go."
|
|
3317
3428
|
] }) : null
|
|
3318
3429
|
] }) : null,
|
|
3319
|
-
outcome ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className:
|
|
3430
|
+
outcome ? outcome.ok ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground", children: outcome.message }) : (
|
|
3431
|
+
// A run that did not happen is a refusal, with a heading and a remedy,
|
|
3432
|
+
// never the host's words in red (lane REFUSAL-SWEEP).
|
|
3433
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3434
|
+
RefusalLine,
|
|
3435
|
+
{
|
|
3436
|
+
error: refusal(
|
|
3437
|
+
"internal",
|
|
3438
|
+
outcome.message,
|
|
3439
|
+
"Nothing was filled in. Try the run again in a moment."
|
|
3440
|
+
)
|
|
3441
|
+
}
|
|
3442
|
+
)
|
|
3443
|
+
) : null,
|
|
3320
3444
|
field ? null : null
|
|
3321
3445
|
] }, row.field_id);
|
|
3322
3446
|
}) });
|
|
@@ -3361,15 +3485,15 @@ function PastePreview({
|
|
|
3361
3485
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlanTable, { plan }),
|
|
3362
3486
|
plan.refusals.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-matrx-paste-refusals": true, className: "flex flex-col gap-1", children: [
|
|
3363
3487
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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:` }),
|
|
3364
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: "flex flex-col gap-1", children: plan.refusals.slice(0, 12).map((
|
|
3488
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: "flex flex-col gap-1", children: plan.refusals.slice(0, 12).map((refusal2, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("li", { className: "text-xs text-muted-foreground", children: [
|
|
3365
3489
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "font-medium", children: [
|
|
3366
3490
|
"Row ",
|
|
3367
|
-
|
|
3491
|
+
refusal2.fromLine,
|
|
3368
3492
|
", ",
|
|
3369
|
-
|
|
3493
|
+
refusal2.column
|
|
3370
3494
|
] }),
|
|
3371
3495
|
" \u2014 ",
|
|
3372
|
-
|
|
3496
|
+
refusal2.why
|
|
3373
3497
|
] }, index)) }),
|
|
3374
3498
|
plan.refusals.length > 12 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
3375
3499
|
plan.refusals.length - 12,
|
|
@@ -3397,7 +3521,13 @@ function PastePreview({
|
|
|
3397
3521
|
setAdding(group.fieldKey);
|
|
3398
3522
|
void onAddOptions(group.fieldKey, group.words).catch(
|
|
3399
3523
|
(err) => setAddFailed(
|
|
3400
|
-
|
|
3524
|
+
// The error's own text is an engineer's; the person
|
|
3525
|
+
// reads what did not happen and what to do.
|
|
3526
|
+
asRefusal(
|
|
3527
|
+
err,
|
|
3528
|
+
"Those options were not added, so the pasted words still do not match a choice.",
|
|
3529
|
+
"Try adding them again, or add them from the column's settings."
|
|
3530
|
+
)
|
|
3401
3531
|
)
|
|
3402
3532
|
).finally(() => setAdding(null));
|
|
3403
3533
|
},
|
|
@@ -3405,7 +3535,7 @@ function PastePreview({
|
|
|
3405
3535
|
}
|
|
3406
3536
|
)
|
|
3407
3537
|
] }, group.fieldKey)),
|
|
3408
|
-
addFailed ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3538
|
+
addFailed ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefusalLine, { error: addFailed }) : null
|
|
3409
3539
|
] }) : null,
|
|
3410
3540
|
plan.pastRightEdge > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
3411
3541
|
plan.pastRightEdge,
|
|
@@ -3585,19 +3715,19 @@ function planPastedBlock(args) {
|
|
|
3585
3715
|
accepted[key] = judged.value;
|
|
3586
3716
|
}
|
|
3587
3717
|
const predicted = (0, import_core2.predictValueRefusals)(args.fields, accepted);
|
|
3588
|
-
for (const
|
|
3589
|
-
const key =
|
|
3718
|
+
for (const refusal2 of predicted) {
|
|
3719
|
+
const key = refusal2.field_key;
|
|
3590
3720
|
if (key === void 0) continue;
|
|
3591
3721
|
const cell = built.find((b) => b.key === key && b.refusal === null);
|
|
3592
3722
|
if (!cell) continue;
|
|
3593
|
-
cell.refusal =
|
|
3723
|
+
cell.refusal = refusal2.message;
|
|
3594
3724
|
cell.value = null;
|
|
3595
3725
|
delete accepted[key];
|
|
3596
3726
|
refusals.push({
|
|
3597
3727
|
fromLine,
|
|
3598
3728
|
column: fieldName(byKey.get(key) ?? { key }),
|
|
3599
3729
|
raw: cell.raw,
|
|
3600
|
-
why:
|
|
3730
|
+
why: refusal2.message
|
|
3601
3731
|
});
|
|
3602
3732
|
}
|
|
3603
3733
|
cells += Object.keys(accepted).length;
|
|
@@ -4361,18 +4491,24 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4361
4491
|
size: "sm",
|
|
4362
4492
|
variant: "outline",
|
|
4363
4493
|
onClick: () => {
|
|
4494
|
+
const failed = (thrown) => setFailure(
|
|
4495
|
+
refusalFromThrown(
|
|
4496
|
+
thrown,
|
|
4497
|
+
"The spreadsheet could not be written.",
|
|
4498
|
+
"The CSV beside this button carries the same rows."
|
|
4499
|
+
)
|
|
4500
|
+
);
|
|
4501
|
+
setFailure(null);
|
|
4364
4502
|
try {
|
|
4365
|
-
|
|
4503
|
+
(0, import_core3.exportXlsx)(fields.data ?? [], exported(), name).then(
|
|
4366
4504
|
(bytes) => download(
|
|
4367
4505
|
`${name}.xlsx`,
|
|
4368
4506
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
4369
4507
|
bytes
|
|
4370
4508
|
)
|
|
4371
|
-
);
|
|
4509
|
+
).catch(failed);
|
|
4372
4510
|
} catch (thrown) {
|
|
4373
|
-
|
|
4374
|
-
`The spreadsheet could not be written (${thrown instanceof Error ? thrown.message : String(thrown)}). The CSV beside this button carries the same rows.`
|
|
4375
|
-
);
|
|
4511
|
+
failed(thrown);
|
|
4376
4512
|
}
|
|
4377
4513
|
},
|
|
4378
4514
|
children: "XLSX"
|
|
@@ -4388,7 +4524,7 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4388
4524
|
human: () => (0, import_core3.exportCsv)(fields.data ?? [], exported())
|
|
4389
4525
|
}
|
|
4390
4526
|
),
|
|
4391
|
-
failure ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4527
|
+
failure ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RefusalLine, { error: failure }) : null
|
|
4392
4528
|
] });
|
|
4393
4529
|
}
|
|
4394
4530
|
|
|
@@ -4489,8 +4625,47 @@ function FilePicker({
|
|
|
4489
4625
|
// src/ImportWizard.tsx
|
|
4490
4626
|
var import_react26 = require("react");
|
|
4491
4627
|
var import_react27 = require("@ai-matrx/records/react");
|
|
4492
|
-
var
|
|
4628
|
+
var import_core5 = require("@ai-matrx/records/core");
|
|
4493
4629
|
var import_design_system13 = require("@ai-matrx/design-system");
|
|
4630
|
+
|
|
4631
|
+
// src/importRuleCheck.ts
|
|
4632
|
+
var import_core4 = require("@ai-matrx/records/core");
|
|
4633
|
+
function checkImportRules(fields, rows, mapping) {
|
|
4634
|
+
const byKey = new Map(fields.map((f) => [f.key, f]));
|
|
4635
|
+
const verdicts = [];
|
|
4636
|
+
for (const [header, key] of Object.entries(mapping)) {
|
|
4637
|
+
const field = byKey.get(key);
|
|
4638
|
+
if (!field) continue;
|
|
4639
|
+
let refused = 0;
|
|
4640
|
+
let judged = 0;
|
|
4641
|
+
let first = null;
|
|
4642
|
+
rows.forEach((row, i) => {
|
|
4643
|
+
const raw = String(row[header] ?? "").trim();
|
|
4644
|
+
if (raw === "") return;
|
|
4645
|
+
judged += 1;
|
|
4646
|
+
const meant = valueFromPastedText(field, raw);
|
|
4647
|
+
let why = null;
|
|
4648
|
+
if ("refusal" in meant) why = meant.refusal;
|
|
4649
|
+
else {
|
|
4650
|
+
const predicted = (0, import_core4.predictValueRefusals)([field], { [key]: meant.value }).filter(
|
|
4651
|
+
(p) => p.field_key === void 0 || p.field_key === key
|
|
4652
|
+
);
|
|
4653
|
+
if (predicted.length > 0) why = predicted[0].message;
|
|
4654
|
+
}
|
|
4655
|
+
if (why !== null) {
|
|
4656
|
+
refused += 1;
|
|
4657
|
+
if (!first) first = { why, line: i + 1, raw };
|
|
4658
|
+
}
|
|
4659
|
+
});
|
|
4660
|
+
if (refused > 0 && first) {
|
|
4661
|
+
const f = first;
|
|
4662
|
+
verdicts.push({ header, field, refused, judged, firstWhy: f.why, firstLine: f.line, firstRaw: f.raw });
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
return verdicts;
|
|
4666
|
+
}
|
|
4667
|
+
|
|
4668
|
+
// src/ImportWizard.tsx
|
|
4494
4669
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
4495
4670
|
var BATCH = 250;
|
|
4496
4671
|
var SAMPLES = 40;
|
|
@@ -4543,9 +4718,13 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4543
4718
|
const [openRun, setOpenRun] = (0, import_react26.useState)(null);
|
|
4544
4719
|
const [runRows, setRunRows] = (0, import_react26.useState)(null);
|
|
4545
4720
|
const [runProblem, setRunProblem] = (0, import_react26.useState)(null);
|
|
4721
|
+
const [runsProblem, setRunsProblem] = (0, import_react26.useState)(null);
|
|
4546
4722
|
const loadRuns = (0, import_react26.useCallback)(async () => {
|
|
4547
4723
|
const answered = await client.imports({ table_id: tableId, limit: 10 });
|
|
4548
|
-
if (answered.ok)
|
|
4724
|
+
if (answered.ok) {
|
|
4725
|
+
setRuns(answered.data);
|
|
4726
|
+
setRunsProblem(null);
|
|
4727
|
+
} else setRunsProblem(answered.error);
|
|
4549
4728
|
}, [client, tableId]);
|
|
4550
4729
|
(0, import_react26.useEffect)(() => {
|
|
4551
4730
|
void loadRuns();
|
|
@@ -4566,7 +4745,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4566
4745
|
limit: 200
|
|
4567
4746
|
});
|
|
4568
4747
|
if (!answered.ok) {
|
|
4569
|
-
setRunProblem(answered.error
|
|
4748
|
+
setRunProblem(answered.error);
|
|
4570
4749
|
return;
|
|
4571
4750
|
}
|
|
4572
4751
|
setRunRows({
|
|
@@ -4593,7 +4772,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4593
4772
|
try {
|
|
4594
4773
|
const xlsx = file.name.toLowerCase().endsWith(".xlsx");
|
|
4595
4774
|
const bytes = await file.arrayBuffer();
|
|
4596
|
-
const result = xlsx ? await (0,
|
|
4775
|
+
const result = xlsx ? await (0, import_core5.importXlsx)(bytes) : (0, import_core5.importCsv)(new TextDecoder().decode(bytes));
|
|
4597
4776
|
setFormat(xlsx ? "xlsx" : "csv");
|
|
4598
4777
|
setFileName(file.name);
|
|
4599
4778
|
setFileBytes(file.size);
|
|
@@ -4607,7 +4786,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4607
4786
|
}));
|
|
4608
4787
|
const answered = await client.importPlan({ table_id: tableId, columns });
|
|
4609
4788
|
if (!answered.ok) {
|
|
4610
|
-
setProblem(answered.error
|
|
4789
|
+
setProblem(answered.error);
|
|
4611
4790
|
setPhase("waiting");
|
|
4612
4791
|
return;
|
|
4613
4792
|
}
|
|
@@ -4624,7 +4803,14 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4624
4803
|
setMapping(next);
|
|
4625
4804
|
setPhase("ready");
|
|
4626
4805
|
} catch (thrown) {
|
|
4627
|
-
setProblem(
|
|
4806
|
+
setProblem(
|
|
4807
|
+
refusalFromThrown(
|
|
4808
|
+
thrown,
|
|
4809
|
+
"That file could not be read, so nothing was imported.",
|
|
4810
|
+
"Save it again as CSV or Excel and pick it again.",
|
|
4811
|
+
"invalid_argument"
|
|
4812
|
+
)
|
|
4813
|
+
);
|
|
4628
4814
|
setPhase("waiting");
|
|
4629
4815
|
}
|
|
4630
4816
|
},
|
|
@@ -4641,7 +4827,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4641
4827
|
setPhase("declaring");
|
|
4642
4828
|
const made = await client.importDeclareColumns({ table_id: tableId, rows, mapping });
|
|
4643
4829
|
if (!made.ok) {
|
|
4644
|
-
setProblem(made.error
|
|
4830
|
+
setProblem(made.error);
|
|
4645
4831
|
setPhase("ready");
|
|
4646
4832
|
return;
|
|
4647
4833
|
}
|
|
@@ -4660,7 +4846,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4660
4846
|
policy: { on_duplicate: onDuplicate, unmapped }
|
|
4661
4847
|
});
|
|
4662
4848
|
if (!opened.ok) {
|
|
4663
|
-
setProblem(opened.error
|
|
4849
|
+
setProblem(opened.error);
|
|
4664
4850
|
setPhase("ready");
|
|
4665
4851
|
return;
|
|
4666
4852
|
}
|
|
@@ -4679,7 +4865,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4679
4865
|
const batch = rows.slice(at, at + take2);
|
|
4680
4866
|
const answered = await client.importRows({ import_id: importId, rows: batch, mapping });
|
|
4681
4867
|
if (!answered.ok) {
|
|
4682
|
-
setProblem(answered.error
|
|
4868
|
+
setProblem(answered.error);
|
|
4683
4869
|
setOutcomes([...collected]);
|
|
4684
4870
|
setLedger({ ...told, final: false });
|
|
4685
4871
|
setPhase("ready");
|
|
@@ -4698,7 +4884,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4698
4884
|
}
|
|
4699
4885
|
const finished = await client.importFinish({ import_id: importId, unmapped });
|
|
4700
4886
|
if (!finished.ok) {
|
|
4701
|
-
setProblem(finished.error
|
|
4887
|
+
setProblem(finished.error);
|
|
4702
4888
|
setPhase("done");
|
|
4703
4889
|
return;
|
|
4704
4890
|
}
|
|
@@ -4736,6 +4922,11 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4736
4922
|
const titleMissing = Boolean(titleKey) && titleMappedTo === null;
|
|
4737
4923
|
const titleFieldLabel = (titleKey ? plan?.fields.find((f) => f.key === titleKey)?.label : null) || (titleKey ? declared.find((f) => f.key === titleKey)?.label : null) || "the name column";
|
|
4738
4924
|
const busy = phase === "reading" || phase === "planning" || phase === "declaring" || phase === "writing";
|
|
4925
|
+
const ruleVerdicts = (0, import_react26.useMemo)(() => checkImportRules(declared, rows, mapping), [declared, rows, mapping]);
|
|
4926
|
+
const ruleVerdictOf = (0, import_react26.useMemo)(() => new Map(ruleVerdicts.map((v) => [v.header, v])), [ruleVerdicts]);
|
|
4927
|
+
const refusedByRules = ruleVerdicts.reduce((n, v) => n + v.refused, 0);
|
|
4928
|
+
const [goAheadAnyway, setGoAheadAnyway] = (0, import_react26.useState)(false);
|
|
4929
|
+
(0, import_react26.useEffect)(() => setGoAheadAnyway(false), [ruleVerdicts]);
|
|
4739
4930
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: (0, import_design_system13.cn)("flex min-w-0 flex-col gap-2 text-xs", className), children: [
|
|
4740
4931
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4741
4932
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: "Import" }),
|
|
@@ -4763,7 +4954,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4763
4954
|
} : {}
|
|
4764
4955
|
}
|
|
4765
4956
|
) : null,
|
|
4766
|
-
problem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4957
|
+
problem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: problem }) : null,
|
|
4767
4958
|
phase === "reading" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
|
|
4768
4959
|
phase === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
|
|
4769
4960
|
plan && parsed ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
@@ -4860,7 +5051,28 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4860
5051
|
column.unit ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
|
|
4861
5052
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
|
|
4862
5053
|
column.ambiguous ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
|
|
4863
|
-
column.collides_with ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null
|
|
5054
|
+
column.collides_with ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Two columns in this file would make the same column." }) : null,
|
|
5055
|
+
ruleVerdictOf.get(column.header) ? (() => {
|
|
5056
|
+
const v = ruleVerdictOf.get(column.header);
|
|
5057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { "data-import-rule-refusal": column.header, className: "mt-1", children: [
|
|
5058
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5059
|
+
RefusalLine,
|
|
5060
|
+
{
|
|
5061
|
+
error: refusal(
|
|
5062
|
+
"refused_by_rule",
|
|
5063
|
+
`${v.refused} of ${v.judged} row${v.judged === 1 ? "" : "s"} would be refused by ${fieldName(v.field)}'s rules. ${v.firstWhy}`,
|
|
5064
|
+
"Send this column somewhere else, fix those rows in your file, or import the rest and they are listed afterwards."
|
|
5065
|
+
)
|
|
5066
|
+
}
|
|
5067
|
+
),
|
|
5068
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "text-muted-foreground", "data-import-rule-sample": "", children: [
|
|
5069
|
+
"Line ",
|
|
5070
|
+
v.firstLine,
|
|
5071
|
+
" in your file: ",
|
|
5072
|
+
v.firstRaw
|
|
5073
|
+
] })
|
|
5074
|
+
] });
|
|
5075
|
+
})() : null
|
|
4864
5076
|
] }),
|
|
4865
5077
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate pt-1.5 text-muted-foreground", children: (column.samples ?? []).slice(0, 3).map(String).join(" \xB7 ") })
|
|
4866
5078
|
]
|
|
@@ -4924,12 +5136,34 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4924
5136
|
)
|
|
4925
5137
|
] })
|
|
4926
5138
|
] }),
|
|
5139
|
+
ruleVerdicts.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5140
|
+
RefusalNotice,
|
|
5141
|
+
{
|
|
5142
|
+
error: refusal(
|
|
5143
|
+
"refused_by_rule",
|
|
5144
|
+
`${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.`,
|
|
5145
|
+
"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."
|
|
5146
|
+
),
|
|
5147
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "mt-1 flex items-center gap-1.5", "data-import-go-ahead": "", children: [
|
|
5148
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5149
|
+
"input",
|
|
5150
|
+
{
|
|
5151
|
+
type: "checkbox",
|
|
5152
|
+
checked: goAheadAnyway,
|
|
5153
|
+
disabled: busy,
|
|
5154
|
+
onChange: (e) => setGoAheadAnyway(e.target.checked)
|
|
5155
|
+
}
|
|
5156
|
+
),
|
|
5157
|
+
"Import the rest anyway"
|
|
5158
|
+
] })
|
|
5159
|
+
}
|
|
5160
|
+
) : null,
|
|
4927
5161
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4928
5162
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4929
5163
|
import_design_system13.Button,
|
|
4930
5164
|
{
|
|
4931
5165
|
size: "sm",
|
|
4932
|
-
disabled: busy || !rights.write || rows.length === 0 || titleMissing,
|
|
5166
|
+
disabled: busy || !rights.write || rows.length === 0 || titleMissing || ruleVerdicts.length > 0 && !goAheadAnyway,
|
|
4933
5167
|
onClick: () => void run(),
|
|
4934
5168
|
children: phase === "declaring" ? "Adding the columns\u2026" : phase === "writing" ? `Writing\u2026 ${progress} of ${rows.length}` : `Import ${rows.length} row${rows.length === 1 ? "" : "s"}`
|
|
4935
5169
|
}
|
|
@@ -4984,7 +5218,12 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
4984
5218
|
] }) }),
|
|
4985
5219
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: interesting.slice(0, 200).map((o) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-t align-top", children: [
|
|
4986
5220
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: o.row }),
|
|
4987
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className:
|
|
5221
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: o.outcome === "refused" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5222
|
+
RefusalLine,
|
|
5223
|
+
{
|
|
5224
|
+
error: refusal("refused_by_rule", o.reason, "Fix this line in your file and import it again.")
|
|
5225
|
+
}
|
|
5226
|
+
) : o.outcome === "duplicate" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-muted-foreground", children: o.reason }) : "written" }),
|
|
4988
5227
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("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 ") : "" })
|
|
4989
5228
|
] }, `${o.outcome}-${o.row}`)) })
|
|
4990
5229
|
] }) }) : null,
|
|
@@ -5007,6 +5246,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5007
5246
|
] }) : null
|
|
5008
5247
|
] }) : null
|
|
5009
5248
|
] }) : null,
|
|
5249
|
+
runsProblem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: runsProblem }) : null,
|
|
5010
5250
|
runs && runs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
5011
5251
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Separator, {}),
|
|
5012
5252
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "font-medium", children: "Imports into this table" }),
|
|
@@ -5039,7 +5279,7 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5039
5279
|
}
|
|
5040
5280
|
) : null
|
|
5041
5281
|
] }),
|
|
5042
|
-
openRun === r.import_id ? runProblem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5282
|
+
openRun === r.import_id ? runProblem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: runProblem }) : runRows ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
5043
5283
|
runRows.note ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: runRows.note }) : null,
|
|
5044
5284
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "max-h-64 overflow-auto rounded border", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("table", { className: "w-full", children: [
|
|
5045
5285
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("thead", { className: "sticky top-0 bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { children: [
|
|
@@ -5051,7 +5291,16 @@ function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
|
5051
5291
|
const source = row.source ?? {};
|
|
5052
5292
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-t align-top", children: [
|
|
5053
5293
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: String(row.row ?? "") }),
|
|
5054
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1
|
|
5294
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5295
|
+
RefusalLine,
|
|
5296
|
+
{
|
|
5297
|
+
error: refusal(
|
|
5298
|
+
"refused_by_rule",
|
|
5299
|
+
String(row.reason ?? "This row was refused."),
|
|
5300
|
+
"Fix this line in your file and import it again."
|
|
5301
|
+
)
|
|
5302
|
+
}
|
|
5303
|
+
) }),
|
|
5055
5304
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "max-w-96 px-2 py-1 text-muted-foreground", children: Object.entries(source).map(([k, v]) => `${k}: ${String(v ?? "")}`).join(" \xB7 ") })
|
|
5056
5305
|
] }, `${r.import_id}-${String(row.row ?? i)}`);
|
|
5057
5306
|
}) })
|
|
@@ -5270,7 +5519,9 @@ function keyFor(label) {
|
|
|
5270
5519
|
if (token === "") return "";
|
|
5271
5520
|
return /^[a-z]/.test(token) ? token : `f_${token}`;
|
|
5272
5521
|
}
|
|
5273
|
-
var
|
|
5522
|
+
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.";
|
|
5523
|
+
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.";
|
|
5524
|
+
var COLUMN_DID_NOT_LAND = `${COLUMN_DID_NOT_LAND_SENTENCE} ${COLUMN_DID_NOT_LAND_REMEDY}`;
|
|
5274
5525
|
function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }) {
|
|
5275
5526
|
const table = (0, import_react31.useTable)(tableId);
|
|
5276
5527
|
const fields = (0, import_react31.useFields)(tableId);
|
|
@@ -5570,14 +5821,20 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5570
5821
|
] }, `${rule.kind}-${index}`))
|
|
5571
5822
|
] }),
|
|
5572
5823
|
mutation.error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(RefusalNotice, { error: mutation.error }) : null,
|
|
5573
|
-
missing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-
|
|
5574
|
-
notLanded ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5824
|
+
missing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { id: "field-editor-missing", className: "text-[11px] text-muted-foreground", "data-field-editor-missing": "", children: missing }) : null,
|
|
5825
|
+
notLanded ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5826
|
+
RefusalNotice,
|
|
5827
|
+
{
|
|
5828
|
+
error: refusal("internal", COLUMN_DID_NOT_LAND_SENTENCE, COLUMN_DID_NOT_LAND_REMEDY)
|
|
5829
|
+
}
|
|
5830
|
+
) : null,
|
|
5575
5831
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5576
5832
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5577
5833
|
import_design_system15.Button,
|
|
5578
5834
|
{
|
|
5579
5835
|
size: "sm",
|
|
5580
5836
|
disabled: mutation.saving || missing !== null,
|
|
5837
|
+
...missing ? { "aria-describedby": "field-editor-missing" } : {},
|
|
5581
5838
|
onClick: () => void save(),
|
|
5582
5839
|
children: mutation.saving ? "Saving\u2026" : field ? EDIT_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
|
|
5583
5840
|
}
|
|
@@ -6790,7 +7047,7 @@ var import_design_system22 = require("@ai-matrx/design-system");
|
|
|
6790
7047
|
// src/RecordForm.tsx
|
|
6791
7048
|
var import_react41 = require("react");
|
|
6792
7049
|
var import_react42 = require("@ai-matrx/records/react");
|
|
6793
|
-
var
|
|
7050
|
+
var import_core6 = require("@ai-matrx/records/core");
|
|
6794
7051
|
var import_design_system20 = require("@ai-matrx/design-system");
|
|
6795
7052
|
|
|
6796
7053
|
// src/systemTable.ts
|
|
@@ -6995,7 +7252,7 @@ function RecordForm({
|
|
|
6995
7252
|
return out;
|
|
6996
7253
|
}, [draft]);
|
|
6997
7254
|
const predicted = (0, import_react41.useMemo)(
|
|
6998
|
-
() => fields.data ? (0,
|
|
7255
|
+
() => fields.data ? (0, import_core6.predictWriteRefusals)({ fields: fields.data, document: document2, ...recordType ? { recordType } : {} }) : [],
|
|
6999
7256
|
[fields.data, document2, recordType]
|
|
7000
7257
|
);
|
|
7001
7258
|
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(RefusalNotice, { error: fields.error, className });
|
|
@@ -7043,7 +7300,13 @@ function RecordForm({
|
|
|
7043
7300
|
},
|
|
7044
7301
|
field.id
|
|
7045
7302
|
)) }),
|
|
7046
|
-
touched ? predicted.filter((p) => !p.field_key).map((p) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7303
|
+
touched ? predicted.filter((p) => !p.field_key).map((p) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7304
|
+
RefusalNotice,
|
|
7305
|
+
{
|
|
7306
|
+
error: { code: "refused_by_rule", message: p.message }
|
|
7307
|
+
},
|
|
7308
|
+
p.message
|
|
7309
|
+
)) : null,
|
|
7047
7310
|
mutation.error ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7048
7311
|
RefusalNotice,
|
|
7049
7312
|
{
|
|
@@ -7098,7 +7361,14 @@ function FieldRow({
|
|
|
7098
7361
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
7099
7362
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldLabel, { field, htmlFor: id, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ProvenanceBadge, { document: document2, fieldKey: field.key }) }),
|
|
7100
7363
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldControl, { field, value, onChange, id }),
|
|
7101
|
-
problems.map((problem) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7364
|
+
problems.map((problem) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
7365
|
+
RefusalNotice,
|
|
7366
|
+
{
|
|
7367
|
+
error: { code: "refused_by_rule", message: problem.message },
|
|
7368
|
+
className: "border-0 p-0"
|
|
7369
|
+
},
|
|
7370
|
+
problem.message
|
|
7371
|
+
))
|
|
7102
7372
|
] });
|
|
7103
7373
|
}
|
|
7104
7374
|
function asWords(value) {
|
|
@@ -7124,12 +7394,16 @@ function ShareControl({
|
|
|
7124
7394
|
subjectId,
|
|
7125
7395
|
name,
|
|
7126
7396
|
may,
|
|
7397
|
+
initiallyOpen = false,
|
|
7127
7398
|
size = "sm",
|
|
7128
7399
|
variant = "ghost",
|
|
7129
7400
|
className
|
|
7130
7401
|
}) {
|
|
7131
7402
|
const host = useRecordsUi();
|
|
7132
|
-
const [open, setOpen] = (0, import_react43.useState)(
|
|
7403
|
+
const [open, setOpen] = (0, import_react43.useState)(initiallyOpen);
|
|
7404
|
+
(0, import_react43.useEffect)(() => {
|
|
7405
|
+
if (initiallyOpen) setOpen(true);
|
|
7406
|
+
}, [initiallyOpen]);
|
|
7133
7407
|
const asked = useRecordRights(may === void 0 ? subjectId : null);
|
|
7134
7408
|
const mayShare = may ?? asked.share;
|
|
7135
7409
|
if (!host.share) return null;
|
|
@@ -7707,10 +7981,22 @@ function Held({
|
|
|
7707
7981
|
);
|
|
7708
7982
|
}
|
|
7709
7983
|
if (pending.kind === "refused") {
|
|
7710
|
-
return
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7984
|
+
return (
|
|
7985
|
+
// THE STAGE GATE SAID NO: the one refusal surface, heading and remedy both,
|
|
7986
|
+
// never its sentence alone in a red box (lane REFUSAL-SWEEP).
|
|
7987
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7988
|
+
RefusalNotice,
|
|
7989
|
+
{
|
|
7990
|
+
className: "mt-1",
|
|
7991
|
+
error: refusal(
|
|
7992
|
+
"refused_by_rule",
|
|
7993
|
+
pending.verdict.why,
|
|
7994
|
+
"The card stays where it was. Fill in what this stage asks for, then move it again."
|
|
7995
|
+
),
|
|
7996
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system23.Button, { size: "sm", variant: "ghost", className: "mt-1 h-6", onClick: onCancel, children: "Close" })
|
|
7997
|
+
}
|
|
7998
|
+
)
|
|
7999
|
+
);
|
|
7714
8000
|
}
|
|
7715
8001
|
if (pending.kind === "approval") {
|
|
7716
8002
|
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "mt-1 flex flex-col gap-1 rounded border p-2 text-xs", children: [
|
|
@@ -8274,7 +8560,7 @@ function ArchivedView({
|
|
|
8274
8560
|
const [loading, setLoading] = (0, import_react51.useState)(true);
|
|
8275
8561
|
const [error, setError] = (0, import_react51.useState)(null);
|
|
8276
8562
|
const [restoring, setRestoring] = (0, import_react51.useState)(null);
|
|
8277
|
-
const [
|
|
8563
|
+
const [refusal2, setRefusal] = (0, import_react51.useState)(null);
|
|
8278
8564
|
(0, import_react51.useEffect)(() => {
|
|
8279
8565
|
if (laneFromHost) setLane(laneFromHost);
|
|
8280
8566
|
}, [laneFromHost]);
|
|
@@ -8351,7 +8637,7 @@ function ArchivedView({
|
|
|
8351
8637
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
8352
8638
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-sm", children: recordName(row.document, titleKey) }),
|
|
8353
8639
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-xs text-muted-foreground", "data-testid": "archived-who", children: (0, import_records7.archivedByLine)(row) }),
|
|
8354
|
-
|
|
8640
|
+
refusal2 && refusal2.recordId === row.id ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(RefusalNotice, { error: refusal2.error, className: "mt-2" }) : null
|
|
8355
8641
|
] }),
|
|
8356
8642
|
mayRestore ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8357
8643
|
import_design_system25.Button,
|
|
@@ -8425,7 +8711,7 @@ function ArchivedPortals({
|
|
|
8425
8711
|
const [confirming, setConfirming] = (0, import_react53.useState)(null);
|
|
8426
8712
|
const [typed, setTyped] = (0, import_react53.useState)("");
|
|
8427
8713
|
const [restoring, setRestoring] = (0, import_react53.useState)(null);
|
|
8428
|
-
const [
|
|
8714
|
+
const [refusal2, setRefusal] = (0, import_react53.useState)(null);
|
|
8429
8715
|
const [said, setSaid] = (0, import_react53.useState)(null);
|
|
8430
8716
|
const read = (0, import_react53.useCallback)(async () => {
|
|
8431
8717
|
const answered = await client.listPortals({ archived: "archived" });
|
|
@@ -8541,7 +8827,7 @@ function ArchivedPortals({
|
|
|
8541
8827
|
)
|
|
8542
8828
|
] })
|
|
8543
8829
|
] }) : null,
|
|
8544
|
-
|
|
8830
|
+
refusal2 && refusal2.portalId === portal.portal_id ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RefusalNotice, { error: refusal2.error }) : null
|
|
8545
8831
|
]
|
|
8546
8832
|
},
|
|
8547
8833
|
portal.portal_id
|
|
@@ -9106,7 +9392,7 @@ function MyChecklistSteps({ className }) {
|
|
|
9106
9392
|
const client = (0, import_react62.useRecordsClient)();
|
|
9107
9393
|
const { steps, loading, error, refresh } = useMyChecklistSteps();
|
|
9108
9394
|
const [busy, setBusy] = (0, import_react61.useState)(null);
|
|
9109
|
-
const [
|
|
9395
|
+
const [refusal2, setRefusal] = (0, import_react61.useState)(null);
|
|
9110
9396
|
const complete = (0, import_react61.useCallback)(
|
|
9111
9397
|
async (step2, evidence) => {
|
|
9112
9398
|
setBusy(step2.step_id);
|
|
@@ -9129,7 +9415,7 @@ function MyChecklistSteps({ className }) {
|
|
|
9129
9415
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "tabular-nums", children: steps.length })
|
|
9130
9416
|
] }),
|
|
9131
9417
|
error ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(RefusalNotice, { error }) : null,
|
|
9132
|
-
|
|
9418
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(RefusalNotice, { error: refusal2 }) : null,
|
|
9133
9419
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ol", { className: "flex flex-col gap-1.5", children: steps.map((step2) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
9134
9420
|
StepRow,
|
|
9135
9421
|
{
|
|
@@ -9306,7 +9592,7 @@ function ChecklistTemplateEditor({
|
|
|
9306
9592
|
const client = (0, import_react62.useRecordsClient)();
|
|
9307
9593
|
const [name, setName] = (0, import_react61.useState)("");
|
|
9308
9594
|
const [rows, setRows] = (0, import_react61.useState)([{ ...EMPTY_ROW }]);
|
|
9309
|
-
const [
|
|
9595
|
+
const [refusal2, setRefusal] = (0, import_react61.useState)(null);
|
|
9310
9596
|
const [error, setError] = (0, import_react61.useState)(null);
|
|
9311
9597
|
const [busy, setBusy] = (0, import_react61.useState)(false);
|
|
9312
9598
|
const [loading, setLoading] = (0, import_react61.useState)(Boolean(templateId));
|
|
@@ -9474,9 +9760,9 @@ function ChecklistTemplateEditor({
|
|
|
9474
9760
|
] }, index)) }),
|
|
9475
9761
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
9476
9762
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_design_system29.Button, { size: "sm", variant: "outline", onClick: () => setRows((held) => [...held, { ...EMPTY_ROW }]), children: "Add a step" }),
|
|
9477
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_design_system29.Button, { size: "sm", disabled: busy ||
|
|
9763
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_design_system29.Button, { size: "sm", disabled: busy || refusal2 !== null || spec.steps.length === 0, onClick: () => void save(), children: busy ? "\u2026" : "Save" })
|
|
9478
9764
|
] }),
|
|
9479
|
-
|
|
9765
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "checklist-editor-refusal", children: refusal2 }) : null
|
|
9480
9766
|
] });
|
|
9481
9767
|
}
|
|
9482
9768
|
function toStepSpec(row, index) {
|
|
@@ -10769,6 +11055,7 @@ function BuildOrAsk({
|
|
|
10769
11055
|
|
|
10770
11056
|
// src/PortalsPanel.tsx
|
|
10771
11057
|
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
11058
|
+
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.";
|
|
10772
11059
|
function revokeConsequence(person, portalTitle) {
|
|
10773
11060
|
const who = person.client ? `${person.email} (${person.client})` : person.email;
|
|
10774
11061
|
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.`;
|
|
@@ -10798,13 +11085,16 @@ function stateWords(person) {
|
|
|
10798
11085
|
};
|
|
10799
11086
|
}
|
|
10800
11087
|
var PORTAL_SUGGESTION = "Let each of my customers sign in and see their own jobs and invoices, and nothing else.";
|
|
10801
|
-
function PortalsPanel({ tableId, className }) {
|
|
11088
|
+
function PortalsPanel({ tableId, activePortalId, className }) {
|
|
10802
11089
|
const client = (0, import_react74.useRecordsClient)();
|
|
10803
11090
|
const host = useRecordsUi();
|
|
10804
11091
|
const [portals, setPortals] = (0, import_react73.useState)(null);
|
|
10805
11092
|
const [exposures, setExposures] = (0, import_react73.useState)([]);
|
|
10806
11093
|
const [listError, setListError] = (0, import_react73.useState)(null);
|
|
10807
|
-
const [openId, setOpenId] = (0, import_react73.useState)(null);
|
|
11094
|
+
const [openId, setOpenId] = (0, import_react73.useState)(activePortalId ?? null);
|
|
11095
|
+
(0, import_react73.useEffect)(() => {
|
|
11096
|
+
if (activePortalId) setOpenId(activePortalId);
|
|
11097
|
+
}, [activePortalId]);
|
|
10808
11098
|
const [building, setBuilding] = (0, import_react73.useState)(false);
|
|
10809
11099
|
const [adding, setAdding] = (0, import_react73.useState)(null);
|
|
10810
11100
|
const [archiveToken, setArchiveToken] = (0, import_react73.useState)(0);
|
|
@@ -10901,59 +11191,73 @@ function PortalsPanel({ tableId, className }) {
|
|
|
10901
11191
|
portal.portal_id
|
|
10902
11192
|
)) })
|
|
10903
11193
|
] }) : null,
|
|
11194
|
+
activePortalId && !shown.some((p) => p.portal_id === activePortalId) ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("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,
|
|
10904
11195
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: "flex flex-col gap-2", children: shown.map((portal) => {
|
|
10905
11196
|
const url = `${origin}${(0, import_records9.portalPath)(portal.slug)}`;
|
|
10906
11197
|
const open = openId === portal.portal_id;
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
|
|
10915
|
-
|
|
10916
|
-
"."
|
|
10917
|
-
] }),
|
|
10918
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mt-1.5 text-xs", children: [
|
|
10919
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "font-medium", children: portal.signed_in }),
|
|
10920
|
-
" signed in",
|
|
10921
|
-
" \xB7 ",
|
|
10922
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "font-medium", children: portal.invited - portal.signed_in }),
|
|
10923
|
-
" invited and waiting"
|
|
10924
|
-
] }),
|
|
10925
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
10926
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CopyLink, { url }),
|
|
10927
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
10928
|
-
import_design_system36.Button,
|
|
10929
|
-
{
|
|
10930
|
-
size: "sm",
|
|
10931
|
-
variant: "ghost",
|
|
10932
|
-
onClick: () => setOpenId(open ? null : portal.portal_id),
|
|
10933
|
-
children: open ? "Close" : "Open it"
|
|
10934
|
-
}
|
|
11198
|
+
const linked = portal.portal_id === activePortalId;
|
|
11199
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
11200
|
+
"li",
|
|
11201
|
+
{
|
|
11202
|
+
"data-linked": linked ? "true" : void 0,
|
|
11203
|
+
ref: linked ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
11204
|
+
className: (0, import_design_system36.cn)(
|
|
11205
|
+
"rounded-md border border-border bg-card p-2.5",
|
|
11206
|
+
linked && "border-primary ring-1 ring-primary"
|
|
10935
11207
|
),
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
portal,
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
11208
|
+
children: [
|
|
11209
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
11210
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: portal.title }),
|
|
11211
|
+
portal.is_active ? null : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_design_system36.Badge, { variant: "outline", children: "closed" })
|
|
11212
|
+
] }),
|
|
11213
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
11214
|
+
"Clients come from ",
|
|
11215
|
+
portal.client_table,
|
|
11216
|
+
portal.tables === 1 ? ", and see 1 Table" : `, and see ${portal.tables} Tables`,
|
|
11217
|
+
"."
|
|
11218
|
+
] }),
|
|
11219
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mt-1.5 text-xs", children: [
|
|
11220
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "font-medium", children: portal.signed_in }),
|
|
11221
|
+
" signed in",
|
|
11222
|
+
" \xB7 ",
|
|
11223
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "font-medium", children: portal.invited - portal.signed_in }),
|
|
11224
|
+
" invited and waiting"
|
|
11225
|
+
] }),
|
|
11226
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
11227
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CopyLink, { url }),
|
|
11228
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11229
|
+
import_design_system36.Button,
|
|
11230
|
+
{
|
|
11231
|
+
size: "sm",
|
|
11232
|
+
variant: "ghost",
|
|
11233
|
+
onClick: () => setOpenId(open ? null : portal.portal_id),
|
|
11234
|
+
children: open ? "Close" : "Open it"
|
|
11235
|
+
}
|
|
11236
|
+
),
|
|
11237
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11238
|
+
ArchivePortalControl,
|
|
11239
|
+
{
|
|
11240
|
+
portal,
|
|
11241
|
+
onArchived: () => {
|
|
11242
|
+
setArchiveToken((t) => t + 1);
|
|
11243
|
+
void load();
|
|
11244
|
+
}
|
|
11245
|
+
}
|
|
11246
|
+
)
|
|
11247
|
+
] }),
|
|
11248
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
11249
|
+
PortalDetail,
|
|
11250
|
+
{
|
|
11251
|
+
portalId: portal.portal_id,
|
|
11252
|
+
...tableId ? { tableId } : {},
|
|
11253
|
+
origin,
|
|
11254
|
+
onChanged: () => void load()
|
|
10943
11255
|
}
|
|
10944
|
-
|
|
10945
|
-
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
{
|
|
10950
|
-
portalId: portal.portal_id,
|
|
10951
|
-
...tableId ? { tableId } : {},
|
|
10952
|
-
origin,
|
|
10953
|
-
onChanged: () => void load()
|
|
10954
|
-
}
|
|
10955
|
-
) : null
|
|
10956
|
-
] }, portal.portal_id);
|
|
11256
|
+
) : null
|
|
11257
|
+
]
|
|
11258
|
+
},
|
|
11259
|
+
portal.portal_id
|
|
11260
|
+
);
|
|
10957
11261
|
}) }),
|
|
10958
11262
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ArchivedPortals, { refreshToken: archiveToken, onRestored: () => void load() })
|
|
10959
11263
|
] });
|
|
@@ -11711,7 +12015,17 @@ function DigestScheduler({
|
|
|
11711
12015
|
preview ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
11712
12016
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "font-medium", children: preview.subject }),
|
|
11713
12017
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
11714
|
-
preview.incomplete ?
|
|
12018
|
+
preview.incomplete ? (
|
|
12019
|
+
// The summary could not be finished: a refusal line naming the missing
|
|
12020
|
+
// piece and what to do, never the bare sentence in red (lane REFUSAL-SWEEP).
|
|
12021
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
12022
|
+
RefusalLine,
|
|
12023
|
+
{
|
|
12024
|
+
className: "mt-1",
|
|
12025
|
+
error: refusal("not_supported", preview.incomplete, "Fill in the missing piece above, then preview again.")
|
|
12026
|
+
}
|
|
12027
|
+
)
|
|
12028
|
+
) : null,
|
|
11715
12029
|
preview.entered.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "mt-1", children: [
|
|
11716
12030
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "font-medium", children: "Arrived:" }),
|
|
11717
12031
|
" ",
|
|
@@ -11733,6 +12047,7 @@ var import_react77 = require("react");
|
|
|
11733
12047
|
var import_react78 = require("@ai-matrx/records/react");
|
|
11734
12048
|
var import_design_system38 = require("@ai-matrx/design-system");
|
|
11735
12049
|
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
12050
|
+
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.";
|
|
11736
12051
|
function whenItFires(subscription) {
|
|
11737
12052
|
if (subscription.cadence === "instant") return "as it happens";
|
|
11738
12053
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
@@ -11740,7 +12055,7 @@ function whenItFires(subscription) {
|
|
|
11740
12055
|
}
|
|
11741
12056
|
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
11742
12057
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
11743
|
-
function SubscriptionsPanel({ tableId, className }) {
|
|
12058
|
+
function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
11744
12059
|
const client = (0, import_react78.useRecordsClient)();
|
|
11745
12060
|
const [rows, setRows] = (0, import_react77.useState)(null);
|
|
11746
12061
|
const [error, setError] = (0, import_react77.useState)(null);
|
|
@@ -11831,81 +12146,104 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
11831
12146
|
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."
|
|
11832
12147
|
}
|
|
11833
12148
|
) : null,
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
"
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
" ",
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
12149
|
+
activeRuleId && !rows.some((r) => r.rule_id === activeRuleId) ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("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,
|
|
12150
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { className: "flex flex-col gap-2", children: rows.map((subscription) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
12151
|
+
"li",
|
|
12152
|
+
{
|
|
12153
|
+
"data-linked": subscription.rule_id === activeRuleId ? "true" : void 0,
|
|
12154
|
+
ref: subscription.rule_id === activeRuleId ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
12155
|
+
className: (0, import_design_system38.cn)(
|
|
12156
|
+
"rounded-md border p-2.5",
|
|
12157
|
+
subscription.rule_id === activeRuleId && "border-primary ring-1 ring-primary"
|
|
12158
|
+
),
|
|
12159
|
+
children: [
|
|
12160
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex items-start gap-2", children: [
|
|
12161
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
12162
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "truncate text-sm font-medium", children: subscription.name }),
|
|
12163
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
12164
|
+
CHANNEL_WORDS2[subscription.channel] ?? `on the ${subscription.channel} channel`,
|
|
12165
|
+
" \xB7 ",
|
|
12166
|
+
whenItFires(subscription)
|
|
12167
|
+
] })
|
|
12168
|
+
] }),
|
|
12169
|
+
subscription.i_may_mute ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
12170
|
+
import_design_system38.Switch,
|
|
12171
|
+
{
|
|
12172
|
+
checked: !subscription.muted,
|
|
12173
|
+
disabled: busy === subscription.rule_id,
|
|
12174
|
+
"aria-label": `Tell me about ${subscription.name}`,
|
|
12175
|
+
onCheckedChange: (on) => void flip(subscription, on)
|
|
12176
|
+
}
|
|
12177
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_design_system38.Badge, { variant: "secondary", children: "someone else's" })
|
|
12178
|
+
] }),
|
|
12179
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("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." }),
|
|
12180
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
12181
|
+
subscription.next_digest_at ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
12182
|
+
"Next summary ",
|
|
12183
|
+
new Date(subscription.next_digest_at).toLocaleString()
|
|
12184
|
+
] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "No next summary while it is off." }) : null,
|
|
12185
|
+
subscription.last_sent_at ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
12186
|
+
"Last told you ",
|
|
12187
|
+
new Date(subscription.last_sent_at).toLocaleString()
|
|
12188
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "It has not told you anything yet." }),
|
|
12189
|
+
subscription.quiet_hours ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
12190
|
+
"Not between ",
|
|
12191
|
+
subscription.quiet_hours.start,
|
|
12192
|
+
" and ",
|
|
12193
|
+
subscription.quiet_hours.end,
|
|
12194
|
+
" \u2014 a send inside those hours waits until they end."
|
|
12195
|
+
] }) : null,
|
|
12196
|
+
subscription.cadence === "instant" ? null : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
12197
|
+
import_design_system38.Button,
|
|
12198
|
+
{
|
|
12199
|
+
size: "sm",
|
|
12200
|
+
variant: "ghost",
|
|
12201
|
+
disabled: busy === subscription.rule_id,
|
|
12202
|
+
onClick: () => void showOne(subscription),
|
|
12203
|
+
children: "Send me a preview now"
|
|
12204
|
+
}
|
|
12205
|
+
)
|
|
12206
|
+
] }),
|
|
12207
|
+
preview && preview.rule_id === subscription.rule_id ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "mt-2 rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
12208
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "font-medium", children: preview.subject }),
|
|
12209
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
12210
|
+
preview.incomplete ? (
|
|
12211
|
+
// Absent or honest: a subscription that cannot produce a summary
|
|
12212
|
+
// says which piece is missing instead of showing an empty one.
|
|
12213
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
12214
|
+
RefusalLine,
|
|
12215
|
+
{
|
|
12216
|
+
className: "mt-1",
|
|
12217
|
+
error: refusal(
|
|
12218
|
+
"not_supported",
|
|
12219
|
+
preview.incomplete,
|
|
12220
|
+
"Fill in the missing piece, then preview again."
|
|
12221
|
+
)
|
|
12222
|
+
}
|
|
12223
|
+
)
|
|
12224
|
+
) : null,
|
|
12225
|
+
preview.entered.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "mt-1", children: [
|
|
12226
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium", children: "Arrived:" }),
|
|
12227
|
+
" ",
|
|
12228
|
+
preview.entered.map((e) => e.name).join(", ")
|
|
12229
|
+
] }) : null,
|
|
12230
|
+
preview.left.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "mt-1", children: [
|
|
12231
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium", children: "Left:" }),
|
|
12232
|
+
" ",
|
|
12233
|
+
preview.left.map((e) => e.name).join(", ")
|
|
12234
|
+
] }) : null,
|
|
12235
|
+
preview.changed.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "mt-1", children: [
|
|
12236
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium", children: "Changed:" }),
|
|
12237
|
+
" ",
|
|
12238
|
+
preview.changed.map((e) => e.name).join(", ")
|
|
12239
|
+
] }) : null,
|
|
12240
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("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." }),
|
|
12241
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_design_system38.Button, { size: "sm", variant: "ghost", className: "mt-1", onClick: () => setPreview(null), children: "Close" })
|
|
12242
|
+
] }) : null
|
|
12243
|
+
]
|
|
12244
|
+
},
|
|
12245
|
+
subscription.rule_id
|
|
12246
|
+
)) })
|
|
11909
12247
|
] });
|
|
11910
12248
|
}
|
|
11911
12249
|
|
|
@@ -11960,9 +12298,16 @@ function ConnectedFormRunner(props) {
|
|
|
11960
12298
|
const submit = (0, import_react81.useCallback)(
|
|
11961
12299
|
async (values) => {
|
|
11962
12300
|
if (!client) {
|
|
12301
|
+
const said = "This form cannot send answers from here, so nothing was sent.";
|
|
11963
12302
|
return {
|
|
11964
12303
|
ok: false,
|
|
11965
|
-
message:
|
|
12304
|
+
message: said,
|
|
12305
|
+
error: refusal(
|
|
12306
|
+
"not_supported",
|
|
12307
|
+
said,
|
|
12308
|
+
"Open the form from its own link and answer it there.",
|
|
12309
|
+
"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."
|
|
12310
|
+
)
|
|
11966
12311
|
};
|
|
11967
12312
|
}
|
|
11968
12313
|
const document2 = {
|
|
@@ -11975,7 +12320,7 @@ function ConnectedFormRunner(props) {
|
|
|
11975
12320
|
})
|
|
11976
12321
|
};
|
|
11977
12322
|
const written = await client.recordWrite({ table_id: form.subject, data: document2 });
|
|
11978
|
-
if (!written.ok) return { ok: false, message: written.error.message };
|
|
12323
|
+
if (!written.ok) return { ok: false, message: written.error.message, error: written.error };
|
|
11979
12324
|
return { ok: true, recordId: written.data };
|
|
11980
12325
|
},
|
|
11981
12326
|
[client, form]
|
|
@@ -12017,7 +12362,7 @@ function FormStage({
|
|
|
12017
12362
|
const [answers, setAnswers] = (0, import_react81.useState)({});
|
|
12018
12363
|
const [at, setAt] = (0, import_react81.useState)(0);
|
|
12019
12364
|
const [error, setError] = (0, import_react81.useState)(null);
|
|
12020
|
-
const [
|
|
12365
|
+
const [refusal2, setRefusal] = (0, import_react81.useState)(null);
|
|
12021
12366
|
const [writing, setWriting] = (0, import_react81.useState)(false);
|
|
12022
12367
|
const [done, setDone] = (0, import_react81.useState)(null);
|
|
12023
12368
|
const [hidden, setHidden] = (0, import_react81.useState)({});
|
|
@@ -12086,12 +12431,21 @@ function FormStage({
|
|
|
12086
12431
|
const outcome = await onSubmit(values);
|
|
12087
12432
|
setWriting(false);
|
|
12088
12433
|
if (!outcome.ok) {
|
|
12089
|
-
|
|
12434
|
+
const inWords = (text) => questions.reduce(
|
|
12435
|
+
(said, q) => said.replace(new RegExp(`\\b${q.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g"), q.ask),
|
|
12436
|
+
text
|
|
12437
|
+
);
|
|
12438
|
+
const told = outcome.error ?? refusal("invalid_argument", outcome.message, "Check your answers, then send again.");
|
|
12439
|
+
setRefusal({
|
|
12440
|
+
...told,
|
|
12441
|
+
message: inWords(told.message),
|
|
12442
|
+
...told.hint ? { hint: inWords(told.hint) } : {}
|
|
12443
|
+
});
|
|
12090
12444
|
return;
|
|
12091
12445
|
}
|
|
12092
12446
|
setDone(outcome.message ?? "sent");
|
|
12093
12447
|
onSubmitted?.(outcome.recordId ?? null);
|
|
12094
|
-
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview]);
|
|
12448
|
+
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
12095
12449
|
function advance() {
|
|
12096
12450
|
if (!oneAtATime) return;
|
|
12097
12451
|
if (index < live.length - 1) setAt(index + 1);
|
|
@@ -12150,7 +12504,22 @@ function FormStage({
|
|
|
12150
12504
|
] }) : null,
|
|
12151
12505
|
form.intro && index === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-sm text-muted-foreground", children: form.intro }) : null,
|
|
12152
12506
|
error ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(RefusalNotice, { error, className: "text-left" }) : null,
|
|
12153
|
-
|
|
12507
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12508
|
+
RefusalNotice,
|
|
12509
|
+
{
|
|
12510
|
+
error: refusal2,
|
|
12511
|
+
className: "text-left",
|
|
12512
|
+
onKeepEditing: () => {
|
|
12513
|
+
setRefusal(null);
|
|
12514
|
+
stage.current?.querySelector("input, textarea, select, button")?.focus();
|
|
12515
|
+
},
|
|
12516
|
+
onDiscard: () => {
|
|
12517
|
+
setRefusal(null);
|
|
12518
|
+
setAnswers({});
|
|
12519
|
+
setAt(0);
|
|
12520
|
+
}
|
|
12521
|
+
}
|
|
12522
|
+
) : null,
|
|
12154
12523
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { ref: stage, className: "flex flex-col gap-4 text-left", children: [
|
|
12155
12524
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12156
12525
|
Question,
|
|
@@ -12224,12 +12593,15 @@ function Question({
|
|
|
12224
12593
|
void upload?.(file).then((result) => {
|
|
12225
12594
|
if (!result) return;
|
|
12226
12595
|
if (result.ok) onChange(result.fileId);
|
|
12227
|
-
else
|
|
12596
|
+
else
|
|
12597
|
+
setUploadError(
|
|
12598
|
+
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
12599
|
+
);
|
|
12228
12600
|
});
|
|
12229
12601
|
}
|
|
12230
12602
|
}
|
|
12231
12603
|
),
|
|
12232
|
-
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12604
|
+
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(RefusalLine, { error: uploadError }) : null
|
|
12233
12605
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(FieldControl, { field, value, onChange, id })
|
|
12234
12606
|
] });
|
|
12235
12607
|
}
|
|
@@ -13016,10 +13388,15 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
13016
13388
|
onChange: (e) => setDraftBody(e.target.value)
|
|
13017
13389
|
}
|
|
13018
13390
|
),
|
|
13019
|
-
unresolved.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { className: "
|
|
13020
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("code", { children: token.raw }),
|
|
13021
|
-
" \u2014
|
|
13022
|
-
|
|
13391
|
+
unresolved.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { className: "flex items-baseline gap-1", children: [
|
|
13392
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("code", { className: "text-destructive", children: token.raw }),
|
|
13393
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { "aria-hidden": "true", children: "\u2014" }),
|
|
13394
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13395
|
+
RefusalLine,
|
|
13396
|
+
{
|
|
13397
|
+
error: refusal("invalid_argument", token.why, "Change it to a Field this table has, or take it out.")
|
|
13398
|
+
}
|
|
13399
|
+
)
|
|
13023
13400
|
] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-muted-foreground", children: "Every token in this body points at a Field this table can answer." }) : null
|
|
13024
13401
|
] });
|
|
13025
13402
|
}
|
|
@@ -13224,7 +13601,21 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
13224
13601
|
signature.document_hash.slice(0, 12)
|
|
13225
13602
|
] })
|
|
13226
13603
|
] }),
|
|
13227
|
-
|
|
13604
|
+
intact === false ? (
|
|
13605
|
+
// THE STORE REFUSED TO CALL IT INTACT: its reason goes through the one
|
|
13606
|
+
// formatter, with what to do next, never raw in red (lane REFUSAL-SWEEP).
|
|
13607
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
13608
|
+
RefusalLine,
|
|
13609
|
+
{
|
|
13610
|
+
className: "mt-0.5",
|
|
13611
|
+
error: refusal(
|
|
13612
|
+
"refused_by_rule",
|
|
13613
|
+
typeof verdict === "object" && verdict !== null && "reason" in verdict ? String(verdict.reason) : "The store says what is there no longer matches what was signed.",
|
|
13614
|
+
"Ask for it to be signed again on the version that is there now."
|
|
13615
|
+
)
|
|
13616
|
+
}
|
|
13617
|
+
)
|
|
13618
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("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 ?? "") })
|
|
13228
13619
|
] }, signature.id);
|
|
13229
13620
|
}) }) : null,
|
|
13230
13621
|
!render ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("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__ */ (0, import_jsx_runtime47.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
@@ -13485,14 +13876,14 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13485
13876
|
var import_react95 = require("react");
|
|
13486
13877
|
var import_recharts = require("recharts");
|
|
13487
13878
|
var import_records11 = require("@ai-matrx/records");
|
|
13488
|
-
var
|
|
13879
|
+
var import_core7 = require("@ai-matrx/records/core");
|
|
13489
13880
|
var import_design_system46 = require("@ai-matrx/design-system");
|
|
13490
13881
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
13491
13882
|
function pretty(n) {
|
|
13492
13883
|
return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
|
|
13493
13884
|
}
|
|
13494
|
-
function
|
|
13495
|
-
return (0,
|
|
13885
|
+
function asRefusal2(block) {
|
|
13886
|
+
return (0, import_core7.mapPgError)(
|
|
13496
13887
|
{
|
|
13497
13888
|
message: block.refused ?? "",
|
|
13498
13889
|
...block.sqlstate ? { code: block.sqlstate } : {}
|
|
@@ -13552,7 +13943,7 @@ function ChartBlock({ block, subject, className }) {
|
|
|
13552
13943
|
" ms"
|
|
13553
13944
|
] }) : null
|
|
13554
13945
|
] }),
|
|
13555
|
-
block.refused ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(RefusalNotice, { error:
|
|
13946
|
+
block.refused ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(RefusalNotice, { error: asRefusal2(block) }) : needs ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: needs }) : kind === "stuck" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(StuckList, { rows: block.rows ?? [] }) : points.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("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__ */ (0, import_jsx_runtime49.jsx)(import_jsx_runtime49.Fragment, { children: kind === "number" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(BigNumber, { points, measure: primary, onDrill: drill }) : kind === "table" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(GroupTable, { points, series, onDrill: drill }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
|
|
13556
13947
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Drawing, { kind, points, series, config, onDrill: drill }),
|
|
13557
13948
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Values, { points, measure: primary, config, onDrill: drill })
|
|
13558
13949
|
] }) })
|
|
@@ -14118,13 +14509,14 @@ var import_react99 = require("@ai-matrx/records/react");
|
|
|
14118
14509
|
var import_records12 = require("@ai-matrx/records");
|
|
14119
14510
|
var import_design_system48 = require("@ai-matrx/design-system");
|
|
14120
14511
|
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
14512
|
+
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.";
|
|
14121
14513
|
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.";
|
|
14122
14514
|
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.";
|
|
14123
14515
|
function formSuggestion(tableName2) {
|
|
14124
14516
|
const subject = tableName2?.trim() ? tableName2.trim() : "this table";
|
|
14125
14517
|
return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
|
|
14126
14518
|
}
|
|
14127
|
-
function FormsPanel({ tableId, className }) {
|
|
14519
|
+
function FormsPanel({ tableId, activeFormId, className }) {
|
|
14128
14520
|
const client = (0, import_react99.useRecordsClient)();
|
|
14129
14521
|
const publishGate = usePublishGate();
|
|
14130
14522
|
const host = useRecordsUi();
|
|
@@ -14135,6 +14527,11 @@ function FormsPanel({ tableId, className }) {
|
|
|
14135
14527
|
const [busy, setBusy] = (0, import_react98.useState)(null);
|
|
14136
14528
|
const [copied, setCopied] = (0, import_react98.useState)(null);
|
|
14137
14529
|
const [building, setBuilding] = (0, import_react98.useState)(false);
|
|
14530
|
+
const linked = activeFormId && forms !== null ? forms.find((f) => f.form_id === activeFormId) ?? null : null;
|
|
14531
|
+
const linkedMissing = Boolean(activeFormId) && forms !== null && linked === null;
|
|
14532
|
+
(0, import_react98.useEffect)(() => {
|
|
14533
|
+
if (linked && rights.structure) setBuilding(true);
|
|
14534
|
+
}, [linked, rights.structure]);
|
|
14138
14535
|
const load = (0, import_react98.useCallback)(async () => {
|
|
14139
14536
|
const answered = await client.forms({ table_id: tableId });
|
|
14140
14537
|
if (!answered.ok) {
|
|
@@ -14183,10 +14580,12 @@ function FormsPanel({ tableId, className }) {
|
|
|
14183
14580
|
rights.structure && forms.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_design_system48.Button, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a form" }) : null
|
|
14184
14581
|
] }),
|
|
14185
14582
|
error ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(RefusalNotice, { error }) : null,
|
|
14583
|
+
linkedMissing ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("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,
|
|
14186
14584
|
building ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
14187
14585
|
FormBuilder,
|
|
14188
14586
|
{
|
|
14189
14587
|
tableId,
|
|
14588
|
+
activeFormId: linked ? linked.form_id : null,
|
|
14190
14589
|
onActiveForm: () => {
|
|
14191
14590
|
void load();
|
|
14192
14591
|
}
|
|
@@ -14212,50 +14611,62 @@ function FormsPanel({ tableId, className }) {
|
|
|
14212
14611
|
) : null,
|
|
14213
14612
|
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
|
|
14214
14613
|
const url = `${origin}${(0, import_records12.publicFormPath)(form.form_id)}`;
|
|
14215
|
-
return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
|
|
14219
|
-
|
|
14220
|
-
|
|
14221
|
-
|
|
14222
|
-
|
|
14223
|
-
|
|
14224
|
-
|
|
14225
|
-
"
|
|
14226
|
-
|
|
14227
|
-
|
|
14228
|
-
|
|
14229
|
-
|
|
14230
|
-
"
|
|
14231
|
-
|
|
14232
|
-
|
|
14233
|
-
|
|
14234
|
-
|
|
14235
|
-
|
|
14236
|
-
|
|
14237
|
-
|
|
14238
|
-
|
|
14239
|
-
|
|
14240
|
-
|
|
14241
|
-
|
|
14242
|
-
|
|
14243
|
-
|
|
14244
|
-
|
|
14245
|
-
|
|
14246
|
-
|
|
14247
|
-
|
|
14248
|
-
|
|
14249
|
-
|
|
14250
|
-
|
|
14251
|
-
|
|
14252
|
-
|
|
14253
|
-
|
|
14254
|
-
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
|
|
14614
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
|
|
14615
|
+
"li",
|
|
14616
|
+
{
|
|
14617
|
+
"data-linked": form.form_id === activeFormId ? "true" : void 0,
|
|
14618
|
+
ref: form.form_id === activeFormId ? (el) => el?.scrollIntoView({ block: "nearest" }) : void 0,
|
|
14619
|
+
className: (0, import_design_system48.cn)(
|
|
14620
|
+
"rounded-md border p-2.5",
|
|
14621
|
+
form.form_id === activeFormId && "border-primary ring-1 ring-primary"
|
|
14622
|
+
),
|
|
14623
|
+
children: [
|
|
14624
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
14625
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: form.title ?? form.slug }),
|
|
14626
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_design_system48.Badge, { variant: form.state === "open" ? "default" : "secondary", children: form.state })
|
|
14627
|
+
] }),
|
|
14628
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "mt-0.5 text-xs text-muted-foreground", children: FORM_STATE_WORDS[form.state] }),
|
|
14629
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("p", { className: "mt-1.5 text-xs", children: [
|
|
14630
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "font-medium", children: form.in_table }),
|
|
14631
|
+
" in the table",
|
|
14632
|
+
form.held > 0 ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
|
|
14633
|
+
" \xB7 ",
|
|
14634
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "font-medium", children: form.held }),
|
|
14635
|
+
" waiting for someone"
|
|
14636
|
+
] }) : null,
|
|
14637
|
+
form.rejected > 0 ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
|
|
14638
|
+
" \xB7 ",
|
|
14639
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "font-medium", children: form.rejected }),
|
|
14640
|
+
" turned away"
|
|
14641
|
+
] }) : null,
|
|
14642
|
+
form.submission_cap !== null ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
14643
|
+
" \xB7 stops at ",
|
|
14644
|
+
form.submission_cap
|
|
14645
|
+
] }) : null
|
|
14646
|
+
] }),
|
|
14647
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
14648
|
+
form.published_at ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_design_system48.Button, { size: "sm", variant: "outline", onClick: () => void copy(url, form.form_id), children: copied === form.form_id ? "Copied" : "Copy link" }) : null,
|
|
14649
|
+
rights.structure && !publishGate.blocked ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
14650
|
+
import_design_system48.Button,
|
|
14651
|
+
{
|
|
14652
|
+
size: "sm",
|
|
14653
|
+
variant: "ghost",
|
|
14654
|
+
disabled: busy === form.form_id,
|
|
14655
|
+
onClick: () => void toggle(form),
|
|
14656
|
+
children: busy === form.form_id ? "\u2026" : form.published_at && !form.closed_at ? "Unpublish" : "Publish"
|
|
14657
|
+
}
|
|
14658
|
+
) : null
|
|
14659
|
+
] }),
|
|
14660
|
+
rights.structure && publishGate.why ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "mt-1.5 text-xs text-muted-foreground", children: publishGate.why }) : null,
|
|
14661
|
+
shown && shown === url ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
|
|
14662
|
+
"This browser would not let the page copy for you, so here it is to copy by hand:",
|
|
14663
|
+
" ",
|
|
14664
|
+
url
|
|
14665
|
+
] }) : null
|
|
14666
|
+
]
|
|
14667
|
+
},
|
|
14668
|
+
form.form_id
|
|
14669
|
+
);
|
|
14259
14670
|
}) }),
|
|
14260
14671
|
!rights.known ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("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
|
|
14261
14672
|
] });
|
|
@@ -14912,7 +15323,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14912
15323
|
void client.captureOpen({ sheet_id: sheetId }).then((res) => {
|
|
14913
15324
|
if (cancelled) return;
|
|
14914
15325
|
if (res.ok) setFace(res.data);
|
|
14915
|
-
else setLoadFailed(res.error
|
|
15326
|
+
else setLoadFailed(refusalOr(res.error, "unreachable", "This sheet could not be opened."));
|
|
14916
15327
|
});
|
|
14917
15328
|
return () => {
|
|
14918
15329
|
cancelled = true;
|
|
@@ -14959,7 +15370,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14959
15370
|
const needed = questions.filter((x) => x.required && !answered(x.field));
|
|
14960
15371
|
if (needed.length > 0) {
|
|
14961
15372
|
setMissing(
|
|
14962
|
-
|
|
15373
|
+
refusal(
|
|
15374
|
+
"invalid_argument",
|
|
15375
|
+
`${needed.map((x) => (x.ask ?? x.field).replace(/\?$/, "")).join(", ")} still ${needed.length === 1 ? "needs" : "need"} an answer.`,
|
|
15376
|
+
"Answer it, then capture again. Nothing has been queued yet."
|
|
15377
|
+
)
|
|
14963
15378
|
);
|
|
14964
15379
|
setAt(questions.findIndex((x) => x.field === needed[0].field));
|
|
14965
15380
|
return;
|
|
@@ -14990,7 +15405,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14990
15405
|
if (online) await sync();
|
|
14991
15406
|
}
|
|
14992
15407
|
if (loadFailed) {
|
|
14993
|
-
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("section", { className: (0, import_design_system51.cn)("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("
|
|
15408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("section", { className: (0, import_design_system51.cn)("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { "data-testid": "capture-refusal", children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(RefusalNotice, { error: loadFailed, className: "text-sm" }) }) });
|
|
14994
15409
|
}
|
|
14995
15410
|
if (face === void 0) {
|
|
14996
15411
|
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_design_system51.Skeleton, { className: (0, import_design_system51.cn)("mx-auto h-64 w-full max-w-sm", className) });
|
|
@@ -15021,7 +15436,21 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
15021
15436
|
] });
|
|
15022
15437
|
const queuePanel = items.filter((i) => i.state !== "landed").length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("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__ */ (0, import_jsx_runtime54.jsxs)("li", { className: "flex items-start gap-2 text-[11px]", children: [
|
|
15023
15438
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "tabular-nums text-muted-foreground", children: new Date(i.captured_at).toLocaleTimeString() }),
|
|
15024
|
-
|
|
15439
|
+
i.state === "refused" ? (
|
|
15440
|
+
// THE STORE REFUSED THIS ONE: a refusal line, heading and remedy
|
|
15441
|
+
// both — never its raw words in red (lane REFUSAL-SWEEP).
|
|
15442
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
15443
|
+
RefusalLine,
|
|
15444
|
+
{
|
|
15445
|
+
className: "flex-1",
|
|
15446
|
+
error: refusal(
|
|
15447
|
+
"refused_by_rule",
|
|
15448
|
+
i.last_error ?? "This one was refused.",
|
|
15449
|
+
"Throw it away and capture it again with the answer changed."
|
|
15450
|
+
)
|
|
15451
|
+
}
|
|
15452
|
+
)
|
|
15453
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "flex-1", children: i.last_error ?? "Waiting for a signal." }),
|
|
15025
15454
|
i.state === "refused" ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
15026
15455
|
import_design_system51.Button,
|
|
15027
15456
|
{
|
|
@@ -15141,7 +15570,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
15141
15570
|
}
|
|
15142
15571
|
}
|
|
15143
15572
|
),
|
|
15144
|
-
missing ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("
|
|
15573
|
+
missing ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { "data-testid": "capture-missing", children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(RefusalNotice, { error: missing, className: "text-sm" }) }) : null,
|
|
15145
15574
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
15146
15575
|
at > 0 ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
15147
15576
|
import_design_system51.Button,
|
|
@@ -15369,13 +15798,13 @@ function AdHocCaptureSheet({
|
|
|
15369
15798
|
setUploadError(null);
|
|
15370
15799
|
void host.upload?.(file).then((result) => {
|
|
15371
15800
|
if (result.ok) setFileId(result.fileId);
|
|
15372
|
-
else setUploadError(result.reason);
|
|
15801
|
+
else setUploadError(refusal("invalid_argument", result.reason, "Pick another file, or try this one again."));
|
|
15373
15802
|
});
|
|
15374
15803
|
}
|
|
15375
15804
|
}
|
|
15376
15805
|
),
|
|
15377
15806
|
fileId ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Attached." }) : null,
|
|
15378
|
-
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
15807
|
+
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(RefusalLine, { error: uploadError }) : null
|
|
15379
15808
|
] }) : null,
|
|
15380
15809
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
15381
15810
|
import_design_system52.Textarea,
|
|
@@ -15838,17 +16267,17 @@ async function declareTable(client, spec) {
|
|
|
15838
16267
|
if (!written.ok) return undo(client, table.data, home.data, spec.name, written.error);
|
|
15839
16268
|
return { ok: true, data: table.data, homeId: home.data };
|
|
15840
16269
|
}
|
|
15841
|
-
async function undo(client, tableId, homeId, name,
|
|
16270
|
+
async function undo(client, tableId, homeId, name, refusal2) {
|
|
15842
16271
|
const removed = await client.recordDelete({ record_id: tableId });
|
|
15843
16272
|
if (removed.ok) {
|
|
15844
16273
|
await client.recordDelete({ record_id: homeId });
|
|
15845
|
-
return { ok: false, error:
|
|
16274
|
+
return { ok: false, error: refusal2 };
|
|
15846
16275
|
}
|
|
15847
16276
|
return {
|
|
15848
16277
|
ok: false,
|
|
15849
16278
|
error: {
|
|
15850
|
-
...
|
|
15851
|
-
hint: `${
|
|
16279
|
+
...refusal2,
|
|
16280
|
+
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.`
|
|
15852
16281
|
}
|
|
15853
16282
|
};
|
|
15854
16283
|
}
|
|
@@ -16102,9 +16531,43 @@ function chooseSurface(current, pressed) {
|
|
|
16102
16531
|
function surfaceChosen(current, asking) {
|
|
16103
16532
|
return asking.main !== void 0 ? current.main === asking.main : current.rail === asking.rail;
|
|
16104
16533
|
}
|
|
16105
|
-
function openingRail(activeRecordId) {
|
|
16106
|
-
|
|
16534
|
+
function openingRail(activeRecordId, activeRail) {
|
|
16535
|
+
if (activeRecordId) return { rail: "record", record: activeRecordId };
|
|
16536
|
+
const named = railFromParam(activeRail);
|
|
16537
|
+
if (named !== null && named !== "share") return { rail: named, record: null };
|
|
16538
|
+
return { rail: "none", record: null };
|
|
16539
|
+
}
|
|
16540
|
+
var RAIL_SPELLINGS = {
|
|
16541
|
+
forms: "forms",
|
|
16542
|
+
form: "forms",
|
|
16543
|
+
bookings: "bookings",
|
|
16544
|
+
booking: "bookings",
|
|
16545
|
+
checklists: "checklists",
|
|
16546
|
+
checklist: "checklists",
|
|
16547
|
+
notifications: "notifications",
|
|
16548
|
+
notification: "notifications",
|
|
16549
|
+
digests: "notifications",
|
|
16550
|
+
digest: "notifications",
|
|
16551
|
+
subscriptions: "notifications",
|
|
16552
|
+
subscription: "notifications",
|
|
16553
|
+
portals: "portals",
|
|
16554
|
+
portal: "portals",
|
|
16555
|
+
settings: "settings",
|
|
16556
|
+
import: "import",
|
|
16557
|
+
inbox: "inbox",
|
|
16558
|
+
share: "share",
|
|
16559
|
+
sharing: "share"
|
|
16560
|
+
};
|
|
16561
|
+
function railFromParam(raw) {
|
|
16562
|
+
if (typeof raw !== "string") return null;
|
|
16563
|
+
const token = raw.trim().toLowerCase();
|
|
16564
|
+
if (token === "") return null;
|
|
16565
|
+
return RAIL_SPELLINGS[token] ?? null;
|
|
16107
16566
|
}
|
|
16567
|
+
function unknownRailLine(raw) {
|
|
16568
|
+
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.`;
|
|
16569
|
+
}
|
|
16570
|
+
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.";
|
|
16108
16571
|
function openingView(activeView, activeDashboardId) {
|
|
16109
16572
|
const named = pageViewFromParam(activeView);
|
|
16110
16573
|
if (named === null) {
|
|
@@ -16132,6 +16595,8 @@ function TablePage({
|
|
|
16132
16595
|
activeGroupField,
|
|
16133
16596
|
cameFrom,
|
|
16134
16597
|
filter,
|
|
16598
|
+
activeRail,
|
|
16599
|
+
activeItemId,
|
|
16135
16600
|
className
|
|
16136
16601
|
}) {
|
|
16137
16602
|
const client = (0, import_react119.useRecordsClient)();
|
|
@@ -16141,8 +16606,13 @@ function TablePage({
|
|
|
16141
16606
|
const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
|
|
16142
16607
|
const organizationId = (0, import_react119.useRecordsClient)().config.organizationId;
|
|
16143
16608
|
const [view, setView] = (0, import_react118.useState)(null);
|
|
16144
|
-
const opening = openingRail(activeRecordId);
|
|
16609
|
+
const opening = openingRail(activeRecordId, activeRail);
|
|
16145
16610
|
const opened = openingView(activeView, activeDashboardId);
|
|
16611
|
+
const railAsked = typeof activeRail === "string" ? activeRail.trim() : "";
|
|
16612
|
+
const railUnknown = railAsked !== "" && railFromParam(railAsked) === null ? railAsked : null;
|
|
16613
|
+
const shareFromLink = railFromParam(activeRail) === "share";
|
|
16614
|
+
const canShare = useCanShare();
|
|
16615
|
+
const itemFor = (which) => railFromParam(activeRail) === which ? activeItemId ?? null : null;
|
|
16146
16616
|
const [asking, setAsking] = (0, import_react118.useState)(null);
|
|
16147
16617
|
const [layoutFromLink, setLayoutFromLink] = (0, import_react118.useState)(opened.layout);
|
|
16148
16618
|
const [surface, setSurface] = (0, import_react118.useState)({
|
|
@@ -16167,6 +16637,12 @@ function TablePage({
|
|
|
16167
16637
|
setOpenRecord(activeRecordId);
|
|
16168
16638
|
setSurface((now) => ({ ...now, rail: "record" }));
|
|
16169
16639
|
}, [activeRecordId]);
|
|
16640
|
+
(0, import_react118.useEffect)(() => {
|
|
16641
|
+
if (activeRecordId) return;
|
|
16642
|
+
const named = railFromParam(activeRail);
|
|
16643
|
+
if (named === null || named === "share") return;
|
|
16644
|
+
setSurface((now) => ({ ...now, rail: named }));
|
|
16645
|
+
}, [activeRail, activeRecordId]);
|
|
16170
16646
|
(0, import_react118.useEffect)(() => {
|
|
16171
16647
|
const named = pageViewFromParam(activeView);
|
|
16172
16648
|
if (named === null) return;
|
|
@@ -16331,7 +16807,8 @@ function TablePage({
|
|
|
16331
16807
|
organizationId,
|
|
16332
16808
|
subjectId: tableId,
|
|
16333
16809
|
name: table.data?.name,
|
|
16334
|
-
may: rights.share
|
|
16810
|
+
may: rights.share,
|
|
16811
|
+
initiallyOpen: shareFromLink
|
|
16335
16812
|
}
|
|
16336
16813
|
),
|
|
16337
16814
|
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ExportMenu, { tableId })
|
|
@@ -16354,6 +16831,8 @@ function TablePage({
|
|
|
16354
16831
|
}
|
|
16355
16832
|
)
|
|
16356
16833
|
) : /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_jsx_runtime61.Fragment, { children: [
|
|
16834
|
+
railUnknown ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("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,
|
|
16835
|
+
shareFromLink && rights.known && (!rights.share || !canShare) ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("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,
|
|
16357
16836
|
opened.unknown ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("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,
|
|
16358
16837
|
cameFrom ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
16359
16838
|
"p",
|
|
@@ -16412,7 +16891,7 @@ function TablePage({
|
|
|
16412
16891
|
}
|
|
16413
16892
|
}
|
|
16414
16893
|
) : null,
|
|
16415
|
-
rail === "forms" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(FormsPanel, { tableId }) : null,
|
|
16894
|
+
rail === "forms" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(FormsPanel, { tableId, activeFormId: itemFor("forms") }) : null,
|
|
16416
16895
|
rail === "bookings" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(BookingSlots, { tableId }) : null,
|
|
16417
16896
|
rail === "checklists" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
16418
16897
|
ChecklistsPanel,
|
|
@@ -16424,8 +16903,8 @@ function TablePage({
|
|
|
16424
16903
|
}
|
|
16425
16904
|
}
|
|
16426
16905
|
) : null,
|
|
16427
|
-
rail === "notifications" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(SubscriptionsPanel, { tableId }) : null,
|
|
16428
|
-
rail === "portals" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(PortalsPanel, { tableId }) : null,
|
|
16906
|
+
rail === "notifications" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(SubscriptionsPanel, { tableId, activeRuleId: itemFor("notifications") }) : null,
|
|
16907
|
+
rail === "portals" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(PortalsPanel, { tableId, activePortalId: itemFor("portals") }) : null,
|
|
16429
16908
|
rail === "import" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ImportWizard, { tableId, onDone: () => setRail("none") }) : null,
|
|
16430
16909
|
rail === "field" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(FieldEditor, { tableId, onSaved: () => setRail("none"), onCancel: () => setRail("none") }) : null,
|
|
16431
16910
|
rail === "new-record" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|