Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/src/packages/frontend/admin/users/create-payment.tsx
Views: 823
import {1Alert,2Button,3Card,4Flex,5Input,6InputNumber,7Space,8Spin,9} from "antd";10import { createPaymentIntent } from "@cocalc/frontend/purchases/api";11import { Icon } from "@cocalc/frontend/components/icon";12import { useRef, useState } from "react";13import { currency } from "@cocalc/util/misc";14import ShowError from "@cocalc/frontend/components/error";15import type { LineItem } from "@cocalc/util/stripe/types";16import { LineItemsTable } from "@cocalc/frontend/purchases/line-items";1718const DEFAULT_PAYMENT = 10;1920interface Props {21account_id: string;22onClose?: () => void;23}2425export default function CreatePayment({ account_id, onClose }: Props) {26const [paymentDescription, setPaymentDescription] = useState<string>(27"Manually entered payment initiated by CoCalc staff",28);29const [amount, setAmount] = useState<number | null>(DEFAULT_PAYMENT);30const [total, setTotal] = useState<number>(0);31const [description, setDescription] = useState<string>(32"Add credit to account",33);34const purposeRef = useRef<string>(`admin-${Date.now()}`);35const [loading, setLoading] = useState<boolean>(false);36const [error, setError] = useState<string>("");37const [done, setDone] = useState<boolean>(false);38const [lineItems, setLineItems] = useState<LineItem[]>([]);3940const doIt = async () => {41if (typeof amount != "number") {42throw Error("amount must be a number");43}44try {45setError("");46setLoading(true);47await createPaymentIntent({48user_account_id: account_id,49lineItems,50description: paymentDescription,51purpose: purposeRef.current,52});53setDone(true);54} catch (err) {55console.warn("Creating payment failed", err);56setError(`${err}`);57} finally {58setLoading(false);59}60};6162return (63<Card title={"Create Payment"}>64<Input65disabled={done || loading}66style={{ flex: 1, maxWidth: "700px", marginBottom: "15px" }}67placeholder="Payment Description..."68value={paymentDescription}69onChange={(e) => setPaymentDescription(e.target.value)}70/>71<LineItemsTable lineItems={lineItems} />72<Flex gap="middle" style={{ margin: "15px 0" }}>73<Input74disabled={done || loading}75style={{ flex: 1, maxWidth: "400px" }}76placeholder="Description..."77value={description}78onChange={(e) => setDescription(e.target.value)}79/>80<InputNumber81disabled={done || loading}82min={0}83max={10000}84addonBefore="$"85placeholder="Amount..."86style={{ maxWidth: "100px" }}87value={amount}88onChange={(value) =>89setAmount(typeof value == "string" ? null : value)90}91/>92<Button93disabled={!amount || !description || loading || done || !!error}94onClick={() => {95if (!amount) {96return;97}98setLineItems(lineItems.concat([{ amount, description }]));99setTotal(total + amount);100setAmount(DEFAULT_PAYMENT);101setDescription("");102}}103>104Add Line Item105</Button>106</Flex>107<Space style={{ marginBottom: "15px" }}>108{onClose != null && (109<Button onClick={onClose}>{done ? "Close" : "Cancel"}</Button>110)}{" "}111<Button112disabled={113!!error ||114done ||115loading ||116!paymentDescription ||117lineItems.length == 0 ||118total == 0119}120type="primary"121onClick={doIt}122>123{done ? (124<>Created Payment</>125) : (126<>127Create Payment{" "}128{loading && <Spin style={{ marginLeft: "15px" }} />}129</>130)}131</Button>132</Space>133<br />134<ShowError error={error} setError={setError} />135<br />136{done && (137<div>138<Alert139showIcon140style={{ margin: "15px auto 0 auto", maxWidth: "700px" }}141type="success"142message="Payment Successfully Created"143/>144</div>145)}146<div>147<Alert148style={{ margin: "15px auto", maxWidth: "700px" }}149type="info"150description={151<>152User will be charged {currency(total)} (+ tax). When the payment153is completed, a credit will be added to the user's account. If154they have an automatic payment method on file (e.g. a credit155card), then this may be instant. Click the "Payments" button above156to see the status of any payments.157</>158}159/>160</div>161</Card>162);163}164165export function CreatePaymentButton(props: Props) {166const [show, setShow] = useState<boolean>(false);167return (168<div>169<Button onClick={() => setShow(!show)} type={show ? "dashed" : undefined}>170<Icon name="credit-card" /> Create Payment171</Button>172{show && (173<div style={{ marginTop: "8px" }}>174<CreatePayment {...props} onClose={() => setShow(false)} />175</div>176)}177</div>178);179}180181182