Skip to content

next/store: "course" purchases #8437

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

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
10 changes: 7 additions & 3 deletions src/packages/next/components/store/add-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { addToCart } from "./add-to-cart";
import { DisplayCost } from "./site-license-cost";
import { periodicCost } from "@cocalc/util/licenses/purchase/compute-cost";
import { decimalDivide } from "@cocalc/util/stripe/calc";
import { LicenseType } from "./types";

export const ADD_STYLE = {
display: "inline-block",
Expand All @@ -37,6 +38,7 @@ interface Props {
dedicatedItem?: boolean;
disabled?: boolean;
noAccount: boolean;
type: LicenseType;
}

export function AddBox({
Expand All @@ -48,6 +50,7 @@ export function AddBox({
dedicatedItem = false,
noAccount,
disabled = false,
type,
}: Props) {
if (cost?.input.type == "cash-voucher") {
return null;
Expand Down Expand Up @@ -76,7 +79,8 @@ export function AddBox({
}}
message={
<>
{money(round2up(costPer))} <b>per project</b>{" "}
{money(round2up(costPer))}{" "}
<b>per {type === "course" ? "student" : "project"}</b>{" "}
{!!cost.period && cost.period != "range" ? cost.period : ""}
</>
}
Expand Down Expand Up @@ -175,8 +179,8 @@ export function AddToCartButton({
{clicked
? "Moving to Cart..."
: router.query.id != null
? "Save Changes"
: "Add to Cart"}
? "Save Changes"
: "Add to Cart"}
{clicked && <Spin style={{ marginLeft: "15px" }} />}
</Button>
);
Expand Down
20 changes: 7 additions & 13 deletions src/packages/next/components/store/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { Alert, Layout } from "antd";
import { useRouter } from "next/router";
import { useEffect, useState, type JSX } from "react";

import * as purchasesApi from "@cocalc/frontend/purchases/api";
import { COLORS } from "@cocalc/util/theme";
import Anonymous from "components/misc/anonymous";
Expand All @@ -16,28 +17,19 @@ import useProfile from "lib/hooks/profile";
import useCustomize from "lib/use-customize";
import Cart from "./cart";
import Checkout from "./checkout";
import Processing from "./processing";
import Congrats from "./congrats";
import Menu from "./menu";
import Overview from "./overview";
import Processing from "./processing";
import SiteLicense from "./site-license";
import { StoreInplaceSignInOrUp } from "./store-inplace-signup";
import { StorePagesTypes } from "./types";
import Vouchers from "./vouchers";

const { Content } = Layout;

interface Props {
page: (
| "site-license"
| "boost"
| "dedicated"
| "cart"
| "checkout"
| "processing"
| "congrats"
| "vouchers"
| undefined
)[];
page: (StorePagesTypes | undefined)[];
}

export default function StoreLayout({ page }: Props) {
Expand Down Expand Up @@ -131,7 +123,9 @@ export default function StoreLayout({ page }: Props) {

switch (main) {
case "site-license":
return <SiteLicense noAccount={noAccount} />;
return <SiteLicense noAccount={noAccount} type="license" />;
case "course":
return <SiteLicense noAccount={noAccount} type="course" />;
case "cart":
return requireAccount(Cart);
case "checkout":
Expand Down
16 changes: 10 additions & 6 deletions src/packages/next/components/store/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,29 @@
* License: MS-RSL – see LICENSE.md for details
*/

import React, { useContext } from "react";
import { Button, Menu, MenuProps, Flex, Spin } from "antd";
import type { MenuProps } from "antd";
import { Button, Flex, Menu, Spin } from "antd";
import { useRouter } from "next/router";
import React, { useContext } from "react";

import { Icon } from "@cocalc/frontend/components/icon";
import { currency, round2down } from "@cocalc/util/misc";
import { COLORS } from "@cocalc/util/theme";
import { Icon } from "@cocalc/frontend/components/icon";
import { StoreBalanceContext } from "../../lib/balance";
import { StoreBalanceContext } from "lib/balance";

type MenuItem = Required<MenuProps>["items"][number];

const styles: { [k: string]: React.CSSProperties } = {
menuBookend: {
height: "100%",
whiteSpace: "nowrap",
flexGrow: 1,
flex: "0 1 auto",
textAlign: "end",
},
menu: {
width: "100%",
height: "100%",
flex: "1 1 auto",
border: 0,
},
menuRoot: {
Expand All @@ -38,7 +41,7 @@ const styles: { [k: string]: React.CSSProperties } = {
maxWidth: "100%",
flexGrow: 1,
},
};
} as const;

export interface ConfigMenuProps {
main?: string;
Expand All @@ -64,6 +67,7 @@ export default function ConfigMenu({ main }: ConfigMenuProps) {
key: "site-license",
icon: <Icon name="key" />,
},
{ label: "Course", key: "course", icon: <Icon name="graduation-cap" /> },
{
label: "Vouchers",
key: "vouchers",
Expand Down
13 changes: 8 additions & 5 deletions src/packages/next/components/store/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,17 @@ export default function Overview() {
</Paragraph>
) : undefined}
<OverviewRow>
<Product icon="key" title="Licenses" href="/store/site-license">
<Product icon="key" title="License" href="/store/site-license">
Buy a license to upgrade projects, get internet access, more CPU, disk
and memory.
</Product>
<Product href={"/store/vouchers"} icon="gift" title="Vouchers">
Purchase a <A href={"/vouchers"}>voucher code</A> to make <SiteName />{" "}
credit easily available to somebody else.
<Product icon="graduation-cap" title="Course" href="/store/course">
Purchase a license for teaching a course.
</Product>
<Paragraph style={{ textAlign: "center", width: "100%" }}>
<Icon name="gift" /> Purchase a <A href={"/vouchers"}>voucher code</A>{" "}
to make <SiteName /> credit easily available to somebody else.
</Paragraph>
<Divider />
<Product
href={"/features/compute-server"}
Expand All @@ -74,7 +77,7 @@ export default function Overview() {
<A href="/store/cart">shopping cart</A> or go straight to{" "}
<A href="/store/checkout">checkout</A>.
</Paragraph>
<Paragraph>
<Paragraph style={{ marginBottom: "4em" }}>
You can also browse your{" "}
<A href="/settings/purchases">purchase history</A>,{" "}
<A href="/settings/licenses">licenses</A>, and{" "}
Expand Down
28 changes: 28 additions & 0 deletions src/packages/next/components/store/quota-config-presets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,31 @@ export const PRESETS: PresetEntries = {
member: true,
},
} as const;

export const COURSE = {
standard: {
icon: "line-chart",
name: PRESET_STANDARD_NAME,
descr: "is a good choice for most use cases in a course",
expect: [
"Run a couple of Jupyter Notebooks at once,",
"Edit LaTeX, Markdown, R Documents, and use VS Code,",
`${STANDARD_DISK} GB disk space is sufficient to store many files and small datasets.`,
],
note: <Paragraph type="secondary">TODO NOTE</Paragraph>,
details: (
<>
You can run a couple of Jupyter Notebooks in a project at once,
depending on the kernel and memory usage. This quota is fine for editing
LaTeX documents, working with Sage Worksheets, using VS Code, and
editing all other document types. Also, {STANDARD_DISK} GB of disk space
is sufficient to store many files and a few small datasets.
</>
),
cpu: STANDARD_CPU,
ram: STANDARD_RAM,
disk: STANDARD_DISK,
uptime: "short",
member: true,
},
} as const satisfies { [key in "standard"]: PresetConfig };
Loading