CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/components/store/index.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2022 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
import { Alert, Layout } from "antd";
6
import { useRouter } from "next/router";
7
import { useEffect, useState } from "react";
8
9
import * as purchasesApi from "@cocalc/frontend/purchases/api";
10
import { COLORS } from "@cocalc/util/theme";
11
import Anonymous from "components/misc/anonymous";
12
import Loading from "components/share/loading";
13
import SiteName from "components/share/site-name";
14
import { StoreBalanceContext } from "lib/balance";
15
import { MAX_WIDTH } from "lib/config";
16
import useProfile from "lib/hooks/profile";
17
import useCustomize from "lib/use-customize";
18
import Boost from "./boost";
19
import Cart from "./cart";
20
import Checkout from "./checkout";
21
import Congrats from "./congrats";
22
import DedicatedResource from "./dedicated";
23
import Menu from "./menu";
24
import Overview from "./overview";
25
import SiteLicense from "./site-license";
26
import { StoreInplaceSignInOrUp } from "./store-inplace-signup";
27
import Vouchers from "./vouchers";
28
29
const { Content } = Layout;
30
31
interface Props {
32
page: (
33
| "site-license"
34
| "boost"
35
| "dedicated"
36
| "cart"
37
| "checkout"
38
| "congrats"
39
| "vouchers"
40
| undefined
41
)[];
42
}
43
44
export default function StoreLayout({ page }: Props) {
45
const { isCommercial } = useCustomize();
46
const router = useRouter();
47
const profile = useProfile({ noCache: true });
48
49
const [balance, setBalance] = useState<number>();
50
51
const refreshBalance = async () => {
52
if (!profile || !profile.account_id) {
53
setBalance(undefined);
54
return;
55
}
56
57
// Set balance if user is logged in
58
//
59
setBalance(await purchasesApi.getBalance());
60
};
61
62
useEffect(() => {
63
router.prefetch("/store/site-license");
64
}, []);
65
66
useEffect(() => {
67
refreshBalance();
68
}, [profile]);
69
70
function renderNotCommercial(): JSX.Element {
71
return (
72
<Alert
73
showIcon
74
style={{
75
margin: "30px auto",
76
maxWidth: "400px",
77
fontSize: "12pt",
78
padding: "15px 30px",
79
}}
80
type="warning"
81
message={
82
<>
83
The <SiteName /> store is not enabled.
84
</>
85
}
86
/>
87
);
88
}
89
90
if (!isCommercial) {
91
return renderNotCommercial();
92
}
93
94
if (!profile) {
95
return <Loading large center />;
96
}
97
const { account_id, is_anonymous } = profile;
98
const noAccount = account_id == null;
99
100
// wrapper: only the pages showing the prices will be shown to the general public or anonymous users
101
function requireAccount(StorePage): JSX.Element {
102
if (noAccount) {
103
return (
104
<Alert
105
style={{ margin: "15px auto" }}
106
type="warning"
107
message={<StoreInplaceSignInOrUp />}
108
/>
109
);
110
}
111
112
return <StorePage />;
113
}
114
115
const [main] = page;
116
117
function body() {
118
if (main == null) return <Overview />;
119
120
if (is_anonymous) {
121
return <Anonymous />;
122
}
123
124
switch (main) {
125
case "site-license":
126
return <SiteLicense noAccount={noAccount} />;
127
case "boost":
128
return <Boost />;
129
case "dedicated":
130
return <DedicatedResource noAccount={noAccount} />;
131
case "cart":
132
return requireAccount(Cart);
133
case "checkout":
134
return requireAccount(Checkout);
135
case "vouchers":
136
return requireAccount(Vouchers);
137
case "congrats":
138
return requireAccount(Congrats);
139
default:
140
return <Alert type="error" message={`Invalid page ${main}`} />;
141
}
142
}
143
144
// this layout is the same as ../licenses/layout.tsx and ../billing/layout.tsx
145
function renderMain(): JSX.Element {
146
return (
147
<Layout
148
style={{
149
padding: "0 24px 24px",
150
backgroundColor: "white",
151
color: COLORS.GRAY_D,
152
}}
153
>
154
<Content
155
style={{
156
margin: "0 30px",
157
minHeight: "60vh",
158
}}
159
>
160
<div style={{ maxWidth: MAX_WIDTH, margin: "auto" }}>
161
<StoreBalanceContext.Provider value={{ balance, refreshBalance }}>
162
<Menu main={main} />
163
{body()}
164
</StoreBalanceContext.Provider>
165
</div>
166
</Content>
167
</Layout>
168
);
169
}
170
171
return renderMain();
172
}
173
174