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/payment-methods.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 { Rendered, useActions, useState } from "../app-framework";
7
import { Button, Row, Col, Panel } from "../antd-bootstrap";
8
import { Icon } from "../components/icon";
9
import { Source } from "./types";
10
import { AddPaymentMethod } from "./add-payment-method";
11
import { PaymentMethod } from "./payment-method";
12
import { ErrorDisplay } from "../components/error-display";
13
14
import { cmp } from "@cocalc/util/misc";
15
16
interface Props {
17
sources?: { data: Source[] }; // could be undefined, if it is a customer and all sources are removed
18
default?: string;
19
}
20
21
type State = "view" | "delete" | "add_new";
22
23
export const PaymentMethods: React.FC<Props> = (props) => {
24
const [state, set_state] = useState<State>("view");
25
const [error, set_error] = useState<string>("");
26
const actions = useActions("billing");
27
28
function add_payment_method(): void {
29
set_state("add_new");
30
}
31
32
function render_add_payment_method(): Rendered {
33
if (state === "add_new") {
34
return <AddPaymentMethod on_close={() => set_state("view")} />;
35
}
36
}
37
38
function render_add_payment_method_button(): Rendered {
39
return (
40
<Button
41
disabled={state !== "view"}
42
onClick={add_payment_method}
43
bsStyle="primary"
44
className="pull-right"
45
>
46
<Icon name="plus-circle" /> Add payment method...
47
</Button>
48
);
49
}
50
51
function render_header(): Rendered {
52
return (
53
<Row>
54
<Col sm={6}>
55
<Icon name="credit-card" /> Payment methods
56
</Col>
57
<Col sm={6}>{render_add_payment_method_button()}</Col>
58
</Row>
59
);
60
}
61
62
function set_as_default(id: string): void {
63
actions.set_as_default_payment_method(id);
64
}
65
66
function delete_method(id: string): void {
67
actions.delete_payment_method(id);
68
}
69
70
function render_payment_method(source: Source): Rendered {
71
if (source.object != "card") {
72
// TODO: non credit cards not yet supported.
73
// These *do* arise naturally already in cocalc, e.g., when you pay via
74
// for an invoice with a failing payment directly on the stripe page
75
// for your invoice.
76
return;
77
}
78
return (
79
<PaymentMethod
80
key={source.id}
81
source={source}
82
default={source.id === props.default}
83
set_as_default={() => set_as_default(source.id)}
84
delete_method={() => delete_method(source.id)}
85
/>
86
);
87
}
88
89
function render_payment_methods(): undefined | Rendered[] {
90
// this happens, when it is a customer but all credit cards are deleted!
91
if (props.sources == null) {
92
return;
93
}
94
// Always sort sources in the same order. This way when you select
95
// a default source, they don't get reordered, which is really confusing.
96
props.sources.data.sort((a, b) => cmp(a.id, b.id));
97
return props.sources.data.map((source) => render_payment_method(source));
98
}
99
100
function render_error(): Rendered {
101
if (error) {
102
return <ErrorDisplay error={error} onClose={() => set_error("")} />;
103
}
104
}
105
106
return (
107
<Panel header={render_header()}>
108
{render_error()}
109
{state == "add_new" ? render_add_payment_method() : undefined}
110
{render_payment_methods()}
111
</Panel>
112
);
113
};
114
115