@ai-matrx/records-ui 0.53.0 → 0.57.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/dist/index.cjs CHANGED
@@ -94,6 +94,7 @@ __export(src_exports, {
94
94
  MyChecklistSteps: () => MyChecklistSteps,
95
95
  NEEDS_A_SENTENCE: () => NEEDS_A_SENTENCE,
96
96
  NOT_ANSWERED_YET: () => NOT_ANSWERED_YET,
97
+ NOT_DRAWN_HERE: () => NOT_DRAWN_HERE,
97
98
  NO_AGENT_PORT: () => NO_AGENT_PORT,
98
99
  NO_CHAT_REASON: () => NO_CHAT_REASON,
99
100
  NO_COLUMNS_WHY: () => NO_COLUMNS_WHY,
@@ -162,6 +163,8 @@ __export(src_exports, {
162
163
  chooseSurface: () => chooseSurface,
163
164
  colorFromTheValue: () => colorFromTheValue,
164
165
  columnForField: () => columnForField,
166
+ conditionInWords: () => conditionInWords,
167
+ conditionIsSimple: () => conditionIsSimple,
165
168
  conditionValue: () => conditionValue,
166
169
  controlFor: () => controlFor,
167
170
  currencyCodeFor: () => currencyCodeFor,
@@ -2728,7 +2731,7 @@ function EnrichPanel({ tableId, fieldId, className }) {
2728
2731
  var import_react17 = require("react");
2729
2732
  var import_design_system8 = require("@ai-matrx/design-system");
2730
2733
  var import_jsx_runtime11 = require("react/jsx-runtime");
2731
- function PastePreview({ plan, noun, onConfirm, onCancel }) {
2734
+ function PastePreview({ plan, noun, nounPlural, onConfirm, onCancel }) {
2732
2735
  const [busy, setBusy] = (0, import_react17.useState)(false);
2733
2736
  const [outcome, setOutcome] = (0, import_react17.useState)(null);
2734
2737
  const nothingToDo = plan.cells === 0;
@@ -2748,7 +2751,7 @@ function PastePreview({ plan, noun, onConfirm, onCancel }) {
2748
2751
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_design_system8.Separator, {}),
2749
2752
  outcome === null ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2750
2753
  nothingToDo ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm", children: "Nothing in what you pasted can be written to this table. Every cell is listed below with the reason." }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-sm", children: [
2751
- sentence(plan, noun),
2754
+ sentence(plan, noun, nounPlural),
2752
2755
  " The values below are exactly what will be stored."
2753
2756
  ] }),
2754
2757
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PlanTable, { plan }),
@@ -2788,7 +2791,7 @@ function PastePreview({ plan, noun, onConfirm, onCancel }) {
2788
2791
  setBusy(true);
2789
2792
  void onConfirm().then(setOutcome).finally(() => setBusy(false));
2790
2793
  },
2791
- children: busy ? "Writing\u2026" : confirmLabel(plan, noun)
2794
+ children: busy ? "Writing\u2026" : confirmLabel(plan, noun, nounPlural)
2792
2795
  }
2793
2796
  )
2794
2797
  ] })
@@ -2843,19 +2846,19 @@ function plural(n, one, many) {
2843
2846
  function summarize(plan) {
2844
2847
  return `${plural(plan.cells, "value", "values")} across ${plural(plan.rows.length, "row", "rows")}`;
2845
2848
  }
2846
- function sentence(plan, noun) {
2849
+ function sentence(plan, noun, nounPlural) {
2847
2850
  const parts = [];
2848
- if (plan.updates > 0) parts.push(`change ${plural(plan.updates, noun, `${noun}s`)}`);
2849
- if (plan.creates > 0) parts.push(`create ${plural(plan.creates, `new ${noun}`, `new ${noun}s`)}`);
2851
+ if (plan.updates > 0) parts.push(`change ${plural(plan.updates, noun, nounPlural)}`);
2852
+ if (plan.creates > 0) parts.push(`create ${plural(plan.creates, `new ${noun}`, `new ${nounPlural}`)}`);
2850
2853
  const what = parts.length === 2 ? `${parts[0]} and ${parts[1]}` : parts[0] ?? "write nothing";
2851
2854
  return `This will ${what}.`;
2852
2855
  }
2853
- function confirmLabel(plan, noun) {
2856
+ function confirmLabel(plan, noun, nounPlural) {
2854
2857
  if (plan.creates > 0 && plan.updates === 0) {
2855
- return `Create ${plural(plan.creates, noun, `${noun}s`)}`;
2858
+ return `Create ${plural(plan.creates, noun, nounPlural)}`;
2856
2859
  }
2857
2860
  if (plan.creates > 0) return `Write ${plural(plan.rows.length, "row", "rows")}`;
2858
- return `Update ${plural(plan.updates, noun, `${noun}s`)}`;
2861
+ return `Update ${plural(plan.updates, noun, nounPlural)}`;
2859
2862
  }
2860
2863
 
2861
2864
  // src/pasteValues.ts
@@ -2890,13 +2893,21 @@ function valueFromPastedText(field, raw) {
2890
2893
  return { value: negative ? -n : n };
2891
2894
  }
2892
2895
  case "datetime": {
2896
+ const wantsTime = String(field.config?.["kind"] ?? "date") === "datetime";
2897
+ const plainDay = /^\s*(\d{4})-(\d{2})-(\d{2})\s*$/.exec(raw);
2898
+ if (!wantsTime && plainDay) return { value: `${plainDay[1]}-${plainDay[2]}-${plainDay[3]}` };
2893
2899
  const at = Date.parse(text);
2894
2900
  if (Number.isNaN(at)) {
2895
2901
  return {
2896
2902
  refusal: `${label} holds a date, and \u201C${raw}\u201D is not one this can read. Paste it as 2026-09-21, or as your spreadsheet's own date format.`
2897
2903
  };
2898
2904
  }
2899
- return { value: new Date(at).toISOString() };
2905
+ const parsed = new Date(at);
2906
+ if (wantsTime) return { value: parsed.toISOString() };
2907
+ const y = parsed.getFullYear();
2908
+ const m = String(parsed.getMonth() + 1).padStart(2, "0");
2909
+ const d = String(parsed.getDate()).padStart(2, "0");
2910
+ return { value: `${y}-${m}-${d}` };
2900
2911
  }
2901
2912
  case "json": {
2902
2913
  try {
@@ -3087,42 +3098,55 @@ function Grid({
3087
3098
  const fieldList = fields.data ?? [];
3088
3099
  const byRecord = /* @__PURE__ */ new Map();
3089
3100
  const order = [];
3090
- for (const patch of sheetPlan.accepted) {
3091
- let held = byRecord.get(patch.rowId);
3101
+ const take = (rowId, columnId, value) => {
3102
+ let held = byRecord.get(rowId);
3092
3103
  if (held === void 0) {
3093
3104
  held = {};
3094
- byRecord.set(patch.rowId, held);
3095
- order.push(patch.rowId);
3105
+ byRecord.set(rowId, held);
3106
+ order.push(rowId);
3096
3107
  }
3097
- held[patch.columnId] = patch.value === null ? "" : String(patch.value);
3108
+ held[columnId] = value === null || value === void 0 ? "" : String(value);
3109
+ };
3110
+ for (const patch of sheetPlan.accepted) take(patch.rowId, patch.columnId, patch.value);
3111
+ for (const refused of sheetPlan.refusals) {
3112
+ if (refused.rowId === null) continue;
3113
+ take(refused.rowId, refused.columnId, refused.value);
3098
3114
  }
3115
+ const onScreen = rows.map((r) => r.id).filter((id) => byRecord.has(id));
3116
+ const touched = [...onScreen, ...order.filter((id) => !onScreen.includes(id))];
3099
3117
  const columnKeys = Array.from(
3100
3118
  /* @__PURE__ */ new Set([
3101
3119
  ...sheetPlan.accepted.map((p) => p.columnId),
3120
+ ...sheetPlan.refusals.map((r) => r.columnId),
3102
3121
  ...sheetPlan.overflow.flatMap((row) => Object.keys(row))
3103
3122
  ])
3104
3123
  );
3105
3124
  const built = planPastedBlock({
3106
3125
  fields: fieldList,
3107
3126
  columnKeys,
3108
- onExisting: order.map((recordId) => ({
3127
+ onExisting: touched.map((recordId) => ({
3109
3128
  recordId,
3110
3129
  raw: columnKeys.map((key) => byRecord.get(recordId)?.[key])
3111
3130
  })),
3112
3131
  onNew: sheetPlan.overflow.map((row) => columnKeys.map((key) => row[key])),
3113
3132
  pastRightEdge: sheetPlan.pastRightEdge
3114
3133
  });
3134
+ const notWritable = /* @__PURE__ */ new Map();
3115
3135
  for (const refused of sheetPlan.refusals) {
3136
+ if (built.refusals.some((r) => r.column === columnLabel(fieldList, refused.columnId))) continue;
3137
+ notWritable.set(refused.columnId, refused.reason);
3138
+ }
3139
+ for (const [columnId, why] of notWritable) {
3116
3140
  built.refusals.push({
3117
3141
  fromLine: 0,
3118
- column: refused.columnId,
3142
+ column: columnLabel(fieldList, columnId),
3119
3143
  raw: "",
3120
- why: refused.reason
3144
+ why
3121
3145
  });
3122
3146
  }
3123
3147
  setPastePlan({ plan: built });
3124
3148
  },
3125
- [fields.data]
3149
+ [fields.data, rows]
3126
3150
  );
3127
3151
  const applyPaste = (0, import_react18.useCallback)(async () => {
3128
3152
  const held = pastePlan;
@@ -3433,6 +3457,7 @@ function Grid({
3433
3457
  {
3434
3458
  plan: pastePlan.plan,
3435
3459
  noun: table.data?.label_singular ?? "record",
3460
+ nounPlural: table.data?.label_plural ?? `${table.data?.label_singular ?? "record"}s`,
3436
3461
  onConfirm: applyPaste,
3437
3462
  onCancel: () => setPastePlan(null)
3438
3463
  }
@@ -3450,6 +3475,10 @@ function Grid({
3450
3475
  ] })
3451
3476
  ] }) });
3452
3477
  }
3478
+ function columnLabel(fields, columnId) {
3479
+ const field = fields.find((f) => f.key === columnId);
3480
+ return field ? fieldName(field) : humanize(columnId);
3481
+ }
3453
3482
  function AddFieldButton({ onClick }) {
3454
3483
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3455
3484
  import_design_system9.Button,
@@ -4788,6 +4817,60 @@ var CONDITION_OPS = [
4788
4817
  { op: "gt", label: "is more than" },
4789
4818
  { op: "lt", label: "is less than" }
4790
4819
  ];
4820
+ function conditionIsSimple(expr) {
4821
+ if (expr === null || expr === void 0) return true;
4822
+ const node = expr;
4823
+ if (typeof node.op !== "string" || !CONDITION_OPS.some((c) => c.op === node.op)) return false;
4824
+ if (!Array.isArray(node.args)) return false;
4825
+ const first = node.args[0];
4826
+ if (!first || typeof first.field !== "string") return false;
4827
+ if (node.op === "present") return node.args.length === 1;
4828
+ const second = node.args[1];
4829
+ return node.args.length === 2 && !!second && "const" in second;
4830
+ }
4831
+ function conditionInWords(expr, fields) {
4832
+ const name = (id) => {
4833
+ const f = fields.find((x) => x.id === id);
4834
+ return f ? f.label || f.key : "a column";
4835
+ };
4836
+ const said = {
4837
+ eq: "is",
4838
+ ne: "is not",
4839
+ gt: "is more than",
4840
+ lt: "is less than",
4841
+ gte: "is at least",
4842
+ lte: "is at most",
4843
+ and: "and",
4844
+ or: "or",
4845
+ matches: "matches"
4846
+ };
4847
+ const walk = (node) => {
4848
+ if (node === null || node === void 0) return "nothing";
4849
+ const n = node;
4850
+ if ("const" in n) return JSON.stringify(n["const"]);
4851
+ if (typeof n["field"] === "string" && !("op" in n)) return name(n["field"]);
4852
+ const op = typeof n["op"] === "string" ? n["op"] : "";
4853
+ if (op === "previous" && typeof n["field"] === "string") return `what ${name(n["field"])} used to say`;
4854
+ if (op === "sibling_count") {
4855
+ const same = n["same"] ?? [];
4856
+ const differs = n["differs"] ?? [];
4857
+ const sameWords = same.length > 0 ? ` for the same ${same.map(name).join(" and ")}` : "";
4858
+ const diffWords = differs.length > 0 ? ` from a different ${differs.map(name).join(" and ")}` : "";
4859
+ return `how many others there are${sameWords}${diffWords}`;
4860
+ }
4861
+ if (op === "stage_count") return "how many are in that column already";
4862
+ if (op === "actor_at_least") return `whoever is moving it is at least ${String(n["level"] ?? "somebody")}`;
4863
+ const args = Array.isArray(n["args"]) ? n["args"] : [];
4864
+ if (op === "not") return `not ${walk(args[0])}`;
4865
+ if (op === "present") return `${walk(args[0])} has been answered`;
4866
+ const word = said[op] ?? op;
4867
+ if (op === "and" || op === "or") return args.map(walk).join(` ${word} `);
4868
+ if (args.length === 2) return `${walk(args[0])} ${word} ${walk(args[1])}`;
4869
+ if (args.length === 1) return `${walk(args[0])} ${word}`;
4870
+ return op;
4871
+ };
4872
+ return walk(expr);
4873
+ }
4791
4874
  function conditionValue(typed) {
4792
4875
  const trimmed = typed.trim();
4793
4876
  if (trimmed === "") return typed;
@@ -4859,6 +4942,7 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
4859
4942
  var import_jsx_runtime18 = require("react/jsx-runtime");
4860
4943
  var NO_RULES_YET = "Nothing has to be true to get here yet.";
4861
4944
  var NEEDS_A_SENTENCE = "Write the sentence somebody reads when this stops them, the way you would say it out loud.";
4945
+ var NOT_DRAWN_HERE = "This part of the rule is not something this builder can draw yet";
4862
4946
  var ON_FAIL = [
4863
4947
  { value: "refuse", label: "Turn it away", note: "The card stays where it is and the person is told why." },
4864
4948
  {
@@ -5039,7 +5123,7 @@ function StageRulesSection({ tableId, stage, className }) {
5039
5123
  }
5040
5124
  ),
5041
5125
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5042
- ConditionRow,
5126
+ Clause,
5043
5127
  {
5044
5128
  lead: "When",
5045
5129
  emptyLabel: "every card",
@@ -5049,7 +5133,7 @@ function StageRulesSection({ tableId, stage, className }) {
5049
5133
  }
5050
5134
  ),
5051
5135
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5052
- ConditionRow,
5136
+ Clause,
5053
5137
  {
5054
5138
  lead: "it cannot get here unless",
5055
5139
  emptyLabel: "\u2014 pick a column \u2014",
@@ -5093,6 +5177,50 @@ function StageRulesSection({ tableId, stage, className }) {
5093
5177
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_design_system15.Separator, {})
5094
5178
  ] });
5095
5179
  }
5180
+ function Clause({
5181
+ lead,
5182
+ emptyLabel,
5183
+ expr,
5184
+ fields,
5185
+ onChange
5186
+ }) {
5187
+ const [replacing, setReplacing] = (0, import_react29.useState)(false);
5188
+ const drawable = conditionIsSimple(expr) || replacing;
5189
+ if (drawable) {
5190
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5191
+ ConditionRow,
5192
+ {
5193
+ lead,
5194
+ emptyLabel,
5195
+ expr: replacing && !conditionIsSimple(expr) ? null : expr,
5196
+ fields,
5197
+ onChange
5198
+ }
5199
+ );
5200
+ }
5201
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-wrap items-center gap-1 text-xs", "data-testid": "stage-rule-clause-in-words", children: [
5202
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-muted-foreground", children: lead }),
5203
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-medium", children: conditionInWords(expr, fields) }),
5204
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-muted-foreground", children: [
5205
+ "\u2014 ",
5206
+ NOT_DRAWN_HERE,
5207
+ ", so it is shown as it reads."
5208
+ ] }),
5209
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5210
+ import_design_system15.Button,
5211
+ {
5212
+ size: "sm",
5213
+ variant: "ghost",
5214
+ className: "h-6",
5215
+ onClick: () => {
5216
+ setReplacing(true);
5217
+ onChange(null);
5218
+ },
5219
+ children: "Replace it"
5220
+ }
5221
+ )
5222
+ ] });
5223
+ }
5096
5224
 
5097
5225
  // src/TableSettings.tsx
5098
5226
  var import_react31 = require("react");
@@ -8827,7 +8955,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
8827
8955
  const all = tables.data ?? [];
8828
8956
  const missingTie = ticked.filter((e) => e.namesVia.trim() === "");
8829
8957
  const ready = clientTableId !== null && ticked.length > 0 && missingTie.length === 0;
8830
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { className: (0, import_design_system30.cn)("flex flex-col gap-3", className), children: [
8958
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { className: (0, import_design_system30.cn)("@container flex flex-col gap-3", className), children: [
8831
8959
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("header", { className: "flex items-center gap-2", children: [
8832
8960
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h3", { className: "text-sm font-medium", children: existing ? "Portal" : "New client portal" }),
8833
8961
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1" }),
@@ -8968,7 +9096,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
8968
9096
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
8969
9097
  import_design_system30.BasicInput,
8970
9098
  {
8971
- className: "h-8 w-56",
9099
+ className: "h-8 w-full @md:w-56",
8972
9100
  value: inviteEmail,
8973
9101
  onChange: (e) => setInviteEmail(e.target.value)
8974
9102
  }
@@ -8979,7 +9107,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
8979
9107
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
8980
9108
  import_design_system30.BasicInput,
8981
9109
  {
8982
- className: "h-8 w-80",
9110
+ className: "h-8 w-full @md:w-80",
8983
9111
  placeholder: "the record id",
8984
9112
  value: inviteRecordId,
8985
9113
  onChange: (e) => setInviteRecordId(e.target.value)
@@ -9676,7 +9804,7 @@ function DigestScheduler({
9676
9804
  setPreview(answered.data);
9677
9805
  }
9678
9806
  if (views === null && !error) return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Skeleton, { className: (0, import_design_system33.cn)("h-56 w-full", className) });
9679
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { className: (0, import_design_system33.cn)("flex flex-col gap-3", className), children: [
9807
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { className: (0, import_design_system33.cn)("@container flex flex-col gap-3", className), children: [
9680
9808
  /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("header", { className: "flex items-center gap-2", children: [
9681
9809
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h3", { className: "text-sm font-medium", children: "Send this on a schedule" }),
9682
9810
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex-1" }),
@@ -9711,7 +9839,7 @@ function DigestScheduler({
9711
9839
  ),
9712
9840
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-xs text-muted-foreground", children: "The first choice saves what is on screen as a view, so the summary and the screen stay the same thing." })
9713
9841
  ] }),
9714
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
9842
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "grid grid-cols-1 gap-2 @md:grid-cols-2", children: [
9715
9843
  /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("label", { className: "flex flex-col gap-1", children: [
9716
9844
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Label, { className: "text-xs font-medium", children: "How often" }),
9717
9845
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
@@ -10452,7 +10580,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10452
10580
  if (!rights.admin) {
10453
10581
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: (0, import_design_system36.cn)("text-xs text-muted-foreground", className), children: rights.why("structure") });
10454
10582
  }
10455
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: (0, import_design_system36.cn)("flex min-h-0 gap-3", className), children: [
10583
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: (0, import_design_system36.cn)("@container flex min-h-0 flex-col gap-3 @3xl:flex-row", className), children: [
10456
10584
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-2 overflow-auto", children: [
10457
10585
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Forms", children: [
10458
10586
  forms.map((form) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
@@ -10613,7 +10741,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10613
10741
  ] })
10614
10742
  ] })
10615
10743
  ] }),
10616
- draft ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("aside", { className: "w-96 shrink-0 overflow-auto rounded border p-2", children: [
10744
+ draft ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("aside", { className: "w-full shrink-0 overflow-auto rounded border p-2 @3xl:w-96", children: [
10617
10745
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("h4", { className: "px-1 text-sm font-medium", children: draft.name }),
10618
10746
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(FormRunner, { form: draft, preview: true })
10619
10747
  ] }) : null
@@ -12379,195 +12507,200 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12379
12507
  }
12380
12508
  const url = formId ? `${publicOrigin}${(0, import_records9.bookingPath)(formId)}` : null;
12381
12509
  const shown = offer ?? null;
12382
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("section", { className: (0, import_design_system45.cn)("flex flex-col gap-3", className), children: [
12383
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("header", { className: "flex items-center gap-2", children: [
12384
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("h3", { className: "text-sm font-medium", children: existing ? "Booking page" : "New booking page" }),
12385
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex-1" }),
12386
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Button, { size: "sm", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" }),
12387
- onClose ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Button, { size: "sm", variant: "ghost", onClick: onClose, children: "Close" }) : null
12388
- ] }),
12389
- error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(RefusalNotice, { error }) : null,
12390
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12391
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "What it is called" }),
12392
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.BasicInput, { value: title, onChange: (e) => setTitle(e.target.value) })
12393
- ] }),
12394
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
12395
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12396
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "How long one appointment is" }),
12397
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12398
- "select",
12399
- {
12400
- className: "h-9 rounded border bg-background px-2 text-sm",
12401
- value: minutes,
12402
- onChange: (e) => setMinutes(Number(e.target.value)),
12403
- children: LENGTHS.map((n) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("option", { value: n, children: [
12404
- n,
12405
- " minutes"
12406
- ] }, n))
12407
- }
12408
- )
12409
- ] }),
12410
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12411
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "Gap kept after each one" }),
12412
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12413
- "select",
12414
- {
12415
- className: "h-9 rounded border bg-background px-2 text-sm",
12416
- value: buffer,
12417
- onChange: (e) => setBuffer(Number(e.target.value)),
12418
- children: [0, 5, 10, 15, 30].map((n) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("option", { value: n, children: n === 0 ? "No gap \u2014 back to back" : `${n} minutes` }, n))
12419
- }
12420
- )
12421
- ] }),
12422
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12423
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "Earliest somebody may book" }),
12424
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12425
- "select",
12426
- {
12427
- className: "h-9 rounded border bg-background px-2 text-sm",
12428
- value: lead,
12429
- onChange: (e) => setLead(Number(e.target.value)),
12430
- children: [0, 60, 120, 240, 1440].map((n) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("option", { value: n, children: n === 0 ? "Right away" : n === 1440 ? "A day from now" : `${n / 60} hour${n === 60 ? "" : "s"} from now` }, n))
12431
- }
12432
- )
12510
+ return (
12511
+ // THE RAIL IS 384px WIDE AND THE VIEWPORT IS NOT (walk 1, 2026-09-21):
12512
+ // every split below is a CONTAINER query, so this screen is the same
12513
+ // screen in a rail, a dialog and a full page.
12514
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("section", { className: (0, import_design_system45.cn)("@container flex flex-col gap-3", className), children: [
12515
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("header", { className: "flex items-center gap-2", children: [
12516
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("h3", { className: "text-sm font-medium", children: existing ? "Booking page" : "New booking page" }),
12517
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex-1" }),
12518
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Button, { size: "sm", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" }),
12519
+ onClose ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Button, { size: "sm", variant: "ghost", onClick: onClose, children: "Close" }) : null
12433
12520
  ] }),
12521
+ error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(RefusalNotice, { error }) : null,
12434
12522
  /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12435
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "Most in one day" }),
12436
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12437
- import_design_system45.BasicInput,
12438
- {
12439
- type: "number",
12440
- min: 1,
12441
- value: perDay,
12442
- onChange: (e) => setPerDay(Math.max(1, Number(e.target.value) || 1))
12443
- }
12444
- )
12523
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "What it is called" }),
12524
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.BasicInput, { value: title, onChange: (e) => setTitle(e.target.value) })
12445
12525
  ] }),
12446
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12447
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "How far ahead the page offers" }),
12448
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12449
- "select",
12450
- {
12451
- className: "h-9 rounded border bg-background px-2 text-sm",
12452
- value: days,
12453
- onChange: (e) => setDays(Number(e.target.value)),
12454
- children: [7, 14, 30, 60, 90].map((n) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("option", { value: n, children: [
12455
- n,
12456
- " days"
12457
- ] }, n))
12458
- }
12459
- )
12460
- ] })
12461
- ] }),
12462
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
12463
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "Hours you are free" }),
12464
- windows.map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-2", children: [
12465
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex w-20 items-center gap-1.5 text-xs", children: [
12526
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "grid grid-cols-1 gap-2 @md:grid-cols-2", children: [
12527
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12528
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "How long one appointment is" }),
12466
12529
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12467
- import_design_system45.Checkbox,
12530
+ "select",
12468
12531
  {
12469
- checked: w.on,
12470
- onCheckedChange: (on) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, on: Boolean(on) } : x))
12532
+ className: "h-9 rounded border bg-background px-2 text-sm",
12533
+ value: minutes,
12534
+ onChange: (e) => setMinutes(Number(e.target.value)),
12535
+ children: LENGTHS.map((n) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("option", { value: n, children: [
12536
+ n,
12537
+ " minutes"
12538
+ ] }, n))
12471
12539
  }
12472
- ),
12473
- DAYS[i]?.label
12540
+ )
12474
12541
  ] }),
12475
- w.on ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
12542
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12543
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "Gap kept after each one" }),
12476
12544
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12477
- import_design_system45.BasicInput,
12545
+ "select",
12478
12546
  {
12479
- type: "time",
12480
- className: "h-8 w-28",
12481
- value: w.from,
12482
- onChange: (e) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, from: e.target.value } : x))
12547
+ className: "h-9 rounded border bg-background px-2 text-sm",
12548
+ value: buffer,
12549
+ onChange: (e) => setBuffer(Number(e.target.value)),
12550
+ children: [0, 5, 10, 15, 30].map((n) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("option", { value: n, children: n === 0 ? "No gap \u2014 back to back" : `${n} minutes` }, n))
12483
12551
  }
12484
- ),
12485
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-xs text-muted-foreground", children: "to" }),
12552
+ )
12553
+ ] }),
12554
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12555
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "Earliest somebody may book" }),
12556
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12557
+ "select",
12558
+ {
12559
+ className: "h-9 rounded border bg-background px-2 text-sm",
12560
+ value: lead,
12561
+ onChange: (e) => setLead(Number(e.target.value)),
12562
+ children: [0, 60, 120, 240, 1440].map((n) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("option", { value: n, children: n === 0 ? "Right away" : n === 1440 ? "A day from now" : `${n / 60} hour${n === 60 ? "" : "s"} from now` }, n))
12563
+ }
12564
+ )
12565
+ ] }),
12566
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12567
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "Most in one day" }),
12486
12568
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12487
12569
  import_design_system45.BasicInput,
12488
12570
  {
12489
- type: "time",
12490
- className: "h-8 w-28",
12491
- value: w.to,
12492
- onChange: (e) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, to: e.target.value } : x))
12571
+ type: "number",
12572
+ min: 1,
12573
+ value: perDay,
12574
+ onChange: (e) => setPerDay(Math.max(1, Number(e.target.value) || 1))
12493
12575
  }
12494
12576
  )
12495
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-xs text-muted-foreground", children: "not taking bookings" })
12496
- ] }, w.weekday))
12497
- ] }),
12498
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
12499
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "What to ask the person booking" }),
12500
- askable.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-xs text-muted-foreground", children: "This table has no fields yet, so there is nothing a booking page could ask for. Add a field first \u2014 every question is one of this table's own fields." }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex flex-wrap gap-x-4 gap-y-1.5", children: askable.map((f) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex items-center gap-1.5 text-xs", children: [
12577
+ ] }),
12578
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12579
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "How far ahead the page offers" }),
12580
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12581
+ "select",
12582
+ {
12583
+ className: "h-9 rounded border bg-background px-2 text-sm",
12584
+ value: days,
12585
+ onChange: (e) => setDays(Number(e.target.value)),
12586
+ children: [7, 14, 30, 60, 90].map((n) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("option", { value: n, children: [
12587
+ n,
12588
+ " days"
12589
+ ] }, n))
12590
+ }
12591
+ )
12592
+ ] })
12593
+ ] }),
12594
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
12595
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "Hours you are free" }),
12596
+ windows.map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-2", children: [
12597
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex w-20 items-center gap-1.5 text-xs", children: [
12598
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12599
+ import_design_system45.Checkbox,
12600
+ {
12601
+ checked: w.on,
12602
+ onCheckedChange: (on) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, on: Boolean(on) } : x))
12603
+ }
12604
+ ),
12605
+ DAYS[i]?.label
12606
+ ] }),
12607
+ w.on ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
12608
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12609
+ import_design_system45.BasicInput,
12610
+ {
12611
+ type: "time",
12612
+ className: "h-8 w-28",
12613
+ value: w.from,
12614
+ onChange: (e) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, from: e.target.value } : x))
12615
+ }
12616
+ ),
12617
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-xs text-muted-foreground", children: "to" }),
12618
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12619
+ import_design_system45.BasicInput,
12620
+ {
12621
+ type: "time",
12622
+ className: "h-8 w-28",
12623
+ value: w.to,
12624
+ onChange: (e) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, to: e.target.value } : x))
12625
+ }
12626
+ )
12627
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-xs text-muted-foreground", children: "not taking bookings" })
12628
+ ] }, w.weekday))
12629
+ ] }),
12630
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
12631
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "What to ask the person booking" }),
12632
+ askable.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-xs text-muted-foreground", children: "This table has no fields yet, so there is nothing a booking page could ask for. Add a field first \u2014 every question is one of this table's own fields." }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex flex-wrap gap-x-4 gap-y-1.5", children: askable.map((f) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex items-center gap-1.5 text-xs", children: [
12633
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12634
+ import_design_system45.Checkbox,
12635
+ {
12636
+ checked: asked.includes(f.key),
12637
+ onCheckedChange: (on) => setAsked((prev) => on ? [...prev, f.key] : prev.filter((k) => k !== f.key))
12638
+ }
12639
+ ),
12640
+ fieldName(f)
12641
+ ] }, f.key)) }),
12642
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-xs text-muted-foreground", children: "The time, whether it is booked or cancelled, and who it is with are filled in by the store \u2014 a booking page cannot ask a visitor for any of the three." })
12643
+ ] }),
12644
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12645
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "What they see after booking" }),
12501
12646
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12502
- import_design_system45.Checkbox,
12647
+ import_design_system45.BasicTextarea,
12503
12648
  {
12504
- checked: asked.includes(f.key),
12505
- onCheckedChange: (on) => setAsked((prev) => on ? [...prev, f.key] : prev.filter((k) => k !== f.key))
12649
+ rows: 2,
12650
+ value: confirmation,
12651
+ placeholder: "You're booked. We'll send a reminder the day before \u2014 reply to that message to move it.",
12652
+ onChange: (e) => setConfirmation(e.target.value)
12653
+ }
12654
+ )
12655
+ ] }),
12656
+ shown ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("p", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
12657
+ "Saved. The page offers ",
12658
+ shown.slot_minutes,
12659
+ "-minute appointments in ",
12660
+ shown.timezone,
12661
+ ", up to",
12662
+ " ",
12663
+ shown.max_per_day,
12664
+ " a day, ",
12665
+ shown.days,
12666
+ " days ahead",
12667
+ shown.buffer_minutes > 0 ? `, with ${shown.buffer_minutes} minutes between them` : "",
12668
+ ":",
12669
+ " ",
12670
+ shown.windows.map(
12671
+ (w) => `${DAYS.find((d) => d.weekday === w.weekday)?.label ?? w.weekday} ${w.from}\u2013${w.to}`
12672
+ ).join(", "),
12673
+ "."
12674
+ ] }) : null,
12675
+ url ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex flex-wrap items-center gap-2 rounded-md border p-2.5", children: [
12676
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "min-w-0 flex-1 break-all text-xs", children: url }),
12677
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12678
+ import_design_system45.Button,
12679
+ {
12680
+ size: "sm",
12681
+ variant: "outline",
12682
+ onClick: () => {
12683
+ void navigator.clipboard.writeText(url).then(() => {
12684
+ setCopied(true);
12685
+ window.setTimeout(() => setCopied(false), 1600);
12686
+ }).catch(() => setCopied(false));
12687
+ },
12688
+ children: copied ? "Copied" : "Copy link"
12506
12689
  }
12507
12690
  ),
12508
- fieldName(f)
12509
- ] }, f.key)) }),
12510
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-xs text-muted-foreground", children: "The time, whether it is booked or cancelled, and who it is with are filled in by the store \u2014 a booking page cannot ask a visitor for any of the three." })
12511
- ] }),
12512
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "flex flex-col gap-1", children: [
12513
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_design_system45.Label, { className: "text-xs font-medium", children: "What they see after booking" }),
12514
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12515
- import_design_system45.BasicTextarea,
12516
- {
12517
- rows: 2,
12518
- value: confirmation,
12519
- placeholder: "You're booked. We'll send a reminder the day before \u2014 reply to that message to move it.",
12520
- onChange: (e) => setConfirmation(e.target.value)
12521
- }
12522
- )
12523
- ] }),
12524
- shown ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("p", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
12525
- "Saved. The page offers ",
12526
- shown.slot_minutes,
12527
- "-minute appointments in ",
12528
- shown.timezone,
12529
- ", up to",
12530
- " ",
12531
- shown.max_per_day,
12532
- " a day, ",
12533
- shown.days,
12534
- " days ahead",
12535
- shown.buffer_minutes > 0 ? `, with ${shown.buffer_minutes} minutes between them` : "",
12536
- ":",
12537
- " ",
12538
- shown.windows.map(
12539
- (w) => `${DAYS.find((d) => d.weekday === w.weekday)?.label ?? w.weekday} ${w.from}\u2013${w.to}`
12540
- ).join(", "),
12541
- "."
12542
- ] }) : null,
12543
- url ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex flex-wrap items-center gap-2 rounded-md border p-2.5", children: [
12544
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "min-w-0 flex-1 break-all text-xs", children: url }),
12545
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12546
- import_design_system45.Button,
12547
- {
12548
- size: "sm",
12549
- variant: "outline",
12550
- onClick: () => {
12551
- void navigator.clipboard.writeText(url).then(() => {
12552
- setCopied(true);
12553
- window.setTimeout(() => setCopied(false), 1600);
12554
- }).catch(() => setCopied(false));
12555
- },
12556
- children: copied ? "Copied" : "Copy link"
12557
- }
12558
- ),
12559
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12560
- import_design_system45.Button,
12561
- {
12562
- size: "sm",
12563
- disabled: publishing,
12564
- onClick: () => void publish(!(existing?.published_at && !existing.closed_at)),
12565
- children: publishing ? "\u2026" : existing?.published_at && !existing.closed_at ? "Unpublish" : "Publish"
12566
- }
12567
- ),
12568
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "w-full text-xs text-muted-foreground", children: existing?.published_at && !existing.closed_at ? "Open. Anyone with this link can book a time." : "Not open yet \u2014 the link does not take bookings until you publish it." })
12569
- ] }) : null
12570
- ] });
12691
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
12692
+ import_design_system45.Button,
12693
+ {
12694
+ size: "sm",
12695
+ disabled: publishing,
12696
+ onClick: () => void publish(!(existing?.published_at && !existing.closed_at)),
12697
+ children: publishing ? "\u2026" : existing?.published_at && !existing.closed_at ? "Unpublish" : "Publish"
12698
+ }
12699
+ ),
12700
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "w-full text-xs text-muted-foreground", children: existing?.published_at && !existing.closed_at ? "Open. Anyone with this link can book a time." : "Not open yet \u2014 the link does not take bookings until you publish it." })
12701
+ ] }) : null
12702
+ ] })
12703
+ );
12571
12704
  }
12572
12705
 
12573
12706
  // src/BookingSlots.tsx