@ai-matrx/records-ui 0.107.0 → 0.107.1

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,11 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.107.1
4
+
5
+ TYPEFORM-2 resume: "Email the respondent" in Notify on answer — off by default, enabled only when the form has an email question, kept as `presentation.notify.email_respondent` + `respondent_field` and sent by `custom.form_respondent_copy` through the platform notification sender, worded from the ending reached. The builder preview and its two option boxes follow the organization's knobs (`client.formKnobs`, records 0.80.4) under the form's own override.
6
+
7
+ **Consumer action:** none; install records 0.80.4 with it.
8
+
3
9
  ## 0.107.0
4
10
 
5
11
  TYPEFORM-2, the second pass on forms against Typeform. Runner: Next/Enter refuse an empty required answer ("This field is required") or a bad email / phone / link ("Enter a valid email" …), announced in an `aria-live` alert with the caret kept in the answer, and the old "still needs an answer" line is gone; one large centred question (question ≥ 20 px, answer ≥ 16 px) filling the screen; "press Enter ↵" beside Next; a single choice moves on after a pick (`autoAdvance` prop / `FormSpec.autoAdvance`, default on — knob `forms/choice_auto_advance`); arrow keys move and pick inside the radiogroup and Back lands on the picked choice; focus lands in the first answer after Start; the count is hidden until the store has answered the route once; progress bar `aria-label`; `{{question}}` recall in question words, help and endings (`pipeAnswers`); runner sentences are labels with tooltips. Inputs: email / phone / link carry `inputmode` and `autocomplete`. `FormLookFrame showOwnerHeader` (knob `forms/show_owner_header`, per-form `FormSpec.showOwnerHeader`). Builder: questions listed in respondent order with drag-to-reorder (dnd-kit), unasked fields as "+" chips, the live preview beside the editor (the container query asked itself and never fired), "Notify on answer": Email me (the owner's email subscription, made and handed to the form) and a webhook (a `form_answered` automation, kept as `presentation.notify`); "Show our logo and name" and "Next after a choice" per form; conditions show option labels and badge a missing question. Results: category ticks wrap at words (`wrapTickWords`).
package/dist/index.cjs CHANGED
@@ -37957,14 +37957,26 @@ function formPresentation(spec) {
37957
37957
  theme: themeDocument(spec.theme ?? null),
37958
37958
  ...typeof spec.showOwnerHeader === "boolean" ? { show_owner_header: spec.showOwnerHeader } : {},
37959
37959
  ...typeof spec.autoAdvance === "boolean" ? { auto_advance: spec.autoAdvance } : {},
37960
- ...spec.notify && (spec.notify.webhookUrl || spec.notify.webhookAutomationId) ? { notify: strip({ webhook_url: text(spec.notify.webhookUrl), webhook_automation_id: text(spec.notify.webhookAutomationId) }) } : {}
37960
+ ...spec.notify && (spec.notify.webhookUrl || spec.notify.webhookAutomationId || spec.notify.emailRespondent) ? {
37961
+ notify: strip({
37962
+ webhook_url: text(spec.notify.webhookUrl),
37963
+ webhook_automation_id: text(spec.notify.webhookAutomationId),
37964
+ email_respondent: spec.notify.emailRespondent ? true : null,
37965
+ respondent_field: spec.notify.emailRespondent ? text(spec.notify.respondentField) : null
37966
+ })
37967
+ } : {}
37961
37968
  };
37962
37969
  }
37963
37970
  function notifyFromDocument(raw) {
37964
37971
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
37965
37972
  const d = raw;
37966
- const n = { webhookUrl: text(d["webhook_url"]), webhookAutomationId: text(d["webhook_automation_id"]) };
37967
- return n.webhookUrl || n.webhookAutomationId ? n : null;
37973
+ const n = {
37974
+ webhookUrl: text(d["webhook_url"]),
37975
+ webhookAutomationId: text(d["webhook_automation_id"]),
37976
+ emailRespondent: d["email_respondent"] === true ? true : null,
37977
+ respondentField: text(d["respondent_field"])
37978
+ };
37979
+ return n.webhookUrl || n.webhookAutomationId || n.emailRespondent ? n : null;
37968
37980
  }
37969
37981
  var bool = (v) => typeof v === "boolean" ? v : null;
37970
37982
  function answerRefusal(kind, required, value) {
@@ -42545,6 +42557,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
42545
42557
  const [shownUrl, setShownUrl] = (0, import_react232.useState)(null);
42546
42558
  const [lookOptions, setLookOptions] = (0, import_react232.useState)(null);
42547
42559
  const [lookUnread, setLookUnread] = (0, import_react232.useState)(null);
42560
+ const [orgKnobs, setOrgKnobs] = (0, import_react232.useState)(null);
42548
42561
  const [restoredAt, setRestoredAt] = (0, import_react232.useState)(null);
42549
42562
  const [showResults, setShowResults] = (0, import_react232.useState)(false);
42550
42563
  const [adding, setAdding] = (0, import_react232.useState)(false);
@@ -42581,6 +42594,15 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
42581
42594
  (0, import_react232.useEffect)(() => {
42582
42595
  void load();
42583
42596
  }, [load]);
42597
+ (0, import_react232.useEffect)(() => {
42598
+ let cancelled = false;
42599
+ Promise.resolve().then(() => client.formKnobs()).then((r) => {
42600
+ if (!cancelled && r.ok) setOrgKnobs(r.data);
42601
+ }).catch(() => void 0);
42602
+ return () => {
42603
+ cancelled = true;
42604
+ };
42605
+ }, [client]);
42584
42606
  (0, import_react232.useEffect)(() => {
42585
42607
  if (!forms || forms.length === 0) return;
42586
42608
  if (createOnMount && makeOnMount !== "made") return;
@@ -42881,6 +42903,11 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
42881
42903
  formName: draft.name,
42882
42904
  ruleId: draft.notifyRuleId,
42883
42905
  notify: draft.notify ?? null,
42906
+ emailQuestion: (
42907
+ // An email field first; else a question whose field is named for an email (an imported
42908
+ // "Contact email" kept as text). The store's resolver refuses an answer that is not one.
42909
+ (draft.questions.map((q) => byKey.get(q.field)).find((f) => f && String(editorKindFor(f)) === "email") ?? draft.questions.map((q) => byKey.get(q.field)).find((f) => f && /e-?mail/i.test(`${f.key} ${f.label ?? ""}`)))?.key ?? null
42910
+ ),
42884
42911
  onRule: (ruleId) => void chooseNotify(ruleId),
42885
42912
  onNotify: (notify) => void chooseWebhook(notify)
42886
42913
  }
@@ -42890,7 +42917,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
42890
42917
  /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
42891
42918
  import_design_system118.Checkbox,
42892
42919
  {
42893
- checked: draft.showOwnerHeader ?? true,
42920
+ checked: draft.showOwnerHeader ?? orgKnobs?.show_owner_header ?? true,
42894
42921
  onCheckedChange: (checked) => patch({ showOwnerHeader: checked === true }),
42895
42922
  "aria-label": "Show our logo and name"
42896
42923
  }
@@ -42901,7 +42928,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
42901
42928
  /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
42902
42929
  import_design_system118.Checkbox,
42903
42930
  {
42904
- checked: draft.autoAdvance ?? true,
42931
+ checked: draft.autoAdvance ?? orgKnobs?.choice_auto_advance ?? true,
42905
42932
  onCheckedChange: (checked) => patch({ autoAdvance: checked === true }),
42906
42933
  "aria-label": "Next after a choice"
42907
42934
  }
@@ -43070,7 +43097,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
43070
43097
  {
43071
43098
  className: "w-full shrink-0 overflow-auto rounded border p-2 @3xl:sticky @3xl:top-2 @3xl:max-h-[calc(100dvh-6rem)] @3xl:w-[min(45%,520px)]",
43072
43099
  "data-records-form-preview": "",
43073
- children: showResults && stored ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormResultsPanel, { form: stored }) : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormLookFrame, { look: lookOptions?.look ?? null, title: draft.name, contained: true, showOwnerHeader: draft.showOwnerHeader ?? true, children: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormRunner, { form: draft, preview: true }) })
43100
+ children: showResults && stored ? /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormResultsPanel, { form: stored }) : /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormLookFrame, { look: lookOptions?.look ?? null, title: draft.name, contained: true, showOwnerHeader: draft.showOwnerHeader ?? orgKnobs?.show_owner_header ?? true, children: /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(FormRunner, { form: draft, preview: true, autoAdvance: draft.autoAdvance ?? orgKnobs?.choice_auto_advance ?? true }) })
43074
43101
  }
43075
43102
  )
43076
43103
  ) : null
@@ -43323,6 +43350,7 @@ function NotifyOnAnswer({
43323
43350
  formName,
43324
43351
  ruleId,
43325
43352
  notify,
43353
+ emailQuestion,
43326
43354
  onRule,
43327
43355
  onNotify
43328
43356
  }) {
@@ -43388,7 +43416,7 @@ function NotifyOnAnswer({
43388
43416
  try {
43389
43417
  if (next === "") {
43390
43418
  if (notify?.webhookAutomationId) await client.automationArchive({ automation_id: notify.webhookAutomationId });
43391
- onNotify(null);
43419
+ onNotify(notify?.emailRespondent ? { ...notify, webhookUrl: null, webhookAutomationId: null } : null);
43392
43420
  return;
43393
43421
  }
43394
43422
  const declared2 = await client.automationDeclare({
@@ -43401,22 +43429,43 @@ function NotifyOnAnswer({
43401
43429
  setSaid(Object.values(declared2.data._errors)[0] ?? "Not saved");
43402
43430
  return;
43403
43431
  }
43404
- onNotify({ webhookUrl: next, webhookAutomationId: declared2.data.automation_id });
43432
+ onNotify({ ...notify ?? {}, webhookUrl: next, webhookAutomationId: declared2.data.automation_id });
43405
43433
  } finally {
43406
43434
  setBusy(false);
43407
43435
  }
43408
43436
  }
43409
43437
  return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("section", { className: "flex flex-col gap-2 rounded border px-2.5 py-2 text-xs", "aria-label": "Notify on answer", "data-records-form-notify": "", children: [
43410
43438
  /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "font-medium", children: "Notify on answer" }),
43411
- /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1", children: /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", title: "An email to you each time somebody answers.", children: [
43412
- /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Checkbox, { checked: Boolean(ruleId), disabled: busy, onCheckedChange: (c) => void emailMe(c === true), "aria-label": "Email me" }),
43413
- "Email me",
43414
- chosen && chosen.channel !== "email" ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "text-muted-foreground", children: [
43415
- "(",
43416
- chosen.channel === "sms" ? "text" : "in app",
43417
- ")"
43418
- ] }) : null
43419
- ] }) }),
43439
+ /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1", children: [
43440
+ /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", title: "An email to you each time somebody answers.", children: [
43441
+ /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(import_design_system118.Checkbox, { checked: Boolean(ruleId), disabled: busy, onCheckedChange: (c) => void emailMe(c === true), "aria-label": "Email me" }),
43442
+ "Email me",
43443
+ chosen && chosen.channel !== "email" ? /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("span", { className: "text-muted-foreground", children: [
43444
+ "(",
43445
+ chosen.channel === "sms" ? "text" : "in app",
43446
+ ")"
43447
+ ] }) : null
43448
+ ] }),
43449
+ /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(
43450
+ "label",
43451
+ {
43452
+ className: (0, import_design_system118.cn)("flex items-center gap-2", !emailQuestion && "text-muted-foreground"),
43453
+ title: emailQuestion ? "An email to the person who answered, in your ending's words." : "Add an email question first.",
43454
+ children: [
43455
+ /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
43456
+ import_design_system118.Checkbox,
43457
+ {
43458
+ checked: Boolean(notify?.emailRespondent) && Boolean(emailQuestion),
43459
+ disabled: busy || !emailQuestion,
43460
+ onCheckedChange: (c) => onNotify({ ...notify ?? {}, emailRespondent: c === true ? true : null, respondentField: c === true ? emailQuestion : null }),
43461
+ "aria-label": "Email the respondent"
43462
+ }
43463
+ ),
43464
+ "Email the respondent"
43465
+ ]
43466
+ }
43467
+ )
43468
+ ] }),
43420
43469
  /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)("label", { className: "flex items-center gap-2", children: [
43421
43470
  /* @__PURE__ */ (0, import_jsx_runtime133.jsx)("span", { className: "shrink-0", children: "Webhook" }),
43422
43471
  /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(