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/next/pages/vouchers/admin.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { useCallback, useMemo, useState } from "react";6import Footer from "components/landing/footer";7import Header from "components/landing/header";8import Head from "components/landing/head";9import { Alert, Button, Card, Checkbox, Layout, Space, Table } from "antd";10import withCustomize from "lib/with-customize";11import { Customize } from "lib/customize";12import { Icon } from "@cocalc/frontend/components/icon";13import A from "components/misc/A";14import InPlaceSignInOrUp from "components/auth/in-place-sign-in-or-up";15import useProfile from "lib/hooks/profile";16import { useRouter } from "next/router";17import Loading from "components/share/loading";18import useDatabase from "lib/hooks/database";19import { field_cmp } from "@cocalc/util/misc";20import type { Voucher } from "@cocalc/util/db-schema/vouchers";21import apiPost from "lib/api/post";22import Help from "components/vouchers/help";2324const QUERY = {25crm_vouchers: [26{27id: null,28when_pay: null,29created: null,30active: null,31expire: null,32cancel_by: null,33title: null,34count: null,35cost: null,36tax: null,37cart: null,38purchased: null,39},40],41} as const;4243import { COLUMNS as COLUMNS0 } from "./created";44const COLUMNS = COLUMNS0.concat([45{46title: "When Pay",47dataIndex: "when_pay",48key: "when_pay",49},50{51title: "Purchased",52dataIndex: "purchased",53key: "purchased",54render: (_, { purchased }) => (55<pre style={{ maxWidth: "200px", overflow: "auto" }}>56{JSON.stringify(purchased, undefined, 2)}57</pre>58),59},60]);6162export default function Created({ customize }) {63const { loading, value, error, setError, query } = useDatabase(QUERY);64const profile = useProfile({ noCache: true });65const router = useRouter();66const [showExpiredOnly, setShowExpiredOnly] = useState<boolean>(false);67const [showAdminOnly, setShowAdminOnly] = useState<boolean>(false);68const [showPaidOnly, setShowPaidOnly] = useState<boolean>(false);6970const [charging, setCharging] = useState<boolean>(false);71const [result, setResult] = useState<any>(null);7273const doInvoiceUnpaid = useCallback(() => {74setCharging(true);75(async () => {76try {77setResult(await apiPost("/vouchers/charge-for-unpaid-vouchers"));78} catch (err) {79setError(`${err}`);80} finally {81setCharging(false);82query(QUERY);83}84})();85}, []);8687const data: Voucher[] = useMemo(() => {88if (error) return [];89const cmp = field_cmp("created");90let v: Voucher[] = (value?.crm_vouchers ?? []).sort((a, b) => -cmp(a, b));91if (showExpiredOnly) {92const now = new Date();93v = v.filter((x) => new Date(x.expire) <= now);94}95if (showPaidOnly) {96v = v.filter((x) => x.purchased != null);97}98if (showAdminOnly) {99v = v.filter((x) => x.when_pay == "admin");100}101102return v;103}, [104value,105showExpiredOnly,106showPaidOnly,107showAdminOnly,108error,109]);110111return (112<Customize value={customize}>113<Head title="Admin: Voucher Payment Status" />114<Layout>115<Header />116<Layout.Content style={{ background: "white" }}>117{profile != null && !profile.is_admin && (118<div>119<Alert120showIcon121style={{ margin: "30px" }}122type="warning"123message={<b>This page is only for system administrators.</b>}124/>125</div>126)}127<div128style={{129width: "100%",130margin: "10vh 0",131display: "flex",132justifyContent: "center",133}}134>135{profile == null && <Loading />}136{profile != null && !profile.account_id && (137<Card>138<div style={{ fontSize: "75px", textAlign: "center" }}>139<Icon name="gift2"/>140</div>141<InPlaceSignInOrUp142title="Voucher Status"143why="as an ADMIN to see voucher payment status"144style={{ width: "450px" }}145onSuccess={() => {146router.reload();147}}148/>149</Card>150)}151{profile?.account_id && (152<Card style={{ background: "#fafafa" }}>153<Space direction="vertical" align="center">154<A href="/vouchers">155<Icon name="gift2" style={{ fontSize: "75px" }} />156</A>157<h1>158<Icon name="users" /> Admin -- Voucher Payment Status (159{data.length})160</h1>161{error && (162<Alert163type="error"164message={error}165showIcon166style={{ width: "100%", marginBottom: "30px" }}167closable168onClose={() => setError("")}169/>170)}171{loading && <Loading />}172{!loading && (173<div>174<Checkbox175disabled={charging}176checked={showExpiredOnly}177onClick={() => setShowExpiredOnly(!showExpiredOnly)}178>179Show expired only180</Checkbox>181<Checkbox182disabled={charging}183checked={showAdminOnly}184onClick={() => setShowAdminOnly(!showAdminOnly)}185>186Show admin only187</Checkbox>188<Checkbox189disabled={charging}190checked={showPaidOnly}191onClick={() => setShowPaidOnly(!showPaidOnly)}192>193Show paid only194</Checkbox>195<div style={{ maxWidth: "600px", marginTop: "15px" }}>196NOTE: Click the unpaid and expired checkboxes to bring197up the button to manually run invoicing. This is198temporary until we automate this later.199</div>200</div>201)}202{!loading && showExpiredOnly && (203<div>204<Button205style={{ marginTop: "30px" }}206type="primary"207onClick={doInvoiceUnpaid}208disabled={charging || data.length == 0}209>210{charging && (211<>212<Loading />{" "}213</>214)}215Create Invoices and Charge for the {data.length} Unpaid216Vouchers217</Button>218{result && (219<div>220Invoice Result:221<pre>{JSON.stringify(result, undefined, 2)}</pre>222</div>223)}224</div>225)}226{!loading && data.length > 0 && (227<Table228columns={COLUMNS}229dataSource={data}230rowKey="id"231pagination={{ defaultPageSize: 50 }}232/>233)}234{!loading && data.length == 0 && (235<div>There are no matching vouchers.</div>236)}237<Help />238</Space>239</Card>240)}241</div>242<Footer />243</Layout.Content>244</Layout>245</Customize>246);247}248249export async function getServerSideProps(context) {250return await withCustomize({ context });251}252253254