Skip to content

Commit 5d455c3

Browse files
committed
fix #7647 -- add to the support page a way to require certain placeholders to be filled in before allowing submission
1 parent 83381f3 commit 5d455c3

File tree

3 files changed

+40
-20
lines changed

3 files changed

+40
-20
lines changed

src/packages/frontend/support/url.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface Options {
1111
type?: "problem" | "question" | "task" | "purchase";
1212
hideExtra?: boolean;
1313
context?: string; // additional context
14+
required?: string; // if required is a string, then the user MUST change the body of the input
1415
}
1516

1617
export default function getURL(options: Options = {}) {
@@ -24,14 +25,20 @@ export default function getURL(options: Options = {}) {
2425
options.url = "";
2526
}
2627
}
27-
// Note that this is a 2K limit on URL lengths, so the body had better
28-
// not be too large (or it gets truncated).
29-
return encodeURI(
30-
supportURL +
31-
`?hideExtra=${options.hideExtra}&url=${options.url}&type=${
32-
options.type ?? ""
33-
}&subject=${options.subject ?? ""}&body=${options.body ?? ""}&context=${
34-
options.context ?? ""
35-
}`,
36-
);
28+
29+
const params = {
30+
hideExtra: options.hideExtra,
31+
url: options.url,
32+
type: options.type ?? "",
33+
subject: options.subject ?? "",
34+
body: options.body ?? "",
35+
required: options.required ?? "",
36+
context: options.context ?? "",
37+
};
38+
39+
const queryParams = Object.keys(params)
40+
.map((key) => `${key}=${encodeURIComponent(params[key] as string)}`)
41+
.join("&");
42+
43+
return `${supportURL}?${queryParams}`;
3744
}

src/packages/next/components/landing/live-demo.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ export function liveDemoUrl(context) {
77
return getSupportUrl({
88
subject: "Live Demo Request",
99
type: "question",
10-
body: `I would like to request a live demo on CoCalc!\n\nWHEN IS A GOOD TIME (include timezone!):\n\n\nTELLS US AS MUCH AS YOU CAN ABOUT YOUR INTENDED USE OF COCALC:\n\n`,
10+
body: `I would like to request a live demo on CoCalc!\n\nWHEN IS A GOOD TIME (include timezone!): [REQUIRED]\n\nTELLS US AS MUCH AS YOU CAN ABOUT YOUR INTENDED USE OF COCALC: [REQUIRED]\n\n`,
1111
hideExtra: true,
1212
context,
1313
url: "",
14+
required: "[REQUIRED]",
1415
});
1516
}
1617

src/packages/next/components/support/create.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
} from "antd";
1111
import { useRouter } from "next/router";
1212
import { ReactNode, useRef, useState } from "react";
13-
1413
import { Icon } from "@cocalc/frontend/components/icon";
1514
import { is_valid_email_address as isValidEmailAddress } from "@cocalc/util/misc";
1615
import { COLORS } from "@cocalc/util/theme";
@@ -62,6 +61,7 @@ export default function Create() {
6261
const [body, setBody] = useState<string>(
6362
router.query.body ? `${router.query.body}` : "",
6463
);
64+
const required = router.query.required ? `${router.query.required}` : "";
6565
const [subject, setSubject] = useState<string>(
6666
router.query.subject ? `${router.query.subject}` : "",
6767
);
@@ -72,14 +72,19 @@ export default function Create() {
7272

7373
const showExtra = router.query.hideExtra != "true";
7474

75+
// hasRequired means "has the required information", which
76+
// means that body does NOT have required in it!
77+
const hasRequired = !required || !body.includes(required);
78+
7579
const submittable = useRef<boolean>(false);
7680
submittable.current = !!(
7781
!submitting &&
7882
!submitError &&
7983
!success &&
8084
isValidEmailAddress(email) &&
8185
subject &&
82-
(body ?? "").length >= MIN_BODY_LENGTH
86+
(body ?? "").length >= MIN_BODY_LENGTH &&
87+
hasRequired
8388
);
8489

8590
if (!zendesk) {
@@ -217,7 +222,7 @@ export default function Create() {
217222
</>
218223
)}
219224
<b>
220-
<Status done={body && body.length >= MIN_BODY_LENGTH} />{" "}
225+
<Status done={body && body.length >= MIN_BODY_LENGTH && hasRequired} />{" "}
221226
Description
222227
</b>
223228
<div
@@ -241,14 +246,16 @@ export default function Create() {
241246
{type == "task" && <Task onChange={setBody} />}
242247
</div>
243248
</VSpace>
244-
<p style={{ marginTop: "30px" }}>
245-
After submitting this, you'll receive a link, which you should save
246-
until you receive a confirmation email. You can also{" "}
247-
<A href="/support/tickets">check the status of your tickets here</A>
248-
.
249-
</p>
250249

251250
<div style={{ textAlign: "center", marginTop: "30px" }}>
251+
{!hasRequired && (
252+
<Alert
253+
showIcon
254+
style={{ margin: "15px 30px" }}
255+
type="error"
256+
description={`You must replace the text '${required}' everywhere above with the requested information.`}
257+
/>
258+
)}
252259
<Button
253260
shape="round"
254261
size="large"
@@ -308,6 +315,11 @@ export default function Create() {
308315
)}
309316
</div>
310317
</form>
318+
<p style={{ marginTop: "30px" }}>
319+
After submitting this, you'll receive a link, which you should save
320+
until you receive a confirmation email. You can also{" "}
321+
<A href="/support/tickets">check the status of your tickets here</A>.
322+
</p>
311323
</div>
312324
</Layout.Content>
313325
);

0 commit comments

Comments
 (0)