@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/CHANGELOG.md +117 -0
- package/dist/index.cjs +331 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +331 -198
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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,
|
|
2849
|
-
if (plan.creates > 0) parts.push(`create ${plural(plan.creates, `new ${noun}`, `new ${
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
3091
|
-
let held = byRecord.get(
|
|
3101
|
+
const take = (rowId, columnId, value) => {
|
|
3102
|
+
let held = byRecord.get(rowId);
|
|
3092
3103
|
if (held === void 0) {
|
|
3093
3104
|
held = {};
|
|
3094
|
-
byRecord.set(
|
|
3095
|
-
order.push(
|
|
3105
|
+
byRecord.set(rowId, held);
|
|
3106
|
+
order.push(rowId);
|
|
3096
3107
|
}
|
|
3097
|
-
held[
|
|
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:
|
|
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:
|
|
3142
|
+
column: columnLabel(fieldList, columnId),
|
|
3119
3143
|
raw: "",
|
|
3120
|
-
why
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
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: "
|
|
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)("
|
|
12447
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.
|
|
12448
|
-
|
|
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
|
-
|
|
12530
|
+
"select",
|
|
12468
12531
|
{
|
|
12469
|
-
|
|
12470
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12545
|
+
"select",
|
|
12478
12546
|
{
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
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
|
-
|
|
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: "
|
|
12490
|
-
|
|
12491
|
-
value:
|
|
12492
|
-
onChange: (e) =>
|
|
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
|
-
] })
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12499
|
-
|
|
12500
|
-
|
|
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.
|
|
12647
|
+
import_design_system45.BasicTextarea,
|
|
12503
12648
|
{
|
|
12504
|
-
|
|
12505
|
-
|
|
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
|
-
|
|
12509
|
-
|
|
12510
|
-
|
|
12511
|
-
|
|
12512
|
-
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
12519
|
-
|
|
12520
|
-
|
|
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
|