@ai-matrx/records-ui 0.113.36 → 0.113.38

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/dist/pickers.js CHANGED
@@ -1036,13 +1036,20 @@ var NOT_ANSWERED_YET = whatYouMayDo(null, false);
1036
1036
  var KERNEL_REASON = "This is one of the tables the platform ships, so its columns are not changed from a screen. Make your own table to change columns.";
1037
1037
  function whatYouMayDoWithTable(table, level, known) {
1038
1038
  const base = whatYouMayDo(level, known);
1039
- if (!table?.is_kernel) return base;
1039
+ if (!table?.is_kernel) return table && tableIsLocked(table) ? lockedStructure(base) : base;
1040
1040
  return {
1041
1041
  ...base,
1042
1042
  structure: false,
1043
1043
  why: (what) => what === "structure" ? KERNEL_REASON : base.why(what)
1044
1044
  };
1045
1045
  }
1046
+ function tableIsLocked(table) {
1047
+ return table?.metadata?.["locked"] === true;
1048
+ }
1049
+ var DATABASE_LOCKED_REASON = "This database is locked, so its columns and settings are read-only. Unlock it to change them.";
1050
+ function lockedStructure(base) {
1051
+ return { ...base, structure: false, why: (what) => what === "structure" ? DATABASE_LOCKED_REASON : base.why(what) };
1052
+ }
1046
1053
 
1047
1054
  // src/host.tsx
1048
1055
  import { jsx as jsx4 } from "react/jsx-runtime";
@@ -4282,10 +4289,46 @@ function SourceArchivedNotice({ fieldId, tableId }) {
4282
4289
  }
4283
4290
 
4284
4291
  // src/Condition.tsx
4285
- import { useEffect as useEffect12, useState as useState17 } from "react";
4292
+ import { createContext as createContext4, useContext as useContext4, useEffect as useEffect12, useState as useState17 } from "react";
4286
4293
  import { BasicInput as BasicInput6, Button as Button10 } from "@ai-matrx/design-system";
4287
4294
  import { optionKey as optionKey2, optionLabel, rankTypedMatches as rankTypedMatches2 } from "@ai-matrx/records";
4288
4295
  import { useOptionalRecordsClient as useOptionalRecordsClient5 } from "@ai-matrx/records/react";
4296
+
4297
+ // src/personTime.ts
4298
+ import { useSyncExternalStore } from "react";
4299
+ import { setColorRuleTimeZone } from "@ai-matrx/design-system/data-table/table-style";
4300
+ var zone = null;
4301
+ var listeners = /* @__PURE__ */ new Set();
4302
+ function getPersonTimeZone() {
4303
+ return zone;
4304
+ }
4305
+ function usePersonTimeZone() {
4306
+ return useSyncExternalStore(
4307
+ (cb) => {
4308
+ listeners.add(cb);
4309
+ return () => listeners.delete(cb);
4310
+ },
4311
+ () => zone,
4312
+ () => null
4313
+ );
4314
+ }
4315
+ function personWallNow(at = /* @__PURE__ */ new Date()) {
4316
+ if (!zone) return at;
4317
+ const parts = new Intl.DateTimeFormat("en-US", {
4318
+ timeZone: zone,
4319
+ year: "numeric",
4320
+ month: "2-digit",
4321
+ day: "2-digit",
4322
+ hour: "2-digit",
4323
+ minute: "2-digit",
4324
+ second: "2-digit",
4325
+ hourCycle: "h23"
4326
+ }).formatToParts(at);
4327
+ const n = (t) => Number(parts.find((p) => p.type === t)?.value ?? 0);
4328
+ return new Date(n("year"), n("month") - 1, n("day"), n("hour"), n("minute"), n("second"));
4329
+ }
4330
+
4331
+ // src/Condition.tsx
4289
4332
  import { Fragment as Fragment7, jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
4290
4333
  var CONDITION_OPS = [
4291
4334
  { op: "eq", label: "is" },
@@ -4296,6 +4339,48 @@ var CONDITION_OPS = [
4296
4339
  { op: "gte", label: "is at least" },
4297
4340
  { op: "lte", label: "is at most" }
4298
4341
  ];
4342
+ var FilterTermsContext = createContext4(false);
4343
+ var RELATIVE_DAY_TERMS = [
4344
+ { kind: "today", label: "is today" },
4345
+ { kind: "tomorrow", label: "is tomorrow" },
4346
+ { kind: "yesterday", label: "is yesterday" },
4347
+ { kind: "this_week", label: "is this week" },
4348
+ { kind: "next_week", label: "is next week" },
4349
+ { kind: "last_week", label: "is last week" },
4350
+ { kind: "this_month", label: "is this month" },
4351
+ { kind: "next_month", label: "is next month" },
4352
+ { kind: "last_month", label: "is last month" },
4353
+ { kind: "before_today", label: "is before today" },
4354
+ { kind: "next_days", label: "is in the next \u2026 days", takesDays: true },
4355
+ { kind: "past_days", label: "is in the past \u2026 days", takesDays: true }
4356
+ ];
4357
+ function relativeDayNode(fieldId, kind, days) {
4358
+ const zone2 = getPersonTimeZone();
4359
+ const window2 = { relative: kind };
4360
+ if (RELATIVE_DAY_TERMS.find((t) => t.kind === kind)?.takesDays) window2["n"] = days ?? 7;
4361
+ if (zone2) window2["tz"] = zone2;
4362
+ return { op: "within", args: [{ field: fieldId }, { const: window2 }] };
4363
+ }
4364
+ function relativeDayOf(expr) {
4365
+ const node = asNode(expr);
4366
+ if (!node || node["op"] !== "within") return null;
4367
+ const [a, b] = argsOf(node);
4368
+ const window2 = asNode(b?.["const"]);
4369
+ if (!a || typeof a["field"] !== "string" || !window2 || typeof window2["relative"] !== "string") return null;
4370
+ const days = typeof window2["n"] === "number" ? window2["n"] : null;
4371
+ return { kind: window2["relative"], days, field: a["field"] };
4372
+ }
4373
+ function meNode(fieldId, negate) {
4374
+ return { op: negate ? "ne" : "eq", args: [{ field: fieldId }, { const: { me: true } }] };
4375
+ }
4376
+ function meOf(expr) {
4377
+ const node = asNode(expr);
4378
+ if (!node || node["op"] !== "eq" && node["op"] !== "ne") return null;
4379
+ const [a, b] = argsOf(node);
4380
+ const value = asNode(b?.["const"]);
4381
+ if (!a || typeof a["field"] !== "string" || !value || value["me"] !== true) return null;
4382
+ return { which: node["op"] === "eq" ? "me" : "not_me", field: a["field"] };
4383
+ }
4299
4384
  function pointsAtARecord(f) {
4300
4385
  return !!f?.field && f.field.type === "relation" && !!f.field.relation_target;
4301
4386
  }
@@ -4366,6 +4451,7 @@ function ChoiceValue({ field, value, onChange }) {
4366
4451
  }
4367
4452
  function conditionIsSimple(expr) {
4368
4453
  if (expr === null || expr === void 0) return true;
4454
+ if (relativeDayOf(expr) !== null || meOf(expr) !== null) return true;
4369
4455
  const node = expr;
4370
4456
  if (typeof node.op !== "string" || !CONDITION_OPS.some((c) => c.op === node.op)) return false;
4371
4457
  if (!Array.isArray(node.args)) return false;
@@ -4410,6 +4496,14 @@ function conditionInWords(expr, fields) {
4410
4496
  const args = Array.isArray(n["args"]) ? n["args"] : [];
4411
4497
  if (op === "not") return `not ${walk(args[0])}`;
4412
4498
  if (op === "present") return `${walk(args[0])} has been answered`;
4499
+ const dayRange = relativeDayOf(n);
4500
+ if (dayRange) {
4501
+ const term = RELATIVE_DAY_TERMS.find((t) => t.kind === dayRange.kind);
4502
+ const said3 = term ? term.label.replace("\u2026", String(dayRange.days ?? 7)) : "is in a day range";
4503
+ return `${name(dayRange.field)} ${said3}`;
4504
+ }
4505
+ const reader = meOf(n);
4506
+ if (reader) return `${name(reader.field)} ${reader.which === "me" ? "is me" : "is not me"}`;
4413
4507
  const word = said2[op] ?? op;
4414
4508
  if (op === "and" || op === "or") return args.map(walk).join(` ${word} `);
4415
4509
  if (args.length === 2) return `${walk(args[0])} ${word} ${walk(args[1])}`;
@@ -4431,11 +4525,31 @@ function conditionValue(typed) {
4431
4525
  }
4432
4526
  function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", className }) {
4433
4527
  const node = expr ?? null;
4434
- const op = node?.op ?? "";
4528
+ const filterTerms = useContext4(FilterTermsContext);
4529
+ const relative = relativeDayOf(expr);
4530
+ const reader = meOf(expr);
4531
+ const op = relative ? `rel:${relative.kind}` : reader ? reader.which : node?.op ?? "";
4435
4532
  const onField = node?.args?.[0]?.field ?? "";
4436
- const value = node?.args?.[1]?.const ?? "";
4533
+ const value = relative || reader ? "" : node?.args?.[1]?.const ?? "";
4437
4534
  const chosen = fields.find((f) => f.id === onField);
4535
+ const chosenKind = chosen?.field ? String(editorKindFor(chosen.field)) : "";
4536
+ const extraTerms = !filterTerms ? [] : chosenKind === "datetime" ? RELATIVE_DAY_TERMS.map((t) => ({ op: `rel:${t.kind}`, label: t.label })) : chosenKind === "member" ? [{ op: "me", label: "is me" }, { op: "not_me", label: "is not me" }] : [];
4537
+ const relativeTerm = relative ? RELATIVE_DAY_TERMS.find((t) => t.kind === relative.kind) : void 0;
4438
4538
  function write(next) {
4539
+ const picked = next.op;
4540
+ const fieldNow = next.field ?? onField;
4541
+ if (fieldNow && picked !== void 0 && (picked.startsWith("rel:") || picked === "me" || picked === "not_me")) {
4542
+ onChange(picked === "me" || picked === "not_me" ? meNode(fieldNow, picked === "not_me") : relativeDayNode(fieldNow, picked.slice(4), relative?.days ?? void 0));
4543
+ return;
4544
+ }
4545
+ if (fieldNow && next.days !== void 0 && relative) {
4546
+ onChange(relativeDayNode(fieldNow, relative.kind, next.days));
4547
+ return;
4548
+ }
4549
+ if (next.field !== void 0 && (relative || reader)) {
4550
+ onChange(next.field ? { op: CONDITION_OPS[0].op, args: [{ field: next.field }, { const: "" }] } : null);
4551
+ return;
4552
+ }
4439
4553
  const chosenOp = next.op ?? (op || (next.field ? CONDITION_OPS[0].op : ""));
4440
4554
  const chosenField = next.field ?? onField;
4441
4555
  const chosenValue = next.value ?? value;
@@ -4477,11 +4591,21 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
4477
4591
  className: "h-8 w-auto min-w-[6rem] text-xs",
4478
4592
  value: op,
4479
4593
  onChange: (next) => write({ op: next }),
4480
- options: CONDITION_OPS.map((c) => ({ value: c.op, label: c.label })),
4594
+ options: [...CONDITION_OPS, ...extraTerms].map((c) => ({ value: c.op, label: c.label })),
4481
4595
  attrs: { "data-condition": "comparison" }
4482
4596
  }
4483
4597
  ),
4484
- op === "present" ? null : pointsAtARecord(chosen) ? (
4598
+ relative ? relativeTerm?.takesDays ? /* @__PURE__ */ jsx23(
4599
+ BasicInput6,
4600
+ {
4601
+ className: "h-8 w-16 text-xs",
4602
+ type: "number",
4603
+ min: 0,
4604
+ "aria-label": "Number of days",
4605
+ value: String(relative.days ?? 7),
4606
+ onChange: (e) => write({ days: Math.max(0, Math.floor(Number(e.target.value) || 0)) })
4607
+ }
4608
+ ) : null : reader || op === "present" ? null : pointsAtARecord(chosen) ? (
4485
4609
  // A RECORD IS PICKED BY ITS WORDS, NEVER TYPED AS AN ID. The same picker the
4486
4610
  // cell uses, on the same `relation_words_many` the cell reads, so a filter and
4487
4611
  // the column it filters can never disagree about what a record is called. It
@@ -5674,36 +5798,6 @@ function readersSentence(count, tableName2) {
5674
5798
  return `${rollups} on ${tableName2 ?? "this table"} read through this.`;
5675
5799
  }
5676
5800
 
5677
- // src/personTime.ts
5678
- import { useSyncExternalStore } from "react";
5679
- var zone = null;
5680
- var listeners = /* @__PURE__ */ new Set();
5681
- function usePersonTimeZone() {
5682
- return useSyncExternalStore(
5683
- (cb) => {
5684
- listeners.add(cb);
5685
- return () => listeners.delete(cb);
5686
- },
5687
- () => zone,
5688
- () => null
5689
- );
5690
- }
5691
- function personWallNow(at = /* @__PURE__ */ new Date()) {
5692
- if (!zone) return at;
5693
- const parts = new Intl.DateTimeFormat("en-US", {
5694
- timeZone: zone,
5695
- year: "numeric",
5696
- month: "2-digit",
5697
- day: "2-digit",
5698
- hour: "2-digit",
5699
- minute: "2-digit",
5700
- second: "2-digit",
5701
- hourCycle: "h23"
5702
- }).formatToParts(at);
5703
- const n = (t) => Number(parts.find((p) => p.type === t)?.value ?? 0);
5704
- return new Date(n("year"), n("month") - 1, n("day"), n("hour"), n("minute"), n("second"));
5705
- }
5706
-
5707
5801
  // src/gridMenus.ts
5708
5802
  function nextFieldSort(fields) {
5709
5803
  let last = 0;