@ai-matrx/records-ui 0.63.0 → 0.66.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 +114 -0
- package/dist/index.cjs +398 -307
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +213 -17
- package/dist/index.d.ts +213 -17
- package/dist/index.js +392 -295
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -31,8 +31,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
var src_exports = {};
|
|
33
33
|
__export(src_exports, {
|
|
34
|
+
ABSENCE_WORD_LABEL: () => ABSENCE_WORD_LABEL,
|
|
35
|
+
ABSENCE_WORD_VALUES: () => ABSENCE_WORD_VALUES,
|
|
34
36
|
ACTION_KINDS: () => ACTION_KINDS,
|
|
35
37
|
ActionInbox: () => ActionInbox,
|
|
38
|
+
BOOKING_PAGE_STATE_LABEL: () => BOOKING_PAGE_STATE_LABEL,
|
|
39
|
+
BOOKING_PAGE_STATE_VALUES: () => BOOKING_PAGE_STATE_VALUES,
|
|
36
40
|
BookingBuilder: () => BookingBuilder,
|
|
37
41
|
BookingSlots: () => BookingSlots,
|
|
38
42
|
BuildOrAsk: () => BuildOrAsk,
|
|
@@ -40,7 +44,10 @@ __export(src_exports, {
|
|
|
40
44
|
CHART_KINDS: () => CHART_KINDS,
|
|
41
45
|
CHART_KIND_LABEL: () => CHART_KIND_LABEL,
|
|
42
46
|
CHART_NEEDS: () => CHART_NEEDS,
|
|
47
|
+
CHECKLIST_REQUIREMENT_KINDS: () => CHECKLIST_REQUIREMENT_KINDS,
|
|
48
|
+
CHECKLIST_REQUIREMENT_LABEL: () => CHECKLIST_REQUIREMENT_LABEL,
|
|
43
49
|
CONDITION_OPS: () => CONDITION_OPS,
|
|
50
|
+
CONTEXT_POLICY_LABEL: () => CONTEXT_POLICY_LABEL,
|
|
44
51
|
CaptureRun: () => CaptureRun,
|
|
45
52
|
CaptureSheet: () => CaptureSheet,
|
|
46
53
|
ChartBlock: () => ChartBlock,
|
|
@@ -56,17 +63,25 @@ __export(src_exports, {
|
|
|
56
63
|
CustomFieldsSection: () => CustomFieldsSection,
|
|
57
64
|
DEFAULT_FIELDS: () => DEFAULT_FIELDS,
|
|
58
65
|
DEFAULT_VIEW_NAME: () => DEFAULT_VIEW_NAME,
|
|
66
|
+
DUE_STATE_LABEL: () => DUE_STATE_LABEL,
|
|
59
67
|
DashboardCanvas: () => DashboardCanvas,
|
|
60
68
|
DigestScheduler: () => DigestScheduler,
|
|
61
69
|
DocRender: () => DocRender,
|
|
62
70
|
DocTemplate: () => DocTemplate,
|
|
63
71
|
EMPTY_PRESENTATION: () => EMPTY_PRESENTATION,
|
|
72
|
+
ENGINEER_TEXT: () => ENGINEER_TEXT,
|
|
64
73
|
EmbedFrame: () => EmbedFrame,
|
|
65
74
|
EnrichBadge: () => EnrichBadge,
|
|
66
75
|
EnrichPanel: () => EnrichPanel,
|
|
67
76
|
ExportMenu: () => ExportMenu,
|
|
77
|
+
FIELD_RULE_KIND_LABEL: () => FIELD_RULE_KIND_LABEL,
|
|
78
|
+
FIELD_RULE_KIND_VALUES: () => FIELD_RULE_KIND_VALUES,
|
|
79
|
+
FIELD_SENSITIVITY_LABEL: () => FIELD_SENSITIVITY_LABEL,
|
|
68
80
|
FIELD_TYPE_CHOICES: () => FIELD_TYPE_CHOICES,
|
|
69
81
|
FIELD_TYPE_GROUPS: () => FIELD_TYPE_GROUPS,
|
|
82
|
+
FORMULA_OP_JOINS: () => FORMULA_OP_JOINS,
|
|
83
|
+
FORMULA_OP_LABEL: () => FORMULA_OP_LABEL,
|
|
84
|
+
FORMULA_OP_VALUES: () => FORMULA_OP_VALUES,
|
|
70
85
|
FORM_FLOWS: () => FORM_FLOWS,
|
|
71
86
|
FROZEN_COLUMN_WIDTH: () => FROZEN_COLUMN_WIDTH,
|
|
72
87
|
FieldControl: () => FieldControl,
|
|
@@ -114,6 +129,8 @@ __export(src_exports, {
|
|
|
114
129
|
PAGE_VIEW_LABEL: () => PAGE_VIEW_LABEL,
|
|
115
130
|
PARITY_LABEL: () => PARITY_LABEL,
|
|
116
131
|
PARITY_MADE_OF: () => PARITY_MADE_OF,
|
|
132
|
+
PROPOSED_CHANGE_ACT_LABEL: () => PROPOSED_CHANGE_ACT_LABEL,
|
|
133
|
+
PROPOSED_CHANGE_ACT_VALUES: () => PROPOSED_CHANGE_ACT_VALUES,
|
|
117
134
|
Peek: () => Peek,
|
|
118
135
|
PersonPicker: () => PersonPicker,
|
|
119
136
|
PipelineBoard: () => PipelineBoard,
|
|
@@ -124,6 +141,8 @@ __export(src_exports, {
|
|
|
124
141
|
ProposalRow: () => ProposalRow,
|
|
125
142
|
ProvenanceBadge: () => ProvenanceBadge,
|
|
126
143
|
PublicViewPage: () => PublicViewPage,
|
|
144
|
+
ROLLUP_AGG_LABEL: () => ROLLUP_AGG_LABEL,
|
|
145
|
+
ROLLUP_AGG_VALUES: () => ROLLUP_AGG_VALUES,
|
|
127
146
|
RecordChat: () => RecordChat,
|
|
128
147
|
RecordChip: () => RecordChip,
|
|
129
148
|
RecordForm: () => RecordForm,
|
|
@@ -139,8 +158,13 @@ __export(src_exports, {
|
|
|
139
158
|
SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS: () => SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS,
|
|
140
159
|
SERIES_COLORS: () => SERIES_COLORS,
|
|
141
160
|
SOMEBODY: () => SOMEBODY,
|
|
161
|
+
STAGE_RULE_ON_FAIL_LABEL: () => STAGE_RULE_ON_FAIL_LABEL,
|
|
142
162
|
STORE_ANSWERS_THESE: () => STORE_ANSWERS_THESE,
|
|
143
163
|
STORE_DECIDES_REASON: () => STORE_DECIDES_REASON,
|
|
164
|
+
SUBSCRIPTION_CADENCES: () => SUBSCRIPTION_CADENCES,
|
|
165
|
+
SUBSCRIPTION_CADENCE_LABEL: () => SUBSCRIPTION_CADENCE_LABEL,
|
|
166
|
+
SUBSCRIPTION_CHANNEL_LABEL: () => SUBSCRIPTION_CHANNEL_LABEL,
|
|
167
|
+
SUBSCRIPTION_CHANNEL_VALUES: () => SUBSCRIPTION_CHANNEL_VALUES,
|
|
144
168
|
ShareControl: () => ShareControl,
|
|
145
169
|
SignBlock: () => SignBlock,
|
|
146
170
|
StageRulesSection: () => StageRulesSection,
|
|
@@ -158,6 +182,9 @@ __export(src_exports, {
|
|
|
158
182
|
ViewBar: () => ViewBar,
|
|
159
183
|
ViewSwitcher: () => ViewSwitcher,
|
|
160
184
|
WITHHELD_RECORD_LABEL: () => WITHHELD_RECORD_LABEL,
|
|
185
|
+
WORK_DUE_STATES: () => WORK_DUE_STATES,
|
|
186
|
+
WORK_INBOX_KINDS: () => WORK_INBOX_KINDS,
|
|
187
|
+
WORK_INBOX_KIND_LABEL: () => WORK_INBOX_KIND_LABEL,
|
|
161
188
|
WhoChangedSource: () => WhoChangedSource,
|
|
162
189
|
actorBadge: () => actorBadge,
|
|
163
190
|
actorWords: () => actorWords,
|
|
@@ -166,6 +193,7 @@ __export(src_exports, {
|
|
|
166
193
|
blockFromSpec: () => blockFromSpec,
|
|
167
194
|
bodyForReading: () => bodyForReading,
|
|
168
195
|
bodyFromKeys: () => bodyFromKeys,
|
|
196
|
+
cameFromLine: () => cameFromLine,
|
|
169
197
|
cellState: () => cellState,
|
|
170
198
|
chooseSurface: () => chooseSurface,
|
|
171
199
|
colorFromTheValue: () => colorFromTheValue,
|
|
@@ -197,6 +225,7 @@ __export(src_exports, {
|
|
|
197
225
|
hintIsMachineIdentity: () => hintIsMachineIdentity,
|
|
198
226
|
humanize: () => humanize,
|
|
199
227
|
idsOf: () => idsOf,
|
|
228
|
+
isEngineerText: () => isEngineerText,
|
|
200
229
|
isId: () => isId,
|
|
201
230
|
isMachineIdentity: () => isMachineIdentity,
|
|
202
231
|
isPlainFieldType: () => isPlainFieldType,
|
|
@@ -347,12 +376,12 @@ var NO_RIGHTS = tableRights(NOT_ANSWERED_YET);
|
|
|
347
376
|
function tableRightsAt(level) {
|
|
348
377
|
return tableRights(whatYouMayDo(level, true));
|
|
349
378
|
}
|
|
350
|
-
var NO_SAVED_VIEWS_REASON = "
|
|
351
|
-
var NO_MEMBERS_REASON = "
|
|
352
|
-
var NO_SHARE_REASON = "
|
|
353
|
-
var NO_OPEN_RECORDS_REASON = "These numbers do not click through yet. Every one of them is a count of real records
|
|
354
|
-
var NO_REASK_REASON = "
|
|
355
|
-
var NO_UPLOAD_REASON = "
|
|
379
|
+
var NO_SAVED_VIEWS_REASON = "There are no saved views here yet, so there is nothing to be told about. Save a view of this table first and it will be offered here.";
|
|
380
|
+
var NO_MEMBERS_REASON = "This app cannot offer the people in your organization on this screen yet, so there is nobody to pick. Type the person's name into a text field for now, and tell us \u2014 it is on our list, not yours.";
|
|
381
|
+
var NO_SHARE_REASON = "Sharing is not available on this screen, so there is no Share button here rather than one that opens nothing. Share this table from the tables list instead.";
|
|
382
|
+
var NO_OPEN_RECORDS_REASON = "These numbers do not click through yet. Every one of them is a count of real records \u2014 open the records view and group it by the same field to see them. It is on our list, not yours.";
|
|
383
|
+
var NO_REASK_REASON = "Changing this dashboard by describing it is not switched on here yet, so this box is off rather than one that goes nowhere. Add and remove the blocks by hand above. It is on our list, not yours.";
|
|
384
|
+
var NO_UPLOAD_REASON = "This screen cannot take a file yet, so there is no chooser here rather than one that loses what you pick. Put a link to the file in a text field for now. It is on our list, not yours.";
|
|
356
385
|
var HostContext = (0, import_react.createContext)(null);
|
|
357
386
|
function RecordsUiProvider({ value, children }) {
|
|
358
387
|
const bound = (0, import_react.useMemo)(() => value, [value]);
|
|
@@ -427,6 +456,10 @@ var TITLE = {
|
|
|
427
456
|
not_supported: "That cannot be done here",
|
|
428
457
|
store_limit: "This has got as big as it can",
|
|
429
458
|
store_not_served: "This organization does not keep its data here",
|
|
459
|
+
unreachable: "We could not reach your data",
|
|
460
|
+
needs_approval: "This is waiting on someone's approval",
|
|
461
|
+
stale_read: "Your view of this is a moment behind",
|
|
462
|
+
timed_out: "That took too long to answer",
|
|
430
463
|
internal: "Something went wrong at our end"
|
|
431
464
|
};
|
|
432
465
|
var REMEDY = {
|
|
@@ -441,8 +474,35 @@ var REMEDY = {
|
|
|
441
474
|
not_supported: "There is another way to do this \u2014 ask, and we will point you at it.",
|
|
442
475
|
store_limit: "Split it up, or ask us to raise the ceiling for your organization.",
|
|
443
476
|
store_not_served: "An administrator of this organization turns the record store on from the data settings screen.",
|
|
477
|
+
// 🚨 NOTHING WAS WRITTEN AND NOTHING WAS REFUSED (lane TAILS-6). The call
|
|
478
|
+
// never arrived, so "try again" is the honest instruction rather than a
|
|
479
|
+
// consolation — and saying nothing changed is the half a person actually
|
|
480
|
+
// worries about.
|
|
481
|
+
unreachable: "Nothing was changed. Check your connection and open this again.",
|
|
482
|
+
needs_approval: "Nothing was changed yet. The people who can approve it have been asked.",
|
|
483
|
+
stale_read: "Open this again in a moment and it will be up to date.",
|
|
484
|
+
timed_out: "Nothing was changed. Try a smaller page, or try again in a moment.",
|
|
444
485
|
internal: "Nothing you did caused this. Try again, and tell us if it keeps happening."
|
|
445
486
|
};
|
|
487
|
+
var ENGINEER_TEXT = [
|
|
488
|
+
/@ai-matrx\//,
|
|
489
|
+
/\bSQLSTATE\b/i,
|
|
490
|
+
/\bPostgREST\b/i,
|
|
491
|
+
/\bIndexedDB\b/i,
|
|
492
|
+
// `<RecordsUiProvider>` — a component, on a screen.
|
|
493
|
+
/<\/?[A-Z][A-Za-z0-9]*\s*\/?>/,
|
|
494
|
+
// `savedViews`, `openRecords`, `onReask` — an identifier, however it is
|
|
495
|
+
// quoted. English does not write camelCase; code does.
|
|
496
|
+
/\b[a-z][a-z0-9]*[A-Z][A-Za-z0-9]*\b/,
|
|
497
|
+
// A backticked token is a thing you type into an editor, not a thing you do.
|
|
498
|
+
/`[A-Za-z_][A-Za-z0-9_.]*`/,
|
|
499
|
+
// "bind X on Y", "pass X", "import X" — an instruction to whoever maintains
|
|
500
|
+
// the app, arriving on the screen of somebody who cannot act on it.
|
|
501
|
+
/\bbind\s+[`'"A-Za-z]/i
|
|
502
|
+
];
|
|
503
|
+
function isEngineerText(text) {
|
|
504
|
+
return ENGINEER_TEXT.some((shape) => shape.test(text));
|
|
505
|
+
}
|
|
446
506
|
function withoutMachineClauses(sentence2) {
|
|
447
507
|
const dropped = [];
|
|
448
508
|
const footnotes = [];
|
|
@@ -480,11 +540,17 @@ function refusalForAPerson(error) {
|
|
|
480
540
|
const message = plainSentence(String(error.message ?? ""));
|
|
481
541
|
const hint = plainSentence(String(error.hint ?? ""));
|
|
482
542
|
const title = TITLE[code] ?? "The store refused this";
|
|
483
|
-
const
|
|
484
|
-
const
|
|
543
|
+
const hintIsForEngineers = isEngineerText(String(error.hint ?? ""));
|
|
544
|
+
const remedy = (hintIsForEngineers ? null : hint.kept) ?? REMEDY[code] ?? REMEDY["internal"];
|
|
545
|
+
const messageIsForEngineers = isEngineerText(String(error.message ?? ""));
|
|
546
|
+
const said = (messageIsForEngineers ? "" : message.kept ?? "").trim();
|
|
485
547
|
const forEngineers = [
|
|
486
548
|
error.sqlstate ? `SQLSTATE ${error.sqlstate}.` : null,
|
|
487
|
-
|
|
549
|
+
// The client's own note about a class it did not recognise, or a call that
|
|
550
|
+
// never reached the store. It is rendered out of sight, never as a sentence.
|
|
551
|
+
error.diagnostic ? error.diagnostic : null,
|
|
552
|
+
messageIsForEngineers ? `Message: ${error.message}` : null,
|
|
553
|
+
error.hint && (hintIsForEngineers || hint.kept !== error.hint) ? `Hint: ${error.hint}` : null,
|
|
488
554
|
message.dropped.length > 0 || hint.dropped.length > 0 ? `Dropped from the person's view: ${[...message.dropped, ...hint.dropped].join(" | ")}` : null
|
|
489
555
|
].filter(Boolean).join(" ");
|
|
490
556
|
return {
|
|
@@ -826,50 +892,9 @@ var PARITY_LABEL = {
|
|
|
826
892
|
var PARITY_MADE_OF = Object.fromEntries(
|
|
827
893
|
import_records3.PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of])
|
|
828
894
|
);
|
|
895
|
+
var KERNEL = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };
|
|
829
896
|
function editorKindFor(field) {
|
|
830
|
-
|
|
831
|
-
const config = field.config ?? {};
|
|
832
|
-
switch (field.type) {
|
|
833
|
-
// LIMITS-FIX 2026-09-21: the tick box is a BEHAVIOUR of its own, so its editor is
|
|
834
|
-
// decided by the behaviour alone — no format, no unit, no target to read. A record
|
|
835
|
-
// that never answered holds no key at all, and `CheckboxControl` draws that third
|
|
836
|
-
// state rather than pretending it is an unticked box.
|
|
837
|
-
case "boolean":
|
|
838
|
-
return "checkbox";
|
|
839
|
-
case "text":
|
|
840
|
-
if (format === "email") return "email";
|
|
841
|
-
if (format === "phone") return "phone";
|
|
842
|
-
if (format === "url") return "url";
|
|
843
|
-
if (format === "json" || config["json"] === true) return "json";
|
|
844
|
-
if (format === "long" || config["multiline"] === true) return "long_text";
|
|
845
|
-
return "text";
|
|
846
|
-
case "range": {
|
|
847
|
-
const kind = String(config["kind"] ?? "number");
|
|
848
|
-
if (kind === "date" || kind === "datetime") return "datetime";
|
|
849
|
-
if (format === "currency") return "currency";
|
|
850
|
-
if (format === "percent" || field.unit === "%") return "percent";
|
|
851
|
-
return "number";
|
|
852
|
-
}
|
|
853
|
-
case "list":
|
|
854
|
-
return field.multi ? "multi_select" : "select";
|
|
855
|
-
case "relation":
|
|
856
|
-
return relationEditor(field);
|
|
857
|
-
case "formula": {
|
|
858
|
-
const via = config["via"];
|
|
859
|
-
if (via && config["agg"]) return "rollup";
|
|
860
|
-
if (via) return "lookup";
|
|
861
|
-
return "formula";
|
|
862
|
-
}
|
|
863
|
-
default:
|
|
864
|
-
return "text";
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
function relationEditor(field) {
|
|
868
|
-
const parity = String(field.config?.["parity"] ?? "");
|
|
869
|
-
if (parity === "attachment" || parity === "member") return parity;
|
|
870
|
-
if (field.relation_target === KERNEL_FILE_TABLE) return "attachment";
|
|
871
|
-
if (field.relation_target === KERNEL_PERSON_TABLE) return "member";
|
|
872
|
-
return "relation";
|
|
897
|
+
return (0, import_records3.fieldKindFor)(field, KERNEL);
|
|
873
898
|
}
|
|
874
899
|
function fieldTypeLabel(field) {
|
|
875
900
|
const kind = editorKindFor(field);
|
|
@@ -1765,18 +1790,137 @@ function useLabelWait(field) {
|
|
|
1765
1790
|
return field ? waiting(field) : null;
|
|
1766
1791
|
}
|
|
1767
1792
|
|
|
1768
|
-
// src/
|
|
1769
|
-
|
|
1770
|
-
function
|
|
1771
|
-
|
|
1772
|
-
|
|
1793
|
+
// src/enumLabels.ts
|
|
1794
|
+
function exhaustiveValuesOf() {
|
|
1795
|
+
return function(values) {
|
|
1796
|
+
return values;
|
|
1797
|
+
};
|
|
1773
1798
|
}
|
|
1774
|
-
var
|
|
1799
|
+
var WORK_DUE_STATES = exhaustiveValuesOf()([
|
|
1800
|
+
"overdue",
|
|
1801
|
+
"due_today",
|
|
1802
|
+
"scheduled",
|
|
1803
|
+
"undated",
|
|
1804
|
+
"finished"
|
|
1805
|
+
]);
|
|
1806
|
+
var DUE_STATE_LABEL = {
|
|
1807
|
+
overdue: "Overdue",
|
|
1808
|
+
due_today: "Today",
|
|
1809
|
+
scheduled: "Scheduled",
|
|
1810
|
+
undated: "No date",
|
|
1811
|
+
finished: "Done"
|
|
1812
|
+
};
|
|
1813
|
+
var WORK_INBOX_KINDS = exhaustiveValuesOf()(["assignment", "approval", "proposal"]);
|
|
1814
|
+
var WORK_INBOX_KIND_LABEL = {
|
|
1815
|
+
assignment: "Yours",
|
|
1816
|
+
approval: "Approve",
|
|
1817
|
+
proposal: "Agent"
|
|
1818
|
+
};
|
|
1819
|
+
var CHECKLIST_REQUIREMENT_KINDS = exhaustiveValuesOf()([
|
|
1820
|
+
"none",
|
|
1821
|
+
"note",
|
|
1822
|
+
"answer",
|
|
1823
|
+
"record_field",
|
|
1824
|
+
"form",
|
|
1825
|
+
"document"
|
|
1826
|
+
]);
|
|
1827
|
+
var CHECKLIST_REQUIREMENT_LABEL = {
|
|
1828
|
+
none: "",
|
|
1829
|
+
note: "Say what you did",
|
|
1830
|
+
answer: "Fill this in",
|
|
1831
|
+
record_field: "This has to be filled in on the record",
|
|
1832
|
+
form: "This step is a form",
|
|
1833
|
+
document: "This step is a document"
|
|
1834
|
+
};
|
|
1835
|
+
var FIELD_SENSITIVITY_LABEL = {
|
|
1836
|
+
public: "Anyone who can open the record",
|
|
1837
|
+
internal: "People in this organization",
|
|
1838
|
+
confidential: "People who have been given the record",
|
|
1839
|
+
restricted: "Owners and administrators only"
|
|
1840
|
+
};
|
|
1841
|
+
var CONTEXT_POLICY_LABEL = {
|
|
1842
|
+
include: "Yes, an assistant can read it",
|
|
1843
|
+
summarize: "Only a summary of it",
|
|
1844
|
+
on_request: "Only when somebody asks for it",
|
|
1845
|
+
exclude: "No, keep it away from assistants"
|
|
1846
|
+
};
|
|
1847
|
+
var STAGE_RULE_ON_FAIL_LABEL = {
|
|
1848
|
+
refuse: "Turn it away",
|
|
1849
|
+
require_approval: "Ask for approval"
|
|
1850
|
+
};
|
|
1851
|
+
var SUBSCRIPTION_CADENCES = exhaustiveValuesOf()(["instant", "hourly", "daily", "weekly"]);
|
|
1852
|
+
var SUBSCRIPTION_CADENCE_LABEL = {
|
|
1853
|
+
instant: "as it happens",
|
|
1854
|
+
hourly: "hourly summary",
|
|
1855
|
+
daily: "daily summary",
|
|
1856
|
+
weekly: "weekly summary"
|
|
1857
|
+
};
|
|
1858
|
+
var SUBSCRIPTION_CHANNEL_VALUES = ["in_app", "email", "sms"];
|
|
1859
|
+
var SUBSCRIPTION_CHANNEL_LABEL = {
|
|
1860
|
+
in_app: "in the app",
|
|
1861
|
+
email: "by email \u2014 only if an address is on the account",
|
|
1862
|
+
sms: "by text \u2014 only if a number is on the account"
|
|
1863
|
+
};
|
|
1864
|
+
var ROLLUP_AGG_VALUES = ["count", "sum", "avg", "min", "max"];
|
|
1865
|
+
var ROLLUP_AGG_LABEL = {
|
|
1866
|
+
count: "How many there are",
|
|
1867
|
+
sum: "The total of",
|
|
1868
|
+
avg: "The average of",
|
|
1869
|
+
min: "The smallest",
|
|
1870
|
+
max: "The largest"
|
|
1871
|
+
};
|
|
1872
|
+
var FORMULA_OP_VALUES = ["concat", "add", "sub", "mul", "div"];
|
|
1873
|
+
var FORMULA_OP_LABEL = {
|
|
1874
|
+
concat: "Join them together",
|
|
1875
|
+
add: "Add them up",
|
|
1876
|
+
sub: "Take the second from the first",
|
|
1877
|
+
mul: "Multiply them",
|
|
1878
|
+
div: "Divide the first by the second"
|
|
1879
|
+
};
|
|
1880
|
+
var FORMULA_OP_JOINS = {
|
|
1881
|
+
concat: true,
|
|
1882
|
+
add: true,
|
|
1883
|
+
sub: false,
|
|
1884
|
+
mul: true,
|
|
1885
|
+
div: false
|
|
1886
|
+
};
|
|
1887
|
+
var FIELD_RULE_KIND_VALUES = ["min", "max", "length", "pattern", "equals_field", "differs_from_field"];
|
|
1888
|
+
var FIELD_RULE_KIND_LABEL = {
|
|
1889
|
+
min: "At least",
|
|
1890
|
+
max: "At most",
|
|
1891
|
+
length: "No longer than",
|
|
1892
|
+
pattern: "Looks like",
|
|
1893
|
+
equals_field: "Same as the column",
|
|
1894
|
+
differs_from_field: "Different from the column"
|
|
1895
|
+
};
|
|
1896
|
+
var PROPOSED_CHANGE_ACT_VALUES = ["add", "update", "remove"];
|
|
1897
|
+
var PROPOSED_CHANGE_ACT_LABEL = {
|
|
1898
|
+
add: "Add",
|
|
1899
|
+
update: "Update",
|
|
1900
|
+
remove: "Remove"
|
|
1901
|
+
};
|
|
1902
|
+
var BOOKING_PAGE_STATE_VALUES = ["draft", "open", "closed", "full"];
|
|
1903
|
+
var BOOKING_PAGE_STATE_LABEL = {
|
|
1904
|
+
draft: "Not open yet \u2014 nobody can book until you open it.",
|
|
1905
|
+
open: "Open. Anyone with the link can book a time.",
|
|
1906
|
+
closed: "Closed. The link still works and says you are not taking bookings.",
|
|
1907
|
+
full: "Every time you offered is taken."
|
|
1908
|
+
};
|
|
1909
|
+
var ABSENCE_WORD_VALUES = ["never asked", "none", "refused", "conflicting"];
|
|
1910
|
+
var ABSENCE_WORD_LABEL = {
|
|
1775
1911
|
"never asked": "never asked",
|
|
1776
1912
|
none: "none",
|
|
1777
1913
|
refused: "refused",
|
|
1778
1914
|
conflicting: "conflicting"
|
|
1779
1915
|
};
|
|
1916
|
+
|
|
1917
|
+
// src/values.tsx
|
|
1918
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1919
|
+
function envelopeFor(document2, key) {
|
|
1920
|
+
const block = document2?._values;
|
|
1921
|
+
return block?.[key];
|
|
1922
|
+
}
|
|
1923
|
+
var ABSENCE_WORDS = ABSENCE_WORD_LABEL;
|
|
1780
1924
|
function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
1781
1925
|
const envelope = envelopeFor(document2, field.key);
|
|
1782
1926
|
if (envelope?.absent) {
|
|
@@ -2600,7 +2744,7 @@ var import_react17 = require("react");
|
|
|
2600
2744
|
var import_react18 = require("@ai-matrx/records/react");
|
|
2601
2745
|
var import_design_system8 = require("@ai-matrx/design-system");
|
|
2602
2746
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2603
|
-
var NO_ENRICH_REASON = "
|
|
2747
|
+
var NO_ENRICH_REASON = "Having an agent fill this column is not switched on here yet, so this panel does not offer it rather than offering something that cannot happen. Everything else on this panel works. It is on our list, not yours.";
|
|
2604
2748
|
function cellState(cell) {
|
|
2605
2749
|
if (!cell || !cell.agent_owned) return "none";
|
|
2606
2750
|
if (cell.pinned) return "yours";
|
|
@@ -3199,113 +3343,11 @@ function choiceOptionsOf(options) {
|
|
|
3199
3343
|
}
|
|
3200
3344
|
return { byWord, labels };
|
|
3201
3345
|
}
|
|
3202
|
-
var TRUE_WORDS = /* @__PURE__ */ new Set(["true", "yes", "y", "1", "\u2713", "x", "checked", "on"]);
|
|
3203
|
-
var FALSE_WORDS = /* @__PURE__ */ new Set(["false", "no", "n", "0", "", "unchecked", "off"]);
|
|
3204
3346
|
function valueFromPastedText(field, raw, options) {
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
switch (kind) {
|
|
3210
|
-
case "checkbox": {
|
|
3211
|
-
const word = text.toLowerCase();
|
|
3212
|
-
if (TRUE_WORDS.has(word)) return { value: true };
|
|
3213
|
-
if (FALSE_WORDS.has(word)) return { value: word === "" ? null : false };
|
|
3214
|
-
return {
|
|
3215
|
-
refusal: `${label} is a tick box, so it takes yes or no \u2014 \u201C${raw}\u201D is neither. Paste TRUE/FALSE, yes/no or 1/0.`
|
|
3216
|
-
};
|
|
3217
|
-
}
|
|
3218
|
-
case "number":
|
|
3219
|
-
case "currency":
|
|
3220
|
-
case "percent": {
|
|
3221
|
-
const negative = /^\(.*\)$/.test(text);
|
|
3222
|
-
const bare = text.replace(/^\(|\)$/g, "").replace(/[\s,]/g, "").replace(/^[^\d.\-+]+/, "").replace(/[^\d.\-+eE]+$/, "");
|
|
3223
|
-
const n = Number(bare);
|
|
3224
|
-
if (bare === "" || Number.isNaN(n)) {
|
|
3225
|
-
return {
|
|
3226
|
-
refusal: `${label} holds a number, and \u201C${raw}\u201D is not one. Paste digits \u2014 a currency symbol, a percent sign, commas and brackets for a negative are all fine.`
|
|
3227
|
-
};
|
|
3228
|
-
}
|
|
3229
|
-
return { value: negative ? -n : n };
|
|
3230
|
-
}
|
|
3231
|
-
case "datetime": {
|
|
3232
|
-
const wantsTime = String(field.config?.["kind"] ?? "date") === "datetime";
|
|
3233
|
-
const plainDay = /^\s*(\d{4})-(\d{2})-(\d{2})\s*$/.exec(raw);
|
|
3234
|
-
if (!wantsTime && plainDay) return { value: `${plainDay[1]}-${plainDay[2]}-${plainDay[3]}` };
|
|
3235
|
-
const at = Date.parse(text);
|
|
3236
|
-
if (Number.isNaN(at)) {
|
|
3237
|
-
return {
|
|
3238
|
-
refusal: `${label} holds a date, and \u201C${raw}\u201D is not one this can read. Paste it as 2026-09-21, or as your spreadsheet's own date format.`
|
|
3239
|
-
};
|
|
3240
|
-
}
|
|
3241
|
-
const parsed = new Date(at);
|
|
3242
|
-
if (wantsTime) return { value: parsed.toISOString() };
|
|
3243
|
-
const y = parsed.getFullYear();
|
|
3244
|
-
const m = String(parsed.getMonth() + 1).padStart(2, "0");
|
|
3245
|
-
const d = String(parsed.getDate()).padStart(2, "0");
|
|
3246
|
-
return { value: `${y}-${m}-${d}` };
|
|
3247
|
-
}
|
|
3248
|
-
case "json": {
|
|
3249
|
-
try {
|
|
3250
|
-
return { value: JSON.parse(text) };
|
|
3251
|
-
} catch {
|
|
3252
|
-
return {
|
|
3253
|
-
refusal: `${label} holds a document, and \u201C${raw}\u201D is not valid JSON. Paste the whole object, braces and all.`
|
|
3254
|
-
};
|
|
3255
|
-
}
|
|
3256
|
-
}
|
|
3257
|
-
// A LIST, A PERSON, A FILE OR ANOTHER RECORD IS AN IDENTITY, NOT A WORD.
|
|
3258
|
-
// The store keeps the id of the option or the record, and a word off a
|
|
3259
|
-
// clipboard is not that id — writing the word would put a string where an
|
|
3260
|
-
// id belongs and every filter, colour and rollup built on that column would
|
|
3261
|
-
// stop agreeing with it. So the cell is left alone and the person is told
|
|
3262
|
-
// which control does hold it.
|
|
3263
|
-
case "select":
|
|
3264
|
-
case "multi_select": {
|
|
3265
|
-
if (!options) {
|
|
3266
|
-
return {
|
|
3267
|
-
refusal: `${label} is a list and its options have not been read yet, so nothing can be matched to \u201C${raw}\u201D. Reopen the paste once the column's choices have loaded.`
|
|
3268
|
-
};
|
|
3269
|
-
}
|
|
3270
|
-
const wanted = kind === "multi_select" ? text.split(/[,;]/).map((w) => w.trim()).filter((w) => w.length > 0) : [text];
|
|
3271
|
-
if (wanted.length === 0) return { value: kind === "multi_select" ? [] : null };
|
|
3272
|
-
const matched = [];
|
|
3273
|
-
const unmatched = [];
|
|
3274
|
-
for (const word of wanted) {
|
|
3275
|
-
const hit = options.byWord.get(word.toLowerCase());
|
|
3276
|
-
if (hit === void 0) unmatched.push(word);
|
|
3277
|
-
else matched.push(hit);
|
|
3278
|
-
}
|
|
3279
|
-
if (unmatched.length > 0) {
|
|
3280
|
-
const offered = options.labels.length > 0 ? options.labels.join(", ") : "it has none yet";
|
|
3281
|
-
return {
|
|
3282
|
-
refusal: `${label} does not have ${unmatched.map((w) => `\u201C${w}\u201D`).join(" or ")} among its options. It offers: ${offered}.`,
|
|
3283
|
-
unmatched
|
|
3284
|
-
};
|
|
3285
|
-
}
|
|
3286
|
-
return { value: kind === "multi_select" ? matched : matched[0] };
|
|
3287
|
-
}
|
|
3288
|
-
case "member":
|
|
3289
|
-
return {
|
|
3290
|
-
refusal: `${label} names a person in this organization, so it is chosen rather than typed. Open the cell and pick them.`
|
|
3291
|
-
};
|
|
3292
|
-
case "attachment":
|
|
3293
|
-
return {
|
|
3294
|
-
refusal: `${label} holds a file, which cannot arrive on a clipboard. Open the cell and attach it.`
|
|
3295
|
-
};
|
|
3296
|
-
case "relation":
|
|
3297
|
-
return {
|
|
3298
|
-
refusal: `${label} points at another record, so it is chosen rather than typed. Open the cell and pick the record.`
|
|
3299
|
-
};
|
|
3300
|
-
case "formula":
|
|
3301
|
-
case "lookup":
|
|
3302
|
-
case "rollup":
|
|
3303
|
-
return {
|
|
3304
|
-
refusal: `${label} is worked out by the system, so nothing can be pasted into it.`
|
|
3305
|
-
};
|
|
3306
|
-
default:
|
|
3307
|
-
return { value: raw };
|
|
3308
|
-
}
|
|
3347
|
+
return (0, import_records5.coerceTypedAnswer)(field, raw, {
|
|
3348
|
+
label: fieldName(field),
|
|
3349
|
+
...options ? { options } : {}
|
|
3350
|
+
});
|
|
3309
3351
|
}
|
|
3310
3352
|
function planPastedBlock(args) {
|
|
3311
3353
|
const byKey = new Map(args.fields.map((f) => [f.key, f]));
|
|
@@ -4475,19 +4517,23 @@ var import_react27 = require("react");
|
|
|
4475
4517
|
var import_react28 = require("@ai-matrx/records/react");
|
|
4476
4518
|
var import_design_system13 = require("@ai-matrx/design-system");
|
|
4477
4519
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
4478
|
-
var
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4520
|
+
var ADDABLE_TYPE_IDS = [
|
|
4521
|
+
"text",
|
|
4522
|
+
"long_text",
|
|
4523
|
+
"number",
|
|
4524
|
+
"currency",
|
|
4525
|
+
"percent",
|
|
4526
|
+
"datetime",
|
|
4527
|
+
"email",
|
|
4528
|
+
"phone",
|
|
4529
|
+
"url",
|
|
4530
|
+
"member",
|
|
4531
|
+
"attachment"
|
|
4490
4532
|
];
|
|
4533
|
+
var ADDABLE_TYPES = ADDABLE_TYPE_IDS.map((value) => ({
|
|
4534
|
+
value,
|
|
4535
|
+
label: fieldTypeChoice(value)?.label ?? value
|
|
4536
|
+
}));
|
|
4491
4537
|
function asField(f) {
|
|
4492
4538
|
return {
|
|
4493
4539
|
id: f.id,
|
|
@@ -4651,41 +4697,20 @@ function CustomFieldsSection({
|
|
|
4651
4697
|
var import_react29 = require("react");
|
|
4652
4698
|
var import_react30 = require("@ai-matrx/records/react");
|
|
4653
4699
|
var import_design_system14 = require("@ai-matrx/design-system");
|
|
4700
|
+
var import_records6 = require("@ai-matrx/records");
|
|
4654
4701
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
4655
|
-
var RULE_KINDS = [
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
{ value: "restricted", label: "Owners and administrators only" }
|
|
4668
|
-
];
|
|
4669
|
-
var AGENT_VISIBILITY = [
|
|
4670
|
-
{ value: "include", label: "Yes, an assistant can read it" },
|
|
4671
|
-
{ value: "summarize", label: "Only a summary of it" },
|
|
4672
|
-
{ value: "on_request", label: "Only when somebody asks for it" },
|
|
4673
|
-
{ value: "exclude", label: "No, keep it away from assistants" }
|
|
4674
|
-
];
|
|
4675
|
-
var ROLLUP_OPERATIONS = [
|
|
4676
|
-
{ value: "count", label: "How many there are" },
|
|
4677
|
-
{ value: "sum", label: "The total of" },
|
|
4678
|
-
{ value: "avg", label: "The average of" },
|
|
4679
|
-
{ value: "min", label: "The smallest" },
|
|
4680
|
-
{ value: "max", label: "The largest" }
|
|
4681
|
-
];
|
|
4682
|
-
var FORMULA_OPERATIONS = [
|
|
4683
|
-
{ value: "concat", label: "Join them together", joins: true },
|
|
4684
|
-
{ value: "add", label: "Add them up", joins: true },
|
|
4685
|
-
{ value: "sub", label: "Take the second from the first", joins: false },
|
|
4686
|
-
{ value: "mul", label: "Multiply them", joins: true },
|
|
4687
|
-
{ value: "div", label: "Divide the first by the second", joins: false }
|
|
4688
|
-
];
|
|
4702
|
+
var RULE_KINDS = FIELD_RULE_KIND_VALUES.map((kind) => ({ kind, label: FIELD_RULE_KIND_LABEL[kind] }));
|
|
4703
|
+
var SENSITIVITIES = import_records6.FIELD_SENSITIVITIES.map((value) => ({ value, label: FIELD_SENSITIVITY_LABEL[value] }));
|
|
4704
|
+
var AGENT_VISIBILITY = import_records6.CONTEXT_POLICIES.map((value) => ({ value, label: CONTEXT_POLICY_LABEL[value] }));
|
|
4705
|
+
var ROLLUP_OPERATIONS = ROLLUP_AGG_VALUES.map((value) => ({
|
|
4706
|
+
value,
|
|
4707
|
+
label: ROLLUP_AGG_LABEL[value]
|
|
4708
|
+
}));
|
|
4709
|
+
var FORMULA_OPERATIONS = FORMULA_OP_VALUES.map((value) => ({
|
|
4710
|
+
value,
|
|
4711
|
+
label: FORMULA_OP_LABEL[value],
|
|
4712
|
+
joins: FORMULA_OP_JOINS[value]
|
|
4713
|
+
}));
|
|
4689
4714
|
function keyFor(label) {
|
|
4690
4715
|
const token = label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 48);
|
|
4691
4716
|
if (token === "") return "";
|
|
@@ -5250,6 +5275,9 @@ var CONDITION_OPS = [
|
|
|
5250
5275
|
{ op: "gt", label: "is more than" },
|
|
5251
5276
|
{ op: "lt", label: "is less than" }
|
|
5252
5277
|
];
|
|
5278
|
+
function pointsAtARecord(f) {
|
|
5279
|
+
return !!f?.field && f.field.type === "relation" && !!f.field.relation_target;
|
|
5280
|
+
}
|
|
5253
5281
|
function conditionIsSimple(expr) {
|
|
5254
5282
|
if (expr === null || expr === void 0) return true;
|
|
5255
5283
|
const node = expr;
|
|
@@ -5320,6 +5348,7 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5320
5348
|
const op = node?.op ?? "";
|
|
5321
5349
|
const onField = node?.args?.[0]?.field ?? "";
|
|
5322
5350
|
const value = node?.args?.[1]?.const ?? "";
|
|
5351
|
+
const chosen = fields.find((f) => f.id === onField);
|
|
5323
5352
|
function write(next) {
|
|
5324
5353
|
const chosenOp = next.op ?? op;
|
|
5325
5354
|
const chosenField = next.field ?? onField;
|
|
@@ -5358,7 +5387,24 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5358
5387
|
children: CONDITION_OPS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: c.op, children: c.label }, c.op))
|
|
5359
5388
|
}
|
|
5360
5389
|
),
|
|
5361
|
-
op
|
|
5390
|
+
op === "present" ? null : pointsAtARecord(chosen) ? (
|
|
5391
|
+
// A RECORD IS PICKED BY ITS WORDS, NEVER TYPED AS AN ID. The same picker the
|
|
5392
|
+
// cell uses, on the same `relation_words_many` the cell reads, so a filter and
|
|
5393
|
+
// the column it filters can never disagree about what a record is called. It
|
|
5394
|
+
// is asked for ONE record even when the column holds several: a clause compares
|
|
5395
|
+
// against one value, and a picker that let somebody choose three would be
|
|
5396
|
+
// writing an expression this row cannot draw back.
|
|
5397
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5398
|
+
RelationPicker,
|
|
5399
|
+
{
|
|
5400
|
+
field: { ...chosen.field, multi: false },
|
|
5401
|
+
value: value === "" ? null : value,
|
|
5402
|
+
onChange: (next) => write({ value: next === null || next === void 0 ? "" : String(next) }),
|
|
5403
|
+
id: "condition-value-record",
|
|
5404
|
+
className: "min-w-[8rem]"
|
|
5405
|
+
}
|
|
5406
|
+
)
|
|
5407
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5362
5408
|
import_design_system15.BasicInput,
|
|
5363
5409
|
{
|
|
5364
5410
|
className: "h-8 w-28 text-xs",
|
|
@@ -5366,7 +5412,7 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5366
5412
|
value: String(value ?? ""),
|
|
5367
5413
|
onChange: (e) => write({ value: conditionValue(e.target.value) })
|
|
5368
5414
|
}
|
|
5369
|
-
)
|
|
5415
|
+
)
|
|
5370
5416
|
] }) : null
|
|
5371
5417
|
] });
|
|
5372
5418
|
}
|
|
@@ -5526,10 +5572,10 @@ var NO_RULES_YET = "Nothing has to be true to get here yet.";
|
|
|
5526
5572
|
var NEEDS_A_SENTENCE = "Write the sentence somebody reads when this stops them, the way you would say it out loud.";
|
|
5527
5573
|
var NOT_DRAWN_HERE = "This part of the rule is not something this builder can draw yet";
|
|
5528
5574
|
var ON_FAIL = [
|
|
5529
|
-
{ value: "refuse", label:
|
|
5575
|
+
{ value: "refuse", label: STAGE_RULE_ON_FAIL_LABEL.refuse, note: "The card stays where it is and the person is told why." },
|
|
5530
5576
|
{
|
|
5531
5577
|
value: "require_approval",
|
|
5532
|
-
label:
|
|
5578
|
+
label: STAGE_RULE_ON_FAIL_LABEL.require_approval,
|
|
5533
5579
|
note: "The card stays where it is, and the people who can approve it are asked. Nobody is turned away."
|
|
5534
5580
|
}
|
|
5535
5581
|
];
|
|
@@ -5580,7 +5626,7 @@ function StageRulesSection({ tableId, stage, className }) {
|
|
|
5580
5626
|
const stageLabel = stages.find((s) => s.key === stageKey)?.label ?? stageKey ?? "";
|
|
5581
5627
|
const gates = (0, import_react32.useMemo)(() => stageKey ? gatesOf(pipeline, stageKey) : [], [pipeline, stageKey]);
|
|
5582
5628
|
const conditionFields = (0, import_react32.useMemo)(
|
|
5583
|
-
() => (fields.data ?? []).map((f) => ({ id: String(f.id), key: f.key, label: fieldName(f) })),
|
|
5629
|
+
() => (fields.data ?? []).map((f) => ({ id: String(f.id), key: f.key, label: fieldName(f), field: f })),
|
|
5584
5630
|
[fields.data]
|
|
5585
5631
|
);
|
|
5586
5632
|
(0, import_react32.useEffect)(() => {
|
|
@@ -6005,7 +6051,7 @@ var import_react36 = require("react");
|
|
|
6005
6051
|
var import_react37 = require("@ai-matrx/records/react");
|
|
6006
6052
|
var import_design_system18 = require("@ai-matrx/design-system");
|
|
6007
6053
|
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
6008
|
-
var NO_CHAT_REASON = "
|
|
6054
|
+
var NO_CHAT_REASON = "Chatting about this record is not switched on here yet, so this panel is empty rather than showing a box that goes nowhere. It is on our list, not yours.";
|
|
6009
6055
|
function RecordChat({ tableId, recordId, className }) {
|
|
6010
6056
|
const client = (0, import_react37.useRecordsClient)();
|
|
6011
6057
|
const host = useRecordsUi();
|
|
@@ -7268,7 +7314,7 @@ function offerableFields(all, want) {
|
|
|
7268
7314
|
return all.filter((field) => {
|
|
7269
7315
|
const kind = editorKindFor(field);
|
|
7270
7316
|
if (want === "choice") return kind === "select" || kind === "multi_select";
|
|
7271
|
-
return kind === "datetime"
|
|
7317
|
+
return kind === "datetime";
|
|
7272
7318
|
});
|
|
7273
7319
|
}
|
|
7274
7320
|
function useStageField(tableId) {
|
|
@@ -7686,11 +7732,7 @@ var import_react54 = require("react");
|
|
|
7686
7732
|
var import_react55 = require("@ai-matrx/records/react");
|
|
7687
7733
|
var import_design_system25 = require("@ai-matrx/design-system");
|
|
7688
7734
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
7689
|
-
var ACT_WORD =
|
|
7690
|
-
add: "Add",
|
|
7691
|
-
update: "Update",
|
|
7692
|
-
remove: "Remove"
|
|
7693
|
-
};
|
|
7735
|
+
var ACT_WORD = PROPOSED_CHANGE_ACT_LABEL;
|
|
7694
7736
|
function ProposalRow({
|
|
7695
7737
|
change,
|
|
7696
7738
|
outcome,
|
|
@@ -7797,21 +7839,8 @@ var import_react56 = require("react");
|
|
|
7797
7839
|
var import_react57 = require("@ai-matrx/records/react");
|
|
7798
7840
|
var import_design_system26 = require("@ai-matrx/design-system");
|
|
7799
7841
|
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
7800
|
-
var DUE_WORD =
|
|
7801
|
-
|
|
7802
|
-
due_today: "Today",
|
|
7803
|
-
scheduled: "Scheduled",
|
|
7804
|
-
undated: "No date",
|
|
7805
|
-
finished: "Done"
|
|
7806
|
-
};
|
|
7807
|
-
var REQUIRES_WORD = {
|
|
7808
|
-
none: "",
|
|
7809
|
-
note: "Say what you did",
|
|
7810
|
-
answer: "Fill this in",
|
|
7811
|
-
record_field: "This has to be filled in on the record",
|
|
7812
|
-
form: "This step is a form",
|
|
7813
|
-
document: "This step is a document"
|
|
7814
|
-
};
|
|
7842
|
+
var DUE_WORD = DUE_STATE_LABEL;
|
|
7843
|
+
var REQUIRES_WORD = CHECKLIST_REQUIREMENT_LABEL;
|
|
7815
7844
|
var NO_TEMPLATES = "No checklist has been written for this table yet. Ask an agent for one in a sentence \u2014 the steps, who each belongs to, and how many days in each is due \u2014 and it is stored whole, judged before a single step is written.";
|
|
7816
7845
|
function ChecklistRunner({
|
|
7817
7846
|
tableId,
|
|
@@ -8519,18 +8548,8 @@ function roleKey(said) {
|
|
|
8519
8548
|
// src/ActionInbox.tsx
|
|
8520
8549
|
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
8521
8550
|
var ACTION_KINDS = ["approval", "assignment", "proposal"];
|
|
8522
|
-
var KIND_WORD =
|
|
8523
|
-
|
|
8524
|
-
approval: "Approve",
|
|
8525
|
-
proposal: "Agent"
|
|
8526
|
-
};
|
|
8527
|
-
var DUE_WORD2 = {
|
|
8528
|
-
overdue: "Overdue",
|
|
8529
|
-
due_today: "Today",
|
|
8530
|
-
scheduled: "Scheduled",
|
|
8531
|
-
undated: "No date",
|
|
8532
|
-
finished: "Finished"
|
|
8533
|
-
};
|
|
8551
|
+
var KIND_WORD = WORK_INBOX_KIND_LABEL;
|
|
8552
|
+
var DUE_WORD2 = DUE_STATE_LABEL;
|
|
8534
8553
|
function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className }) {
|
|
8535
8554
|
const client = (0, import_react59.useRecordsClient)();
|
|
8536
8555
|
const [items, setItems] = (0, import_react58.useState)(null);
|
|
@@ -9751,7 +9770,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
|
|
|
9751
9770
|
// src/PortalsPanel.tsx
|
|
9752
9771
|
var import_react68 = require("react");
|
|
9753
9772
|
var import_react69 = require("@ai-matrx/records/react");
|
|
9754
|
-
var
|
|
9773
|
+
var import_records7 = require("@ai-matrx/records");
|
|
9755
9774
|
var import_design_system33 = require("@ai-matrx/design-system");
|
|
9756
9775
|
|
|
9757
9776
|
// src/BuildOrAsk.tsx
|
|
@@ -9768,18 +9787,18 @@ function BuildOrAsk({
|
|
|
9768
9787
|
buildLabel,
|
|
9769
9788
|
className
|
|
9770
9789
|
}) {
|
|
9790
|
+
const mayAsk = mayBuild && Boolean(onAsk);
|
|
9771
9791
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: (0, import_design_system32.cn)("rounded-md border border-dashed p-3", className), children: [
|
|
9772
9792
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-xs text-muted-foreground", children }),
|
|
9773
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "mt-2.5 flex flex-wrap items-center gap-2", children: [
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
] }),
|
|
9777
|
-
|
|
9793
|
+
mayBuild ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "mt-2.5 flex flex-wrap items-center gap-2", children: [
|
|
9794
|
+
mayAsk ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_design_system32.Button, { size: "sm", variant: "outline", onClick: onAsk, children: "Ask an agent" }) : null,
|
|
9795
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_design_system32.Button, { size: "sm", onClick: onBuild, children: buildLabel })
|
|
9796
|
+
] }) : null,
|
|
9797
|
+
!mayBuild ? whyNot ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "mt-2 text-xs text-muted-foreground", children: whyNot }) : null : mayAsk ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { className: "mt-2 text-xs text-muted-foreground", children: [
|
|
9778
9798
|
"You would say something like: \u201C",
|
|
9779
9799
|
suggestion,
|
|
9780
9800
|
"\u201D"
|
|
9781
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "mt-2 text-xs text-muted-foreground", children: NO_AGENT_PORT })
|
|
9782
|
-
!mayBuild && whyNot ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "mt-2 text-xs text-muted-foreground", children: whyNot }) : null
|
|
9801
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "mt-2 text-xs text-muted-foreground", children: NO_AGENT_PORT })
|
|
9783
9802
|
] });
|
|
9784
9803
|
}
|
|
9785
9804
|
|
|
@@ -9818,30 +9837,41 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9818
9837
|
const client = (0, import_react69.useRecordsClient)();
|
|
9819
9838
|
const host = useRecordsUi();
|
|
9820
9839
|
const [portals, setPortals] = (0, import_react68.useState)(null);
|
|
9840
|
+
const [exposures, setExposures] = (0, import_react68.useState)([]);
|
|
9821
9841
|
const [listError, setListError] = (0, import_react68.useState)(null);
|
|
9822
9842
|
const [openId, setOpenId] = (0, import_react68.useState)(null);
|
|
9823
9843
|
const [building, setBuilding] = (0, import_react68.useState)(false);
|
|
9844
|
+
const [adding, setAdding] = (0, import_react68.useState)(null);
|
|
9824
9845
|
const load = (0, import_react68.useCallback)(async () => {
|
|
9825
|
-
const answered = await client.portals();
|
|
9846
|
+
const [answered, mapped] = await Promise.all([client.portals(), client.portalTables()]);
|
|
9826
9847
|
if (!answered.ok) {
|
|
9827
9848
|
setListError(answered.error);
|
|
9828
9849
|
setPortals([]);
|
|
9850
|
+
setExposures([]);
|
|
9829
9851
|
return;
|
|
9830
9852
|
}
|
|
9831
9853
|
setListError(null);
|
|
9832
9854
|
setPortals(answered.data);
|
|
9855
|
+
setExposures(mapped.ok ? mapped.data : []);
|
|
9833
9856
|
}, [client]);
|
|
9834
9857
|
(0, import_react68.useEffect)(() => {
|
|
9835
9858
|
void load();
|
|
9836
9859
|
}, [load]);
|
|
9837
9860
|
if (portals === null) return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Skeleton, { className: (0, import_design_system33.cn)("h-32 w-full", className) });
|
|
9861
|
+
const onATable = Boolean(tableId);
|
|
9862
|
+
const holdingThis = new Set(
|
|
9863
|
+
exposures.filter((e) => e.table_id === tableId).map((e) => e.portal_id)
|
|
9864
|
+
);
|
|
9865
|
+
const shown = onATable ? portals.filter((p) => holdingThis.has(p.portal_id)) : portals;
|
|
9866
|
+
const couldTakeIt = onATable ? portals.filter((p) => !holdingThis.has(p.portal_id)) : [];
|
|
9867
|
+
const elsewhere = couldTakeIt.length;
|
|
9838
9868
|
const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
9839
9869
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { className: (0, import_design_system33.cn)("flex flex-col gap-3", className), children: [
|
|
9840
9870
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("header", { className: "flex items-center gap-2", children: [
|
|
9841
9871
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h3", { className: "text-sm font-medium", children: "Portals" }),
|
|
9842
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-xs text-muted-foreground", children:
|
|
9872
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-xs text-muted-foreground", children: shown.length === 0 ? "none for this table" : `${shown.length}` }),
|
|
9843
9873
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex-1" }),
|
|
9844
|
-
|
|
9874
|
+
shown.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Button, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a portal" }) : null
|
|
9845
9875
|
] }),
|
|
9846
9876
|
listError ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(RefusalNotice, { error: listError }) : null,
|
|
9847
9877
|
building ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
@@ -9854,7 +9884,19 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9854
9884
|
onClose: () => setBuilding(false)
|
|
9855
9885
|
}
|
|
9856
9886
|
) : null,
|
|
9857
|
-
|
|
9887
|
+
adding ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9888
|
+
PortalBuilder,
|
|
9889
|
+
{
|
|
9890
|
+
...tableId ? { tableId } : {},
|
|
9891
|
+
portalId: adding,
|
|
9892
|
+
onSaved: () => {
|
|
9893
|
+
setAdding(null);
|
|
9894
|
+
void load();
|
|
9895
|
+
},
|
|
9896
|
+
onClose: () => setAdding(null)
|
|
9897
|
+
}
|
|
9898
|
+
) : null,
|
|
9899
|
+
shown.length === 0 && !listError && !building && !adding ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
9858
9900
|
BuildOrAsk,
|
|
9859
9901
|
{
|
|
9860
9902
|
mayBuild: true,
|
|
@@ -9863,12 +9905,38 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9863
9905
|
onAsk: () => host.onAskForOne?.({ kind: "portal", tableId, suggestion: PORTAL_SUGGESTION })
|
|
9864
9906
|
} : {},
|
|
9865
9907
|
suggestion: PORTAL_SUGGESTION,
|
|
9866
|
-
buildLabel: "Build the portal",
|
|
9867
|
-
children:
|
|
9908
|
+
buildLabel: elsewhere > 0 ? "Build a new portal" : "Build the portal",
|
|
9909
|
+
children: [
|
|
9910
|
+
"A portal is how one of your clients signs in and sees the records that name them \u2014 their jobs, their invoices \u2014 and nothing else.",
|
|
9911
|
+
elsewhere > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
9912
|
+
" ",
|
|
9913
|
+
"This table is not in",
|
|
9914
|
+
" ",
|
|
9915
|
+
elsewhere === 1 ? "the portal this organization already has" : `any of the ${elsewhere} portals this organization already has`,
|
|
9916
|
+
"."
|
|
9917
|
+
] }) : null
|
|
9918
|
+
]
|
|
9868
9919
|
}
|
|
9869
9920
|
) : null,
|
|
9870
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.
|
|
9871
|
-
|
|
9921
|
+
shown.length === 0 && !listError && !building && !adding && elsewhere > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-1.5 rounded-md border border-dashed border-border p-2.5", children: [
|
|
9922
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "text-xs text-muted-foreground", children: "Or add this table to a portal your clients already sign in to:" }),
|
|
9923
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex flex-wrap gap-1.5", children: couldTakeIt.map((portal) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
9924
|
+
import_design_system33.Button,
|
|
9925
|
+
{
|
|
9926
|
+
size: "sm",
|
|
9927
|
+
variant: "outline",
|
|
9928
|
+
onClick: () => setAdding(portal.portal_id),
|
|
9929
|
+
children: [
|
|
9930
|
+
"Add to \u201C",
|
|
9931
|
+
portal.title,
|
|
9932
|
+
"\u201D"
|
|
9933
|
+
]
|
|
9934
|
+
},
|
|
9935
|
+
portal.portal_id
|
|
9936
|
+
)) })
|
|
9937
|
+
] }) : null,
|
|
9938
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { className: "flex flex-col gap-2", children: shown.map((portal) => {
|
|
9939
|
+
const url = `${origin}${(0, import_records7.portalPath)(portal.slug)}`;
|
|
9872
9940
|
const open = openId === portal.portal_id;
|
|
9873
9941
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { className: "rounded-md border border-border bg-card p-2.5", children: [
|
|
9874
9942
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
@@ -10594,11 +10662,7 @@ function whenItFires(subscription) {
|
|
|
10594
10662
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
10595
10663
|
return subscription.schedule ? `${every}, ${subscription.schedule}` : every;
|
|
10596
10664
|
}
|
|
10597
|
-
var CHANNEL_WORDS2 =
|
|
10598
|
-
in_app: "in the app",
|
|
10599
|
-
email: "by email \u2014 only if an address is on the account",
|
|
10600
|
-
sms: "by text \u2014 only if a number is on the account"
|
|
10601
|
-
};
|
|
10665
|
+
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
10602
10666
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
10603
10667
|
function SubscriptionsPanel({ tableId, className }) {
|
|
10604
10668
|
const client = (0, import_react73.useRecordsClient)();
|
|
@@ -10772,7 +10836,7 @@ function SubscriptionsPanel({ tableId, className }) {
|
|
|
10772
10836
|
// src/FormBuilder.tsx
|
|
10773
10837
|
var import_react76 = require("react");
|
|
10774
10838
|
var import_react77 = require("@ai-matrx/records/react");
|
|
10775
|
-
var
|
|
10839
|
+
var import_records8 = require("@ai-matrx/records");
|
|
10776
10840
|
var import_design_system37 = require("@ai-matrx/design-system");
|
|
10777
10841
|
|
|
10778
10842
|
// src/FormRunner.tsx
|
|
@@ -11247,7 +11311,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
11247
11311
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
11248
11312
|
PublishRow,
|
|
11249
11313
|
{
|
|
11250
|
-
url: `${publicOrigin}${(0,
|
|
11314
|
+
url: `${publicOrigin}${(0, import_records8.publicFormPath)(draft.id)}`,
|
|
11251
11315
|
state: draft.state,
|
|
11252
11316
|
mayPublish: rights.structure,
|
|
11253
11317
|
why: rights.why("structure"),
|
|
@@ -11366,7 +11430,12 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
11366
11430
|
Condition,
|
|
11367
11431
|
{
|
|
11368
11432
|
question,
|
|
11369
|
-
fieldKeys: fields.data ?? []
|
|
11433
|
+
fieldKeys: (fields.data ?? []).map((f) => ({
|
|
11434
|
+
id: String(f.id),
|
|
11435
|
+
key: f.key,
|
|
11436
|
+
label: f.label || f.key,
|
|
11437
|
+
field: f
|
|
11438
|
+
})),
|
|
11370
11439
|
onChange: (showIf) => patchQuestion(index, { showIf })
|
|
11371
11440
|
}
|
|
11372
11441
|
)
|
|
@@ -12099,17 +12168,8 @@ var import_react86 = require("react");
|
|
|
12099
12168
|
var import_react87 = require("@ai-matrx/records/react");
|
|
12100
12169
|
var import_design_system42 = require("@ai-matrx/design-system");
|
|
12101
12170
|
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
12102
|
-
var CADENCE_WORDS2 =
|
|
12103
|
-
|
|
12104
|
-
hourly: "hourly summary",
|
|
12105
|
-
daily: "daily summary",
|
|
12106
|
-
weekly: "weekly summary"
|
|
12107
|
-
};
|
|
12108
|
-
var CHANNEL_WORDS3 = {
|
|
12109
|
-
in_app: "in the app",
|
|
12110
|
-
email: "by email",
|
|
12111
|
-
sms: "by text"
|
|
12112
|
-
};
|
|
12171
|
+
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
12172
|
+
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
12113
12173
|
function NotifyRuleEditor({ tableId, seed, className }) {
|
|
12114
12174
|
const client = (0, import_react87.useRecordsClient)();
|
|
12115
12175
|
const host = useRecordsUi();
|
|
@@ -12313,7 +12373,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
|
|
|
12313
12373
|
// src/ChartBlock.tsx
|
|
12314
12374
|
var import_react88 = require("react");
|
|
12315
12375
|
var import_recharts = require("recharts");
|
|
12316
|
-
var
|
|
12376
|
+
var import_records9 = require("@ai-matrx/records");
|
|
12317
12377
|
var import_core6 = require("@ai-matrx/records/core");
|
|
12318
12378
|
var import_design_system43 = require("@ai-matrx/design-system");
|
|
12319
12379
|
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
@@ -12334,7 +12394,7 @@ function ChartBlock({ block, subject, className }) {
|
|
|
12334
12394
|
const kind = block.kind;
|
|
12335
12395
|
const tableId = block.table_id ?? subject;
|
|
12336
12396
|
const measures = block.measures && block.measures.length > 0 ? block.measures : [{ op: "count" }];
|
|
12337
|
-
const primary = (0,
|
|
12397
|
+
const primary = (0, import_records9.measureKey)(measures[0]);
|
|
12338
12398
|
const series = [primary];
|
|
12339
12399
|
const points = (0, import_react88.useMemo)(() => {
|
|
12340
12400
|
if (kind === "stuck") return [];
|
|
@@ -12868,7 +12928,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
|
|
|
12868
12928
|
// src/FormsPanel.tsx
|
|
12869
12929
|
var import_react91 = require("react");
|
|
12870
12930
|
var import_react92 = require("@ai-matrx/records/react");
|
|
12871
|
-
var
|
|
12931
|
+
var import_records10 = require("@ai-matrx/records");
|
|
12872
12932
|
var import_design_system45 = require("@ai-matrx/design-system");
|
|
12873
12933
|
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
12874
12934
|
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.";
|
|
@@ -12963,7 +13023,7 @@ function FormsPanel({ tableId, className }) {
|
|
|
12963
13023
|
}
|
|
12964
13024
|
) : null,
|
|
12965
13025
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
|
|
12966
|
-
const url = `${origin}${(0,
|
|
13026
|
+
const url = `${origin}${(0, import_records10.publicFormPath)(form.form_id)}`;
|
|
12967
13027
|
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("li", { className: "rounded-md border p-2.5", children: [
|
|
12968
13028
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
12969
13029
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: form.title ?? form.slug }),
|
|
@@ -13015,7 +13075,7 @@ function FormsPanel({ tableId, className }) {
|
|
|
13015
13075
|
// src/BookingBuilder.tsx
|
|
13016
13076
|
var import_react93 = require("react");
|
|
13017
13077
|
var import_react94 = require("@ai-matrx/records/react");
|
|
13018
|
-
var
|
|
13078
|
+
var import_records11 = require("@ai-matrx/records");
|
|
13019
13079
|
var import_design_system46 = require("@ai-matrx/design-system");
|
|
13020
13080
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
13021
13081
|
var STORE_ANSWERS_THESE = ["slot", "status", "booked_with"];
|
|
@@ -13158,7 +13218,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
13158
13218
|
if (!rights.structure) {
|
|
13159
13219
|
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: (0, import_design_system46.cn)("text-xs text-muted-foreground", className), children: rights.why("structure") ?? "Making a booking page needs the admin level on this table, because it lets people with no account take time in your calendar." });
|
|
13160
13220
|
}
|
|
13161
|
-
const url = formId ? `${publicOrigin}${(0,
|
|
13221
|
+
const url = formId ? `${publicOrigin}${(0, import_records11.bookingPath)(formId)}` : null;
|
|
13162
13222
|
const shown = offer ?? null;
|
|
13163
13223
|
return (
|
|
13164
13224
|
// THE RAIL IS 384px WIDE AND THE VIEWPORT IS NOT (walk 1, 2026-09-21):
|
|
@@ -13360,7 +13420,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
|
|
|
13360
13420
|
// src/BookingSlots.tsx
|
|
13361
13421
|
var import_react95 = require("react");
|
|
13362
13422
|
var import_react96 = require("@ai-matrx/records/react");
|
|
13363
|
-
var
|
|
13423
|
+
var import_records12 = require("@ai-matrx/records");
|
|
13364
13424
|
var import_design_system47 = require("@ai-matrx/design-system");
|
|
13365
13425
|
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
13366
13426
|
var WHAT_A_BOOKING_PAGE_IS = "A booking page offers times you are free and writes each appointment into this table as an ordinary record.";
|
|
@@ -13369,12 +13429,7 @@ function bookingSuggestion(tableName2) {
|
|
|
13369
13429
|
return `Let clients book a 30-minute ${subject} slot on Tuesday and Thursday afternoons.`;
|
|
13370
13430
|
}
|
|
13371
13431
|
var NO_ADMIN2 = "Opening a booking page lets people with no account take time in your calendar, so it needs the admin level on the table the appointments land in.";
|
|
13372
|
-
var STATE_WORDS =
|
|
13373
|
-
draft: "Not open yet \u2014 nobody can book until you open it.",
|
|
13374
|
-
open: "Open. Anyone with the link can book a time.",
|
|
13375
|
-
closed: "Closed. The link still works and says you are not taking bookings.",
|
|
13376
|
-
full: "Every time you offered is taken."
|
|
13377
|
-
};
|
|
13432
|
+
var STATE_WORDS = BOOKING_PAGE_STATE_LABEL;
|
|
13378
13433
|
function BookingSlots({ tableId, className }) {
|
|
13379
13434
|
const client = (0, import_react96.useRecordsClient)();
|
|
13380
13435
|
const host = useRecordsUi();
|
|
@@ -13398,10 +13453,21 @@ function BookingSlots({ tableId, className }) {
|
|
|
13398
13453
|
void load();
|
|
13399
13454
|
}, [load]);
|
|
13400
13455
|
const subjectIds = (0, import_react95.useMemo)(
|
|
13401
|
-
() => Array.from(
|
|
13402
|
-
|
|
13456
|
+
() => Array.from(
|
|
13457
|
+
/* @__PURE__ */ new Set([
|
|
13458
|
+
...(pages ?? []).map((p) => p.table_id),
|
|
13459
|
+
// THE EMPTY STATE ASKS THE SAME QUESTION. There is no page to read a
|
|
13460
|
+
// table off yet, and the empty state's two buttons need the answer
|
|
13461
|
+
// about the table this rail is standing in — otherwise the rail
|
|
13462
|
+
// offers a viewer both ways into an act the door will refuse.
|
|
13463
|
+
...tableId ? [tableId] : []
|
|
13464
|
+
])
|
|
13465
|
+
),
|
|
13466
|
+
[pages, tableId]
|
|
13403
13467
|
);
|
|
13404
13468
|
const levels = (0, import_react96.useMyLevels)(subjectIds);
|
|
13469
|
+
const levelsKnown = !levels.loading && Boolean(levels.data);
|
|
13470
|
+
const mayBuildHere = levelsKnown && Boolean(tableId) && levels.data?.[tableId] === "admin";
|
|
13405
13471
|
const toggle = (0, import_react95.useCallback)(
|
|
13406
13472
|
async (page) => {
|
|
13407
13473
|
setBusy(page.form_id);
|
|
@@ -13450,7 +13516,8 @@ function BookingSlots({ tableId, className }) {
|
|
|
13450
13516
|
pages.length === 0 && !building ? tableId ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
13451
13517
|
BuildOrAsk,
|
|
13452
13518
|
{
|
|
13453
|
-
mayBuild:
|
|
13519
|
+
mayBuild: mayBuildHere,
|
|
13520
|
+
whyNot: mayBuildHere ? null : levelsKnown ? NO_ADMIN2 : null,
|
|
13454
13521
|
onBuild: () => setBuilding(true),
|
|
13455
13522
|
...host.onAskForOne ? {
|
|
13456
13523
|
onAsk: () => host.onAskForOne?.({
|
|
@@ -13468,7 +13535,7 @@ function BookingSlots({ tableId, className }) {
|
|
|
13468
13535
|
" Open the table the appointments should land in to make one."
|
|
13469
13536
|
] }) : null,
|
|
13470
13537
|
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("ul", { className: "flex flex-col gap-2", children: pages.map((page) => {
|
|
13471
|
-
const url = `${origin}${(0,
|
|
13538
|
+
const url = `${origin}${(0, import_records12.bookingPath)(page.form_id)}`;
|
|
13472
13539
|
const mayOpen = levels.data?.[page.table_id] === "admin";
|
|
13473
13540
|
const open = page.published_at !== null && page.closed_at === null;
|
|
13474
13541
|
return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("li", { className: "rounded-md border p-2.5", "data-testid": "booking-page", children: [
|
|
@@ -13557,7 +13624,7 @@ var import_design_system49 = require("@ai-matrx/design-system");
|
|
|
13557
13624
|
|
|
13558
13625
|
// src/CaptureRun.tsx
|
|
13559
13626
|
var import_react97 = require("react");
|
|
13560
|
-
var
|
|
13627
|
+
var import_records13 = require("@ai-matrx/records");
|
|
13561
13628
|
var import_react98 = require("@ai-matrx/records/react");
|
|
13562
13629
|
var import_design_system48 = require("@ai-matrx/design-system");
|
|
13563
13630
|
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
@@ -13612,7 +13679,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
13612
13679
|
const [sending, setSending] = (0, import_react97.useState)(false);
|
|
13613
13680
|
const queueRef = (0, import_react97.useRef)(null);
|
|
13614
13681
|
(0, import_react97.useEffect)(() => {
|
|
13615
|
-
const q2 = (0,
|
|
13682
|
+
const q2 = (0, import_records13.openCaptureQueue)({
|
|
13616
13683
|
onChange: (c, all) => {
|
|
13617
13684
|
setCounts(c);
|
|
13618
13685
|
setItems(all);
|
|
@@ -13861,7 +13928,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
13861
13928
|
value: String(answers[q.field] ?? ""),
|
|
13862
13929
|
onChange: (e) => {
|
|
13863
13930
|
const raw = e.target.value;
|
|
13864
|
-
|
|
13931
|
+
const answered2 = field ? (0, import_records13.coerceTypedAnswer)(field, raw, { label: q.ask ?? (0, import_records13.coercionLabel)(field) }) : { value: raw };
|
|
13932
|
+
setAnswers((held) => ({
|
|
13933
|
+
...held,
|
|
13934
|
+
[q.field]: raw === "" ? "" : "refusal" in answered2 ? raw : answered2.value
|
|
13935
|
+
}));
|
|
13865
13936
|
setMissing(null);
|
|
13866
13937
|
}
|
|
13867
13938
|
}
|
|
@@ -13918,7 +13989,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
13918
13989
|
// src/CaptureSheet.tsx
|
|
13919
13990
|
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
13920
13991
|
var CAPTURE_MODES = ["reading", "photo", "voice"];
|
|
13921
|
-
var IN_MEMORY_QUEUE_REASON = "
|
|
13992
|
+
var IN_MEMORY_QUEUE_REASON = "Anything you capture while offline is held only in this page until it can be sent, and it is lost if you close the tab. Keep this page open until you are back on a connection. It is said here rather than discovered later.";
|
|
13922
13993
|
function mintClientKey() {
|
|
13923
13994
|
const c = globalThis.crypto;
|
|
13924
13995
|
if (typeof c?.randomUUID === "function") return `capture:${c.randomUUID()}`;
|
|
@@ -14794,6 +14865,10 @@ var import_react111 = require("react");
|
|
|
14794
14865
|
var import_react112 = require("@ai-matrx/records/react");
|
|
14795
14866
|
var import_design_system54 = require("@ai-matrx/design-system");
|
|
14796
14867
|
var import_jsx_runtime58 = require("react/jsx-runtime");
|
|
14868
|
+
function cameFromLine(label, groupField) {
|
|
14869
|
+
const grouped = groupField ? `grouped into the same columns the chart used` : `for this table`;
|
|
14870
|
+
return `You came here from \u201C${label}\u201D. This is every record ${grouped} \u2014 not only the ones that number counted \u2014 so the column you clicked is here with the rest beside it.`;
|
|
14871
|
+
}
|
|
14797
14872
|
var TABLE_NOT_REACHABLE = "This table is not in the organization you are working in, so there is nothing here to show. Switch to the organization that owns it and open it again \u2014 or it may have been deleted.";
|
|
14798
14873
|
var DEFAULT_VIEW_NAME = "All records";
|
|
14799
14874
|
var VIEW_NOT_SAVED_YET = "This table has no saved view yet, so the layout you pick here is not being kept. Save one from the bar above and it is remembered.";
|
|
@@ -14837,6 +14912,8 @@ function TablePage({
|
|
|
14837
14912
|
activeRecordId,
|
|
14838
14913
|
activeView,
|
|
14839
14914
|
onViewChanged,
|
|
14915
|
+
activeGroupField,
|
|
14916
|
+
cameFrom,
|
|
14840
14917
|
className
|
|
14841
14918
|
}) {
|
|
14842
14919
|
const client = (0, import_react112.useRecordsClient)();
|
|
@@ -15030,10 +15107,24 @@ function TablePage({
|
|
|
15030
15107
|
] }),
|
|
15031
15108
|
main === "dashboards" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
|
|
15032
15109
|
opened.unknown ? /* @__PURE__ */ (0, import_jsx_runtime58.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,
|
|
15110
|
+
cameFrom ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
15111
|
+
"p",
|
|
15112
|
+
{
|
|
15113
|
+
"data-testid": "came-from-a-number",
|
|
15114
|
+
className: "rounded-md border px-3 py-2 text-xs leading-relaxed text-muted-foreground",
|
|
15115
|
+
children: cameFromLine(cameFrom, activeGroupField ?? null)
|
|
15116
|
+
}
|
|
15117
|
+
) : null,
|
|
15033
15118
|
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
15034
15119
|
ViewSwitcher,
|
|
15035
15120
|
{
|
|
15036
|
-
view: {
|
|
15121
|
+
view: {
|
|
15122
|
+
...view ?? defaultView(tableId),
|
|
15123
|
+
layout: shownLayout,
|
|
15124
|
+
// The address's field wins for this visit; silence keeps the
|
|
15125
|
+
// saved view's own choice, exactly as it always did.
|
|
15126
|
+
...activeGroupField ? { groupField: activeGroupField } : {}
|
|
15127
|
+
},
|
|
15037
15128
|
pageSize,
|
|
15038
15129
|
onLayoutChange: (layout) => {
|
|
15039
15130
|
setLayoutFromLink(layout);
|