@ai-matrx/records-ui 0.78.0 → 0.79.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 CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.79.0
4
+
5
+ **A joined column says what joins it.** (lane STORE-TAILS-2, VERIFIER-11 finding F5.)
6
+
7
+ Greenline Landscaping Crew's Jobs board, "Job label" joining Job Number and Service Type,
8
+ read `GL-2034mow_edge` on all 35 rows — beside a cell reading `Mow & Edge`. Two wrongs in
9
+ one string. The machine's word is the store's half and is fixed there (`custom.rule_eval`'s
10
+ concat arm now resolves a choice to its label and a relation to its display words through
11
+ `custom.field_words`, while every comparison keeps the stored value). The missing separator
12
+ is this package's: nothing on the field panel ever asked for one, so there was no way to
13
+ send one.
14
+
15
+ * **"Joined by"** — a new box on the worked-out-column builder, offered only for *Join them
16
+ together*, defaulting to ` — `. It is sent as `expr.separator`, which the store has
17
+ accepted since the migration above. Emptying it sends no key at all, which is exactly what
18
+ every formula written before today already holds, so nothing saved changes its answer.
19
+ * A sum, a product or a difference is not offered the box and never sends the key — a
20
+ control that set something the evaluator ignores would be a setting that silently does
21
+ nothing.
22
+ * **The panel reads an existing worked-out column back** — its operation, its columns and
23
+ its separator — instead of starting at "concat" with nothing picked whatever the column
24
+ actually held.
25
+ * **And it says out loud what it still cannot do.** Opening an existing worked-out column
26
+ draws no formula controls (this panel never has), and now it says so in a sentence instead
27
+ of standing silent over the one thing that column is. Re-aiming an existing formula needs
28
+ `expr` on `FieldPatch` in `@ai-matrx/records`; the store door already takes it.
29
+
30
+ Consumer action: none.
31
+
3
32
  ## 0.78.0
4
33
 
5
34
  **The button that could not get rid of a table now can, and says what it does.** (lane
package/dist/index.cjs CHANGED
@@ -5132,6 +5132,9 @@ var ROLLUP_OPERATIONS = ROLLUP_AGG_VALUES.map((value) => ({
5132
5132
  value,
5133
5133
  label: ROLLUP_AGG_LABEL[value]
5134
5134
  }));
5135
+ var FORMULA_SEPARATOR_LABEL = "Joined by";
5136
+ var EDITING_A_WORKED_OUT_COLUMN = "This column works out its answer from other columns. Which columns it uses, and what joins them, cannot be changed on this screen yet \u2014 remove it and add it again to change them.";
5137
+ var FORMULA_DEFAULT_SEPARATOR = " \u2014 ";
5135
5138
  var FORMULA_OPERATIONS = FORMULA_OP_VALUES.map((value) => ({
5136
5139
  value,
5137
5140
  label: FORMULA_OP_LABEL[value],
@@ -5168,8 +5171,9 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
5168
5171
  const [of, setOf] = (0, import_react30.useState)(String(field?.config?.["of"] ?? ""));
5169
5172
  const [relationTarget, setRelationTarget] = (0, import_react30.useState)(String(field?.relation_target ?? ""));
5170
5173
  const [display, setDisplay] = (0, import_react30.useState)(field?.display ?? null);
5171
- const [formulaOp, setFormulaOp] = (0, import_react30.useState)("concat");
5172
- const [formulaFields, setFormulaFields] = (0, import_react30.useState)([]);
5174
+ const [formulaOp, setFormulaOp] = (0, import_react30.useState)(() => formulaOpOf(field));
5175
+ const [formulaFields, setFormulaFields] = (0, import_react30.useState)(() => formulaFieldIdsOf(field));
5176
+ const [formulaSeparator, setFormulaSeparator] = (0, import_react30.useState)(() => formulaSeparatorOf(field));
5173
5177
  const [askingToRemove, setAskingToRemove] = (0, import_react30.useState)(false);
5174
5178
  const [notLanded, setNotLanded] = (0, import_react30.useState)(false);
5175
5179
  const others = (0, import_react30.useMemo)(
@@ -5243,7 +5247,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
5243
5247
  ...type === "datetime" ? { kind: withTime ? "datetime" : "date" } : {},
5244
5248
  ...type === "lookup" ? { via, pick } : {},
5245
5249
  ...type === "rollup" ? { via, agg, ...agg === "count" ? {} : { of } } : {},
5246
- ...type === "formula" ? { expr: formulaExpression(formulaOp, formulaFields) } : {}
5250
+ ...type === "formula" ? { expr: formulaExpression(formulaOp, formulaFields, formulaSeparator) } : {}
5247
5251
  };
5248
5252
  const written = await mutation.declare({ table_id: tableId, spec });
5249
5253
  if (written === null) return;
@@ -5301,6 +5305,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
5301
5305
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-muted-foreground", children: editing ? "What a column holds cannot be changed here \u2014 every value already saved would have to be converted. Add a new column and move the values across." : choice?.explanation ?? "" })
5302
5306
  ] })
5303
5307
  ] }),
5308
+ editing && type === "formula" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-muted-foreground", children: EDITING_A_WORKED_OUT_COLUMN }) : null,
5304
5309
  !editing || type === "select" || type === "multi_select" || type === "relation" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5305
5310
  TypeQuestions,
5306
5311
  {
@@ -5329,6 +5334,8 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
5329
5334
  setFormulaOp,
5330
5335
  formulaFields,
5331
5336
  setFormulaFields,
5337
+ formulaSeparator,
5338
+ setFormulaSeparator,
5332
5339
  relations,
5333
5340
  others,
5334
5341
  existingOptionsTable: Boolean(field?.options_table_id ?? field?.config?.["options_table_id"])
@@ -5513,10 +5520,30 @@ function startingType(field) {
5513
5520
  if (fieldTypeChoice(kind)) return kind;
5514
5521
  return "text";
5515
5522
  }
5516
- function formulaExpression(op, fieldIds) {
5523
+ function formulaExpression(op, fieldIds, separator) {
5517
5524
  const args = fieldIds.filter((id) => id !== "").map((id) => ({ field: id }));
5518
- if (args.length === 0) return { op, args: [{ const: "" }] };
5519
- return { op, args };
5525
+ const joined = op === "concat" && separator !== "" ? { separator } : {};
5526
+ if (args.length === 0) return { op, ...joined, args: [{ const: "" }] };
5527
+ return { op, ...joined, args };
5528
+ }
5529
+ function formulaExprOf(field) {
5530
+ const expr = field?.config?.["expr"];
5531
+ return expr !== null && typeof expr === "object" ? expr : null;
5532
+ }
5533
+ function formulaOpOf(field) {
5534
+ const op = formulaExprOf(field)?.["op"];
5535
+ return typeof op === "string" && op !== "" ? op : "concat";
5536
+ }
5537
+ function formulaFieldIdsOf(field) {
5538
+ const args = formulaExprOf(field)?.["args"];
5539
+ if (!Array.isArray(args)) return [];
5540
+ return args.map((a) => a !== null && typeof a === "object" ? a["field"] : void 0).filter((id) => typeof id === "string" && id !== "");
5541
+ }
5542
+ function formulaSeparatorOf(field) {
5543
+ const expr = formulaExprOf(field);
5544
+ if (expr === null) return FORMULA_DEFAULT_SEPARATOR;
5545
+ const sep = expr["separator"];
5546
+ return typeof sep === "string" ? sep : "";
5520
5547
  }
5521
5548
  function TypeQuestions(props) {
5522
5549
  const { type } = props;
@@ -5655,7 +5682,21 @@ function TypeQuestions(props) {
5655
5682
  );
5656
5683
  }) }),
5657
5684
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "In the order you pick them. Columns are remembered by which column they are, so renaming one never breaks this." })
5658
- ] })
5685
+ ] }),
5686
+ props.formulaOp === "concat" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col gap-1", children: [
5687
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_design_system15.Label, { className: "text-xs", children: FORMULA_SEPARATOR_LABEL }),
5688
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5689
+ import_design_system15.BasicInput,
5690
+ {
5691
+ value: props.formulaSeparator,
5692
+ "aria-label": FORMULA_SEPARATOR_LABEL,
5693
+ placeholder: "\u2014",
5694
+ className: "max-w-[10rem]",
5695
+ onChange: (e) => props.setFormulaSeparator(e.target.value)
5696
+ }
5697
+ ),
5698
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-[11px] text-muted-foreground", children: "What goes between the columns. Leave it empty to run them straight together." })
5699
+ ] }) : null
5659
5700
  ] });
5660
5701
  }
5661
5702
  return null;