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/frontend/billing/plan-info.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { PROJECT_UPGRADES } from "@cocalc/util/schema";
7
import { plural, capitalize } from "@cocalc/util/misc";
8
import { Component, Rendered } from "../app-framework";
9
import { Tip } from "../components/tip";
10
import { Icon } from "../components/icon";
11
import { Gap } from "../components/gap";
12
import { r_join } from "../components/r_join";
13
import { Button, Panel } from "@cocalc/frontend/antd-bootstrap";
14
import { PeriodName } from "./types";
15
16
interface Props {
17
plan: string;
18
periods: PeriodName[];
19
selected?: boolean;
20
on_click?: Function;
21
}
22
23
export class PlanInfo extends Component<Props> {
24
private render_plan_info_line(name: string, value: number, data): Rendered {
25
return (
26
<div key={name} style={{ marginBottom: "5px", marginLeft: "10px" }}>
27
<Tip title={data.display} tip={data.desc}>
28
<span style={{ fontWeight: "bold", color: "#444" }}>
29
{value * data.pricing_factor}{" "}
30
{plural(value * data.pricing_factor, data.pricing_unit)}
31
</span>
32
<Gap />
33
<span style={{ color: "#666" }}>{data.display}</span>
34
</Tip>
35
</div>
36
);
37
}
38
39
private render_cost(price: string, period: string): Rendered {
40
period =
41
PROJECT_UPGRADES.period_names[period] != null
42
? PROJECT_UPGRADES.period_names[period]
43
: period;
44
return (
45
<span key={period} style={{ whiteSpace: "nowrap" }}>
46
<span style={{ fontSize: "16px", verticalAlign: "super" }}>$</span>
47
<Gap />
48
<span style={{ fontSize: "30px" }}>{price}</span>
49
<span style={{ fontSize: "14px" }}> / {period}</span>
50
</span>
51
);
52
}
53
54
private render_price(prices: string[]): Rendered[] | Rendered {
55
if (this.props.on_click != null) {
56
// note: in non-static, there is always just *one* price
57
// (several only on "static" pages)
58
const result: Rendered[] = [];
59
for (let i = 0; i < prices.length; i++) {
60
result.push(
61
<Button key={i} bsStyle={this.props.selected ? "primary" : undefined}>
62
{this.render_cost(prices[i], this.props.periods[i])}
63
</Button>,
64
);
65
}
66
return result;
67
} else {
68
const result: Rendered[] = [];
69
for (let i = 0; i < prices.length; i++) {
70
result.push(this.render_cost(prices[i], this.props.periods[i]));
71
}
72
return <h3 style={{ textAlign: "left" }}>{r_join(result, <br />)}</h3>;
73
}
74
}
75
76
private render_plan_name(plan_data): Rendered {
77
let name;
78
if (plan_data.desc != null) {
79
name = plan_data.desc;
80
if (name.indexOf("\n") !== -1) {
81
const v = name.split("\n");
82
name = (
83
<span>
84
{v[0].trim()}
85
<br />
86
{v[1].trim()}
87
</span>
88
);
89
}
90
} else {
91
name = capitalize(this.props.plan).replace(/_/g, " ") + " plan";
92
}
93
return (
94
<div style={{ paddingLeft: "10px" }}>
95
<Icon name={plan_data.icon} />{" "}
96
<span style={{ fontWeight: "bold" }}>{name}</span>
97
</div>
98
);
99
}
100
101
public render(): Rendered {
102
const plan_data = PROJECT_UPGRADES.subscription[this.props.plan];
103
if (plan_data == null) {
104
return <div>Unknown plan type: {this.props.plan}</div>;
105
}
106
107
const { params } = PROJECT_UPGRADES;
108
const prices: string[] = [];
109
for (const period of this.props.periods) {
110
prices.push(plan_data.price[period]);
111
}
112
const { benefits } = plan_data;
113
114
const style = {
115
cursor: this.props.on_click != null ? "pointer" : undefined,
116
};
117
118
return (
119
<Panel
120
style={style}
121
header={this.render_plan_name(plan_data)}
122
onClick={() =>
123
this.props.on_click != null ? this.props.on_click() : undefined
124
}
125
>
126
<Gap />
127
{PROJECT_UPGRADES.field_order
128
.filter((name) => benefits[name])
129
.map((name) =>
130
this.render_plan_info_line(
131
name,
132
benefits[name] != null ? benefits[name] : 0,
133
params[name],
134
),
135
)}
136
<Gap />
137
138
<div style={{ textAlign: "center", marginTop: "10px" }}>
139
{this.render_price(prices)}
140
</div>
141
</Panel>
142
);
143
}
144
}
145
146