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/add-box.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
6
/*
7
Add a cash voucher to your shopping cart.
8
*/
9
import { CostInputPeriod } from "@cocalc/util/licenses/purchase/types";
10
import { round2up, round4 } from "@cocalc/util/misc";
11
import { money } from "@cocalc/util/licenses/purchase/utils";
12
import { Alert, Button, Tooltip } from "antd";
13
import { addToCart } from "./add-to-cart";
14
import { DisplayCost } from "./site-license-cost";
15
import { periodicCost } from "@cocalc/util/licenses/purchase/compute-cost";
16
17
interface Props {
18
cost?: CostInputPeriod;
19
router;
20
form;
21
cartError: string | undefined;
22
setCartError: (error) => void;
23
dedicatedItem?: boolean;
24
disabled?: boolean;
25
noAccount: boolean;
26
}
27
28
export function AddBox(props: Props) {
29
const {
30
cost,
31
router,
32
form,
33
cartError,
34
setCartError,
35
dedicatedItem = false,
36
noAccount,
37
} = props;
38
// console.log({ cost });
39
if (cost?.input.type == "cash-voucher") {
40
return null;
41
}
42
let disabled = props.disabled ?? false;
43
// if any of the fields in cost that start with the string "cost" are NaN, disable submission:
44
if (
45
!cost ||
46
Object.keys(cost).some((k) => k.startsWith("cost") && isNaN(cost[k]))
47
) {
48
disabled = true;
49
}
50
51
function costPerProject() {
52
if (cost?.input.type != "quota") {
53
return;
54
}
55
if (dedicatedItem || cost.input.quantity == null) {
56
return;
57
}
58
const costPer = periodicCost(cost) / cost.input.quantity;
59
return (
60
<Tooltip title={`$${round4(costPer)} per project`}>
61
<div>{money(round2up(costPer))} per project</div>
62
</Tooltip>
63
);
64
}
65
66
function renderButton(): JSX.Element | null {
67
if (noAccount) return null;
68
69
return (
70
<div style={{ textAlign: "center" }}>
71
{router.query.id != null && (
72
<Button
73
size="large"
74
style={{ marginRight: "5px" }}
75
onClick={() => router.push("/store/cart")}
76
disabled={disabled}
77
>
78
Cancel
79
</Button>
80
)}
81
<AddToCartButton
82
cartError={cartError}
83
cost={cost}
84
disabled={disabled}
85
form={form}
86
router={router}
87
setCartError={setCartError}
88
/>
89
{cartError && <Alert type="error" message={cartError} />}
90
</div>
91
);
92
}
93
94
return (
95
<div style={{ textAlign: "center" }}>
96
<div
97
style={{
98
display: "inline-block",
99
maxWidth: "550px",
100
background: "white",
101
border: "1px solid #ccc",
102
padding: "10px 20px",
103
borderRadius: "5px",
104
margin: "15px 0",
105
fontSize: "12pt",
106
}}
107
>
108
{cost && <DisplayCost cost={cost} />}
109
{cost && costPerProject()}
110
{renderButton()}
111
</div>
112
</div>
113
);
114
}
115
116
interface CartButtonProps {
117
cost: CostInputPeriod | undefined;
118
router;
119
form;
120
setCartError: (error) => void;
121
disabled?: boolean;
122
cartError: string | undefined;
123
variant?: "primary" | "small";
124
}
125
126
export function AddToCartButton(props: CartButtonProps) {
127
const {
128
cost,
129
form,
130
router,
131
setCartError,
132
cartError,
133
variant = "primary",
134
} = props;
135
136
const style = variant === "primary" ? { marginTop: "5px" } : {};
137
const disabled =
138
(props.disabled ?? false) || !!cartError || cost == null || cost.cost === 0;
139
140
return (
141
<Button
142
size={variant === "small" ? "small" : "large"}
143
type="primary"
144
htmlType="submit"
145
style={style}
146
onClick={() => addToCart({ form, setCartError, router })}
147
disabled={disabled}
148
>
149
{disabled
150
? "Finish configuring the license..."
151
: router.query.id != null
152
? "Save Changes"
153
: "Add to Cart"}
154
</Button>
155
);
156
}
157
158