@ai-matrx/records-ui 0.79.0 → 0.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/dist/index.cjs +1097 -482
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +140 -11
- package/dist/index.d.ts +140 -11
- package/dist/index.js +1184 -569
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.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,33 +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),
|
|
1238
|
+
className: (0, import_design_system3.cn)(
|
|
1110
1239
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1111
1240
|
picked.includes(row.id) ? "bg-accent text-accent-foreground" : ""
|
|
1112
1241
|
),
|
|
1113
1242
|
children: titleOf(row.id)
|
|
1114
1243
|
}
|
|
1115
1244
|
) }, row.id)) }),
|
|
1116
|
-
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
|
|
1117
1246
|
] }),
|
|
1118
|
-
allowCreate && !full ? /* @__PURE__ */ (0,
|
|
1119
|
-
targetRefusal ? /* @__PURE__ */ (0,
|
|
1120
|
-
/* @__PURE__ */ (0,
|
|
1121
|
-
|
|
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,
|
|
1122
1251
|
{
|
|
1123
1252
|
autoFocus: true,
|
|
1124
1253
|
value: newTitle,
|
|
@@ -1134,8 +1263,8 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1134
1263
|
"aria-label": `Name the new ${targetWord}`
|
|
1135
1264
|
}
|
|
1136
1265
|
),
|
|
1137
|
-
/* @__PURE__ */ (0,
|
|
1138
|
-
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1267
|
+
import_design_system3.Button,
|
|
1139
1268
|
{
|
|
1140
1269
|
type: "button",
|
|
1141
1270
|
size: "sm",
|
|
@@ -1145,12 +1274,13 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1145
1274
|
children: busy ? "Creating\u2026" : "Create"
|
|
1146
1275
|
}
|
|
1147
1276
|
)
|
|
1148
|
-
] }) : /* @__PURE__ */ (0,
|
|
1149
|
-
|
|
1277
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1278
|
+
import_design_system3.Button,
|
|
1150
1279
|
{
|
|
1151
1280
|
type: "button",
|
|
1152
1281
|
size: "sm",
|
|
1153
1282
|
variant: "ghost",
|
|
1283
|
+
title: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}`,
|
|
1154
1284
|
className: "w-full justify-start text-xs",
|
|
1155
1285
|
disabled: busy,
|
|
1156
1286
|
onClick: () => {
|
|
@@ -1160,10 +1290,25 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1160
1290
|
setCreating(true);
|
|
1161
1291
|
}
|
|
1162
1292
|
},
|
|
1163
|
-
children: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}`
|
|
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}` })
|
|
1164
1294
|
}
|
|
1165
1295
|
),
|
|
1166
|
-
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
|
|
1167
1312
|
] }) : null
|
|
1168
1313
|
] })
|
|
1169
1314
|
] })
|
|
@@ -1171,12 +1316,12 @@ function RelationPicker({ field, value, onChange, disabled, id, className, allow
|
|
|
1171
1316
|
}
|
|
1172
1317
|
|
|
1173
1318
|
// src/editors.tsx
|
|
1174
|
-
var
|
|
1319
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1175
1320
|
function FieldLabel({ field, htmlFor, children }) {
|
|
1176
|
-
return /* @__PURE__ */ (0,
|
|
1177
|
-
/* @__PURE__ */ (0,
|
|
1178
|
-
field.required ? /* @__PURE__ */ (0,
|
|
1179
|
-
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,
|
|
1180
1325
|
children
|
|
1181
1326
|
] });
|
|
1182
1327
|
}
|
|
@@ -1187,18 +1332,18 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1187
1332
|
case "formula":
|
|
1188
1333
|
case "lookup":
|
|
1189
1334
|
case "rollup":
|
|
1190
|
-
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: [
|
|
1191
1336
|
value === null || value === void 0 ? "\u2014" : String(value),
|
|
1192
|
-
/* @__PURE__ */ (0,
|
|
1337
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ml-2 italic", children: [
|
|
1193
1338
|
fieldName(field),
|
|
1194
1339
|
" is worked out by the system, so it cannot be typed in"
|
|
1195
1340
|
] })
|
|
1196
1341
|
] });
|
|
1197
1342
|
case "json":
|
|
1198
|
-
return /* @__PURE__ */ (0,
|
|
1343
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(JsonControl, { field, value, onChange, disabled, id: inputId });
|
|
1199
1344
|
case "long_text":
|
|
1200
|
-
return /* @__PURE__ */ (0,
|
|
1201
|
-
|
|
1345
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1346
|
+
import_design_system4.BasicTextarea,
|
|
1202
1347
|
{
|
|
1203
1348
|
id: inputId,
|
|
1204
1349
|
rows: 3,
|
|
@@ -1210,8 +1355,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1210
1355
|
case "number":
|
|
1211
1356
|
case "currency":
|
|
1212
1357
|
case "percent":
|
|
1213
|
-
return /* @__PURE__ */ (0,
|
|
1214
|
-
|
|
1358
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1359
|
+
import_design_system4.BasicInput,
|
|
1215
1360
|
{
|
|
1216
1361
|
id: inputId,
|
|
1217
1362
|
type: "number",
|
|
@@ -1223,8 +1368,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1223
1368
|
}
|
|
1224
1369
|
);
|
|
1225
1370
|
case "datetime":
|
|
1226
|
-
return /* @__PURE__ */ (0,
|
|
1227
|
-
|
|
1371
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1372
|
+
import_design_system4.BasicInput,
|
|
1228
1373
|
{
|
|
1229
1374
|
id: inputId,
|
|
1230
1375
|
type: field.format === "datetime" ? "datetime-local" : "date",
|
|
@@ -1236,8 +1381,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1236
1381
|
case "email":
|
|
1237
1382
|
case "phone":
|
|
1238
1383
|
case "url":
|
|
1239
|
-
return /* @__PURE__ */ (0,
|
|
1240
|
-
|
|
1384
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1385
|
+
import_design_system4.BasicInput,
|
|
1241
1386
|
{
|
|
1242
1387
|
id: inputId,
|
|
1243
1388
|
type: kind === "email" ? "email" : kind === "phone" ? "tel" : "url",
|
|
@@ -1247,19 +1392,19 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1247
1392
|
}
|
|
1248
1393
|
);
|
|
1249
1394
|
case "checkbox":
|
|
1250
|
-
return /* @__PURE__ */ (0,
|
|
1395
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CheckboxControl, { field, value, onChange, disabled, id: inputId });
|
|
1251
1396
|
case "select":
|
|
1252
1397
|
case "multi_select":
|
|
1253
|
-
return /* @__PURE__ */ (0,
|
|
1398
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OptionControl, { field, value, onChange, disabled, id: inputId });
|
|
1254
1399
|
case "member":
|
|
1255
|
-
return /* @__PURE__ */ (0,
|
|
1400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PersonPicker, { field, value, onChange, disabled, id: inputId });
|
|
1256
1401
|
case "relation":
|
|
1257
1402
|
case "attachment":
|
|
1258
|
-
return /* @__PURE__ */ (0,
|
|
1403
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RelationPicker, { field, value, onChange, disabled, id: inputId });
|
|
1259
1404
|
default:
|
|
1260
1405
|
if (typeof value === "boolean" || field.config?.["kind"] === "boolean") {
|
|
1261
|
-
return /* @__PURE__ */ (0,
|
|
1262
|
-
|
|
1406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1407
|
+
import_design_system4.Checkbox,
|
|
1263
1408
|
{
|
|
1264
1409
|
id: inputId,
|
|
1265
1410
|
checked: value === true,
|
|
@@ -1268,8 +1413,8 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1268
1413
|
}
|
|
1269
1414
|
);
|
|
1270
1415
|
}
|
|
1271
|
-
return /* @__PURE__ */ (0,
|
|
1272
|
-
|
|
1416
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1417
|
+
import_design_system4.BasicInput,
|
|
1273
1418
|
{
|
|
1274
1419
|
id: inputId,
|
|
1275
1420
|
value: value === null || value === void 0 ? "" : String(value),
|
|
@@ -1281,9 +1426,9 @@ function FieldControl({ field, value, onChange, disabled, id }) {
|
|
|
1281
1426
|
}
|
|
1282
1427
|
function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
1283
1428
|
const unanswered = value === null || value === void 0;
|
|
1284
|
-
return /* @__PURE__ */ (0,
|
|
1285
|
-
/* @__PURE__ */ (0,
|
|
1286
|
-
|
|
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,
|
|
1287
1432
|
{
|
|
1288
1433
|
id,
|
|
1289
1434
|
checked: value === true,
|
|
@@ -1292,10 +1437,10 @@ function CheckboxControl({ field, value, onChange, disabled, id }) {
|
|
|
1292
1437
|
onCheckedChange: (next) => onChange(next === true)
|
|
1293
1438
|
}
|
|
1294
1439
|
),
|
|
1295
|
-
unanswered ? /* @__PURE__ */ (0,
|
|
1296
|
-
/* @__PURE__ */ (0,
|
|
1297
|
-
field.required ? null : /* @__PURE__ */ (0,
|
|
1298
|
-
|
|
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,
|
|
1299
1444
|
{
|
|
1300
1445
|
size: "sm",
|
|
1301
1446
|
variant: "ghost",
|
|
@@ -1312,9 +1457,9 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1312
1457
|
const initial = value === null || value === void 0 ? "" : typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
1313
1458
|
const [text, setText] = (0, import_react7.useState)(initial);
|
|
1314
1459
|
const [broken, setBroken] = (0, import_react7.useState)(null);
|
|
1315
|
-
return /* @__PURE__ */ (0,
|
|
1316
|
-
/* @__PURE__ */ (0,
|
|
1317
|
-
|
|
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,
|
|
1318
1463
|
{
|
|
1319
1464
|
id,
|
|
1320
1465
|
rows: 4,
|
|
@@ -1333,38 +1478,54 @@ function JsonControl({ field, value, onChange, disabled, id }) {
|
|
|
1333
1478
|
onChange(JSON.parse(next));
|
|
1334
1479
|
setBroken(null);
|
|
1335
1480
|
} catch (thrown) {
|
|
1336
|
-
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
|
+
);
|
|
1337
1489
|
}
|
|
1338
1490
|
}
|
|
1339
1491
|
}
|
|
1340
1492
|
),
|
|
1341
|
-
broken ?
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
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
|
|
1346
1507
|
] });
|
|
1347
1508
|
}
|
|
1348
1509
|
function OptionControl({ field, value, onChange, disabled, id }) {
|
|
1349
1510
|
const client = (0, import_react8.useOptionalRecordsClient)();
|
|
1350
1511
|
const [options, setOptions] = (0, import_react7.useState)(null);
|
|
1351
|
-
const [
|
|
1512
|
+
const [refusal2, setRefusal] = (0, import_react7.useState)(null);
|
|
1352
1513
|
(0, import_react7.useEffect)(() => {
|
|
1353
1514
|
if (!client) return;
|
|
1354
1515
|
let cancelled = false;
|
|
1355
1516
|
void client.fieldOptions({ field_id: field.id }).then((result) => {
|
|
1356
1517
|
if (cancelled) return;
|
|
1357
1518
|
if (result.ok) setOptions(result.data ?? []);
|
|
1358
|
-
else setRefusal(
|
|
1519
|
+
else setRefusal(result.error);
|
|
1359
1520
|
});
|
|
1360
1521
|
return () => {
|
|
1361
1522
|
cancelled = true;
|
|
1362
1523
|
};
|
|
1363
1524
|
}, [client, field.id]);
|
|
1364
1525
|
if (!client) {
|
|
1365
|
-
return /* @__PURE__ */ (0,
|
|
1366
|
-
/* @__PURE__ */ (0,
|
|
1367
|
-
|
|
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,
|
|
1368
1529
|
{
|
|
1369
1530
|
id,
|
|
1370
1531
|
value: value === null || value === void 0 ? "" : String(value),
|
|
@@ -1372,20 +1533,21 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1372
1533
|
onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
|
|
1373
1534
|
}
|
|
1374
1535
|
),
|
|
1375
|
-
/* @__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." })
|
|
1376
1537
|
] });
|
|
1377
1538
|
}
|
|
1378
|
-
if (
|
|
1379
|
-
|
|
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" });
|
|
1380
1542
|
if (options.length === 0) {
|
|
1381
|
-
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.` });
|
|
1382
1544
|
}
|
|
1383
1545
|
const titleOf = (option) => recordName(option.data, null, "Unnamed choice");
|
|
1384
1546
|
if (field.multi) {
|
|
1385
1547
|
const chosen = options.filter((option) => (0, import_records4.choiceValuesOf)(value).some((v) => (0, import_records4.isTheChosen)(option, v)));
|
|
1386
1548
|
const picked = new Set(chosen.map((option) => (0, import_records4.optionKey)(option)));
|
|
1387
|
-
return /* @__PURE__ */ (0,
|
|
1388
|
-
|
|
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,
|
|
1389
1551
|
{
|
|
1390
1552
|
type: "button",
|
|
1391
1553
|
size: "sm",
|
|
@@ -1403,15 +1565,15 @@ function OptionControl({ field, value, onChange, disabled, id }) {
|
|
|
1403
1565
|
)) });
|
|
1404
1566
|
}
|
|
1405
1567
|
const current = options.find((option) => (0, import_records4.isTheChosen)(option, value));
|
|
1406
|
-
return /* @__PURE__ */ (0,
|
|
1407
|
-
|
|
1568
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1569
|
+
import_design_system4.Select,
|
|
1408
1570
|
{
|
|
1409
1571
|
value: current ? (0, import_records4.optionKey)(current) : "",
|
|
1410
1572
|
disabled: disabled ?? false,
|
|
1411
1573
|
onValueChange: (next) => onChange(next === "" ? null : next),
|
|
1412
1574
|
children: [
|
|
1413
|
-
/* @__PURE__ */ (0,
|
|
1414
|
-
/* @__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)) })
|
|
1415
1577
|
]
|
|
1416
1578
|
}
|
|
1417
1579
|
);
|
|
@@ -1421,14 +1583,21 @@ function RecordChip({
|
|
|
1421
1583
|
onRemove,
|
|
1422
1584
|
className
|
|
1423
1585
|
}) {
|
|
1424
|
-
return
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1586
|
+
return (
|
|
1587
|
+
// A chip carries a RECORD'S OWN TITLE, which nobody here chose the length of —
|
|
1588
|
+
// "Marisol Okonkwo — 418 Calle Puerto Vallarta, Camarillo" is an ordinary customer
|
|
1589
|
+
// name. So it caps at its container and ends in an ellipsis with the whole name in
|
|
1590
|
+
// its tooltip, rather than being cut mid-word by whatever box it landed in
|
|
1591
|
+
// (lane FIX-14, 2026-09-22).
|
|
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
|
|
1595
|
+
] })
|
|
1596
|
+
);
|
|
1428
1597
|
}
|
|
1429
1598
|
|
|
1430
1599
|
// src/PersonPicker.tsx
|
|
1431
|
-
var
|
|
1600
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1432
1601
|
function personFromRow(row) {
|
|
1433
1602
|
const data = row.document ?? {};
|
|
1434
1603
|
const userId = data["user_id"];
|
|
@@ -1481,7 +1650,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1481
1650
|
]);
|
|
1482
1651
|
setMembers(roster);
|
|
1483
1652
|
if (records && !records.ok) {
|
|
1484
|
-
setError(
|
|
1653
|
+
setError(records.error);
|
|
1485
1654
|
return;
|
|
1486
1655
|
}
|
|
1487
1656
|
setPeople(records ? records.data.rows.map(personFromRow).filter((p) => p.userId !== null) : []);
|
|
@@ -1502,10 +1671,10 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1502
1671
|
};
|
|
1503
1672
|
}, [client, personTable, picked.length, people.length]);
|
|
1504
1673
|
if (!membersPort || !client) {
|
|
1505
|
-
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 });
|
|
1506
1675
|
}
|
|
1507
1676
|
if (!personTable) {
|
|
1508
|
-
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: [
|
|
1509
1678
|
fieldName(field),
|
|
1510
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."
|
|
1511
1680
|
] });
|
|
@@ -1523,7 +1692,7 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1523
1692
|
const resolved = await personRecordForMember(client, personTable, people, member);
|
|
1524
1693
|
setBusy(false);
|
|
1525
1694
|
if (!resolved.ok) {
|
|
1526
|
-
setError(
|
|
1695
|
+
setError(resolved.error);
|
|
1527
1696
|
return;
|
|
1528
1697
|
}
|
|
1529
1698
|
if (!people.some((p) => p.id === resolved.data)) {
|
|
@@ -1549,8 +1718,8 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1549
1718
|
const pickedUserIds = new Set(
|
|
1550
1719
|
picked.map((recordId) => people.find((p) => p.id === recordId)?.userId).filter(Boolean)
|
|
1551
1720
|
);
|
|
1552
|
-
return /* @__PURE__ */ (0,
|
|
1553
|
-
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)(
|
|
1554
1723
|
RecordChip,
|
|
1555
1724
|
{
|
|
1556
1725
|
title: nameOfRecord(recordId),
|
|
@@ -1558,9 +1727,9 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1558
1727
|
},
|
|
1559
1728
|
recordId
|
|
1560
1729
|
)),
|
|
1561
|
-
disabled ? null : /* @__PURE__ */ (0,
|
|
1562
|
-
/* @__PURE__ */ (0,
|
|
1563
|
-
|
|
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,
|
|
1564
1733
|
{
|
|
1565
1734
|
id,
|
|
1566
1735
|
type: "button",
|
|
@@ -1570,9 +1739,9 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1570
1739
|
children: picked.length === 0 ? "Pick a person" : "Change"
|
|
1571
1740
|
}
|
|
1572
1741
|
) }),
|
|
1573
|
-
/* @__PURE__ */ (0,
|
|
1574
|
-
/* @__PURE__ */ (0,
|
|
1575
|
-
|
|
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,
|
|
1576
1745
|
{
|
|
1577
1746
|
value: search,
|
|
1578
1747
|
onChange: (e) => setSearch(e.target.value),
|
|
@@ -1580,29 +1749,29 @@ function PersonPicker({ field, value, onChange, disabled, id, className }) {
|
|
|
1580
1749
|
className: "mb-1 h-7 text-xs"
|
|
1581
1750
|
}
|
|
1582
1751
|
),
|
|
1583
|
-
error ? /* @__PURE__ */ (0,
|
|
1584
|
-
members === null ? /* @__PURE__ */ (0,
|
|
1585
|
-
/* @__PURE__ */ (0,
|
|
1586
|
-
/* @__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)(
|
|
1587
1756
|
"button",
|
|
1588
1757
|
{
|
|
1589
1758
|
type: "button",
|
|
1590
1759
|
disabled: busy,
|
|
1591
1760
|
onClick: () => void pick(member),
|
|
1592
|
-
className: (0,
|
|
1761
|
+
className: (0, import_design_system5.cn)(
|
|
1593
1762
|
"w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-muted",
|
|
1594
1763
|
pickedUserIds.has(member.userId) ? "bg-accent text-accent-foreground" : ""
|
|
1595
1764
|
),
|
|
1596
1765
|
children: [
|
|
1597
|
-
/* @__PURE__ */ (0,
|
|
1598
|
-
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: [
|
|
1599
1768
|
"\xB7 ",
|
|
1600
1769
|
member.email
|
|
1601
1770
|
] }) : null
|
|
1602
1771
|
]
|
|
1603
1772
|
}
|
|
1604
1773
|
) }, member.userId)) }),
|
|
1605
|
-
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
|
|
1606
1775
|
] })
|
|
1607
1776
|
] })
|
|
1608
1777
|
] })
|
|
@@ -1615,55 +1784,6 @@ var import_core = require("@ai-matrx/records/core");
|
|
|
1615
1784
|
var import_react16 = require("@ai-matrx/records/react");
|
|
1616
1785
|
var import_design_system7 = require("@ai-matrx/design-system");
|
|
1617
1786
|
|
|
1618
|
-
// src/Refusal.tsx
|
|
1619
|
-
var import_design_system5 = require("@ai-matrx/design-system");
|
|
1620
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1621
|
-
function hintIsMachineIdentity(hint) {
|
|
1622
|
-
return isMachineIdentity(hint);
|
|
1623
|
-
}
|
|
1624
|
-
function hintForAPerson(hint) {
|
|
1625
|
-
const said = (hint ?? "").trim();
|
|
1626
|
-
if (said === "") return null;
|
|
1627
|
-
return isMachineIdentity(said) ? null : said;
|
|
1628
|
-
}
|
|
1629
|
-
function RefusalNotice({
|
|
1630
|
-
error,
|
|
1631
|
-
className,
|
|
1632
|
-
actions
|
|
1633
|
-
}) {
|
|
1634
|
-
const plain = refusalForAPerson(error);
|
|
1635
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1636
|
-
import_design_system5.Alert,
|
|
1637
|
-
{
|
|
1638
|
-
variant: "destructive",
|
|
1639
|
-
className: (0, import_design_system5.cn)("text-xs", className),
|
|
1640
|
-
...plain.forEngineers ? { title: plain.forEngineers } : {},
|
|
1641
|
-
children: [
|
|
1642
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.AlertTitle, { className: "text-xs font-medium", children: plain.title }),
|
|
1643
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system5.AlertDescription, { className: "space-y-1 text-xs", children: [
|
|
1644
|
-
plain.sentence.replace(/\.$/, "") === plain.title ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: plain.sentence }),
|
|
1645
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "opacity-80", children: plain.remedy }),
|
|
1646
|
-
plain.forEngineers ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1647
|
-
"p",
|
|
1648
|
-
{
|
|
1649
|
-
className: "sr-only",
|
|
1650
|
-
"aria-hidden": "true",
|
|
1651
|
-
"data-for-engineers": "",
|
|
1652
|
-
...error.sqlstate ? { "data-sqlstate": error.sqlstate } : {},
|
|
1653
|
-
children: plain.forEngineers
|
|
1654
|
-
}
|
|
1655
|
-
) : null,
|
|
1656
|
-
actions
|
|
1657
|
-
] })
|
|
1658
|
-
]
|
|
1659
|
-
}
|
|
1660
|
-
);
|
|
1661
|
-
}
|
|
1662
|
-
function RefusalLine({ error, className }) {
|
|
1663
|
-
const plain = refusalForAPerson(error);
|
|
1664
|
-
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) });
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
1787
|
// src/whoChangedSource.tsx
|
|
1668
1788
|
var import_react11 = require("react");
|
|
1669
1789
|
var import_react12 = require("@ai-matrx/records/react");
|
|
@@ -2499,7 +2619,7 @@ function GridCell({
|
|
|
2499
2619
|
);
|
|
2500
2620
|
const value = editing ? editing.valueFor(row, field) : row.document?.[field.key];
|
|
2501
2621
|
const state = editing ? editing.stateOf(row.id, field.key) : null;
|
|
2502
|
-
const
|
|
2622
|
+
const refusal2 = editing ? editing.refusalOf(row.id, field.key) : null;
|
|
2503
2623
|
const editable = Boolean(editing) && canWrite && fieldIsEditable(field);
|
|
2504
2624
|
if (open && editing) {
|
|
2505
2625
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -2528,8 +2648,8 @@ function GridCell({
|
|
|
2528
2648
|
}
|
|
2529
2649
|
),
|
|
2530
2650
|
answered ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-xs text-muted-foreground", title: "nobody has answered this yet", children: "\u2014" }),
|
|
2531
|
-
|
|
2532
|
-
/* @__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 }),
|
|
2533
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" })
|
|
2534
2654
|
] }) : null
|
|
2535
2655
|
] });
|
|
@@ -2604,11 +2724,11 @@ function GridCell({
|
|
|
2604
2724
|
),
|
|
2605
2725
|
onAskWhoChanged ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WhoBadge, { field, row, onAskWhoChanged }) : null,
|
|
2606
2726
|
state === "saving" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "px-1 text-[10px] text-muted-foreground", children: "Saving\u2026" }) : null,
|
|
2607
|
-
|
|
2608
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RefusalLine, { error:
|
|
2609
|
-
|
|
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: [
|
|
2610
2730
|
"Theirs: ",
|
|
2611
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: formatTheirs(
|
|
2731
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: formatTheirs(refusal2.conflict, field.key) })
|
|
2612
2732
|
] }) : null,
|
|
2613
2733
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex gap-1", children: [
|
|
2614
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" }),
|
|
@@ -3307,7 +3427,20 @@ function EnrichPanel({ tableId, fieldId, className }) {
|
|
|
3307
3427
|
" in one go."
|
|
3308
3428
|
] }) : null
|
|
3309
3429
|
] }) : null,
|
|
3310
|
-
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,
|
|
3311
3444
|
field ? null : null
|
|
3312
3445
|
] }, row.field_id);
|
|
3313
3446
|
}) });
|
|
@@ -3352,15 +3485,15 @@ function PastePreview({
|
|
|
3352
3485
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlanTable, { plan }),
|
|
3353
3486
|
plan.refusals.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-matrx-paste-refusals": true, className: "flex flex-col gap-1", children: [
|
|
3354
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:` }),
|
|
3355
|
-
/* @__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: [
|
|
3356
3489
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "font-medium", children: [
|
|
3357
3490
|
"Row ",
|
|
3358
|
-
|
|
3491
|
+
refusal2.fromLine,
|
|
3359
3492
|
", ",
|
|
3360
|
-
|
|
3493
|
+
refusal2.column
|
|
3361
3494
|
] }),
|
|
3362
3495
|
" \u2014 ",
|
|
3363
|
-
|
|
3496
|
+
refusal2.why
|
|
3364
3497
|
] }, index)) }),
|
|
3365
3498
|
plan.refusals.length > 12 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
3366
3499
|
plan.refusals.length - 12,
|
|
@@ -3388,7 +3521,13 @@ function PastePreview({
|
|
|
3388
3521
|
setAdding(group.fieldKey);
|
|
3389
3522
|
void onAddOptions(group.fieldKey, group.words).catch(
|
|
3390
3523
|
(err) => setAddFailed(
|
|
3391
|
-
|
|
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
|
+
)
|
|
3392
3531
|
)
|
|
3393
3532
|
).finally(() => setAdding(null));
|
|
3394
3533
|
},
|
|
@@ -3396,7 +3535,7 @@ function PastePreview({
|
|
|
3396
3535
|
}
|
|
3397
3536
|
)
|
|
3398
3537
|
] }, group.fieldKey)),
|
|
3399
|
-
addFailed ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3538
|
+
addFailed ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefusalLine, { error: addFailed }) : null
|
|
3400
3539
|
] }) : null,
|
|
3401
3540
|
plan.pastRightEdge > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
3402
3541
|
plan.pastRightEdge,
|
|
@@ -3576,19 +3715,19 @@ function planPastedBlock(args) {
|
|
|
3576
3715
|
accepted[key] = judged.value;
|
|
3577
3716
|
}
|
|
3578
3717
|
const predicted = (0, import_core2.predictValueRefusals)(args.fields, accepted);
|
|
3579
|
-
for (const
|
|
3580
|
-
const key =
|
|
3718
|
+
for (const refusal2 of predicted) {
|
|
3719
|
+
const key = refusal2.field_key;
|
|
3581
3720
|
if (key === void 0) continue;
|
|
3582
3721
|
const cell = built.find((b) => b.key === key && b.refusal === null);
|
|
3583
3722
|
if (!cell) continue;
|
|
3584
|
-
cell.refusal =
|
|
3723
|
+
cell.refusal = refusal2.message;
|
|
3585
3724
|
cell.value = null;
|
|
3586
3725
|
delete accepted[key];
|
|
3587
3726
|
refusals.push({
|
|
3588
3727
|
fromLine,
|
|
3589
3728
|
column: fieldName(byKey.get(key) ?? { key }),
|
|
3590
3729
|
raw: cell.raw,
|
|
3591
|
-
why:
|
|
3730
|
+
why: refusal2.message
|
|
3592
3731
|
});
|
|
3593
3732
|
}
|
|
3594
3733
|
cells += Object.keys(accepted).length;
|
|
@@ -3691,6 +3830,9 @@ function Grid({
|
|
|
3691
3830
|
[client, host, refreshEnrich]
|
|
3692
3831
|
);
|
|
3693
3832
|
const [pastePlan, setPastePlan] = (0, import_react20.useState)(null);
|
|
3833
|
+
const [deleting, setDeleting] = (0, import_react20.useState)(null);
|
|
3834
|
+
const [deleteBusy, setDeleteBusy] = (0, import_react20.useState)(false);
|
|
3835
|
+
const [archived, setArchived] = (0, import_react20.useState)(null);
|
|
3694
3836
|
const choiceOptions = (0, import_react20.useRef)(/* @__PURE__ */ new Map());
|
|
3695
3837
|
const loadChoiceOptions = (0, import_react20.useCallback)(
|
|
3696
3838
|
async (fieldList) => {
|
|
@@ -3978,6 +4120,21 @@ function Grid({
|
|
|
3978
4120
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ml-auto flex items-center gap-1", children: toolbarActions })
|
|
3979
4121
|
] }),
|
|
3980
4122
|
records.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RefusalNotice, { error: records.error }) : null,
|
|
4123
|
+
archived ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
4124
|
+
"p",
|
|
4125
|
+
{
|
|
4126
|
+
"data-records-archived-notice": "",
|
|
4127
|
+
role: "status",
|
|
4128
|
+
className: "flex items-center gap-2 rounded border border-border bg-muted/40 px-2 py-1 text-xs",
|
|
4129
|
+
children: [
|
|
4130
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
4131
|
+
archived,
|
|
4132
|
+
" is archived. Nothing was destroyed \u2014 it is in this table\u2019s Archived items, where Restore brings it back."
|
|
4133
|
+
] }),
|
|
4134
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.Button, { size: "sm", variant: "ghost", className: "ml-auto h-6", onClick: () => setArchived(null), children: "Dismiss" })
|
|
4135
|
+
]
|
|
4136
|
+
}
|
|
4137
|
+
) : null,
|
|
3981
4138
|
editing.rowError ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3982
4139
|
RefusalNotice,
|
|
3983
4140
|
{
|
|
@@ -4094,11 +4251,13 @@ function Grid({
|
|
|
4094
4251
|
}
|
|
4095
4252
|
) : null,
|
|
4096
4253
|
canWrite && mayWriteRow(row.id) ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4097
|
-
|
|
4254
|
+
import_design_system10.Button,
|
|
4098
4255
|
{
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4256
|
+
size: "sm",
|
|
4257
|
+
variant: "ghost",
|
|
4258
|
+
title: "Delete this record",
|
|
4259
|
+
onClick: () => setDeleting(row),
|
|
4260
|
+
children: "Delete"
|
|
4102
4261
|
}
|
|
4103
4262
|
) : null
|
|
4104
4263
|
] })
|
|
@@ -4141,7 +4300,32 @@ function Grid({
|
|
|
4141
4300
|
onPage: setPage
|
|
4142
4301
|
}
|
|
4143
4302
|
)
|
|
4144
|
-
] })
|
|
4303
|
+
] }),
|
|
4304
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4305
|
+
import_design_system10.ConfirmDialog,
|
|
4306
|
+
{
|
|
4307
|
+
open: deleting !== null,
|
|
4308
|
+
onOpenChange: (next) => {
|
|
4309
|
+
if (!next && !deleteBusy) setDeleting(null);
|
|
4310
|
+
},
|
|
4311
|
+
variant: "destructive",
|
|
4312
|
+
title: `Delete ${deleting ? recordNameIn(table.data, deleting.document ?? {}) : "this record"}?`,
|
|
4313
|
+
description: table.data?.retention_days ? `It leaves the table and waits in Archived items. Nothing is destroyed: Restore brings it back, for ${table.data.retention_days} days.` : "It leaves the table and waits in Archived items. Nothing is destroyed: Restore brings it back.",
|
|
4314
|
+
confirmLabel: "Delete",
|
|
4315
|
+
cancelLabel: "Keep",
|
|
4316
|
+
busy: deleteBusy,
|
|
4317
|
+
onConfirm: async () => {
|
|
4318
|
+
const row = deleting;
|
|
4319
|
+
if (!row) return;
|
|
4320
|
+
const name = recordNameIn(table.data, row.document ?? {});
|
|
4321
|
+
setDeleteBusy(true);
|
|
4322
|
+
const failed = await editing.remove(row.id);
|
|
4323
|
+
setDeleteBusy(false);
|
|
4324
|
+
setDeleting(null);
|
|
4325
|
+
if (!failed) setArchived(name);
|
|
4326
|
+
}
|
|
4327
|
+
}
|
|
4328
|
+
)
|
|
4145
4329
|
] }) });
|
|
4146
4330
|
}
|
|
4147
4331
|
function columnLabel(fields, columnId) {
|
|
@@ -4161,42 +4345,6 @@ function AddFieldButton({ onClick }) {
|
|
|
4161
4345
|
}
|
|
4162
4346
|
);
|
|
4163
4347
|
}
|
|
4164
|
-
function DeleteRecordButton({
|
|
4165
|
-
row,
|
|
4166
|
-
retentionDays,
|
|
4167
|
-
onDelete
|
|
4168
|
-
}) {
|
|
4169
|
-
const [asking, setAsking] = (0, import_react20.useState)(false);
|
|
4170
|
-
if (!asking) {
|
|
4171
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4172
|
-
import_design_system10.Button,
|
|
4173
|
-
{
|
|
4174
|
-
size: "sm",
|
|
4175
|
-
variant: "ghost",
|
|
4176
|
-
title: "Delete this record",
|
|
4177
|
-
onClick: () => setAsking(true),
|
|
4178
|
-
children: "Delete"
|
|
4179
|
-
}
|
|
4180
|
-
);
|
|
4181
|
-
}
|
|
4182
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1 text-[11px]", children: [
|
|
4183
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-muted-foreground", children: retentionDays ? `Removes it from the table. Restorable for ${retentionDays} days.` : "Removes it from the table. It can be restored from the record's history." }),
|
|
4184
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4185
|
-
import_design_system10.Button,
|
|
4186
|
-
{
|
|
4187
|
-
size: "sm",
|
|
4188
|
-
variant: "destructive",
|
|
4189
|
-
onClick: () => {
|
|
4190
|
-
setAsking(false);
|
|
4191
|
-
void onDelete();
|
|
4192
|
-
},
|
|
4193
|
-
children: "Delete"
|
|
4194
|
-
}
|
|
4195
|
-
),
|
|
4196
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.Button, { size: "sm", variant: "ghost", onClick: () => setAsking(false), children: "Keep" }),
|
|
4197
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "sr-only", children: row.id })
|
|
4198
|
-
] });
|
|
4199
|
-
}
|
|
4200
4348
|
function Pager({
|
|
4201
4349
|
page,
|
|
4202
4350
|
pageSize,
|
|
@@ -4343,18 +4491,24 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4343
4491
|
size: "sm",
|
|
4344
4492
|
variant: "outline",
|
|
4345
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);
|
|
4346
4502
|
try {
|
|
4347
|
-
|
|
4503
|
+
(0, import_core3.exportXlsx)(fields.data ?? [], exported(), name).then(
|
|
4348
4504
|
(bytes) => download(
|
|
4349
4505
|
`${name}.xlsx`,
|
|
4350
4506
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
4351
4507
|
bytes
|
|
4352
4508
|
)
|
|
4353
|
-
);
|
|
4509
|
+
).catch(failed);
|
|
4354
4510
|
} catch (thrown) {
|
|
4355
|
-
|
|
4356
|
-
`The spreadsheet could not be written (${thrown instanceof Error ? thrown.message : String(thrown)}). The CSV beside this button carries the same rows.`
|
|
4357
|
-
);
|
|
4511
|
+
failed(thrown);
|
|
4358
4512
|
}
|
|
4359
4513
|
},
|
|
4360
4514
|
children: "XLSX"
|
|
@@ -4370,7 +4524,7 @@ function ExportMenu({ tableId, rows, label, className }) {
|
|
|
4370
4524
|
human: () => (0, import_core3.exportCsv)(fields.data ?? [], exported())
|
|
4371
4525
|
}
|
|
4372
4526
|
),
|
|
4373
|
-
failure ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4527
|
+
failure ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RefusalLine, { error: failure }) : null
|
|
4374
4528
|
] });
|
|
4375
4529
|
}
|
|
4376
4530
|
|
|
@@ -4471,8 +4625,47 @@ function FilePicker({
|
|
|
4471
4625
|
// src/ImportWizard.tsx
|
|
4472
4626
|
var import_react26 = require("react");
|
|
4473
4627
|
var import_react27 = require("@ai-matrx/records/react");
|
|
4474
|
-
var
|
|
4628
|
+
var import_core5 = require("@ai-matrx/records/core");
|
|
4475
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
|
|
4476
4669
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
4477
4670
|
var BATCH = 250;
|
|
4478
4671
|
var SAMPLES = 40;
|
|
@@ -4495,7 +4688,7 @@ async function sha256(bytes) {
|
|
|
4495
4688
|
function typeWord(t) {
|
|
4496
4689
|
return fieldTypeChoice(t)?.label ?? (t === "relation" ? "Points at another table" : t);
|
|
4497
4690
|
}
|
|
4498
|
-
function ImportWizard({ tableId, onDone, className }) {
|
|
4691
|
+
function ImportWizard({ tableId, onDone, onWrote, className }) {
|
|
4499
4692
|
const client = (0, import_react27.useRecordsClient)();
|
|
4500
4693
|
const table = (0, import_react27.useTable)(tableId);
|
|
4501
4694
|
const fields = (0, import_react27.useFields)(tableId);
|
|
@@ -4521,6 +4714,50 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4521
4714
|
const [columnsAdded, setColumnsAdded] = (0, import_react26.useState)(null);
|
|
4522
4715
|
const [show, setShow] = (0, import_react26.useState)("all");
|
|
4523
4716
|
const [ledger, setLedger] = (0, import_react26.useState)(null);
|
|
4717
|
+
const [runs, setRuns] = (0, import_react26.useState)(null);
|
|
4718
|
+
const [openRun, setOpenRun] = (0, import_react26.useState)(null);
|
|
4719
|
+
const [runRows, setRunRows] = (0, import_react26.useState)(null);
|
|
4720
|
+
const [runProblem, setRunProblem] = (0, import_react26.useState)(null);
|
|
4721
|
+
const [runsProblem, setRunsProblem] = (0, import_react26.useState)(null);
|
|
4722
|
+
const loadRuns = (0, import_react26.useCallback)(async () => {
|
|
4723
|
+
const answered = await client.imports({ table_id: tableId, limit: 10 });
|
|
4724
|
+
if (answered.ok) {
|
|
4725
|
+
setRuns(answered.data);
|
|
4726
|
+
setRunsProblem(null);
|
|
4727
|
+
} else setRunsProblem(answered.error);
|
|
4728
|
+
}, [client, tableId]);
|
|
4729
|
+
(0, import_react26.useEffect)(() => {
|
|
4730
|
+
void loadRuns();
|
|
4731
|
+
}, [loadRuns]);
|
|
4732
|
+
const openRefusals = (0, import_react26.useCallback)(
|
|
4733
|
+
async (importId) => {
|
|
4734
|
+
setRunProblem(null);
|
|
4735
|
+
if (openRun === importId) {
|
|
4736
|
+
setOpenRun(null);
|
|
4737
|
+
setRunRows(null);
|
|
4738
|
+
return;
|
|
4739
|
+
}
|
|
4740
|
+
setOpenRun(importId);
|
|
4741
|
+
setRunRows(null);
|
|
4742
|
+
const answered = await client.importReport({
|
|
4743
|
+
import_id: importId,
|
|
4744
|
+
state: "refused",
|
|
4745
|
+
limit: 200
|
|
4746
|
+
});
|
|
4747
|
+
if (!answered.ok) {
|
|
4748
|
+
setRunProblem(answered.error);
|
|
4749
|
+
return;
|
|
4750
|
+
}
|
|
4751
|
+
setRunRows({
|
|
4752
|
+
import_id: answered.data.import_id,
|
|
4753
|
+
rows: answered.data.rows,
|
|
4754
|
+
kept: answered.data.kept,
|
|
4755
|
+
total: answered.data.total,
|
|
4756
|
+
note: answered.data.note
|
|
4757
|
+
});
|
|
4758
|
+
},
|
|
4759
|
+
[client, openRun]
|
|
4760
|
+
);
|
|
4524
4761
|
const declared = fields.data ?? [];
|
|
4525
4762
|
const take = (0, import_react26.useCallback)(
|
|
4526
4763
|
async (file) => {
|
|
@@ -4535,7 +4772,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4535
4772
|
try {
|
|
4536
4773
|
const xlsx = file.name.toLowerCase().endsWith(".xlsx");
|
|
4537
4774
|
const bytes = await file.arrayBuffer();
|
|
4538
|
-
const result = xlsx ? await (0,
|
|
4775
|
+
const result = xlsx ? await (0, import_core5.importXlsx)(bytes) : (0, import_core5.importCsv)(new TextDecoder().decode(bytes));
|
|
4539
4776
|
setFormat(xlsx ? "xlsx" : "csv");
|
|
4540
4777
|
setFileName(file.name);
|
|
4541
4778
|
setFileBytes(file.size);
|
|
@@ -4549,7 +4786,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4549
4786
|
}));
|
|
4550
4787
|
const answered = await client.importPlan({ table_id: tableId, columns });
|
|
4551
4788
|
if (!answered.ok) {
|
|
4552
|
-
setProblem(answered.error
|
|
4789
|
+
setProblem(answered.error);
|
|
4553
4790
|
setPhase("waiting");
|
|
4554
4791
|
return;
|
|
4555
4792
|
}
|
|
@@ -4566,7 +4803,14 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4566
4803
|
setMapping(next);
|
|
4567
4804
|
setPhase("ready");
|
|
4568
4805
|
} catch (thrown) {
|
|
4569
|
-
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
|
+
);
|
|
4570
4814
|
setPhase("waiting");
|
|
4571
4815
|
}
|
|
4572
4816
|
},
|
|
@@ -4583,7 +4827,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4583
4827
|
setPhase("declaring");
|
|
4584
4828
|
const made = await client.importDeclareColumns({ table_id: tableId, rows, mapping });
|
|
4585
4829
|
if (!made.ok) {
|
|
4586
|
-
setProblem(made.error
|
|
4830
|
+
setProblem(made.error);
|
|
4587
4831
|
setPhase("ready");
|
|
4588
4832
|
return;
|
|
4589
4833
|
}
|
|
@@ -4602,7 +4846,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4602
4846
|
policy: { on_duplicate: onDuplicate, unmapped }
|
|
4603
4847
|
});
|
|
4604
4848
|
if (!opened.ok) {
|
|
4605
|
-
setProblem(opened.error
|
|
4849
|
+
setProblem(opened.error);
|
|
4606
4850
|
setPhase("ready");
|
|
4607
4851
|
return;
|
|
4608
4852
|
}
|
|
@@ -4621,7 +4865,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4621
4865
|
const batch = rows.slice(at, at + take2);
|
|
4622
4866
|
const answered = await client.importRows({ import_id: importId, rows: batch, mapping });
|
|
4623
4867
|
if (!answered.ok) {
|
|
4624
|
-
setProblem(answered.error
|
|
4868
|
+
setProblem(answered.error);
|
|
4625
4869
|
setOutcomes([...collected]);
|
|
4626
4870
|
setLedger({ ...told, final: false });
|
|
4627
4871
|
setPhase("ready");
|
|
@@ -4640,7 +4884,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4640
4884
|
}
|
|
4641
4885
|
const finished = await client.importFinish({ import_id: importId, unmapped });
|
|
4642
4886
|
if (!finished.ok) {
|
|
4643
|
-
setProblem(finished.error
|
|
4887
|
+
setProblem(finished.error);
|
|
4644
4888
|
setPhase("done");
|
|
4645
4889
|
return;
|
|
4646
4890
|
}
|
|
@@ -4654,8 +4898,9 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4654
4898
|
final: true
|
|
4655
4899
|
});
|
|
4656
4900
|
setPhase("done");
|
|
4657
|
-
|
|
4658
|
-
|
|
4901
|
+
onWrote?.(finished.data.rows_written);
|
|
4902
|
+
void loadRuns();
|
|
4903
|
+
}, [client, dedupeKey, fileBytes, fileHash, fileName, format, loadRuns, mapping, onDuplicate, onWrote, plan, rows, tableId, unmapped]);
|
|
4659
4904
|
const counts = (0, import_react26.useMemo)(() => {
|
|
4660
4905
|
let landed = 0;
|
|
4661
4906
|
let duplicate = 0;
|
|
@@ -4677,6 +4922,11 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4677
4922
|
const titleMissing = Boolean(titleKey) && titleMappedTo === null;
|
|
4678
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";
|
|
4679
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]);
|
|
4680
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: [
|
|
4681
4931
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4682
4932
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: "Import" }),
|
|
@@ -4704,7 +4954,7 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4704
4954
|
} : {}
|
|
4705
4955
|
}
|
|
4706
4956
|
) : null,
|
|
4707
|
-
problem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4957
|
+
problem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: problem }) : null,
|
|
4708
4958
|
phase === "reading" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Reading the file\u2026" }) : null,
|
|
4709
4959
|
phase === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Working out what each column is\u2026" }) : null,
|
|
4710
4960
|
plan && parsed ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
@@ -4801,7 +5051,28 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4801
5051
|
column.unit ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "ml-1 opacity-60", children: column.unit }) : null,
|
|
4802
5052
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mt-0.5 text-muted-foreground", children: column.why }),
|
|
4803
5053
|
column.ambiguous ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-destructive", children: "Check a date you know before you run this." }) : null,
|
|
4804
|
-
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
|
|
4805
5076
|
] }),
|
|
4806
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 ") })
|
|
4807
5078
|
]
|
|
@@ -4865,12 +5136,34 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4865
5136
|
)
|
|
4866
5137
|
] })
|
|
4867
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,
|
|
4868
5161
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4869
5162
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4870
5163
|
import_design_system13.Button,
|
|
4871
5164
|
{
|
|
4872
5165
|
size: "sm",
|
|
4873
|
-
disabled: busy || !rights.write || rows.length === 0 || titleMissing,
|
|
5166
|
+
disabled: busy || !rights.write || rows.length === 0 || titleMissing || ruleVerdicts.length > 0 && !goAheadAnyway,
|
|
4874
5167
|
onClick: () => void run(),
|
|
4875
5168
|
children: phase === "declaring" ? "Adding the columns\u2026" : phase === "writing" ? `Writing\u2026 ${progress} of ${rows.length}` : `Import ${rows.length} row${rows.length === 1 ? "" : "s"}`
|
|
4876
5169
|
}
|
|
@@ -4925,14 +5218,95 @@ function ImportWizard({ tableId, onDone, className }) {
|
|
|
4925
5218
|
] }) }),
|
|
4926
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: [
|
|
4927
5220
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: o.row }),
|
|
4928
|
-
/* @__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" }),
|
|
4929
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 ") : "" })
|
|
4930
5228
|
] }, `${o.outcome}-${o.row}`)) })
|
|
4931
5229
|
] }) }) : null,
|
|
4932
5230
|
proposals.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-0.5", children: proposals.map((p) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("li", { className: "text-muted-foreground", children: [
|
|
4933
5231
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium text-foreground", children: p.column }),
|
|
4934
5232
|
p.state === "waiting" ? " is waiting in the approvals inbox." : p.state === "accepted" ? " was added as a column." : p.state === "ignored" ? " was left out." : p.state === "refused" ? ` could not be added: ${p.reason ?? ""}` : ` \u2014 ${p.state}`
|
|
4935
|
-
] }, p.column)) }) : null
|
|
5233
|
+
] }, p.column)) }) : null,
|
|
5234
|
+
phase === "done" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5235
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Button, { size: "sm", onClick: () => onDone?.(ledger?.written ?? 0), children: "Done" }),
|
|
5236
|
+
(ledger?.refused ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
5237
|
+
"The ",
|
|
5238
|
+
ledger?.refused,
|
|
5239
|
+
" row",
|
|
5240
|
+
ledger?.refused === 1 ? "" : "s",
|
|
5241
|
+
" that ",
|
|
5242
|
+
ledger?.refused === 1 ? "was" : "were",
|
|
5243
|
+
" refused",
|
|
5244
|
+
" ",
|
|
5245
|
+
"are kept with this import \u2014 open them again from the list of imports below, with the store's reason and the line from your file on each."
|
|
5246
|
+
] }) : null
|
|
5247
|
+
] }) : null
|
|
5248
|
+
] }) : null,
|
|
5249
|
+
runsProblem ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: runsProblem }) : null,
|
|
5250
|
+
runs && runs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
5251
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Separator, {}),
|
|
5252
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "font-medium", children: "Imports into this table" }),
|
|
5253
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-1", children: runs.map((r) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("li", { className: "flex flex-col gap-1", children: [
|
|
5254
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5255
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: r.source_name ?? "a file" }),
|
|
5256
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
5257
|
+
new Date(r.opened_at).toLocaleString(),
|
|
5258
|
+
" \xB7 ",
|
|
5259
|
+
r.rows_written,
|
|
5260
|
+
" landed \xB7",
|
|
5261
|
+
" ",
|
|
5262
|
+
r.rows_duplicate,
|
|
5263
|
+
" already here \xB7 ",
|
|
5264
|
+
r.rows_refused,
|
|
5265
|
+
" refused, of ",
|
|
5266
|
+
r.rows_seen,
|
|
5267
|
+
" row",
|
|
5268
|
+
r.rows_seen === 1 ? "" : "s",
|
|
5269
|
+
" offered"
|
|
5270
|
+
] }),
|
|
5271
|
+
r.rows_refused > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5272
|
+
"button",
|
|
5273
|
+
{
|
|
5274
|
+
type: "button",
|
|
5275
|
+
className: "underline underline-offset-2",
|
|
5276
|
+
"aria-expanded": openRun === r.import_id,
|
|
5277
|
+
onClick: () => void openRefusals(r.import_id),
|
|
5278
|
+
children: openRun === r.import_id ? "hide the rows it refused" : `see the ${r.rows_refused} row${r.rows_refused === 1 ? "" : "s"} it refused`
|
|
5279
|
+
}
|
|
5280
|
+
) : null
|
|
5281
|
+
] }),
|
|
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: [
|
|
5283
|
+
runRows.note ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: runRows.note }) : null,
|
|
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: [
|
|
5285
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("thead", { className: "sticky top-0 bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { children: [
|
|
5286
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "Line in your file" }),
|
|
5287
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "What happened" }),
|
|
5288
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "px-2 py-1 text-left font-medium", children: "The row in your file" })
|
|
5289
|
+
] }) }),
|
|
5290
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: runRows.rows.map((row, i) => {
|
|
5291
|
+
const source = row.source ?? {};
|
|
5292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-t align-top", children: [
|
|
5293
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-2 py-1", children: String(row.row ?? "") }),
|
|
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
|
+
) }),
|
|
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 ") })
|
|
5305
|
+
] }, `${r.import_id}-${String(row.row ?? i)}`);
|
|
5306
|
+
}) })
|
|
5307
|
+
] }) })
|
|
5308
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-muted-foreground", children: "Reading the rows this import refused\u2026" }) : null
|
|
5309
|
+
] }, r.import_id)) })
|
|
4936
5310
|
] }) : null,
|
|
4937
5311
|
table.error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefusalNotice, { error: table.error }) : null
|
|
4938
5312
|
] });
|
|
@@ -5145,7 +5519,9 @@ function keyFor(label) {
|
|
|
5145
5519
|
if (token === "") return "";
|
|
5146
5520
|
return /^[a-z]/.test(token) ? token : `f_${token}`;
|
|
5147
5521
|
}
|
|
5148
|
-
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}`;
|
|
5149
5525
|
function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }) {
|
|
5150
5526
|
const table = (0, import_react31.useTable)(tableId);
|
|
5151
5527
|
const fields = (0, import_react31.useFields)(tableId);
|
|
@@ -5445,14 +5821,20 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5445
5821
|
] }, `${rule.kind}-${index}`))
|
|
5446
5822
|
] }),
|
|
5447
5823
|
mutation.error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(RefusalNotice, { error: mutation.error }) : null,
|
|
5448
|
-
missing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-
|
|
5449
|
-
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,
|
|
5450
5831
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
5451
5832
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5452
5833
|
import_design_system15.Button,
|
|
5453
5834
|
{
|
|
5454
5835
|
size: "sm",
|
|
5455
5836
|
disabled: mutation.saving || missing !== null,
|
|
5837
|
+
...missing ? { "aria-describedby": "field-editor-missing" } : {},
|
|
5456
5838
|
onClick: () => void save(),
|
|
5457
5839
|
children: mutation.saving ? "Saving\u2026" : field ? EDIT_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
|
|
5458
5840
|
}
|
|
@@ -6665,7 +7047,7 @@ var import_design_system22 = require("@ai-matrx/design-system");
|
|
|
6665
7047
|
// src/RecordForm.tsx
|
|
6666
7048
|
var import_react41 = require("react");
|
|
6667
7049
|
var import_react42 = require("@ai-matrx/records/react");
|
|
6668
|
-
var
|
|
7050
|
+
var import_core6 = require("@ai-matrx/records/core");
|
|
6669
7051
|
var import_design_system20 = require("@ai-matrx/design-system");
|
|
6670
7052
|
|
|
6671
7053
|
// src/systemTable.ts
|
|
@@ -6870,7 +7252,7 @@ function RecordForm({
|
|
|
6870
7252
|
return out;
|
|
6871
7253
|
}, [draft]);
|
|
6872
7254
|
const predicted = (0, import_react41.useMemo)(
|
|
6873
|
-
() => fields.data ? (0,
|
|
7255
|
+
() => fields.data ? (0, import_core6.predictWriteRefusals)({ fields: fields.data, document: document2, ...recordType ? { recordType } : {} }) : [],
|
|
6874
7256
|
[fields.data, document2, recordType]
|
|
6875
7257
|
);
|
|
6876
7258
|
if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(RefusalNotice, { error: fields.error, className });
|
|
@@ -6918,7 +7300,13 @@ function RecordForm({
|
|
|
6918
7300
|
},
|
|
6919
7301
|
field.id
|
|
6920
7302
|
)) }),
|
|
6921
|
-
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,
|
|
6922
7310
|
mutation.error ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
6923
7311
|
RefusalNotice,
|
|
6924
7312
|
{
|
|
@@ -6973,7 +7361,14 @@ function FieldRow({
|
|
|
6973
7361
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
6974
7362
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldLabel, { field, htmlFor: id, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ProvenanceBadge, { document: document2, fieldKey: field.key }) }),
|
|
6975
7363
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldControl, { field, value, onChange, id }),
|
|
6976
|
-
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
|
+
))
|
|
6977
7372
|
] });
|
|
6978
7373
|
}
|
|
6979
7374
|
function asWords(value) {
|
|
@@ -6999,12 +7394,16 @@ function ShareControl({
|
|
|
6999
7394
|
subjectId,
|
|
7000
7395
|
name,
|
|
7001
7396
|
may,
|
|
7397
|
+
initiallyOpen = false,
|
|
7002
7398
|
size = "sm",
|
|
7003
7399
|
variant = "ghost",
|
|
7004
7400
|
className
|
|
7005
7401
|
}) {
|
|
7006
7402
|
const host = useRecordsUi();
|
|
7007
|
-
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]);
|
|
7008
7407
|
const asked = useRecordRights(may === void 0 ? subjectId : null);
|
|
7009
7408
|
const mayShare = may ?? asked.share;
|
|
7010
7409
|
if (!host.share) return null;
|
|
@@ -7392,8 +7791,8 @@ function PipelineBoard({
|
|
|
7392
7791
|
const asked = await client.pipelineTransitionRefusal({ record_id: recordId, to });
|
|
7393
7792
|
if (!alive.current) return;
|
|
7394
7793
|
if (!asked.ok) {
|
|
7395
|
-
|
|
7396
|
-
|
|
7794
|
+
setPending({ kind: "clash", recordId, to, error: asked.error });
|
|
7795
|
+
onMoved?.();
|
|
7397
7796
|
return;
|
|
7398
7797
|
}
|
|
7399
7798
|
const verdict = asked.data;
|
|
@@ -7406,8 +7805,8 @@ function PipelineBoard({
|
|
|
7406
7805
|
const moved = await client.pipelineMove({ record_id: recordId, to });
|
|
7407
7806
|
if (!alive.current) return;
|
|
7408
7807
|
if (!moved.ok) {
|
|
7409
|
-
setPending({ kind: "
|
|
7410
|
-
|
|
7808
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7809
|
+
onMoved?.();
|
|
7411
7810
|
return;
|
|
7412
7811
|
}
|
|
7413
7812
|
const result = moved.data;
|
|
@@ -7428,21 +7827,22 @@ function PipelineBoard({
|
|
|
7428
7827
|
const moved = await client.pipelineMove({ record_id: recordId, to });
|
|
7429
7828
|
if (!alive.current) return;
|
|
7430
7829
|
if (!moved.ok) {
|
|
7431
|
-
setPending({ kind: "
|
|
7432
|
-
|
|
7830
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7831
|
+
onMoved?.();
|
|
7433
7832
|
return;
|
|
7434
7833
|
}
|
|
7435
7834
|
setPending({ kind: "filed", recordId, to, result: moved.data });
|
|
7436
7835
|
void reload();
|
|
7437
7836
|
},
|
|
7438
|
-
[client, reload]
|
|
7837
|
+
[client, onMoved, reload]
|
|
7439
7838
|
);
|
|
7440
7839
|
const moveWith = (0, import_react47.useCallback)(
|
|
7441
7840
|
async (recordId, to, also) => {
|
|
7442
7841
|
const moved = await client.pipelineMove({ record_id: recordId, to, also });
|
|
7443
7842
|
if (!alive.current) return;
|
|
7444
7843
|
if (!moved.ok) {
|
|
7445
|
-
|
|
7844
|
+
setPending({ kind: "clash", recordId, to, error: moved.error });
|
|
7845
|
+
onMoved?.();
|
|
7446
7846
|
return;
|
|
7447
7847
|
}
|
|
7448
7848
|
setPending({ kind: "none" });
|
|
@@ -7570,11 +7970,33 @@ function Held({
|
|
|
7570
7970
|
if (pending.kind === "asking") {
|
|
7571
7971
|
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "px-2 py-1 text-xs text-muted-foreground", children: "Asking\u2026" });
|
|
7572
7972
|
}
|
|
7973
|
+
if (pending.kind === "clash") {
|
|
7974
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
7975
|
+
RefusalNotice,
|
|
7976
|
+
{
|
|
7977
|
+
className: "mt-1",
|
|
7978
|
+
error: pending.error,
|
|
7979
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "mt-1", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system23.Button, { size: "sm", variant: "ghost", className: "h-6", onClick: onCancel, children: "Close" }) })
|
|
7980
|
+
}
|
|
7981
|
+
);
|
|
7982
|
+
}
|
|
7573
7983
|
if (pending.kind === "refused") {
|
|
7574
|
-
return
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
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
|
+
);
|
|
7578
8000
|
}
|
|
7579
8001
|
if (pending.kind === "approval") {
|
|
7580
8002
|
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "mt-1 flex flex-col gap-1 rounded border p-2 text-xs", children: [
|
|
@@ -8138,7 +8560,7 @@ function ArchivedView({
|
|
|
8138
8560
|
const [loading, setLoading] = (0, import_react51.useState)(true);
|
|
8139
8561
|
const [error, setError] = (0, import_react51.useState)(null);
|
|
8140
8562
|
const [restoring, setRestoring] = (0, import_react51.useState)(null);
|
|
8141
|
-
const [
|
|
8563
|
+
const [refusal2, setRefusal] = (0, import_react51.useState)(null);
|
|
8142
8564
|
(0, import_react51.useEffect)(() => {
|
|
8143
8565
|
if (laneFromHost) setLane(laneFromHost);
|
|
8144
8566
|
}, [laneFromHost]);
|
|
@@ -8215,7 +8637,7 @@ function ArchivedView({
|
|
|
8215
8637
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
8216
8638
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-sm", children: recordName(row.document, titleKey) }),
|
|
8217
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) }),
|
|
8218
|
-
|
|
8640
|
+
refusal2 && refusal2.recordId === row.id ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(RefusalNotice, { error: refusal2.error, className: "mt-2" }) : null
|
|
8219
8641
|
] }),
|
|
8220
8642
|
mayRestore ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
8221
8643
|
import_design_system25.Button,
|
|
@@ -8289,7 +8711,7 @@ function ArchivedPortals({
|
|
|
8289
8711
|
const [confirming, setConfirming] = (0, import_react53.useState)(null);
|
|
8290
8712
|
const [typed, setTyped] = (0, import_react53.useState)("");
|
|
8291
8713
|
const [restoring, setRestoring] = (0, import_react53.useState)(null);
|
|
8292
|
-
const [
|
|
8714
|
+
const [refusal2, setRefusal] = (0, import_react53.useState)(null);
|
|
8293
8715
|
const [said, setSaid] = (0, import_react53.useState)(null);
|
|
8294
8716
|
const read = (0, import_react53.useCallback)(async () => {
|
|
8295
8717
|
const answered = await client.listPortals({ archived: "archived" });
|
|
@@ -8405,7 +8827,7 @@ function ArchivedPortals({
|
|
|
8405
8827
|
)
|
|
8406
8828
|
] })
|
|
8407
8829
|
] }) : null,
|
|
8408
|
-
|
|
8830
|
+
refusal2 && refusal2.portalId === portal.portal_id ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RefusalNotice, { error: refusal2.error }) : null
|
|
8409
8831
|
]
|
|
8410
8832
|
},
|
|
8411
8833
|
portal.portal_id
|
|
@@ -8970,7 +9392,7 @@ function MyChecklistSteps({ className }) {
|
|
|
8970
9392
|
const client = (0, import_react62.useRecordsClient)();
|
|
8971
9393
|
const { steps, loading, error, refresh } = useMyChecklistSteps();
|
|
8972
9394
|
const [busy, setBusy] = (0, import_react61.useState)(null);
|
|
8973
|
-
const [
|
|
9395
|
+
const [refusal2, setRefusal] = (0, import_react61.useState)(null);
|
|
8974
9396
|
const complete = (0, import_react61.useCallback)(
|
|
8975
9397
|
async (step2, evidence) => {
|
|
8976
9398
|
setBusy(step2.step_id);
|
|
@@ -8993,7 +9415,7 @@ function MyChecklistSteps({ className }) {
|
|
|
8993
9415
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "tabular-nums", children: steps.length })
|
|
8994
9416
|
] }),
|
|
8995
9417
|
error ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(RefusalNotice, { error }) : null,
|
|
8996
|
-
|
|
9418
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(RefusalNotice, { error: refusal2 }) : null,
|
|
8997
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)(
|
|
8998
9420
|
StepRow,
|
|
8999
9421
|
{
|
|
@@ -9170,7 +9592,7 @@ function ChecklistTemplateEditor({
|
|
|
9170
9592
|
const client = (0, import_react62.useRecordsClient)();
|
|
9171
9593
|
const [name, setName] = (0, import_react61.useState)("");
|
|
9172
9594
|
const [rows, setRows] = (0, import_react61.useState)([{ ...EMPTY_ROW }]);
|
|
9173
|
-
const [
|
|
9595
|
+
const [refusal2, setRefusal] = (0, import_react61.useState)(null);
|
|
9174
9596
|
const [error, setError] = (0, import_react61.useState)(null);
|
|
9175
9597
|
const [busy, setBusy] = (0, import_react61.useState)(false);
|
|
9176
9598
|
const [loading, setLoading] = (0, import_react61.useState)(Boolean(templateId));
|
|
@@ -9338,9 +9760,9 @@ function ChecklistTemplateEditor({
|
|
|
9338
9760
|
] }, index)) }),
|
|
9339
9761
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
9340
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" }),
|
|
9341
|
-
/* @__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" })
|
|
9342
9764
|
] }),
|
|
9343
|
-
|
|
9765
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "checklist-editor-refusal", children: refusal2 }) : null
|
|
9344
9766
|
] });
|
|
9345
9767
|
}
|
|
9346
9768
|
function toStepSpec(row, index) {
|
|
@@ -10633,6 +11055,7 @@ function BuildOrAsk({
|
|
|
10633
11055
|
|
|
10634
11056
|
// src/PortalsPanel.tsx
|
|
10635
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.";
|
|
10636
11059
|
function revokeConsequence(person, portalTitle) {
|
|
10637
11060
|
const who = person.client ? `${person.email} (${person.client})` : person.email;
|
|
10638
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.`;
|
|
@@ -10662,13 +11085,16 @@ function stateWords(person) {
|
|
|
10662
11085
|
};
|
|
10663
11086
|
}
|
|
10664
11087
|
var PORTAL_SUGGESTION = "Let each of my customers sign in and see their own jobs and invoices, and nothing else.";
|
|
10665
|
-
function PortalsPanel({ tableId, className }) {
|
|
11088
|
+
function PortalsPanel({ tableId, activePortalId, className }) {
|
|
10666
11089
|
const client = (0, import_react74.useRecordsClient)();
|
|
10667
11090
|
const host = useRecordsUi();
|
|
10668
11091
|
const [portals, setPortals] = (0, import_react73.useState)(null);
|
|
10669
11092
|
const [exposures, setExposures] = (0, import_react73.useState)([]);
|
|
10670
11093
|
const [listError, setListError] = (0, import_react73.useState)(null);
|
|
10671
|
-
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]);
|
|
10672
11098
|
const [building, setBuilding] = (0, import_react73.useState)(false);
|
|
10673
11099
|
const [adding, setAdding] = (0, import_react73.useState)(null);
|
|
10674
11100
|
const [archiveToken, setArchiveToken] = (0, import_react73.useState)(0);
|
|
@@ -10765,59 +11191,73 @@ function PortalsPanel({ tableId, className }) {
|
|
|
10765
11191
|
portal.portal_id
|
|
10766
11192
|
)) })
|
|
10767
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,
|
|
10768
11195
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: "flex flex-col gap-2", children: shown.map((portal) => {
|
|
10769
11196
|
const url = `${origin}${(0, import_records9.portalPath)(portal.slug)}`;
|
|
10770
11197
|
const open = openId === portal.portal_id;
|
|
10771
|
-
|
|
10772
|
-
|
|
10773
|
-
|
|
10774
|
-
|
|
10775
|
-
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
"."
|
|
10781
|
-
] }),
|
|
10782
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mt-1.5 text-xs", children: [
|
|
10783
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "font-medium", children: portal.signed_in }),
|
|
10784
|
-
" signed in",
|
|
10785
|
-
" \xB7 ",
|
|
10786
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "font-medium", children: portal.invited - portal.signed_in }),
|
|
10787
|
-
" invited and waiting"
|
|
10788
|
-
] }),
|
|
10789
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
10790
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CopyLink, { url }),
|
|
10791
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
10792
|
-
import_design_system36.Button,
|
|
10793
|
-
{
|
|
10794
|
-
size: "sm",
|
|
10795
|
-
variant: "ghost",
|
|
10796
|
-
onClick: () => setOpenId(open ? null : portal.portal_id),
|
|
10797
|
-
children: open ? "Close" : "Open it"
|
|
10798
|
-
}
|
|
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"
|
|
10799
11207
|
),
|
|
10800
|
-
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
portal,
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
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()
|
|
10807
11255
|
}
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
{
|
|
10814
|
-
portalId: portal.portal_id,
|
|
10815
|
-
...tableId ? { tableId } : {},
|
|
10816
|
-
origin,
|
|
10817
|
-
onChanged: () => void load()
|
|
10818
|
-
}
|
|
10819
|
-
) : null
|
|
10820
|
-
] }, portal.portal_id);
|
|
11256
|
+
) : null
|
|
11257
|
+
]
|
|
11258
|
+
},
|
|
11259
|
+
portal.portal_id
|
|
11260
|
+
);
|
|
10821
11261
|
}) }),
|
|
10822
11262
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ArchivedPortals, { refreshToken: archiveToken, onRestored: () => void load() })
|
|
10823
11263
|
] });
|
|
@@ -11575,7 +12015,17 @@ function DigestScheduler({
|
|
|
11575
12015
|
preview ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
|
|
11576
12016
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "font-medium", children: preview.subject }),
|
|
11577
12017
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
|
|
11578
|
-
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,
|
|
11579
12029
|
preview.entered.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "mt-1", children: [
|
|
11580
12030
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "font-medium", children: "Arrived:" }),
|
|
11581
12031
|
" ",
|
|
@@ -11597,6 +12047,7 @@ var import_react77 = require("react");
|
|
|
11597
12047
|
var import_react78 = require("@ai-matrx/records/react");
|
|
11598
12048
|
var import_design_system38 = require("@ai-matrx/design-system");
|
|
11599
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.";
|
|
11600
12051
|
function whenItFires(subscription) {
|
|
11601
12052
|
if (subscription.cadence === "instant") return "as it happens";
|
|
11602
12053
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
@@ -11604,7 +12055,7 @@ function whenItFires(subscription) {
|
|
|
11604
12055
|
}
|
|
11605
12056
|
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
11606
12057
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
11607
|
-
function SubscriptionsPanel({ tableId, className }) {
|
|
12058
|
+
function SubscriptionsPanel({ tableId, activeRuleId, className }) {
|
|
11608
12059
|
const client = (0, import_react78.useRecordsClient)();
|
|
11609
12060
|
const [rows, setRows] = (0, import_react77.useState)(null);
|
|
11610
12061
|
const [error, setError] = (0, import_react77.useState)(null);
|
|
@@ -11695,81 +12146,104 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
11695
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."
|
|
11696
12147
|
}
|
|
11697
12148
|
) : null,
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
"
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
" ",
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
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
|
+
)) })
|
|
11773
12247
|
] });
|
|
11774
12248
|
}
|
|
11775
12249
|
|
|
@@ -11824,9 +12298,16 @@ function ConnectedFormRunner(props) {
|
|
|
11824
12298
|
const submit = (0, import_react81.useCallback)(
|
|
11825
12299
|
async (values) => {
|
|
11826
12300
|
if (!client) {
|
|
12301
|
+
const said = "This form cannot send answers from here, so nothing was sent.";
|
|
11827
12302
|
return {
|
|
11828
12303
|
ok: false,
|
|
11829
|
-
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
|
+
)
|
|
11830
12311
|
};
|
|
11831
12312
|
}
|
|
11832
12313
|
const document2 = {
|
|
@@ -11839,7 +12320,7 @@ function ConnectedFormRunner(props) {
|
|
|
11839
12320
|
})
|
|
11840
12321
|
};
|
|
11841
12322
|
const written = await client.recordWrite({ table_id: form.subject, data: document2 });
|
|
11842
|
-
if (!written.ok) return { ok: false, message: written.error.message };
|
|
12323
|
+
if (!written.ok) return { ok: false, message: written.error.message, error: written.error };
|
|
11843
12324
|
return { ok: true, recordId: written.data };
|
|
11844
12325
|
},
|
|
11845
12326
|
[client, form]
|
|
@@ -11881,7 +12362,7 @@ function FormStage({
|
|
|
11881
12362
|
const [answers, setAnswers] = (0, import_react81.useState)({});
|
|
11882
12363
|
const [at, setAt] = (0, import_react81.useState)(0);
|
|
11883
12364
|
const [error, setError] = (0, import_react81.useState)(null);
|
|
11884
|
-
const [
|
|
12365
|
+
const [refusal2, setRefusal] = (0, import_react81.useState)(null);
|
|
11885
12366
|
const [writing, setWriting] = (0, import_react81.useState)(false);
|
|
11886
12367
|
const [done, setDone] = (0, import_react81.useState)(null);
|
|
11887
12368
|
const [hidden, setHidden] = (0, import_react81.useState)({});
|
|
@@ -11950,12 +12431,21 @@ function FormStage({
|
|
|
11950
12431
|
const outcome = await onSubmit(values);
|
|
11951
12432
|
setWriting(false);
|
|
11952
12433
|
if (!outcome.ok) {
|
|
11953
|
-
|
|
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
|
+
});
|
|
11954
12444
|
return;
|
|
11955
12445
|
}
|
|
11956
12446
|
setDone(outcome.message ?? "sent");
|
|
11957
12447
|
onSubmitted?.(outcome.recordId ?? null);
|
|
11958
|
-
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview]);
|
|
12448
|
+
}, [answers, form, honeypotKey, live, onSubmit, onSubmitted, preview, questions]);
|
|
11959
12449
|
function advance() {
|
|
11960
12450
|
if (!oneAtATime) return;
|
|
11961
12451
|
if (index < live.length - 1) setAt(index + 1);
|
|
@@ -12014,7 +12504,22 @@ function FormStage({
|
|
|
12014
12504
|
] }) : null,
|
|
12015
12505
|
form.intro && index === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-sm text-muted-foreground", children: form.intro }) : null,
|
|
12016
12506
|
error ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(RefusalNotice, { error, className: "text-left" }) : null,
|
|
12017
|
-
|
|
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,
|
|
12018
12523
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { ref: stage, className: "flex flex-col gap-4 text-left", children: [
|
|
12019
12524
|
(oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12020
12525
|
Question,
|
|
@@ -12088,12 +12593,15 @@ function Question({
|
|
|
12088
12593
|
void upload?.(file).then((result) => {
|
|
12089
12594
|
if (!result) return;
|
|
12090
12595
|
if (result.ok) onChange(result.fileId);
|
|
12091
|
-
else
|
|
12596
|
+
else
|
|
12597
|
+
setUploadError(
|
|
12598
|
+
refusal("invalid_argument", result.reason, "Pick another file, or try this one again.")
|
|
12599
|
+
);
|
|
12092
12600
|
});
|
|
12093
12601
|
}
|
|
12094
12602
|
}
|
|
12095
12603
|
),
|
|
12096
|
-
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
12604
|
+
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(RefusalLine, { error: uploadError }) : null
|
|
12097
12605
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(FieldControl, { field, value, onChange, id })
|
|
12098
12606
|
] });
|
|
12099
12607
|
}
|
|
@@ -12880,10 +13388,15 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
|
|
|
12880
13388
|
onChange: (e) => setDraftBody(e.target.value)
|
|
12881
13389
|
}
|
|
12882
13390
|
),
|
|
12883
|
-
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: "
|
|
12884
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("code", { children: token.raw }),
|
|
12885
|
-
" \u2014
|
|
12886
|
-
|
|
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
|
+
)
|
|
12887
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
|
|
12888
13401
|
] });
|
|
12889
13402
|
}
|
|
@@ -13088,7 +13601,21 @@ function SignBlock({ tableId, recordId, render, className }) {
|
|
|
13088
13601
|
signature.document_hash.slice(0, 12)
|
|
13089
13602
|
] })
|
|
13090
13603
|
] }),
|
|
13091
|
-
|
|
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 ?? "") })
|
|
13092
13619
|
] }, signature.id);
|
|
13093
13620
|
}) }) : null,
|
|
13094
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: [
|
|
@@ -13349,14 +13876,14 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
13349
13876
|
var import_react95 = require("react");
|
|
13350
13877
|
var import_recharts = require("recharts");
|
|
13351
13878
|
var import_records11 = require("@ai-matrx/records");
|
|
13352
|
-
var
|
|
13879
|
+
var import_core7 = require("@ai-matrx/records/core");
|
|
13353
13880
|
var import_design_system46 = require("@ai-matrx/design-system");
|
|
13354
13881
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
13355
13882
|
function pretty(n) {
|
|
13356
13883
|
return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
|
|
13357
13884
|
}
|
|
13358
|
-
function
|
|
13359
|
-
return (0,
|
|
13885
|
+
function asRefusal2(block) {
|
|
13886
|
+
return (0, import_core7.mapPgError)(
|
|
13360
13887
|
{
|
|
13361
13888
|
message: block.refused ?? "",
|
|
13362
13889
|
...block.sqlstate ? { code: block.sqlstate } : {}
|
|
@@ -13416,7 +13943,7 @@ function ChartBlock({ block, subject, className }) {
|
|
|
13416
13943
|
" ms"
|
|
13417
13944
|
] }) : null
|
|
13418
13945
|
] }),
|
|
13419
|
-
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: [
|
|
13420
13947
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Drawing, { kind, points, series, config, onDrill: drill }),
|
|
13421
13948
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Values, { points, measure: primary, config, onDrill: drill })
|
|
13422
13949
|
] }) })
|
|
@@ -13982,13 +14509,14 @@ var import_react99 = require("@ai-matrx/records/react");
|
|
|
13982
14509
|
var import_records12 = require("@ai-matrx/records");
|
|
13983
14510
|
var import_design_system48 = require("@ai-matrx/design-system");
|
|
13984
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.";
|
|
13985
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.";
|
|
13986
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.";
|
|
13987
14515
|
function formSuggestion(tableName2) {
|
|
13988
14516
|
const subject = tableName2?.trim() ? tableName2.trim() : "this table";
|
|
13989
14517
|
return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
|
|
13990
14518
|
}
|
|
13991
|
-
function FormsPanel({ tableId, className }) {
|
|
14519
|
+
function FormsPanel({ tableId, activeFormId, className }) {
|
|
13992
14520
|
const client = (0, import_react99.useRecordsClient)();
|
|
13993
14521
|
const publishGate = usePublishGate();
|
|
13994
14522
|
const host = useRecordsUi();
|
|
@@ -13999,6 +14527,11 @@ function FormsPanel({ tableId, className }) {
|
|
|
13999
14527
|
const [busy, setBusy] = (0, import_react98.useState)(null);
|
|
14000
14528
|
const [copied, setCopied] = (0, import_react98.useState)(null);
|
|
14001
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]);
|
|
14002
14535
|
const load = (0, import_react98.useCallback)(async () => {
|
|
14003
14536
|
const answered = await client.forms({ table_id: tableId });
|
|
14004
14537
|
if (!answered.ok) {
|
|
@@ -14047,10 +14580,12 @@ function FormsPanel({ tableId, className }) {
|
|
|
14047
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
|
|
14048
14581
|
] }),
|
|
14049
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,
|
|
14050
14584
|
building ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
14051
14585
|
FormBuilder,
|
|
14052
14586
|
{
|
|
14053
14587
|
tableId,
|
|
14588
|
+
activeFormId: linked ? linked.form_id : null,
|
|
14054
14589
|
onActiveForm: () => {
|
|
14055
14590
|
void load();
|
|
14056
14591
|
}
|
|
@@ -14076,50 +14611,62 @@ function FormsPanel({ tableId, className }) {
|
|
|
14076
14611
|
) : null,
|
|
14077
14612
|
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
|
|
14078
14613
|
const url = `${origin}${(0, import_records12.publicFormPath)(form.form_id)}`;
|
|
14079
|
-
return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
|
|
14080
|
-
|
|
14081
|
-
|
|
14082
|
-
|
|
14083
|
-
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
"
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
"
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
|
|
14101
|
-
|
|
14102
|
-
|
|
14103
|
-
|
|
14104
|
-
|
|
14105
|
-
|
|
14106
|
-
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
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
|
+
);
|
|
14123
14670
|
}) }),
|
|
14124
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
|
|
14125
14672
|
] });
|
|
@@ -14776,7 +15323,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14776
15323
|
void client.captureOpen({ sheet_id: sheetId }).then((res) => {
|
|
14777
15324
|
if (cancelled) return;
|
|
14778
15325
|
if (res.ok) setFace(res.data);
|
|
14779
|
-
else setLoadFailed(res.error
|
|
15326
|
+
else setLoadFailed(refusalOr(res.error, "unreachable", "This sheet could not be opened."));
|
|
14780
15327
|
});
|
|
14781
15328
|
return () => {
|
|
14782
15329
|
cancelled = true;
|
|
@@ -14823,7 +15370,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14823
15370
|
const needed = questions.filter((x) => x.required && !answered(x.field));
|
|
14824
15371
|
if (needed.length > 0) {
|
|
14825
15372
|
setMissing(
|
|
14826
|
-
|
|
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
|
+
)
|
|
14827
15378
|
);
|
|
14828
15379
|
setAt(questions.findIndex((x) => x.field === needed[0].field));
|
|
14829
15380
|
return;
|
|
@@ -14854,7 +15405,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14854
15405
|
if (online) await sync();
|
|
14855
15406
|
}
|
|
14856
15407
|
if (loadFailed) {
|
|
14857
|
-
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" }) }) });
|
|
14858
15409
|
}
|
|
14859
15410
|
if (face === void 0) {
|
|
14860
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) });
|
|
@@ -14885,7 +15436,21 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
14885
15436
|
] });
|
|
14886
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: [
|
|
14887
15438
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "tabular-nums text-muted-foreground", children: new Date(i.captured_at).toLocaleTimeString() }),
|
|
14888
|
-
|
|
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." }),
|
|
14889
15454
|
i.state === "refused" ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
14890
15455
|
import_design_system51.Button,
|
|
14891
15456
|
{
|
|
@@ -15005,7 +15570,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
15005
15570
|
}
|
|
15006
15571
|
}
|
|
15007
15572
|
),
|
|
15008
|
-
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,
|
|
15009
15574
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
15010
15575
|
at > 0 ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
15011
15576
|
import_design_system51.Button,
|
|
@@ -15233,13 +15798,13 @@ function AdHocCaptureSheet({
|
|
|
15233
15798
|
setUploadError(null);
|
|
15234
15799
|
void host.upload?.(file).then((result) => {
|
|
15235
15800
|
if (result.ok) setFileId(result.fileId);
|
|
15236
|
-
else setUploadError(result.reason);
|
|
15801
|
+
else setUploadError(refusal("invalid_argument", result.reason, "Pick another file, or try this one again."));
|
|
15237
15802
|
});
|
|
15238
15803
|
}
|
|
15239
15804
|
}
|
|
15240
15805
|
),
|
|
15241
15806
|
fileId ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "Attached." }) : null,
|
|
15242
|
-
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
15807
|
+
uploadError ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(RefusalLine, { error: uploadError }) : null
|
|
15243
15808
|
] }) : null,
|
|
15244
15809
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
15245
15810
|
import_design_system52.Textarea,
|
|
@@ -15702,17 +16267,17 @@ async function declareTable(client, spec) {
|
|
|
15702
16267
|
if (!written.ok) return undo(client, table.data, home.data, spec.name, written.error);
|
|
15703
16268
|
return { ok: true, data: table.data, homeId: home.data };
|
|
15704
16269
|
}
|
|
15705
|
-
async function undo(client, tableId, homeId, name,
|
|
16270
|
+
async function undo(client, tableId, homeId, name, refusal2) {
|
|
15706
16271
|
const removed = await client.recordDelete({ record_id: tableId });
|
|
15707
16272
|
if (removed.ok) {
|
|
15708
16273
|
await client.recordDelete({ record_id: homeId });
|
|
15709
|
-
return { ok: false, error:
|
|
16274
|
+
return { ok: false, error: refusal2 };
|
|
15710
16275
|
}
|
|
15711
16276
|
return {
|
|
15712
16277
|
ok: false,
|
|
15713
16278
|
error: {
|
|
15714
|
-
...
|
|
15715
|
-
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.`
|
|
15716
16281
|
}
|
|
15717
16282
|
};
|
|
15718
16283
|
}
|
|
@@ -15966,9 +16531,43 @@ function chooseSurface(current, pressed) {
|
|
|
15966
16531
|
function surfaceChosen(current, asking) {
|
|
15967
16532
|
return asking.main !== void 0 ? current.main === asking.main : current.rail === asking.rail;
|
|
15968
16533
|
}
|
|
15969
|
-
function openingRail(activeRecordId) {
|
|
15970
|
-
|
|
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;
|
|
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.`;
|
|
15971
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.";
|
|
15972
16571
|
function openingView(activeView, activeDashboardId) {
|
|
15973
16572
|
const named = pageViewFromParam(activeView);
|
|
15974
16573
|
if (named === null) {
|
|
@@ -15996,6 +16595,8 @@ function TablePage({
|
|
|
15996
16595
|
activeGroupField,
|
|
15997
16596
|
cameFrom,
|
|
15998
16597
|
filter,
|
|
16598
|
+
activeRail,
|
|
16599
|
+
activeItemId,
|
|
15999
16600
|
className
|
|
16000
16601
|
}) {
|
|
16001
16602
|
const client = (0, import_react119.useRecordsClient)();
|
|
@@ -16005,8 +16606,13 @@ function TablePage({
|
|
|
16005
16606
|
const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
|
|
16006
16607
|
const organizationId = (0, import_react119.useRecordsClient)().config.organizationId;
|
|
16007
16608
|
const [view, setView] = (0, import_react118.useState)(null);
|
|
16008
|
-
const opening = openingRail(activeRecordId);
|
|
16609
|
+
const opening = openingRail(activeRecordId, activeRail);
|
|
16009
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;
|
|
16010
16616
|
const [asking, setAsking] = (0, import_react118.useState)(null);
|
|
16011
16617
|
const [layoutFromLink, setLayoutFromLink] = (0, import_react118.useState)(opened.layout);
|
|
16012
16618
|
const [surface, setSurface] = (0, import_react118.useState)({
|
|
@@ -16031,6 +16637,12 @@ function TablePage({
|
|
|
16031
16637
|
setOpenRecord(activeRecordId);
|
|
16032
16638
|
setSurface((now) => ({ ...now, rail: "record" }));
|
|
16033
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]);
|
|
16034
16646
|
(0, import_react118.useEffect)(() => {
|
|
16035
16647
|
const named = pageViewFromParam(activeView);
|
|
16036
16648
|
if (named === null) return;
|
|
@@ -16195,7 +16807,8 @@ function TablePage({
|
|
|
16195
16807
|
organizationId,
|
|
16196
16808
|
subjectId: tableId,
|
|
16197
16809
|
name: table.data?.name,
|
|
16198
|
-
may: rights.share
|
|
16810
|
+
may: rights.share,
|
|
16811
|
+
initiallyOpen: shareFromLink
|
|
16199
16812
|
}
|
|
16200
16813
|
),
|
|
16201
16814
|
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ExportMenu, { tableId })
|
|
@@ -16218,6 +16831,8 @@ function TablePage({
|
|
|
16218
16831
|
}
|
|
16219
16832
|
)
|
|
16220
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,
|
|
16221
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,
|
|
16222
16837
|
cameFrom ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
16223
16838
|
"p",
|
|
@@ -16276,7 +16891,7 @@ function TablePage({
|
|
|
16276
16891
|
}
|
|
16277
16892
|
}
|
|
16278
16893
|
) : null,
|
|
16279
|
-
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,
|
|
16280
16895
|
rail === "bookings" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(BookingSlots, { tableId }) : null,
|
|
16281
16896
|
rail === "checklists" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
16282
16897
|
ChecklistsPanel,
|
|
@@ -16288,8 +16903,8 @@ function TablePage({
|
|
|
16288
16903
|
}
|
|
16289
16904
|
}
|
|
16290
16905
|
) : null,
|
|
16291
|
-
rail === "notifications" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(SubscriptionsPanel, { tableId }) : null,
|
|
16292
|
-
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,
|
|
16293
16908
|
rail === "import" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ImportWizard, { tableId, onDone: () => setRail("none") }) : null,
|
|
16294
16909
|
rail === "field" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(FieldEditor, { tableId, onSaved: () => setRail("none"), onCancel: () => setRail("none") }) : null,
|
|
16295
16910
|
rail === "new-record" ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|