Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/billing/billing-page.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45// Ensure the billing Actions and Store are created:6import "./actions";78import {9Component,10rclass,11redux,12Rendered,13rtypes,14} from "@cocalc/frontend/app-framework";15import {16A,17ActivityDisplay,18ErrorDisplay,19Loading,20} from "@cocalc/frontend/components";21import {22Footer,23HelpEmailLink,24PolicyPricingPageUrl,25} from "@cocalc/frontend/customize";26import { Map } from "immutable";27import { PaymentMethods } from "./payment-methods";28import { Customer, InvoicesMap } from "./types";2930interface ReactProps {31is_simplified?: boolean;32for_course?: boolean;33}3435interface ReduxProps {36customer?: Customer;37invoices?: InvoicesMap;38error?: string | Error;39action?: string;40loaded?: boolean;41no_stripe?: boolean; // if true, stripe definitely isn't configured on the server42selected_plan: string;43project_map: Map<string, any>; // used, e.g., for course project payments; also computing available upgrades44stripe_customer: Map<string, any>; // to get total upgrades user has available45}4647export const BillingPage = rclass<ReactProps>(48class BillingPage extends Component<ReactProps & ReduxProps> {49static reduxProps() {50return {51billing: {52customer: rtypes.object,53invoices: rtypes.immutable.Map,54error: rtypes.oneOfType([rtypes.string, rtypes.object]),55action: rtypes.string,56loaded: rtypes.bool,57no_stripe: rtypes.bool, // if true, stripe definitely isn't configured on the server58selected_plan: rtypes.string,59},60projects: {61project_map: rtypes.immutable, // used, e.g., for course project payments; also computing available upgrades62},63account: {64stripe_customer: rtypes.immutable, // to get total upgrades user has available65},66};67}6869private render_action(): Rendered {70if (this.props.action) {71return (72<ActivityDisplay73style={{ position: "fixed", right: "45px", top: "85px" }}74activity={[this.props.action]}75on_clear={() => redux.getActions("billing").clear_action()}76/>77);78}79}8081private render_error(): Rendered {82if (this.props.error) {83return (84<ErrorDisplay85error={this.props.error}86onClose={() => redux.getActions("billing").clear_error()}87/>88);89}90}9192private render_enterprise_support(): Rendered {93return (94<li>95<b>Enterprise Support:</b> Contact us at <HelpEmailLink /> for{" "}96<i>enterprise support</i>, including customized course packages,97modified terms of service, additional legal agreements, purchase98orders, insurance and priority technical support.99</li>100);101}102103private render_on_prem(): Rendered {104return (105<li>106<b>Commercial on Premises:</b> Contact us at <HelpEmailLink /> for{" "}107questions about our{" "}108<A href={PolicyPricingPageUrl + "/onprem"}>109commercial on premises offering.110</A>111</li>112);113}114115private render_help_suggestion(): Rendered {116return (117<>118<li>119<b>Questions: </b>120If you have any questions at all, read the{" "}121<A href={"https://doc.cocalc.com/billing.html"}>122Billing{"/"}Upgrades FAQ123</A>{" "}124or email <HelpEmailLink />.125</li>126127<li>128<b>Teaching:</b>{" "}129<HelpEmailLink text={<span>Contact us</span>} /> if you are130considering purchasing a course subscription and need a short131evaluation trial.132</li>133{this.render_enterprise_support()}134{this.render_on_prem()}135</>136);137}138139private counts(): { cards: number; subs: number; invoices: number } {140const cards = this.props.customer?.sources?.total_count ?? 0;141const subs = this.props.customer?.subscriptions?.total_count ?? 0;142const invoices = this.props.invoices?.get("total_count") ?? 0;143return { cards, subs, invoices };144}145146private render_page(): Rendered {147if (!this.props.for_course) return;148if (!this.props.loaded) {149// nothing loaded yet from backend150return <Loading />;151} else if (this.props.customer == null && this.props.for_course) {152// user not initialized yet -- only thing to do is add a card.153return (154<div>155<PaymentMethods sources={{ data: [] }} default="" />156</div>157);158} else {159// data loaded and customer exists160if (this.props.customer == null) return; // can't happen; for typescript161const { subs } = this.counts();162if (this.props.is_simplified && subs > 0) {163return (164<div>165<PaymentMethods166sources={this.props.customer.sources}167default={this.props.customer.default_source}168/>169</div>170);171} else if (this.props.is_simplified) {172return (173<div>174<PaymentMethods175sources={this.props.customer.sources}176default={this.props.customer.default_source}177/>178</div>179);180}181}182}183184renderLinks() {185return (186<div>187<h3>Links</h3>188<ul>189{!this.props.for_course ? this.render_help_suggestion() : undefined}190{!this.props.no_stripe ? this.render_action() : undefined}191{this.render_error()}192{!this.props.no_stripe ? this.render_page() : undefined}193</ul>194</div>195);196}197198public render(): Rendered {199return (200<>201{this.renderLinks()}202{!this.props.is_simplified ? <Footer /> : undefined}203</>204);205}206}207);208209210