Path: blob/master/src/packages/next/components/store/index.tsx
5857 views
/*1* This file is part of CoCalc: Copyright © 2022 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/4import { Alert, Layout } from "antd";5import { useRouter } from "next/router";6import { useEffect, useState, type JSX } from "react";78import * as purchasesApi from "@cocalc/frontend/purchases/api";9import { COLORS } from "@cocalc/util/theme";10import Anonymous from "components/misc/anonymous";11import Loading from "components/share/loading";12import SiteName from "components/share/site-name";13import { StoreBalanceContext } from "lib/balance";14import { MAX_WIDTH } from "lib/config";15import useProfile from "lib/hooks/profile";16import useCustomize from "lib/use-customize";17import Cart from "./cart";18import Checkout from "./checkout";19import Congrats from "./congrats";20import Menu from "./menu";21import Overview from "./overview";22import Processing from "./processing";23import SiteLicense from "./site-license";24import { StoreInplaceSignInOrUp } from "./store-inplace-signup";25import { StorePagesTypes } from "./types";26import Vouchers from "./vouchers";2728const { Content } = Layout;2930interface Props {31page: (StorePagesTypes | undefined)[];32}3334export default function StoreLayout({ page }: Props) {35const { isCommercial } = useCustomize();36const router = useRouter();37const profile = useProfile({ noCache: true });3839const [loading, setLoading] = useState<boolean>(false);4041const [balance, setBalance] = useState<number>();4243const refreshBalance = async () => {44if (!profile || !profile.account_id) {45setBalance(undefined);46return;47}4849// Set balance if user is logged in50//51try {52setLoading(true);53setBalance(await purchasesApi.getBalance());54} catch (err) {55console.warn("Error updating balance", err);56} finally {57setLoading(false);58}59};6061useEffect(() => {62router.prefetch("/store/site-license");63}, []);6465useEffect(() => {66refreshBalance();67}, [profile]);6869function renderNotCommercial(): JSX.Element {70return (71<Alert72showIcon73style={{74margin: "30px auto",75maxWidth: "400px",76fontSize: "12pt",77padding: "15px 30px",78}}79type="warning"80message={81<>82The <SiteName /> store is not enabled.83</>84}85/>86);87}8889if (!isCommercial) {90return renderNotCommercial();91}9293if (!profile) {94return <Loading large center />;95}96const { account_id, is_anonymous } = profile;97const noAccount = account_id == null;9899// wrapper: only the pages showing the prices will be shown to the general public or anonymous users100function requireAccount(StorePage): JSX.Element {101if (noAccount) {102return (103<Alert104style={{ margin: "15px auto" }}105type="warning"106message={<StoreInplaceSignInOrUp />}107/>108);109}110111return <StorePage />;112}113114const [main] = page;115116function body() {117if (main == null) return <Overview />;118119if (is_anonymous) {120return <Anonymous />;121}122123switch (main) {124case "site-license":125return <SiteLicense noAccount={noAccount} source="site-license" />;126case "course":127return <SiteLicense noAccount={noAccount} source="course" />;128case "cart":129return requireAccount(Cart);130case "checkout":131return requireAccount(Checkout);132case "processing":133return requireAccount(Processing);134case "vouchers":135return requireAccount(Vouchers);136case "congrats":137return requireAccount(Congrats);138default:139return <Alert type="error" message={`Invalid page ${main}`} />;140}141}142143// this layout is the same as ../licenses/layout.tsx and ../billing/layout.tsx144function renderMain(): JSX.Element {145return (146<Layout147style={{148padding: "0 24px 24px",149backgroundColor: "white",150color: COLORS.GRAY_D,151}}152>153<Content154style={{155margin: "0 30px",156minHeight: "60vh",157}}158>159<div style={{ maxWidth: MAX_WIDTH, margin: "auto" }}>160<StoreBalanceContext.Provider161value={{ balance, refreshBalance, loading }}162>163<Menu main={main} />164{body()}165</StoreBalanceContext.Provider>166</div>167</Content>168</Layout>169);170}171172return renderMain();173}174175176