Skip to content

Commit 727bfec

Browse files
committed
next: make video call links configurable, typing, and emphasize sign up/in on index page
1 parent 3326913 commit 727bfec

File tree

11 files changed

+161
-96
lines changed

11 files changed

+161
-96
lines changed

src/packages/database/settings/customize.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export default async function getCustomize(
7777
imprint: settings.imprint,
7878
policies: settings.policies,
7979
support: settings.support,
80+
supportVideoCall: settings.support_video_call,
8081

8182
// Is important for invite emails, password reset, etc. (e.g., so we can construct a url to our site).
8283
// This *can* start with http:// to explicitly use http instead of https, and can end

src/packages/next/components/landing/content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ export default function Content(props: Props) {
224224
}
225225
/>
226226
</div>
227-
<SignIn startup={startup ?? title} hideFree={true} />
227+
<SignIn startup={startup ?? title} hideFree={true} emphasize />
228228
</Space>
229229
</Col>
230230
<Col sm={14} xs={24}>

src/packages/next/components/landing/index-list.tsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,27 @@ import { MAX_WIDTH } from "lib/config";
1515
import useCustomize, { CustomizeType } from "lib/use-customize";
1616

1717
export interface Item {
18-
link: string;
18+
link: string | ((customize: CustomizeType) => string | undefined);
1919
linkText?: ReactNode;
2020
title: ReactNode;
2121
logo: IconName | StaticImageData;
2222
logoBackground?: string; // #color
2323
image?: StaticImageData;
2424
imageWidth?: string;
25-
description: ReactNode;
25+
description: ReactNode | ((customize: CustomizeType) => ReactNode);
2626
shareServer?: boolean; // only show if the share server is enabled
2727
landingPages?: boolean; // only show if landing pages are enabled.
2828
hide?: (customize: CustomizeType) => boolean; // if returns true, then this item will be hidden.
2929
}
3030

3131
export type DataSource = Item[];
3232

33+
// replaces the description attribute by {description: ReactNode}
34+
type ItemProcessed = Omit<Item, "description" | "link"> & {
35+
description: ReactNode;
36+
link: string;
37+
};
38+
3339
interface Props {
3440
title: ReactNode;
3541
description: ReactNode;
@@ -41,13 +47,27 @@ interface Props {
4147
export default function IndexList({ title, description, dataSource }: Props) {
4248
const customize = useCustomize();
4349
const { shareServer, landingPages } = customize;
44-
const filteredDataSource = useMemo(() => {
45-
return dataSource.filter((item) => {
46-
if (item.shareServer && !shareServer) return false;
47-
if (item.landingPages && !landingPages) return false;
48-
if (item.hide?.(customize)) return false;
49-
return true;
50-
});
50+
const filteredDataSource: ItemProcessed[] = useMemo(() => {
51+
return dataSource
52+
.filter((item) => {
53+
if (item.shareServer && !shareServer) return false;
54+
if (item.landingPages && !landingPages) return false;
55+
if (item.hide?.(customize)) return false;
56+
return true;
57+
})
58+
.map((item) => {
59+
return {
60+
...item,
61+
description:
62+
typeof item.description === "function"
63+
? item.description(customize)
64+
: item.description,
65+
link:
66+
typeof item.link === "function"
67+
? item.link(customize) ?? ""
68+
: item.link,
69+
};
70+
});
5171
}, [shareServer, landingPages, dataSource]);
5272
return (
5373
<Layout.Content
@@ -80,8 +100,8 @@ export default function IndexList({ title, description, dataSource }: Props) {
80100
);
81101
}
82102

83-
function DataList({ dataSource }: { dataSource: Item[] }) {
84-
function renderItem(item): ReactNode {
103+
function DataList({ dataSource }: { dataSource: ItemProcessed[] }) {
104+
function renderItem(item: ItemProcessed): ReactNode {
85105
const icon = (
86106
<div>
87107
{isValidElement(item.logo) ? (

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import getSupportUrl from "@cocalc/frontend/support/url";
21
import { Button } from "antd";
3-
import { Icon } from "@cocalc/frontend/components/icon";
42
import { useEffect, useState } from "react";
53

4+
import { Icon } from "@cocalc/frontend/components/icon";
5+
import getSupportUrl from "@cocalc/frontend/support/url";
6+
67
export function liveDemoUrl(context) {
78
return getSupportUrl({
89
subject: "Contact Us!",

src/packages/next/components/landing/sign-in.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface Props {
1717
startup?: ReactNode; // customize the button, e.g. "Start Jupyter Now".
1818
hideFree?: boolean;
1919
style?: React.CSSProperties;
20+
emphasize?: boolean;
2021
}
2122

2223
const STYLE: CSSProperties = {
@@ -25,7 +26,7 @@ const STYLE: CSSProperties = {
2526
marginBottom: "0",
2627
} as const;
2728

28-
export default function SignIn({ startup, hideFree, style }: Props) {
29+
export default function SignIn({ startup, hideFree, style, emphasize }: Props) {
2930
const { anonymousSignup, siteName, account, emailSignup } = useCustomize();
3031
style = { ...STYLE, ...style };
3132
const router = useRouter();
@@ -55,6 +56,7 @@ export default function SignIn({ startup, hideFree, style }: Props) {
5556
style={{ margin: "10px" }}
5657
title={"Create a new account."}
5758
onClick={() => router.push("/auth/sign-up")}
59+
type={emphasize ? "primary" : undefined}
5860
>
5961
Sign Up
6062
</Button>
@@ -65,6 +67,7 @@ export default function SignIn({ startup, hideFree, style }: Props) {
6567
"Either create a new account or sign into an existing account."
6668
}
6769
onClick={() => router.push("/auth/sign-in")}
70+
type={emphasize ? "primary" : undefined}
6871
>
6972
Sign In
7073
</Button>

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

Lines changed: 80 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from "antd";
1111
import { useRouter } from "next/router";
1212
import { ReactNode, useRef, useState } from "react";
13+
1314
import { Icon } from "@cocalc/frontend/components/icon";
1415
import { is_valid_email_address as isValidEmailAddress } from "@cocalc/util/misc";
1516
import { COLORS } from "@cocalc/util/theme";
@@ -19,14 +20,14 @@ import ChatGPTHelp from "components/openai/chatgpt-help";
1920
import CodeMirror from "components/share/codemirror";
2021
import Loading from "components/share/loading";
2122
import SiteName from "components/share/site-name";
23+
import { VideoItem } from "components/videos";
2224
import apiPost from "lib/api/post";
2325
import { MAX_WIDTH } from "lib/config";
2426
import { useCustomize } from "lib/customize";
2527
import getBrowserInfo from "./browser-info";
2628
import RecentFiles from "./recent-files";
2729
import { Type } from "./tickets";
2830
import { NoZendesk } from "./util";
29-
import { VideoItem } from "components/videos";
3031

3132
const CHATGPT_DISABLED = true;
3233
const MIN_BODY_LENGTH = 16;
@@ -43,19 +44,26 @@ export type Type = "problem" | "question" | "task" | "purchase" | "chat";
4344

4445
function stringToType(s?: any): Type {
4546
if (
46-
s == "problem" ||
47-
s == "question" ||
48-
s == "task" ||
49-
s == "purchase" ||
50-
s == "chat"
47+
s === "problem" ||
48+
s === "question" ||
49+
s === "task" ||
50+
s === "purchase" ||
51+
s === "chat"
5152
)
5253
return s;
5354
return "problem"; // default;
5455
}
5556

5657
export default function Create() {
57-
const { account, onCoCalcCom, helpEmail, openaiEnabled, siteName, zendesk } =
58-
useCustomize();
58+
const {
59+
account,
60+
onCoCalcCom,
61+
helpEmail,
62+
openaiEnabled,
63+
siteName,
64+
zendesk,
65+
supportVideoCall,
66+
} = useCustomize();
5967
const router = useRouter();
6068
// The URL the user was viewing when they requested support.
6169
// This could easily be blank, but if it is set it can be useful.
@@ -131,6 +139,50 @@ export default function Create() {
131139
);
132140
}
133141

142+
function renderChat() {
143+
if (type === "chat" && supportVideoCall) {
144+
return (
145+
<h1 style={{ textAlign: "center" }}>
146+
<b>
147+
<A href={supportVideoCall}>Book a Video Chat...</A>
148+
</b>
149+
</h1>
150+
);
151+
}
152+
if (type !== "chat") {
153+
return (
154+
<>
155+
<b>
156+
<Status
157+
done={body && body.length >= MIN_BODY_LENGTH && hasRequired}
158+
/>{" "}
159+
Description
160+
</b>
161+
<div
162+
style={{
163+
marginLeft: "30px",
164+
borderLeft: "1px solid lightgrey",
165+
paddingLeft: "15px",
166+
}}
167+
>
168+
{type == "problem" && <Problem onChange={setBody} />}
169+
{type == "question" && (
170+
<Question onChange={setBody} defaultValue={body} />
171+
)}
172+
{type == "purchase" && (
173+
<Purchase
174+
onChange={setBody}
175+
defaultValue={body}
176+
showExtra={showExtra}
177+
/>
178+
)}
179+
{type == "task" && <Task onChange={setBody} />}
180+
</div>
181+
</>
182+
);
183+
}
184+
}
185+
134186
return (
135187
<Layout.Content style={{ backgroundColor: "white" }}>
136188
<div
@@ -157,10 +209,15 @@ export default function Create() {
157209
{helpEmail ? (
158210
<>
159211
You can also email us directly at{" "}
160-
<A href={`mailto:${helpEmail}`}>{helpEmail}</A> or{" "}
161-
<A href="https://calendly.com/cocalc/discovery">
162-
book a demo or discovery call
163-
</A>
212+
<A href={`mailto:${helpEmail}`}>{helpEmail}</A>{" "}
213+
{supportVideoCall ? (
214+
<>
215+
or{" "}
216+
<A href={supportVideoCall}>
217+
book a demo or discovery call
218+
</A>
219+
</>
220+
) : undefined}
164221
.
165222
</>
166223
) : undefined}
@@ -242,43 +299,7 @@ export default function Create() {
242299
<br />
243300
</>
244301
)}
245-
{type == "chat" && (
246-
<h1 style={{ textAlign: "center" }}>
247-
<b>
248-
<A href="https://calendly.com/cocalc">Book a Video Chat...</A>
249-
</b>
250-
</h1>
251-
)}
252-
{type != "chat" && (
253-
<>
254-
<b>
255-
<Status
256-
done={body && body.length >= MIN_BODY_LENGTH && hasRequired}
257-
/>{" "}
258-
Description
259-
</b>
260-
<div
261-
style={{
262-
marginLeft: "30px",
263-
borderLeft: "1px solid lightgrey",
264-
paddingLeft: "15px",
265-
}}
266-
>
267-
{type == "problem" && <Problem onChange={setBody} />}
268-
{type == "question" && (
269-
<Question onChange={setBody} defaultValue={body} />
270-
)}
271-
{type == "purchase" && (
272-
<Purchase
273-
onChange={setBody}
274-
defaultValue={body}
275-
showExtra={showExtra}
276-
/>
277-
)}
278-
{type == "task" && <Task onChange={setBody} />}
279-
</div>
280-
</>
281-
)}
302+
{renderChat()}
282303
</VSpace>
283304

284305
<div style={{ textAlign: "center", marginTop: "30px" }}>
@@ -302,16 +323,16 @@ export default function Create() {
302323
{submitting
303324
? "Submitting..."
304325
: success
305-
? "Thank you for creating a ticket"
306-
: submitError
307-
? "Close the error box to try again"
308-
: !isValidEmailAddress(email)
309-
? "Enter Valid Email Address above"
310-
: !subject
311-
? "Enter Subject above"
312-
: (body ?? "").length < MIN_BODY_LENGTH
313-
? `Describe your ${type} in detail above`
314-
: "Create Support Ticket"}
326+
? "Thank you for creating a ticket"
327+
: submitError
328+
? "Close the error box to try again"
329+
: !isValidEmailAddress(email)
330+
? "Enter Valid Email Address above"
331+
: !subject
332+
? "Enter Subject above"
333+
: (body ?? "").length < MIN_BODY_LENGTH
334+
? `Describe your ${type} in detail above`
335+
: "Create Support Ticket"}
315336
</Button>
316337
)}
317338
{submitting && <Loading style={{ fontSize: "32pt" }} />}

src/packages/next/lib/customize.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ interface Customize extends ServerCustomize {
7070
computeServersEnabled?: boolean; // backend configured to run on external compute servers
7171
enabledPages?: EnabledPageTree; // tree structure which specifies supported routes for this install
7272
support?: string; // HTML/MD to replace the generic support pages
73+
supportVideoCall?: string;
7374
}
7475

7576
const CustomizeContext = createContext<Partial<Customize>>({});

src/packages/next/pages/pricing/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
* License: MS-RSL – see LICENSE.md for details
44
*/
55

6+
import { Layout } from "antd";
7+
68
import Footer from "components/landing/footer";
79
import Head from "components/landing/head";
810
import Header from "components/landing/header";
911
import IndexList, { DataSource } from "components/landing/index-list";
1012
import A from "components/misc/A";
1113
import { Customize } from "lib/customize";
1214
import withCustomize from "lib/with-customize";
13-
import { Layout } from "antd";
1415

1516
const dataSource: DataSource = [
1617
{

0 commit comments

Comments
 (0)