@ai-matrx/records-ui 0.65.0 → 0.67.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 +95 -0
- package/dist/index.cjs +94 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -9
- package/dist/index.d.ts +124 -9
- package/dist/index.js +94 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,90 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.67.0
|
|
4
|
+
|
|
5
|
+
**The office can copy a waiting client's own link, from the panel where it invited them.**
|
|
6
|
+
(lane TAILS-5.)
|
|
7
|
+
|
|
8
|
+
* **`PortalsPanel` renders `CopyLink` beside a pending principal**, fed from that person's
|
|
9
|
+
`accept_path` (new in `@ai-matrx/records` 0.48.0). It is the SAME `CopyLink` this panel
|
|
10
|
+
already uses for the portal's own address — the one that shows the address to copy by hand
|
|
11
|
+
when a browser refuses the clipboard — not a second copy control.
|
|
12
|
+
* **A person with no link gets a sentence, not a missing button.** Somebody who is waiting and
|
|
13
|
+
has no `accept_path` was asked some other way (the server's magic-link route binds without an
|
|
14
|
+
invitation) or their link expired, and the panel says exactly that with what to do: invite
|
|
15
|
+
them again and a fresh one is made. Drawing nothing would read as a broken control; drawing a
|
|
16
|
+
dead button would be worse.
|
|
17
|
+
* **`PortalCardView` takes an optional `origin`** so the bare-mountable view can build a whole
|
|
18
|
+
URL, falling back to the browser's own origin. It still holds no client and no network, so
|
|
19
|
+
the blocking gate can keep mounting it against a recorded `custom.portal_card` answer.
|
|
20
|
+
|
|
21
|
+
**And the two buttons that read alike no longer do.** The Jobs toolbar's control that OPENS the
|
|
22
|
+
add-a-column panel read exactly `"Add field"`, and the panel's own SAVE button read exactly
|
|
23
|
+
`"Add field"` too — `FieldEditor` said `"Save field"` only when EDITING. The toolbar control is
|
|
24
|
+
a TOGGLE, so pressing it while the panel is open CLOSES the panel and throws away everything
|
|
25
|
+
typed into it: a person who had typed a column name, chosen a type and picked the table it
|
|
26
|
+
points at could lose all of it by pressing the words they were told to press, silently, with no
|
|
27
|
+
request ever sent. Measured on 2026-09-21 by a headless walk that lost the column twice and
|
|
28
|
+
spent a round blaming the store.
|
|
29
|
+
|
|
30
|
+
* The words now live once, in `fieldTypes.ts` beside `FIELD_TYPE_CHOICES` — the module that
|
|
31
|
+
already owns what a person READS about a column — as `ADD_FIELD_OPEN_LABEL` ("Add field"),
|
|
32
|
+
`ADD_FIELD_SAVE_LABEL` ("Create field") and `EDIT_FIELD_SAVE_LABEL` ("Save field"). Both
|
|
33
|
+
surfaces import them; neither writes the literal any more.
|
|
34
|
+
* **They are NOT in `@ai-matrx/design-system`, deliberately:** "field" and "column" are records
|
|
35
|
+
vocabulary and the design system knows nothing about either, so a labels module there would
|
|
36
|
+
be a domain word in a generic package that the next person adding a button would not find.
|
|
37
|
+
* `the-opener-and-the-committer-never-read-alike.test.tsx` is a forcing function, not a style
|
|
38
|
+
note: it asserts the strings differ, that the committer never says "add", and that neither
|
|
39
|
+
file carries the literal. Shown RED (2 of 3 failing, `expected 'Add field' not to be 'Add
|
|
40
|
+
field'`) with the collision planted, then GREEN.
|
|
41
|
+
|
|
42
|
+
**Consumer action:** none for a host that mounts `PortalsPanel`. A host that mounts
|
|
43
|
+
`PortalCardView` directly and serves the app from a known origin should pass `origin`; without
|
|
44
|
+
it the view reads `window.location.origin`, which is right in a browser and empty in SSR.
|
|
45
|
+
|
|
46
|
+
## 0.66.0
|
|
47
|
+
|
|
48
|
+
**A screen stops talking to a developer.** (lane TAILS-6.)
|
|
49
|
+
|
|
50
|
+
Two sightings on ONE table's rail, watched from the admin seat by the GUIDE lane on 2026-09-21:
|
|
51
|
+
|
|
52
|
+
> Checklists — "Something went wrong at our end" ·
|
|
53
|
+
> `@ai-matrx/records did not recognise SQLSTATE from checklistTemplates`
|
|
54
|
+
>
|
|
55
|
+
> Dashboards — "… bind `openRecords` on `<RecordsUiProvider>` …"
|
|
56
|
+
|
|
57
|
+
Both are the same defect in two coats: a sentence written for whoever maintains the code,
|
|
58
|
+
printed to the person who owns the business.
|
|
59
|
+
|
|
60
|
+
* **`refusalForAPerson` no longer promotes an engineer's hint to the person's remedy.** It
|
|
61
|
+
prefers the door's own hint over its table, which is right — but it assumed every hint came
|
|
62
|
+
from the store, and the machine-identity filter only catches a five-character SQLSTATE.
|
|
63
|
+
New `isEngineerText` / `ENGINEER_TEXT` judge a sentence by SHAPE — a package name, the word
|
|
64
|
+
SQLSTATE, a `<Component>`, a camelCase identifier, a backticked token, "bind X on …" — and
|
|
65
|
+
send it to `forEngineers` wherever it came from. `error.diagnostic` from
|
|
66
|
+
`@ai-matrx/records` 0.47.0 joins the same line.
|
|
67
|
+
* **Titles and remedies for `unreachable`, `needs_approval`, `stale_read` and `timed_out`,**
|
|
68
|
+
which had none and fell through to "Something went wrong at our end". A call that never
|
|
69
|
+
reached the store now says so and says nothing was changed.
|
|
70
|
+
* **Every unbound-port sentence rewritten for the person it is shown to** — the nine of them,
|
|
71
|
+
not the two that were caught: `NO_OPEN_RECORDS_REASON`, `NO_REASK_REASON`,
|
|
72
|
+
`NO_SAVED_VIEWS_REASON`, `NO_MEMBERS_REASON`, `NO_SHARE_REASON`, `NO_UPLOAD_REASON`,
|
|
73
|
+
`NO_CHAT_REASON`, `NO_ENRICH_REASON`, `IN_MEMORY_QUEUE_REASON`. Each still says what is
|
|
74
|
+
missing and now says what to do instead; the instruction for whoever maintains the app moved
|
|
75
|
+
into the TSDoc above it, where it always belonged.
|
|
76
|
+
* **`TablePage` gains `activeGroupField` and `cameFrom`,** so a host CAN bind `openRecords`.
|
|
77
|
+
The store has no door that returns the rows behind an aggregate filter, so a click cannot
|
|
78
|
+
promise a narrowed list — what it can do is open this table's board with its columns set to
|
|
79
|
+
the field the chart grouped by, and `cameFromLine` says plainly that this is every record
|
|
80
|
+
grouped that way rather than only the ones the number counted.
|
|
81
|
+
* **Guard:** `src/no-developer-sentences.test.tsx` — shown failing on the pre-fix bytes with
|
|
82
|
+
the exact sentence the GUIDE lane read, then passing. It needs no server.
|
|
83
|
+
|
|
84
|
+
**Consumer action:** bind `openRecords` on your `RecordsUiProvider` and pass `activeGroupField`
|
|
85
|
+
/ `cameFrom` through from the address, or leave it unbound and the canvas says so in plain
|
|
86
|
+
English. `matrx-frontend`'s `/data-v2/[tableId]` route is the worked example.
|
|
87
|
+
|
|
3
88
|
## 0.65.0
|
|
4
89
|
|
|
5
90
|
**A condition about a relation column stops asking a person to type a uuid.**
|
|
@@ -37,6 +122,16 @@ version somebody else's tag already names.*
|
|
|
37
122
|
|
|
38
123
|
## 0.64.0
|
|
39
124
|
|
|
125
|
+
> 🚨 **0.64.0 NEVER PUBLISHED, AND EVERYTHING IN IT SHIPPED INSIDE 0.65.0.** Its publish run
|
|
126
|
+
> (35638062219) was **cancelled — not failed** — because `npm/records/*` and `npm/records-ui/*`
|
|
127
|
+
> share ONE concurrency slot that keeps only the newest pending run, and `npm/records-ui/v0.65.0`
|
|
128
|
+
> displaced it. Nothing was lost: 0.65.0 is built from `origin/main`, which contains this
|
|
129
|
+
> commit, and the tarball carries `fieldKindFor`, `coerceTypedAnswer`, `valueFromPastedText`
|
|
130
|
+
> and `enumLabels` — verified by `npm pack`, not by version number. `npm view` goes
|
|
131
|
+
> 0.63.0 → 0.65.0 with no 0.64.0 in between. The entry is kept as written so the work is
|
|
132
|
+
> still findable by what it did. *(Recorded by lane RELATION-DISPLAY-2, whose tag did the
|
|
133
|
+
> displacing.)*
|
|
134
|
+
|
|
40
135
|
**The paste and the editor stop owning their own opinion of a column, and the capture sheet
|
|
41
136
|
stops minting NaN.**
|
|
42
137
|
|
package/dist/index.cjs
CHANGED
|
@@ -69,6 +69,7 @@ __export(src_exports, {
|
|
|
69
69
|
DocRender: () => DocRender,
|
|
70
70
|
DocTemplate: () => DocTemplate,
|
|
71
71
|
EMPTY_PRESENTATION: () => EMPTY_PRESENTATION,
|
|
72
|
+
ENGINEER_TEXT: () => ENGINEER_TEXT,
|
|
72
73
|
EmbedFrame: () => EmbedFrame,
|
|
73
74
|
EnrichBadge: () => EnrichBadge,
|
|
74
75
|
EnrichPanel: () => EnrichPanel,
|
|
@@ -192,6 +193,7 @@ __export(src_exports, {
|
|
|
192
193
|
blockFromSpec: () => blockFromSpec,
|
|
193
194
|
bodyForReading: () => bodyForReading,
|
|
194
195
|
bodyFromKeys: () => bodyFromKeys,
|
|
196
|
+
cameFromLine: () => cameFromLine,
|
|
195
197
|
cellState: () => cellState,
|
|
196
198
|
chooseSurface: () => chooseSurface,
|
|
197
199
|
colorFromTheValue: () => colorFromTheValue,
|
|
@@ -223,6 +225,7 @@ __export(src_exports, {
|
|
|
223
225
|
hintIsMachineIdentity: () => hintIsMachineIdentity,
|
|
224
226
|
humanize: () => humanize,
|
|
225
227
|
idsOf: () => idsOf,
|
|
228
|
+
isEngineerText: () => isEngineerText,
|
|
226
229
|
isId: () => isId,
|
|
227
230
|
isMachineIdentity: () => isMachineIdentity,
|
|
228
231
|
isPlainFieldType: () => isPlainFieldType,
|
|
@@ -373,12 +376,12 @@ var NO_RIGHTS = tableRights(NOT_ANSWERED_YET);
|
|
|
373
376
|
function tableRightsAt(level) {
|
|
374
377
|
return tableRights(whatYouMayDo(level, true));
|
|
375
378
|
}
|
|
376
|
-
var NO_SAVED_VIEWS_REASON = "
|
|
377
|
-
var NO_MEMBERS_REASON = "
|
|
378
|
-
var NO_SHARE_REASON = "
|
|
379
|
-
var NO_OPEN_RECORDS_REASON = "These numbers do not click through yet. Every one of them is a count of real records
|
|
380
|
-
var NO_REASK_REASON = "
|
|
381
|
-
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.";
|
|
382
385
|
var HostContext = (0, import_react.createContext)(null);
|
|
383
386
|
function RecordsUiProvider({ value, children }) {
|
|
384
387
|
const bound = (0, import_react.useMemo)(() => value, [value]);
|
|
@@ -453,6 +456,10 @@ var TITLE = {
|
|
|
453
456
|
not_supported: "That cannot be done here",
|
|
454
457
|
store_limit: "This has got as big as it can",
|
|
455
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",
|
|
456
463
|
internal: "Something went wrong at our end"
|
|
457
464
|
};
|
|
458
465
|
var REMEDY = {
|
|
@@ -467,8 +474,35 @@ var REMEDY = {
|
|
|
467
474
|
not_supported: "There is another way to do this \u2014 ask, and we will point you at it.",
|
|
468
475
|
store_limit: "Split it up, or ask us to raise the ceiling for your organization.",
|
|
469
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.",
|
|
470
485
|
internal: "Nothing you did caused this. Try again, and tell us if it keeps happening."
|
|
471
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
|
+
}
|
|
472
506
|
function withoutMachineClauses(sentence2) {
|
|
473
507
|
const dropped = [];
|
|
474
508
|
const footnotes = [];
|
|
@@ -506,11 +540,17 @@ function refusalForAPerson(error) {
|
|
|
506
540
|
const message = plainSentence(String(error.message ?? ""));
|
|
507
541
|
const hint = plainSentence(String(error.hint ?? ""));
|
|
508
542
|
const title = TITLE[code] ?? "The store refused this";
|
|
509
|
-
const
|
|
510
|
-
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();
|
|
511
547
|
const forEngineers = [
|
|
512
548
|
error.sqlstate ? `SQLSTATE ${error.sqlstate}.` : null,
|
|
513
|
-
|
|
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,
|
|
514
554
|
message.dropped.length > 0 || hint.dropped.length > 0 ? `Dropped from the person's view: ${[...message.dropped, ...hint.dropped].join(" | ")}` : null
|
|
515
555
|
].filter(Boolean).join(" ");
|
|
516
556
|
return {
|
|
@@ -828,6 +868,9 @@ function isRelationFieldType(id) {
|
|
|
828
868
|
function parityTypesWithNoExplanation() {
|
|
829
869
|
return import_records2.PARITY_FIELD_TYPES.map((row) => row.parity_type).filter((type) => !BY_ID.has(type));
|
|
830
870
|
}
|
|
871
|
+
var ADD_FIELD_OPEN_LABEL = "Add field";
|
|
872
|
+
var ADD_FIELD_SAVE_LABEL = "Create field";
|
|
873
|
+
var EDIT_FIELD_SAVE_LABEL = "Save field";
|
|
831
874
|
|
|
832
875
|
// src/parity.ts
|
|
833
876
|
var kernelId = (name) => import_records3.KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
|
|
@@ -2704,7 +2747,7 @@ var import_react17 = require("react");
|
|
|
2704
2747
|
var import_react18 = require("@ai-matrx/records/react");
|
|
2705
2748
|
var import_design_system8 = require("@ai-matrx/design-system");
|
|
2706
2749
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2707
|
-
var NO_ENRICH_REASON = "
|
|
2750
|
+
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.";
|
|
2708
2751
|
function cellState(cell) {
|
|
2709
2752
|
if (!cell || !cell.agent_owned) return "none";
|
|
2710
2753
|
if (cell.pinned) return "yours";
|
|
@@ -4981,7 +5024,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
4981
5024
|
size: "sm",
|
|
4982
5025
|
disabled: mutation.saving || missing !== null,
|
|
4983
5026
|
onClick: () => void save(),
|
|
4984
|
-
children: mutation.saving ? "Saving\u2026" : field ?
|
|
5027
|
+
children: mutation.saving ? "Saving\u2026" : field ? EDIT_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
|
|
4985
5028
|
}
|
|
4986
5029
|
),
|
|
4987
5030
|
onCancel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system14.Button, { size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }) : null,
|
|
@@ -6011,7 +6054,7 @@ var import_react36 = require("react");
|
|
|
6011
6054
|
var import_react37 = require("@ai-matrx/records/react");
|
|
6012
6055
|
var import_design_system18 = require("@ai-matrx/design-system");
|
|
6013
6056
|
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
6014
|
-
var NO_CHAT_REASON = "
|
|
6057
|
+
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.";
|
|
6015
6058
|
function RecordChat({ tableId, recordId, className }) {
|
|
6016
6059
|
const client = (0, import_react37.useRecordsClient)();
|
|
6017
6060
|
const host = useRecordsUi();
|
|
@@ -9933,6 +9976,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9933
9976
|
{
|
|
9934
9977
|
portalId: portal.portal_id,
|
|
9935
9978
|
...tableId ? { tableId } : {},
|
|
9979
|
+
origin,
|
|
9936
9980
|
onChanged: () => void load()
|
|
9937
9981
|
}
|
|
9938
9982
|
) : null
|
|
@@ -9940,7 +9984,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9940
9984
|
}) })
|
|
9941
9985
|
] });
|
|
9942
9986
|
}
|
|
9943
|
-
function CopyLink({ url }) {
|
|
9987
|
+
function CopyLink({ url, what }) {
|
|
9944
9988
|
const [copied, setCopied] = (0, import_react68.useState)(false);
|
|
9945
9989
|
const [shown, setShown] = (0, import_react68.useState)(false);
|
|
9946
9990
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
@@ -9961,7 +10005,7 @@ function CopyLink({ url }) {
|
|
|
9961
10005
|
}
|
|
9962
10006
|
})();
|
|
9963
10007
|
},
|
|
9964
|
-
children: copied ? "Copied" : "Copy link"
|
|
10008
|
+
children: copied ? "Copied" : what ? `Copy ${what}` : "Copy link"
|
|
9965
10009
|
}
|
|
9966
10010
|
),
|
|
9967
10011
|
shown ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("p", { className: "mt-1.5 w-full break-all rounded border border-dashed border-border px-2 py-1 text-xs", children: [
|
|
@@ -9973,6 +10017,7 @@ function CopyLink({ url }) {
|
|
|
9973
10017
|
function PortalDetail({
|
|
9974
10018
|
portalId,
|
|
9975
10019
|
tableId,
|
|
10020
|
+
origin,
|
|
9976
10021
|
onChanged
|
|
9977
10022
|
}) {
|
|
9978
10023
|
const client = (0, import_react69.useRecordsClient)();
|
|
@@ -10022,6 +10067,7 @@ function PortalDetail({
|
|
|
10022
10067
|
PortalCardView,
|
|
10023
10068
|
{
|
|
10024
10069
|
card,
|
|
10070
|
+
origin,
|
|
10025
10071
|
...tableId ? { tableId } : {},
|
|
10026
10072
|
onRevoke: revoke,
|
|
10027
10073
|
onPreview: preview,
|
|
@@ -10044,6 +10090,7 @@ function PortalCardView({
|
|
|
10044
10090
|
onRevoke,
|
|
10045
10091
|
onPreview,
|
|
10046
10092
|
invite,
|
|
10093
|
+
origin,
|
|
10047
10094
|
className
|
|
10048
10095
|
}) {
|
|
10049
10096
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: (0, import_design_system33.cn)("mt-2.5 flex flex-col gap-2.5 border-t border-border pt-2.5", className), children: [
|
|
@@ -10074,7 +10121,7 @@ function PortalCardView({
|
|
|
10074
10121
|
] }, exposed.table_id))
|
|
10075
10122
|
] }),
|
|
10076
10123
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Separator, {}),
|
|
10077
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(People, { card, onRevoke, onPreview }),
|
|
10124
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(People, { card, onRevoke, onPreview, origin }),
|
|
10078
10125
|
invite ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
10079
10126
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Separator, {}),
|
|
10080
10127
|
invite
|
|
@@ -10084,8 +10131,10 @@ function PortalCardView({
|
|
|
10084
10131
|
function People({
|
|
10085
10132
|
card,
|
|
10086
10133
|
onRevoke,
|
|
10087
|
-
onPreview
|
|
10134
|
+
onPreview,
|
|
10135
|
+
origin
|
|
10088
10136
|
}) {
|
|
10137
|
+
const here = origin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
10089
10138
|
const [askingToRevoke, setAskingToRevoke] = (0, import_react68.useState)(null);
|
|
10090
10139
|
const [busy, setBusy] = (0, import_react68.useState)(false);
|
|
10091
10140
|
const [said, setSaid] = (0, import_react68.useState)(null);
|
|
@@ -10127,6 +10176,7 @@ function People({
|
|
|
10127
10176
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "mt-0.5 break-all text-muted-foreground", children: person.email }),
|
|
10128
10177
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "mt-0.5 text-muted-foreground", children: state.why }),
|
|
10129
10178
|
person.is_active ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "mt-1.5 flex flex-wrap gap-1.5", children: [
|
|
10179
|
+
person.accept_path ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CopyLink, { url: `${here}${person.accept_path}`, what: "their link" }) : null,
|
|
10130
10180
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
10131
10181
|
import_design_system33.Button,
|
|
10132
10182
|
{
|
|
@@ -10138,6 +10188,11 @@ function People({
|
|
|
10138
10188
|
),
|
|
10139
10189
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Button, { size: "sm", variant: "ghost", onClick: () => setAskingToRevoke(person), children: "Remove their access" })
|
|
10140
10190
|
] }) : null,
|
|
10191
|
+
person.is_active && !person.signed_in && !person.accept_path ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("p", { className: "mt-1.5 text-muted-foreground", children: [
|
|
10192
|
+
"There is no link to copy for ",
|
|
10193
|
+
person.client || person.email,
|
|
10194
|
+
": they were asked some other way, or the link they were sent has expired. Invite them again and a fresh one is made."
|
|
10195
|
+
] }) : null,
|
|
10141
10196
|
asking ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "mt-1.5 flex flex-col gap-1.5 rounded border border-destructive/40 px-2 py-1.5", children: [
|
|
10142
10197
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-destructive", children: revokeConsequence(person, card.title) }),
|
|
10143
10198
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
@@ -13949,7 +14004,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
13949
14004
|
// src/CaptureSheet.tsx
|
|
13950
14005
|
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
13951
14006
|
var CAPTURE_MODES = ["reading", "photo", "voice"];
|
|
13952
|
-
var IN_MEMORY_QUEUE_REASON = "
|
|
14007
|
+
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.";
|
|
13953
14008
|
function mintClientKey() {
|
|
13954
14009
|
const c = globalThis.crypto;
|
|
13955
14010
|
if (typeof c?.randomUUID === "function") return `capture:${c.randomUUID()}`;
|
|
@@ -14825,6 +14880,10 @@ var import_react111 = require("react");
|
|
|
14825
14880
|
var import_react112 = require("@ai-matrx/records/react");
|
|
14826
14881
|
var import_design_system54 = require("@ai-matrx/design-system");
|
|
14827
14882
|
var import_jsx_runtime58 = require("react/jsx-runtime");
|
|
14883
|
+
function cameFromLine(label, groupField) {
|
|
14884
|
+
const grouped = groupField ? `grouped into the same columns the chart used` : `for this table`;
|
|
14885
|
+
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.`;
|
|
14886
|
+
}
|
|
14828
14887
|
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.";
|
|
14829
14888
|
var DEFAULT_VIEW_NAME = "All records";
|
|
14830
14889
|
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.";
|
|
@@ -14868,6 +14927,8 @@ function TablePage({
|
|
|
14868
14927
|
activeRecordId,
|
|
14869
14928
|
activeView,
|
|
14870
14929
|
onViewChanged,
|
|
14930
|
+
activeGroupField,
|
|
14931
|
+
cameFrom,
|
|
14871
14932
|
className
|
|
14872
14933
|
}) {
|
|
14873
14934
|
const client = (0, import_react112.useRecordsClient)();
|
|
@@ -14953,7 +15014,7 @@ function TablePage({
|
|
|
14953
15014
|
variant: surfaceChosen(surface, { rail: "field" }) ? "secondary" : "outline",
|
|
14954
15015
|
"aria-pressed": surfaceChosen(surface, { rail: "field" }),
|
|
14955
15016
|
onClick: () => press({ rail: "field" }),
|
|
14956
|
-
children:
|
|
15017
|
+
children: ADD_FIELD_OPEN_LABEL
|
|
14957
15018
|
}
|
|
14958
15019
|
),
|
|
14959
15020
|
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
@@ -15061,10 +15122,24 @@ function TablePage({
|
|
|
15061
15122
|
] }),
|
|
15062
15123
|
main === "dashboards" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
|
|
15063
15124
|
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,
|
|
15125
|
+
cameFrom ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
15126
|
+
"p",
|
|
15127
|
+
{
|
|
15128
|
+
"data-testid": "came-from-a-number",
|
|
15129
|
+
className: "rounded-md border px-3 py-2 text-xs leading-relaxed text-muted-foreground",
|
|
15130
|
+
children: cameFromLine(cameFrom, activeGroupField ?? null)
|
|
15131
|
+
}
|
|
15132
|
+
) : null,
|
|
15064
15133
|
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
15065
15134
|
ViewSwitcher,
|
|
15066
15135
|
{
|
|
15067
|
-
view: {
|
|
15136
|
+
view: {
|
|
15137
|
+
...view ?? defaultView(tableId),
|
|
15138
|
+
layout: shownLayout,
|
|
15139
|
+
// The address's field wins for this visit; silence keeps the
|
|
15140
|
+
// saved view's own choice, exactly as it always did.
|
|
15141
|
+
...activeGroupField ? { groupField: activeGroupField } : {}
|
|
15142
|
+
},
|
|
15068
15143
|
pageSize,
|
|
15069
15144
|
onLayoutChange: (layout) => {
|
|
15070
15145
|
setLayoutFromLink(layout);
|