@ai-matrx/records-ui 0.101.2 → 0.101.8

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 CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.101.8
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/records-ui/v0.101.7..npm/records-ui/v0.101.8 -- apps/shared/records-ui`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.101.7
10
+
11
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
12
+ `git diff npm/records-ui/v0.101.2..npm/records-ui/v0.101.7 -- apps/shared/records-ui`).
13
+ No source changes intended and no consumer action required.
14
+
15
+ ## 0.101.6
16
+
17
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
18
+ `git diff npm/records-ui/v0.101.2..npm/records-ui/v0.101.6 -- apps/shared/records-ui`).
19
+ No source changes intended and no consumer action required.
20
+
21
+ ## 0.101.5
22
+
23
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
24
+ `git diff npm/records-ui/v0.101.2..npm/records-ui/v0.101.5 -- apps/shared/records-ui`).
25
+ No source changes intended and no consumer action required.
26
+
27
+ ## 0.101.4
28
+
29
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
30
+ `git diff npm/records-ui/v0.101.2..npm/records-ui/v0.101.4 -- apps/shared/records-ui`).
31
+ No source changes intended and no consumer action required.
32
+
33
+ ## 0.101.3
34
+
35
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
36
+ `git diff npm/records-ui/v0.101.2..npm/records-ui/v0.101.3 -- apps/shared/records-ui`).
37
+ No source changes intended and no consumer action required.
38
+
3
39
  ## 0.101.2
4
40
 
5
41
  `TablesHome`: "Start from a template" (`templatesHref`) also shows while a new table is being named, so a
package/dist/index.cjs CHANGED
@@ -269,6 +269,7 @@ __export(src_exports, {
269
269
  TableSettings: () => TableSettings,
270
270
  TableToolbarSlotContext: () => TableToolbarSlotContext,
271
271
  TablesHome: () => TablesHome,
272
+ TemplatePreview: () => TemplatePreview,
272
273
  TimelineView: () => TimelineView,
273
274
  UNREAD_RECORD_LABEL: () => UNREAD_RECORD_LABEL,
274
275
  VIEW_ADDRESS_KEYS: () => VIEW_ADDRESS_KEYS,
@@ -550,6 +551,7 @@ __export(src_exports, {
550
551
  useTableRights: () => useTableRights,
551
552
  useTableToolbarSlot: () => useTableToolbarSlot,
552
553
  useTablesAnywhere: () => useTablesAnywhere,
554
+ useTemplatePreview: () => useTemplatePreview,
553
555
  useViewRecords: () => useViewRecords,
554
556
  useWhoChanged: () => useWhoChanged,
555
557
  viewActions: () => viewActions,
@@ -676,8 +678,16 @@ function RecordsUiProvider({ value, children }) {
676
678
  function useRecordsUi() {
677
679
  return (0, import_react.useContext)(HostContext) ?? {};
678
680
  }
681
+ function quiet(may, say2) {
682
+ if (say2 !== false) return may;
683
+ return { ...may, why: () => "", ..."reason" in may ? { reason: "" } : {} };
684
+ }
679
685
  function useTableRights(table) {
680
686
  const host = useRecordsUi();
687
+ const asked3 = useTableRightsAsked(table, host);
688
+ return (0, import_react.useMemo)(() => quiet(asked3, host.sayLevelNeeded), [asked3, host.sayLevelNeeded]);
689
+ }
690
+ function useTableRightsAsked(table, host) {
681
691
  const bound = Boolean(host.rights);
682
692
  const asked3 = (0, import_react2.useMyLevel)(bound || !table ? null : table.id);
683
693
  return (0, import_react.useMemo)(() => {
@@ -690,22 +700,24 @@ function useTableRights(table) {
690
700
  }
691
701
  function useRecordRights(recordId) {
692
702
  const asked3 = (0, import_react2.useMyLevel)(recordId);
703
+ const say2 = useRecordsUi().sayLevelNeeded;
693
704
  return (0, import_react.useMemo)(() => {
694
705
  if (!recordId) return NOT_ANSWERED_YET;
695
706
  if (asked3.loading) return NOT_ANSWERED_YET;
696
- if (asked3.error) return whatYouMayDo(null, true);
697
- return whatYouMayDo(asked3.data, true);
698
- }, [recordId, asked3.loading, asked3.error, asked3.data]);
707
+ if (asked3.error) return quiet(whatYouMayDo(null, true), say2);
708
+ return quiet(whatYouMayDo(asked3.data, true), say2);
709
+ }, [recordId, asked3.loading, asked3.error, asked3.data, say2]);
699
710
  }
700
711
  function useRowRights(rowIds, enabled) {
701
712
  const asked3 = (0, import_react2.useMyLevels)(enabled ? rowIds : []);
713
+ const say2 = useRecordsUi().sayLevelNeeded;
702
714
  return (0, import_react.useMemo)(() => {
703
715
  if (!enabled) return () => null;
704
716
  if (asked3.loading) return () => NOT_ANSWERED_YET;
705
- if (asked3.error) return () => whatYouMayDo(null, true);
717
+ if (asked3.error) return () => quiet(whatYouMayDo(null, true), say2);
706
718
  const held = asked3.data ?? {};
707
- return (rowId) => whatYouMayDo(held[rowId] ?? null, true);
708
- }, [enabled, asked3.loading, asked3.error, asked3.data]);
719
+ return (rowId) => quiet(whatYouMayDo(held[rowId] ?? null, true), say2);
720
+ }, [enabled, asked3.loading, asked3.error, asked3.data, say2]);
709
721
  }
710
722
 
711
723
  // src/plainWords.ts
@@ -26572,7 +26584,7 @@ function Peek({ tableId, recordId, onClose, className, onPage = false }) {
26572
26584
  ) : host.chat && !askOpenBelow ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_design_system80.Button, { size: "sm", variant: talking ? "ghost" : "outline", onClick: () => setTalking((t) => !t), children: talking ? "Close chat" : "Talk to this record" }) : null,
26573
26585
  organizationId ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(ShareControl, { kind: "record", organizationId, subjectId: recordId, name: title, may: may.share }) : null,
26574
26586
  /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(RowShownTo, { recordId }),
26575
- may.write ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_design_system80.Button, { size: "sm", variant: editing ? "ghost" : "outline", onClick: () => setEditing((e) => !e), children: editing ? "Stop editing" : "Edit" }) : may.known ? (
26587
+ may.write ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_design_system80.Button, { size: "sm", variant: editing ? "ghost" : "outline", onClick: () => setEditing((e) => !e), children: editing ? "Stop editing" : "Edit" }) : may.known && may.why("write") ? (
26576
26588
  // Absent AND explained: no Edit button, and one sentence saying what
26577
26589
  // it would take and who can give it.
26578
26590
  /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("span", { className: "max-w-[18rem] text-[11px] text-muted-foreground", children: may.why("write") })
@@ -35965,7 +35977,7 @@ function DigestScheduler({
35965
35977
  const [channel, setChannel] = (0, import_react197.useState)(
35966
35978
  existing?.channel === "email" || existing?.channel === "sms" ? existing.channel : "in_app"
35967
35979
  );
35968
- const [quiet, setQuiet] = (0, import_react197.useState)((0, import_records50.hasQuietHours)(existing?.quiet_hours));
35980
+ const [quiet2, setQuiet] = (0, import_react197.useState)((0, import_records50.hasQuietHours)(existing?.quiet_hours));
35969
35981
  const [quietFrom, setQuietFrom] = (0, import_react197.useState)(existing?.quiet_hours?.start ?? "22:00");
35970
35982
  const [quietTo, setQuietTo] = (0, import_react197.useState)(existing?.quiet_hours?.end ?? "07:00");
35971
35983
  const [recipients, setRecipients] = (0, import_react197.useState)(
@@ -36003,8 +36015,8 @@ function DigestScheduler({
36003
36015
  return null;
36004
36016
  }, [cadence, weekday, time]);
36005
36017
  const quietHours = (0, import_react197.useMemo)(
36006
- () => quiet ? { start: quietFrom, end: quietTo, tz: timezone } : { tz: timezone },
36007
- [quiet, quietFrom, quietTo, timezone]
36018
+ () => quiet2 ? { start: quietFrom, end: quietTo, tz: timezone } : { tz: timezone },
36019
+ [quiet2, quietFrom, quietTo, timezone]
36008
36020
  );
36009
36021
  const fallbackName = `${CADENCE_TITLE[cadence] ?? "Weekly"} ${tableName2 ?? "records"} summary`;
36010
36022
  async function scheduleIt() {
@@ -36172,10 +36184,10 @@ function DigestScheduler({
36172
36184
  ] })
36173
36185
  ] }),
36174
36186
  /* @__PURE__ */ (0, import_jsx_runtime117.jsxs)("label", { className: "flex items-center gap-1.5 text-xs", children: [
36175
- /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_design_system104.Checkbox, { checked: quiet, onCheckedChange: (on) => setQuiet(Boolean(on)) }),
36187
+ /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(import_design_system104.Checkbox, { checked: quiet2, onCheckedChange: (on) => setQuiet(Boolean(on)) }),
36176
36188
  "Not between certain hours"
36177
36189
  ] }),
36178
- quiet ? /* @__PURE__ */ (0, import_jsx_runtime117.jsxs)("div", { className: "flex items-center gap-2", children: [
36190
+ quiet2 ? /* @__PURE__ */ (0, import_jsx_runtime117.jsxs)("div", { className: "flex items-center gap-2", children: [
36179
36191
  /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
36180
36192
  import_design_system104.BasicInput,
36181
36193
  {
@@ -37209,7 +37221,7 @@ function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className })
37209
37221
  await load();
37210
37222
  })();
37211
37223
  }, [subscriptions, seed, write, load]);
37212
- async function subscribe(viewId, viewName, cadence, channel, schedule, quiet) {
37224
+ async function subscribe(viewId, viewName, cadence, channel, schedule, quiet2) {
37213
37225
  setBusy(true);
37214
37226
  const ok = await write({
37215
37227
  name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
@@ -37217,7 +37229,7 @@ function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className })
37217
37229
  cadence,
37218
37230
  channel,
37219
37231
  schedule: schedule.trim() === "" ? null : schedule.trim(),
37220
- quietHours: quiet
37232
+ quietHours: quiet2
37221
37233
  });
37222
37234
  setBusy(false);
37223
37235
  if (!ok) return;
@@ -37334,8 +37346,8 @@ function NotifyRuleEditor({ tableId, seed, chosenRuleId, onChoose, className })
37334
37346
  const start = form.elements.namedItem("quiet_start").value;
37335
37347
  const end = form.elements.namedItem("quiet_end").value;
37336
37348
  const name = views.find((v) => v.id === view)?.name ?? "View";
37337
- const quiet = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
37338
- if (view) void subscribe(view, name, cadence, channel, schedule, quiet);
37349
+ const quiet2 = start && end ? { start, end, tz: Intl.DateTimeFormat().resolvedOptions().timeZone } : null;
37350
+ if (view) void subscribe(view, name, cadence, channel, schedule, quiet2);
37339
37351
  },
37340
37352
  children: [
37341
37353
  /* @__PURE__ */ (0, import_jsx_runtime121.jsx)("select", { name: "view", "aria-label": "View to be told about", className: "h-7 rounded border bg-background px-1 text-xs", children: views.map((view) => /* @__PURE__ */ (0, import_jsx_runtime121.jsx)("option", { value: view.id, children: view.name }, view.id)) }),
@@ -46054,4 +46066,88 @@ function Decision({ approvalId, onDecided }) {
46054
46066
  error ? /* @__PURE__ */ (0, import_jsx_runtime151.jsx)(RefusalLine, { error }) : null
46055
46067
  ] });
46056
46068
  }
46069
+
46070
+ // src/TemplatePreview.tsx
46071
+ var import_react263 = require("react");
46072
+ var import_memory = require("@ai-matrx/records/memory");
46073
+ var import_react264 = require("@ai-matrx/records/react");
46074
+ var import_jsx_runtime152 = require("react/jsx-runtime");
46075
+ function useTemplatePreview(spec, today) {
46076
+ return (0, import_react263.useMemo)(() => (0, import_memory.templatePreview)(spec, today ? { today } : {}), [spec, today]);
46077
+ }
46078
+ function TemplatePreview({ spec, today, initialTable, host, aside, booking, className }) {
46079
+ const preview = useTemplatePreview(spec, today);
46080
+ const bound = (0, import_react263.useMemo)(() => ({ ...host, sayLevelNeeded: false }), [host]);
46081
+ const first = preview.tables.find((t) => t.token === initialTable) ?? preview.tables[0];
46082
+ const [active, setActive] = (0, import_react263.useState)(first?.id ?? null);
46083
+ const table = preview.tables.find((t) => t.id === active) ?? first;
46084
+ if (!table) return null;
46085
+ return /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: ["flex min-w-0 flex-col gap-3", className].filter(Boolean).join(" "), "data-template-preview": preview.tables.length, children: [
46086
+ preview.tables.length > 1 || aside ? /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5", role: "tablist", "aria-label": "Tables", children: [
46087
+ preview.tables.map((t) => /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)(
46088
+ "button",
46089
+ {
46090
+ type: "button",
46091
+ role: "tab",
46092
+ "aria-selected": t.id === table.id,
46093
+ "data-template-preview-table": t.token,
46094
+ onClick: () => setActive(t.id),
46095
+ className: [
46096
+ "inline-flex h-8 items-center gap-1.5 rounded-md border px-3 text-sm",
46097
+ t.id === table.id ? "border-primary bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground"
46098
+ ].join(" "),
46099
+ children: [
46100
+ t.name,
46101
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("span", { className: "text-xs tabular-nums text-muted-foreground", children: t.rows })
46102
+ ]
46103
+ },
46104
+ t.id
46105
+ )),
46106
+ aside ? /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { className: "ml-auto", children: aside }) : null
46107
+ ] }) : null,
46108
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)(RecordsMount, { config: preview.config, host: bound, children: [
46109
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { className: "min-h-[32rem] min-w-0 overflow-hidden rounded-xl border border-border bg-card", children: /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(TablePage, { tableId: table.id }, table.id) }),
46110
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(PreviewForms, { preview, booking }),
46111
+ preview.dashboards.length ? /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("section", { className: "flex min-w-0 flex-col gap-3", "aria-label": "Dashboards", "data-template-preview-dashboards": preview.dashboards.length, children: [
46112
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("h2", { className: "text-lg font-semibold tracking-tight", children: "Dashboards" }),
46113
+ preview.dashboards.map((d) => /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { className: "min-w-0 overflow-hidden rounded-xl border border-border bg-card p-3", "data-template-preview-dashboard": d.token, children: /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(DashboardCanvas, { tableId: d.tableId, activeDashboardId: d.id }) }, d.id))
46114
+ ] }) : null
46115
+ ] })
46116
+ ] });
46117
+ }
46118
+ function PreviewForms({ preview, booking }) {
46119
+ const client = (0, import_react264.useRecordsClient)();
46120
+ const [forms, setForms] = (0, import_react263.useState)(null);
46121
+ const [pages, setPages] = (0, import_react263.useState)([]);
46122
+ (0, import_react263.useEffect)(() => {
46123
+ let gone = false;
46124
+ void client.forms().then((answered) => {
46125
+ if (!gone) setForms(answered.ok ? answered.data.map(formFromSummary) : []);
46126
+ });
46127
+ const wanted = booking ? preview.forms.filter((f) => f.booking) : [];
46128
+ void Promise.all(
46129
+ wanted.map(async (f) => {
46130
+ const answered = await preview.config.dataSource.rpc("booking_public", { p_form_id: f.id });
46131
+ return answered.error ? null : { id: f.id, page: answered.data };
46132
+ })
46133
+ ).then((got) => {
46134
+ if (!gone) setPages(got.filter((g) => g !== null));
46135
+ });
46136
+ return () => {
46137
+ gone = true;
46138
+ };
46139
+ }, [client, preview, booking]);
46140
+ if (!forms?.length && !pages.length) return null;
46141
+ return /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("section", { className: "flex min-w-0 flex-col gap-3", "aria-label": "Forms and booking", "data-template-preview-forms": forms?.length ?? 0, children: [
46142
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("h2", { className: "text-lg font-semibold tracking-tight", children: "Forms and booking" }),
46143
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { className: "grid min-w-0 gap-4 md:grid-cols-2", children: (forms ?? []).map((f) => /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "min-w-0 rounded-xl border border-border bg-card p-4", "data-template-preview-form": f.slug ?? f.id, children: [
46144
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("h3", { className: "mb-3 text-sm font-semibold", children: f.name }),
46145
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(FormRunner, { form: f })
46146
+ ] }, f.id)) }),
46147
+ booking ? pages.map((p) => /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "min-w-0 rounded-xl border border-border bg-card p-4", "data-template-preview-booking": p.id, children: [
46148
+ /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("h3", { className: "mb-3 text-sm font-semibold", children: String(p.page["title"] ?? "") }),
46149
+ booking(p.page)
46150
+ ] }, p.id)) : null
46151
+ ] });
46152
+ }
46057
46153
  //# sourceMappingURL=index.cjs.map