Skip to content

next/support: streamline video call booking #8342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/packages/database/settings/customize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default async function getCustomize(
imprint: settings.imprint,
policies: settings.policies,
support: settings.support,
supportVideoCall: settings.support_video_call,

// Is important for invite emails, password reset, etc. (e.g., so we can construct a url to our site).
// This *can* start with http:// to explicitly use http instead of https, and can end
Expand Down
18 changes: 14 additions & 4 deletions src/packages/next/components/auth/sign-up.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
* License: MS-RSL – see LICENSE.md for details
*/

import { Alert, Button, Checkbox, Input } from "antd";
import { Alert, Button, Checkbox, Divider, Input } from "antd";
import { CSSProperties, useEffect, useRef, useState } from "react";
import {
GoogleReCaptchaProvider,
useGoogleReCaptcha,
} from "react-google-recaptcha-v3";

import Markdown from "@cocalc/frontend/editors/slate/static-markdown";
import {
CONTACT_TAG,
Expand All @@ -22,6 +23,7 @@ import {
} from "@cocalc/util/misc";
import { COLORS } from "@cocalc/util/theme";
import { Strategy } from "@cocalc/util/types/sso";
import { Paragraph } from "components/misc";
import A from "components/misc/A";
import Loading from "components/share/loading";
import apiPost from "lib/api/post";
Expand Down Expand Up @@ -99,7 +101,7 @@ function SignUp0({

const submittable = useRef<boolean>(false);
const { executeRecaptcha } = useGoogleReCaptcha();
const { strategies } = useCustomize();
const { strategies, supportVideoCall } = useCustomize();

// Sometimes the user if this component knows requiresToken and sometimes they don't.
// If they don't, we have to make an API call to figure it out.
Expand Down Expand Up @@ -263,13 +265,21 @@ function SignUp0({
minimal={minimal}
title={`Create a free account with ${siteName}`}
>
<div>
<Paragraph>
By creating an account, you agree to the{" "}
<A external={true} href="/policies/terms">
Terms of Service
</A>
.
</div>
</Paragraph>
{onCoCalcCom && supportVideoCall ? (
<Paragraph>
Do you need more information how {siteName} can be useful for you?{" "}
<A href={supportVideoCall}>Book a video call</A> and we'll help you
decide.
</Paragraph>
) : undefined}
<Divider />
{!minimal && onCoCalcCom ? (
<Tags
setTags={setTags}
Expand Down
2 changes: 1 addition & 1 deletion src/packages/next/components/landing/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export default function Content(props: Props) {
}
/>
</div>
<SignIn startup={startup ?? title} hideFree={true} />
<SignIn startup={startup ?? title} hideFree={true} emphasize />
</Space>
</Col>
<Col sm={14} xs={24}>
Expand Down
9 changes: 7 additions & 2 deletions src/packages/next/components/landing/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function Footer() {
organizationURL,
enabledPages,
termsOfServiceURL,
account,
supportVideoCall,
} = useCustomize();

const footerColumns: Array<FooterColumn> = [
Expand Down Expand Up @@ -140,8 +140,13 @@ export default function Footer() {
},
{
text: "Get a Live Demo",
url: supportVideoCall ?? "",
hide: !enabledPages?.liveDemo || !supportVideoCall,
},
{
text: "Contact Us",
url: liveDemoUrl("footer"),
hide: !account || !enabledPages?.liveDemo,
hide: !enabledPages?.support,
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion src/packages/next/components/landing/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default function Header(props: Props) {
}}
>
{true || account ? (
<LiveDemo context="header" type="primary" />
<LiveDemo context="header" btnType="primary" />
) : (
<Button
type="primary"
Expand Down
42 changes: 31 additions & 11 deletions src/packages/next/components/landing/index-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,27 @@ import { MAX_WIDTH } from "lib/config";
import useCustomize, { CustomizeType } from "lib/use-customize";

export interface Item {
link: string;
link: string | ((customize: CustomizeType) => string | undefined);
linkText?: ReactNode;
title: ReactNode;
logo: IconName | StaticImageData;
logoBackground?: string; // #color
image?: StaticImageData;
imageWidth?: string;
description: ReactNode;
description: ReactNode | ((customize: CustomizeType) => ReactNode);
shareServer?: boolean; // only show if the share server is enabled
landingPages?: boolean; // only show if landing pages are enabled.
hide?: (customize: CustomizeType) => boolean; // if returns true, then this item will be hidden.
}

export type DataSource = Item[];

// replaces the description attribute by {description: ReactNode}
type ItemProcessed = Omit<Item, "description" | "link"> & {
description: ReactNode;
link: string;
};

interface Props {
title: ReactNode;
description: ReactNode;
Expand All @@ -41,13 +47,27 @@ interface Props {
export default function IndexList({ title, description, dataSource }: Props) {
const customize = useCustomize();
const { shareServer, landingPages } = customize;
const filteredDataSource = useMemo(() => {
return dataSource.filter((item) => {
if (item.shareServer && !shareServer) return false;
if (item.landingPages && !landingPages) return false;
if (item.hide?.(customize)) return false;
return true;
});
const filteredDataSource: ItemProcessed[] = useMemo(() => {
return dataSource
.filter((item) => {
if (item.shareServer && !shareServer) return false;
if (item.landingPages && !landingPages) return false;
if (item.hide?.(customize)) return false;
return true;
})
.map((item) => {
return {
...item,
description:
typeof item.description === "function"
? item.description(customize)
: item.description,
link:
typeof item.link === "function"
? item.link(customize) ?? ""
: item.link,
};
});
}, [shareServer, landingPages, dataSource]);
return (
<Layout.Content
Expand Down Expand Up @@ -80,8 +100,8 @@ export default function IndexList({ title, description, dataSource }: Props) {
);
}

function DataList({ dataSource }: { dataSource: Item[] }) {
function renderItem(item): ReactNode {
function DataList({ dataSource }: { dataSource: ItemProcessed[] }) {
function renderItem(item: ItemProcessed): ReactNode {
const icon = (
<div>
{isValidElement(item.logo) ? (
Expand Down
27 changes: 16 additions & 11 deletions src/packages/next/components/landing/live-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import getSupportUrl from "@cocalc/frontend/support/url";
import { Button } from "antd";
import type { ButtonType } from "antd/es/button";
import type { ReactNode } from "react";

import { Icon } from "@cocalc/frontend/components/icon";
import { useEffect, useState } from "react";
import getSupportUrl from "@cocalc/frontend/support/url";
import { useCustomize } from "lib/customize";

export function liveDemoUrl(context) {
return getSupportUrl({
Expand All @@ -14,18 +17,20 @@ export function liveDemoUrl(context) {

interface Props {
context: string;
label?;
type?;
label?: string | ReactNode;
btnType?: ButtonType;
}

export default function LiveDemo({ context, label, type }: Props) {
const [href, setHref] = useState<string | undefined>(undefined);
useEffect(() => {
setHref(liveDemoUrl(context));
}, []);
export default function LiveDemo({ label, btnType }: Props) {
const { supportVideoCall } = useCustomize();

if (!supportVideoCall) {
return null;
}

return (
<Button href={href} type={type}>
<Icon name="users" /> {label ?? "Contact Us!"}
<Button href={supportVideoCall} type={btnType}>
<Icon name="video-camera" /> {label ?? "Book a Demo!"}
</Button>
);
}
7 changes: 6 additions & 1 deletion src/packages/next/components/landing/sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useRouter } from "next/router";
import { join } from "path";
import { CSSProperties, ReactNode } from "react";

import { Icon } from "@cocalc/frontend/components/icon";
import SSO from "components/auth/sso";
import { Paragraph } from "components/misc";
import basePath from "lib/base-path";
Expand All @@ -17,6 +18,7 @@ interface Props {
startup?: ReactNode; // customize the button, e.g. "Start Jupyter Now".
hideFree?: boolean;
style?: React.CSSProperties;
emphasize?: boolean;
}

const STYLE: CSSProperties = {
Expand All @@ -25,7 +27,7 @@ const STYLE: CSSProperties = {
marginBottom: "0",
} as const;

export default function SignIn({ startup, hideFree, style }: Props) {
export default function SignIn({ startup, hideFree, style, emphasize }: Props) {
const { anonymousSignup, siteName, account, emailSignup } = useCustomize();
style = { ...STYLE, ...style };
const router = useRouter();
Expand All @@ -38,6 +40,7 @@ export default function SignIn({ startup, hideFree, style }: Props) {
onClick={() => (window.location.href = join(basePath, "projects"))}
title={`Open the ${siteName} app and view your projects.`}
type="primary"
icon={<Icon name="edit" />}
>
Your {siteName} Projects
</Button>
Expand All @@ -55,6 +58,7 @@ export default function SignIn({ startup, hideFree, style }: Props) {
style={{ margin: "10px" }}
title={"Create a new account."}
onClick={() => router.push("/auth/sign-up")}
type={emphasize ? "primary" : undefined}
>
Sign Up
</Button>
Expand All @@ -65,6 +69,7 @@ export default function SignIn({ startup, hideFree, style }: Props) {
"Either create a new account or sign into an existing account."
}
onClick={() => router.push("/auth/sign-in")}
type={emphasize ? "primary" : undefined}
>
Sign In
</Button>
Expand Down
14 changes: 12 additions & 2 deletions src/packages/next/components/store/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import { Divider } from "antd";
import { useRouter } from "next/router";
import { useEffect } from "react";

import { Icon, PAYASYOUGO_ICON } from "@cocalc/frontend/components/icon";
import { Paragraph } from "components/misc";
import A from "components/misc/A";
import SiteName from "components/share/site-name";
import { useCustomize } from "lib/customize";
import {
OVERVIEW_LARGE_ICON,
OVERVIEW_STYLE,
Expand All @@ -19,6 +21,7 @@ import {

export default function Overview() {
const router = useRouter();
const { supportVideoCall } = useCustomize();

// most likely, user will go to the cart next
useEffect(() => {
Expand All @@ -31,11 +34,18 @@ export default function Overview() {
<h2 style={{ marginBottom: "30px" }}>
Welcome to the <SiteName /> Store!
</h2>
<div style={{ fontSize: "13pt" }}>
<Paragraph style={{ fontSize: "13pt" }}>
Shop below for <A href="/store/site-license">licenses</A> and{" "}
<A href="/store/vouchers">vouchers</A> or explore{" "}
<A href="/pricing">all available products and pricing</A>.
</div>
</Paragraph>
{supportVideoCall ? (
<Paragraph>
Not sure what you need?{" "}
<A href={supportVideoCall}>Book a video call</A> and we'll help you
decide.
</Paragraph>
) : undefined}
<OverviewRow>
<Product icon="key" title="Licenses" href="/store/site-license">
Buy a license to upgrade projects, get internet access, more CPU, disk
Expand Down
Loading