glitchgrab 1.57.0 → 1.58.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/README.md +36 -1
- package/dist/index.js +207 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +207 -13
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +48 -2
- package/dist/server.d.ts +48 -2
- package/dist/server.js +20 -1
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +20 -1
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1023,9 +1023,35 @@ if (!saved.ok) console.error(saved.error); // e.g. WhatsApp code already taken
|
|
|
1023
1023
|
back instead of trusting what you sent.
|
|
1024
1024
|
- **Never throws.** Failures come back as `{ ok: false, error }`, never as defaults.
|
|
1025
1025
|
|
|
1026
|
+
### Asking questions before a demo is booked
|
|
1027
|
+
|
|
1028
|
+
Set `questions` and the booking dialog asks them before it shows the calendar;
|
|
1029
|
+
the WhatsApp booking chat asks the same ones. Each is a list of choices the
|
|
1030
|
+
prospect taps — which language they want the demo in, what kind of firm they
|
|
1031
|
+
run — so the right person on your team takes the call.
|
|
1032
|
+
|
|
1033
|
+
```ts
|
|
1034
|
+
await updateBookingSettings({
|
|
1035
|
+
questions: [
|
|
1036
|
+
{ id: "profession", label: "What best describes you?", options: ["CA", "CS", "Tax consultant", "Other"] },
|
|
1037
|
+
{ id: "language", label: "Which language would you like the demo in?", options: ["English", "Hindi", "Marathi"] },
|
|
1038
|
+
],
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
const demos = await listBookings();
|
|
1042
|
+
demos[0].answers; // [{ id: "language", question: "Which language…", answer: "Hindi" }, …]
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
- Up to 5 questions, 2–9 choices each. `required: false` lets a prospect skip one.
|
|
1046
|
+
- Send the whole list to change it; `[]` asks nothing. Left out, it is kept.
|
|
1047
|
+
- Keep a question's `id` when you reword it, so earlier answers still line up.
|
|
1048
|
+
- The answers are also on the Google Calendar event and in the WhatsApp alert.
|
|
1049
|
+
- The dialog needs a `glitchgrab` version newer than 1.57 on the site. An older one
|
|
1050
|
+
still books, without asking.
|
|
1051
|
+
|
|
1026
1052
|
```ts
|
|
1027
1053
|
getBookingSettings(options?: CallsOptions): Promise<BookingSettingsResult>
|
|
1028
|
-
updateBookingSettings(changes:
|
|
1054
|
+
updateBookingSettings(changes: BookingSettingsChanges, options?: CallsOptions): Promise<BookingSettingsResult>
|
|
1029
1055
|
|
|
1030
1056
|
type BookingSettingsResult =
|
|
1031
1057
|
| { ok: true; data: { settings: BookingSettings; saved: boolean; calendarConnected: boolean } }
|
|
@@ -1044,6 +1070,15 @@ interface BookingSettings {
|
|
|
1044
1070
|
horizonDays: number;
|
|
1045
1071
|
noticeMinutes: number;
|
|
1046
1072
|
whatsappCode: string | null;
|
|
1073
|
+
notifyRecipients: { name: string; phone: string }[]; // extra WhatsApp numbers alerted on a booking, max 5
|
|
1074
|
+
questions: BookingQuestion[]; // asked before the calendar, max 5
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
interface BookingQuestion {
|
|
1078
|
+
id: string; // stable name, e.g. "language"; made from the label when left out
|
|
1079
|
+
label: string; // up to 80 characters
|
|
1080
|
+
options: string[]; // 2–9 choices, up to 40 characters each
|
|
1081
|
+
required: boolean; // false lets the prospect skip it
|
|
1047
1082
|
}
|
|
1048
1083
|
```
|
|
1049
1084
|
|
package/dist/index.js
CHANGED
|
@@ -97,6 +97,7 @@ function toAssistTurnResult(ok, body) {
|
|
|
97
97
|
question: (_d = d.question) != null ? _d : null,
|
|
98
98
|
options: Array.isArray(d.options) ? d.options.filter((o) => typeof o === "string") : [],
|
|
99
99
|
report: (_e = d.report) != null ? _e : null,
|
|
100
|
+
title: typeof d.title === "string" && d.title.trim() ? d.title.trim() : null,
|
|
100
101
|
duplicate: (_f = d.duplicate) != null ? _f : null,
|
|
101
102
|
related: (_g = d.related) != null ? _g : null,
|
|
102
103
|
solved: (_h = d.solved) != null ? _h : null,
|
|
@@ -2016,6 +2017,8 @@ function AssistSheet({
|
|
|
2016
2017
|
reporterName,
|
|
2017
2018
|
description,
|
|
2018
2019
|
onDescriptionChange,
|
|
2020
|
+
issueTitle,
|
|
2021
|
+
onIssueTitleChange,
|
|
2019
2022
|
severity,
|
|
2020
2023
|
onSeverityChange,
|
|
2021
2024
|
showSeverity,
|
|
@@ -2114,7 +2117,7 @@ function AssistSheet({
|
|
|
2114
2117
|
onDegrade(message, retryable);
|
|
2115
2118
|
}
|
|
2116
2119
|
async function runTurn(history2, autoFile = false) {
|
|
2117
|
-
var _a, _b;
|
|
2120
|
+
var _a, _b, _c;
|
|
2118
2121
|
setBusy(true);
|
|
2119
2122
|
setAutoFileLeft(null);
|
|
2120
2123
|
let result;
|
|
@@ -2171,6 +2174,7 @@ function AssistSheet({
|
|
|
2171
2174
|
}
|
|
2172
2175
|
if (result.report) {
|
|
2173
2176
|
onDescriptionChange(result.report);
|
|
2177
|
+
onIssueTitleChange((_b = result.title) != null ? _b : "");
|
|
2174
2178
|
aiDraftRef.current = result.report;
|
|
2175
2179
|
logEvent("draft_shown");
|
|
2176
2180
|
setMessages([...history2, { role: "assistant", content: result.report, kind: "report" }]);
|
|
@@ -2183,7 +2187,7 @@ function AssistSheet({
|
|
|
2183
2187
|
}
|
|
2184
2188
|
if (result.question) {
|
|
2185
2189
|
setMessages([...history2, { role: "assistant", content: result.question }]);
|
|
2186
|
-
setOptions((
|
|
2190
|
+
setOptions((_c = result.options) != null ? _c : []);
|
|
2187
2191
|
return;
|
|
2188
2192
|
}
|
|
2189
2193
|
onDegrade("The assistant had nothing to add \u2014 write your report below and send it as normal.");
|
|
@@ -3027,6 +3031,40 @@ function AssistSheet({
|
|
|
3027
3031
|
title: true,
|
|
3028
3032
|
footer: "Your details will be added to it instead of opening a new issue."
|
|
3029
3033
|
}),
|
|
3034
|
+
!duplicate && issueTitle && // Wraps rather than scrolls sideways: a title cut off at the
|
|
3035
|
+
// box edge is the very thing #543 is about. One line of text,
|
|
3036
|
+
// so Enter is swallowed.
|
|
3037
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3038
|
+
"textarea",
|
|
3039
|
+
{
|
|
3040
|
+
"aria-label": "Title",
|
|
3041
|
+
value: issueTitle,
|
|
3042
|
+
maxLength: 120,
|
|
3043
|
+
rows: 2,
|
|
3044
|
+
onKeyDown: (e) => {
|
|
3045
|
+
if (e.key === "Enter") e.preventDefault();
|
|
3046
|
+
},
|
|
3047
|
+
onChange: (e) => {
|
|
3048
|
+
stopAutoFile("edited the title");
|
|
3049
|
+
onIssueTitleChange(e.target.value.replace(/[\r\n]+/g, " "));
|
|
3050
|
+
},
|
|
3051
|
+
style: {
|
|
3052
|
+
width: "100%",
|
|
3053
|
+
padding: "8px 10px",
|
|
3054
|
+
lineHeight: 1.45,
|
|
3055
|
+
resize: "none",
|
|
3056
|
+
borderRadius: "8px",
|
|
3057
|
+
border: `1px solid ${t.inputBorder}`,
|
|
3058
|
+
backgroundColor: t.inputBg,
|
|
3059
|
+
color: t.text,
|
|
3060
|
+
fontSize: "13px",
|
|
3061
|
+
fontWeight: 600,
|
|
3062
|
+
fontFamily: "inherit",
|
|
3063
|
+
outline: "none",
|
|
3064
|
+
boxSizing: "border-box"
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
),
|
|
3030
3068
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3031
3069
|
"textarea",
|
|
3032
3070
|
{
|
|
@@ -4668,6 +4706,7 @@ function ReportDialog({
|
|
|
4668
4706
|
const [pickedSimilar, setPickedSimilar] = (0, import_react.useState)(null);
|
|
4669
4707
|
const [addedToIssue, setAddedToIssue] = (0, import_react.useState)(null);
|
|
4670
4708
|
const [aiConversationId, setAiConversationId] = (0, import_react.useState)(null);
|
|
4709
|
+
const [issueTitle, setIssueTitle] = (0, import_react.useState)("");
|
|
4671
4710
|
const [isEnhanced, setIsEnhanced] = (0, import_react.useState)(false);
|
|
4672
4711
|
const [originalDescription, setOriginalDescription] = (0, import_react.useState)(
|
|
4673
4712
|
null
|
|
@@ -5369,6 +5408,7 @@ function ReportDialog({
|
|
|
5369
5408
|
}
|
|
5370
5409
|
if (aiConversationId) {
|
|
5371
5410
|
metadata.aiConversationId = aiConversationId;
|
|
5411
|
+
if (sheetUp && !duplicateOf && issueTitle.trim()) metadata.issueTitle = issueTitle.trim();
|
|
5372
5412
|
}
|
|
5373
5413
|
const result = await report(
|
|
5374
5414
|
// The RATING branch returned above, so anything reaching here is a real
|
|
@@ -5387,6 +5427,7 @@ function ReportDialog({
|
|
|
5387
5427
|
setScreenshots([]);
|
|
5388
5428
|
setAttachments([]);
|
|
5389
5429
|
setAiConversationId(null);
|
|
5430
|
+
setIssueTitle("");
|
|
5390
5431
|
setDuplicateIssueNumber(null);
|
|
5391
5432
|
setDuplicateKind("duplicate");
|
|
5392
5433
|
setSimilarIssues(null);
|
|
@@ -7490,6 +7531,8 @@ function ReportDialog({
|
|
|
7490
7531
|
setDescription(value);
|
|
7491
7532
|
if (validationError) setValidationError(null);
|
|
7492
7533
|
},
|
|
7534
|
+
issueTitle,
|
|
7535
|
+
onIssueTitleChange: setIssueTitle,
|
|
7493
7536
|
severity,
|
|
7494
7537
|
onSeverityChange: (value) => {
|
|
7495
7538
|
setSeverity(value);
|
|
@@ -7655,6 +7698,16 @@ function getTypeIcon(type, color, size = 24) {
|
|
|
7655
7698
|
// src/booking-dialog.tsx
|
|
7656
7699
|
var import_react5 = require("react");
|
|
7657
7700
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
7701
|
+
function readQuestions(value) {
|
|
7702
|
+
if (!Array.isArray(value)) return [];
|
|
7703
|
+
return value.flatMap((row) => {
|
|
7704
|
+
if (!row || typeof row !== "object") return [];
|
|
7705
|
+
const { id, label, options, required } = row;
|
|
7706
|
+
if (typeof id !== "string" || typeof label !== "string" || !Array.isArray(options)) return [];
|
|
7707
|
+
const choices = options.filter((o) => typeof o === "string" && o !== "");
|
|
7708
|
+
return choices.length > 0 ? [{ id, label, options: choices, required: required !== false }] : [];
|
|
7709
|
+
});
|
|
7710
|
+
}
|
|
7658
7711
|
var LIGHT = {
|
|
7659
7712
|
bg: "#ffffff",
|
|
7660
7713
|
pane: "#ffffff",
|
|
@@ -7808,6 +7861,11 @@ function BookingDialog({
|
|
|
7808
7861
|
const [email, setEmail] = (0, import_react5.useState)("");
|
|
7809
7862
|
const [phone, setPhone] = (0, import_react5.useState)("");
|
|
7810
7863
|
const [note, setNote] = (0, import_react5.useState)("");
|
|
7864
|
+
const [questions, setQuestions] = (0, import_react5.useState)([]);
|
|
7865
|
+
const [answers, setAnswers] = (0, import_react5.useState)({});
|
|
7866
|
+
const [pageLoaded, setPageLoaded] = (0, import_react5.useState)(false);
|
|
7867
|
+
const [asked, setAsked] = (0, import_react5.useState)(false);
|
|
7868
|
+
const [requireOtp, setRequireOtp] = (0, import_react5.useState)(true);
|
|
7811
7869
|
const [code, setCode] = (0, import_react5.useState)("");
|
|
7812
7870
|
const [bookingId, setBookingId] = (0, import_react5.useState)(null);
|
|
7813
7871
|
const [meetUrl, setMeetUrl] = (0, import_react5.useState)(null);
|
|
@@ -7836,6 +7894,7 @@ function BookingDialog({
|
|
|
7836
7894
|
if (!json.success) {
|
|
7837
7895
|
setError((_a2 = json.error) != null ? _a2 : "Couldn't load available times");
|
|
7838
7896
|
setSlotsByMonth((prev) => ({ ...prev, [target]: [] }));
|
|
7897
|
+
setPageLoaded(true);
|
|
7839
7898
|
return;
|
|
7840
7899
|
}
|
|
7841
7900
|
const data = (_b2 = json.data) != null ? _b2 : {};
|
|
@@ -7848,9 +7907,13 @@ function BookingDialog({
|
|
|
7848
7907
|
slotMinutes: typeof data.slotMinutes === "number" ? data.slotMinutes : 30
|
|
7849
7908
|
});
|
|
7850
7909
|
setWhatsappUrl((_c2 = data.whatsappUrl) != null ? _c2 : null);
|
|
7910
|
+
setQuestions(readQuestions(data.questions));
|
|
7911
|
+
setRequireOtp(data.requireOtp !== false);
|
|
7912
|
+
setPageLoaded(true);
|
|
7851
7913
|
} catch (e) {
|
|
7852
7914
|
setError("Couldn't reach the booking service");
|
|
7853
7915
|
setSlotsByMonth((prev) => ({ ...prev, [target]: [] }));
|
|
7916
|
+
setPageLoaded(true);
|
|
7854
7917
|
}
|
|
7855
7918
|
},
|
|
7856
7919
|
[baseUrl, headers]
|
|
@@ -7868,6 +7931,8 @@ function BookingDialog({
|
|
|
7868
7931
|
setBookingId(null);
|
|
7869
7932
|
setError(null);
|
|
7870
7933
|
setAvatarFailed(false);
|
|
7934
|
+
setPageLoaded(false);
|
|
7935
|
+
setAsked(false);
|
|
7871
7936
|
}, [open]);
|
|
7872
7937
|
const monthLoaded = month in slotsByMonth;
|
|
7873
7938
|
(0, import_react5.useEffect)(() => {
|
|
@@ -7896,8 +7961,12 @@ function BookingDialog({
|
|
|
7896
7961
|
const daySlots = day ? (_d = byDay.get(day)) != null ? _d : [] : [];
|
|
7897
7962
|
const atFirstMonth = month <= today.slice(0, 7);
|
|
7898
7963
|
const pastHorizon = monthLoaded && !error && byDay.size === 0 && !atFirstMonth;
|
|
7964
|
+
const given = questions.flatMap((q) => q.options.includes(answers[q.id]) ? [{ ...q, answer: answers[q.id] }] : []);
|
|
7965
|
+
const canContinue = questions.every((q) => !q.required || q.options.includes(answers[q.id]));
|
|
7966
|
+
const view = step !== "slots" ? step : !pageLoaded ? "loading" : questions.length > 0 && !asked ? "questions" : "slots";
|
|
7967
|
+
const roomy = step === "slots";
|
|
7899
7968
|
async function startBooking() {
|
|
7900
|
-
var _a2;
|
|
7969
|
+
var _a2, _b2;
|
|
7901
7970
|
if (!selected) return;
|
|
7902
7971
|
setBusy(true);
|
|
7903
7972
|
setError(null);
|
|
@@ -7910,9 +7979,13 @@ function BookingDialog({
|
|
|
7910
7979
|
email,
|
|
7911
7980
|
phone,
|
|
7912
7981
|
note,
|
|
7982
|
+
answers: Object.fromEntries(given.map((q) => [q.id, q.answer])),
|
|
7913
7983
|
startsAt: selected.startsAt,
|
|
7914
7984
|
// The zone the times were shown in, which is the one the visitor chose.
|
|
7915
|
-
timezone: zone
|
|
7985
|
+
timezone: zone,
|
|
7986
|
+
// This dialog can show a booking confirmed in one step; the server
|
|
7987
|
+
// only skips the code for a dialog that says so.
|
|
7988
|
+
instant: true
|
|
7916
7989
|
})
|
|
7917
7990
|
});
|
|
7918
7991
|
const json = await res.json();
|
|
@@ -7920,6 +7993,11 @@ function BookingDialog({
|
|
|
7920
7993
|
setError((_a2 = json.error) != null ? _a2 : "Couldn't hold that slot");
|
|
7921
7994
|
return;
|
|
7922
7995
|
}
|
|
7996
|
+
if (json.data.confirmed) {
|
|
7997
|
+
setMeetUrl((_b2 = json.data.meetUrl) != null ? _b2 : null);
|
|
7998
|
+
setStep("done");
|
|
7999
|
+
return;
|
|
8000
|
+
}
|
|
7923
8001
|
setBookingId(json.data.bookingId);
|
|
7924
8002
|
setStep("code");
|
|
7925
8003
|
} catch (e) {
|
|
@@ -7960,7 +8038,7 @@ function BookingDialog({
|
|
|
7960
8038
|
"div",
|
|
7961
8039
|
{
|
|
7962
8040
|
style: {
|
|
7963
|
-
marginTop: wide ? whatsappUrl &&
|
|
8041
|
+
marginTop: wide ? whatsappUrl && roomy ? 14 : "auto" : 0,
|
|
7964
8042
|
padding: wide ? 0 : "4px 20px 18px",
|
|
7965
8043
|
display: "flex",
|
|
7966
8044
|
justifyContent: wide ? "flex-start" : "center"
|
|
@@ -8101,9 +8179,36 @@ function BookingDialog({
|
|
|
8101
8179
|
selectedEnd && formatTime(selectedEnd, zone, twelve)
|
|
8102
8180
|
] })
|
|
8103
8181
|
] }) }),
|
|
8182
|
+
asked && given.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "check", t, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
|
|
8183
|
+
given.map((q) => q.answer).join(" \xB7 "),
|
|
8184
|
+
view === "slots" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
8185
|
+
" ",
|
|
8186
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
8187
|
+
"button",
|
|
8188
|
+
{
|
|
8189
|
+
type: "button",
|
|
8190
|
+
onClick: () => setAsked(false),
|
|
8191
|
+
className: "gg-bk-link",
|
|
8192
|
+
style: {
|
|
8193
|
+
border: 0,
|
|
8194
|
+
// Room for a thumb without pushing the rows apart.
|
|
8195
|
+
padding: "8px 6px",
|
|
8196
|
+
margin: "-8px 0",
|
|
8197
|
+
background: "transparent",
|
|
8198
|
+
color: t.muted,
|
|
8199
|
+
fontFamily: "inherit",
|
|
8200
|
+
fontSize: 13,
|
|
8201
|
+
textDecoration: "underline",
|
|
8202
|
+
cursor: "pointer"
|
|
8203
|
+
},
|
|
8204
|
+
children: "Change"
|
|
8205
|
+
}
|
|
8206
|
+
)
|
|
8207
|
+
] })
|
|
8208
|
+
] }) }),
|
|
8104
8209
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "clock", t, children: formatDuration(page.slotMinutes) }),
|
|
8105
8210
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "video", t, children: "Google Meet" }),
|
|
8106
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "globe", t, children:
|
|
8211
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "globe", t, children: roomy ? (
|
|
8107
8212
|
// The visible label is plain text; the native select sits on top of
|
|
8108
8213
|
// it, invisible. A styled select sizes itself to the longest zone
|
|
8109
8214
|
// name in the list, which pushed the chevron to the pane's edge.
|
|
@@ -8123,7 +8228,7 @@ function BookingDialog({
|
|
|
8123
8228
|
] })
|
|
8124
8229
|
) : zone })
|
|
8125
8230
|
] }),
|
|
8126
|
-
whatsappUrl &&
|
|
8231
|
+
whatsappUrl && roomy && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
8127
8232
|
"a",
|
|
8128
8233
|
{
|
|
8129
8234
|
href: whatsappUrl,
|
|
@@ -8394,6 +8499,93 @@ function BookingDialog({
|
|
|
8394
8499
|
]
|
|
8395
8500
|
}
|
|
8396
8501
|
);
|
|
8502
|
+
const loading = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
8503
|
+
"div",
|
|
8504
|
+
{
|
|
8505
|
+
style: {
|
|
8506
|
+
flex: "1 1 auto",
|
|
8507
|
+
minHeight: wide ? void 0 : 200,
|
|
8508
|
+
display: "flex",
|
|
8509
|
+
alignItems: "center",
|
|
8510
|
+
justifyContent: "center",
|
|
8511
|
+
fontSize: 13,
|
|
8512
|
+
...muted
|
|
8513
|
+
},
|
|
8514
|
+
children: "Loading\u2026"
|
|
8515
|
+
}
|
|
8516
|
+
);
|
|
8517
|
+
const ask = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
8518
|
+
"div",
|
|
8519
|
+
{
|
|
8520
|
+
style: {
|
|
8521
|
+
flex: "1 1 auto",
|
|
8522
|
+
minWidth: 0,
|
|
8523
|
+
padding: wide ? "28px 32px" : "20px",
|
|
8524
|
+
boxSizing: "border-box",
|
|
8525
|
+
overflowY: wide ? "auto" : void 0
|
|
8526
|
+
},
|
|
8527
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { maxWidth: 520, display: "flex", flexDirection: "column", gap: 22 }, children: [
|
|
8528
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
8529
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 16, fontWeight: 600 }, children: questions.length === 1 ? "One question first" : "A few questions first" }),
|
|
8530
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 13, lineHeight: 1.5, marginTop: 4, ...muted }, children: "So the right person takes your demo." })
|
|
8531
|
+
] }),
|
|
8532
|
+
questions.map((q) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "group", "aria-label": q.label, children: [
|
|
8533
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { fontSize: 14, fontWeight: 500, overflowWrap: "anywhere" }, children: [
|
|
8534
|
+
q.label,
|
|
8535
|
+
!q.required && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontWeight: 400, ...muted }, children: " (optional)" })
|
|
8536
|
+
] }),
|
|
8537
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, marginTop: 10 }, children: q.options.map((option) => {
|
|
8538
|
+
const on = answers[q.id] === option;
|
|
8539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
8540
|
+
"button",
|
|
8541
|
+
{
|
|
8542
|
+
type: "button",
|
|
8543
|
+
"aria-pressed": on,
|
|
8544
|
+
className: on ? void 0 : "gg-bk-slot",
|
|
8545
|
+
onClick: () => setAnswers((prev) => {
|
|
8546
|
+
if (on && !q.required) {
|
|
8547
|
+
const rest = { ...prev };
|
|
8548
|
+
delete rest[q.id];
|
|
8549
|
+
return rest;
|
|
8550
|
+
}
|
|
8551
|
+
return { ...prev, [q.id]: option };
|
|
8552
|
+
}),
|
|
8553
|
+
style: {
|
|
8554
|
+
padding: "9px 14px",
|
|
8555
|
+
minHeight: 40,
|
|
8556
|
+
borderRadius: 8,
|
|
8557
|
+
border: `1px solid ${on ? t.ink : t.border}`,
|
|
8558
|
+
background: on ? t.ink : t.pane,
|
|
8559
|
+
color: on ? t.onInk : t.text,
|
|
8560
|
+
fontFamily: "inherit",
|
|
8561
|
+
fontSize: 14,
|
|
8562
|
+
fontWeight: 500,
|
|
8563
|
+
cursor: "pointer",
|
|
8564
|
+
overflowWrap: "anywhere",
|
|
8565
|
+
textAlign: "left"
|
|
8566
|
+
},
|
|
8567
|
+
children: option
|
|
8568
|
+
},
|
|
8569
|
+
option
|
|
8570
|
+
);
|
|
8571
|
+
}) })
|
|
8572
|
+
] }, q.id)),
|
|
8573
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: "8px 12px", marginTop: 2 }, children: [
|
|
8574
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
8575
|
+
"button",
|
|
8576
|
+
{
|
|
8577
|
+
type: "button",
|
|
8578
|
+
onClick: () => setAsked(true),
|
|
8579
|
+
disabled: !canContinue,
|
|
8580
|
+
style: { ...primaryStyle(t, !canContinue), whiteSpace: "nowrap" },
|
|
8581
|
+
children: "See available times"
|
|
8582
|
+
}
|
|
8583
|
+
),
|
|
8584
|
+
!canContinue && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 12, ...muted }, children: "Pick one for each to continue" })
|
|
8585
|
+
] })
|
|
8586
|
+
] })
|
|
8587
|
+
}
|
|
8588
|
+
);
|
|
8397
8589
|
const form = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
8398
8590
|
"div",
|
|
8399
8591
|
{
|
|
@@ -8418,7 +8610,7 @@ function BookingDialog({
|
|
|
8418
8610
|
onChange: setPhone,
|
|
8419
8611
|
type: "tel",
|
|
8420
8612
|
autoComplete: "tel",
|
|
8421
|
-
hint: "We send a code here to confirm, then a reminder before the call."
|
|
8613
|
+
hint: requireOtp ? "We send a code here to confirm, then a reminder before the call." : "We send a reminder here before the call."
|
|
8422
8614
|
}
|
|
8423
8615
|
),
|
|
8424
8616
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Field, { t, label: "Anything we should know? (optional)", value: note, onChange: setNote, multiline: true }),
|
|
@@ -8443,7 +8635,7 @@ function BookingDialog({
|
|
|
8443
8635
|
onClick: startBooking,
|
|
8444
8636
|
disabled: !canSubmitDetails || busy,
|
|
8445
8637
|
style: primaryStyle(t, !canSubmitDetails || busy),
|
|
8446
|
-
children: busy ? "Sending code\u2026" : "Send verification code"
|
|
8638
|
+
children: requireOtp ? busy ? "Sending code\u2026" : "Send verification code" : busy ? "Booking\u2026" : "Book demo"
|
|
8447
8639
|
}
|
|
8448
8640
|
)
|
|
8449
8641
|
] })
|
|
@@ -8507,6 +8699,7 @@ function BookingDialog({
|
|
|
8507
8699
|
.gg-bk-day:hover { box-shadow: inset 0 0 0 1.5px ${t.ink}; }
|
|
8508
8700
|
.gg-bk-slot:hover { border-color: ${t.ink} !important; }
|
|
8509
8701
|
.gg-bk-ghost:hover { background: ${t.open} !important; }
|
|
8702
|
+
.gg-bk-link:hover { color: ${t.text} !important; }
|
|
8510
8703
|
.gg-bk-root button:focus-visible, .gg-bk-root a:focus-visible,
|
|
8511
8704
|
.gg-bk-root span:has(> select:focus-visible), .gg-bk-root input:focus-visible, .gg-bk-root textarea:focus-visible {
|
|
8512
8705
|
outline: 2px solid ${t.ink}; outline-offset: 2px;
|
|
@@ -8520,11 +8713,11 @@ function BookingDialog({
|
|
|
8520
8713
|
"aria-label": page.title,
|
|
8521
8714
|
className: "gg-bk-root",
|
|
8522
8715
|
style: {
|
|
8523
|
-
width:
|
|
8716
|
+
width: roomy ? "min(1060px, 100%)" : "min(820px, 100%)",
|
|
8524
8717
|
// A month grid should not jump in height between months; the form
|
|
8525
8718
|
// steps are short and would sit in a mostly empty box.
|
|
8526
|
-
height: wide &&
|
|
8527
|
-
minHeight: wide &&
|
|
8719
|
+
height: wide && roomy ? "min(640px, calc(100vh - 48px))" : void 0,
|
|
8720
|
+
minHeight: wide && !roomy ? 420 : void 0,
|
|
8528
8721
|
margin: wide ? void 0 : "auto 0",
|
|
8529
8722
|
display: "flex",
|
|
8530
8723
|
flexDirection: wide ? "row" : "column",
|
|
@@ -8540,7 +8733,7 @@ function BookingDialog({
|
|
|
8540
8733
|
},
|
|
8541
8734
|
children: [
|
|
8542
8735
|
summary,
|
|
8543
|
-
|
|
8736
|
+
view === "loading" ? loading : view === "questions" ? ask : view === "slots" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
8544
8737
|
calendar,
|
|
8545
8738
|
times
|
|
8546
8739
|
] }) : form,
|
|
@@ -8627,6 +8820,7 @@ function Icon({ name }) {
|
|
|
8627
8820
|
left: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m15 18-6-6 6-6" }),
|
|
8628
8821
|
right: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m9 18 6-6-6-6" }),
|
|
8629
8822
|
down: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 9 6 6 6-6" }),
|
|
8823
|
+
check: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M20 6 9 17l-5-5" }),
|
|
8630
8824
|
clock: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
8631
8825
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "9" }),
|
|
8632
8826
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 7v5l3 2" })
|