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/billing-page.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
// Ensure the billing Actions and Store are created:
7
import "./actions";
8
9
import {
10
Component,
11
rclass,
12
redux,
13
Rendered,
14
rtypes,
15
} from "@cocalc/frontend/app-framework";
16
import {
17
A,
18
ActivityDisplay,
19
ErrorDisplay,
20
Loading,
21
} from "@cocalc/frontend/components";
22
import {
23
Footer,
24
HelpEmailLink,
25
PolicyPricingPageUrl,
26
} from "@cocalc/frontend/customize";
27
import { Map } from "immutable";
28
import { PaymentMethods } from "./payment-methods";
29
import { Customer, InvoicesMap } from "./types";
30
31
interface ReactProps {
32
is_simplified?: boolean;
33
for_course?: boolean;
34
}
35
36
interface ReduxProps {
37
customer?: Customer;
38
invoices?: InvoicesMap;
39
error?: string | Error;
40
action?: string;
41
loaded?: boolean;
42
no_stripe?: boolean; // if true, stripe definitely isn't configured on the server
43
selected_plan: string;
44
project_map: Map<string, any>; // used, e.g., for course project payments; also computing available upgrades
45
stripe_customer: Map<string, any>; // to get total upgrades user has available
46
}
47
48
export const BillingPage = rclass<ReactProps>(
49
class BillingPage extends Component<ReactProps & ReduxProps> {
50
static reduxProps() {
51
return {
52
billing: {
53
customer: rtypes.object,
54
invoices: rtypes.immutable.Map,
55
error: rtypes.oneOfType([rtypes.string, rtypes.object]),
56
action: rtypes.string,
57
loaded: rtypes.bool,
58
no_stripe: rtypes.bool, // if true, stripe definitely isn't configured on the server
59
selected_plan: rtypes.string,
60
},
61
projects: {
62
project_map: rtypes.immutable, // used, e.g., for course project payments; also computing available upgrades
63
},
64
account: {
65
stripe_customer: rtypes.immutable, // to get total upgrades user has available
66
},
67
};
68
}
69
70
private render_action(): Rendered {
71
if (this.props.action) {
72
return (
73
<ActivityDisplay
74
style={{ position: "fixed", right: "45px", top: "85px" }}
75
activity={[this.props.action]}
76
on_clear={() => redux.getActions("billing").clear_action()}
77
/>
78
);
79
}
80
}
81
82
private render_error(): Rendered {
83
if (this.props.error) {
84
return (
85
<ErrorDisplay
86
error={this.props.error}
87
onClose={() => redux.getActions("billing").clear_error()}
88
/>
89
);
90
}
91
}
92
93
private render_enterprise_support(): Rendered {
94
return (
95
<li>
96
<b>Enterprise Support:</b> Contact us at <HelpEmailLink /> for{" "}
97
<i>enterprise support</i>, including customized course packages,
98
modified terms of service, additional legal agreements, purchase
99
orders, insurance and priority technical support.
100
</li>
101
);
102
}
103
104
private render_on_prem(): Rendered {
105
return (
106
<li>
107
<b>Commercial on Premises:</b> Contact us at <HelpEmailLink /> for{" "}
108
questions about our{" "}
109
<A href={PolicyPricingPageUrl + "/onprem"}>
110
commercial on premises offering.
111
</A>
112
</li>
113
);
114
}
115
116
private render_help_suggestion(): Rendered {
117
return (
118
<>
119
<li>
120
<b>Questions: </b>
121
If you have any questions at all, read the{" "}
122
<A href={"https://doc.cocalc.com/billing.html"}>
123
Billing{"/"}Upgrades FAQ
124
</A>{" "}
125
or email <HelpEmailLink />.
126
</li>
127
128
<li>
129
<b>Teaching:</b>{" "}
130
<HelpEmailLink text={<span>Contact&nbsp;us</span>} /> if you are
131
considering purchasing a course subscription and need a short
132
evaluation trial.
133
</li>
134
{this.render_enterprise_support()}
135
{this.render_on_prem()}
136
</>
137
);
138
}
139
140
private counts(): { cards: number; subs: number; invoices: number } {
141
const cards = this.props.customer?.sources?.total_count ?? 0;
142
const subs = this.props.customer?.subscriptions?.total_count ?? 0;
143
const invoices = this.props.invoices?.get("total_count") ?? 0;
144
return { cards, subs, invoices };
145
}
146
147
private render_page(): Rendered {
148
if (!this.props.for_course) return;
149
if (!this.props.loaded) {
150
// nothing loaded yet from backend
151
return <Loading />;
152
} else if (this.props.customer == null && this.props.for_course) {
153
// user not initialized yet -- only thing to do is add a card.
154
return (
155
<div>
156
<PaymentMethods sources={{ data: [] }} default="" />
157
</div>
158
);
159
} else {
160
// data loaded and customer exists
161
if (this.props.customer == null) return; // can't happen; for typescript
162
const { subs } = this.counts();
163
if (this.props.is_simplified && subs > 0) {
164
return (
165
<div>
166
<PaymentMethods
167
sources={this.props.customer.sources}
168
default={this.props.customer.default_source}
169
/>
170
</div>
171
);
172
} else if (this.props.is_simplified) {
173
return (
174
<div>
175
<PaymentMethods
176
sources={this.props.customer.sources}
177
default={this.props.customer.default_source}
178
/>
179
</div>
180
);
181
}
182
}
183
}
184
185
renderLinks() {
186
return (
187
<div>
188
<h3>Links</h3>
189
<ul>
190
{!this.props.for_course ? this.render_help_suggestion() : undefined}
191
{!this.props.no_stripe ? this.render_action() : undefined}
192
{this.render_error()}
193
{!this.props.no_stripe ? this.render_page() : undefined}
194
</ul>
195
</div>
196
);
197
}
198
199
public render(): Rendered {
200
return (
201
<>
202
{this.renderLinks()}
203
{!this.props.is_simplified ? <Footer /> : undefined}
204
</>
205
);
206
}
207
}
208
);
209
210