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/redeem.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { 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, Divider, Input, Layout, Space } 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 apiPost from "lib/api/post";18import useIsMounted from "lib/hooks/mounted";19import Loading from "components/share/loading";20import Project from "components/project/link";21import License from "components/licenses/license";22import type { CreatedItem } from "@cocalc/server/vouchers/redeem";23import { currency } from "@cocalc/util/misc";2425type State = "input" | "redeeming" | "redeemed";2627interface Props {28customize;29id?: string;30}3132export default function Redeem({ customize, id }: Props) {33const isMounted = useIsMounted();34const [code, setCode] = useState<string>(id ?? "");35const [error, setError] = useState<string>("");36const [state, setState] = useState<State>("input");37const profile = useProfile({ noCache: true });38const [signedIn, setSignedIn] = useState<boolean>(!!profile?.account_id);39const router = useRouter();40const [createdItems, setCreatedItems] = useState<CreatedItem[] | null>(null);4142// optional project_id to automatically apply all the licenses we get on redeeming the voucher43const { project_id } = router.query;4445async function redeemCode() {46try {47setError("");48setState("redeeming");49// This api call tells the backend, "create requested vouchers from everything in my50// shopping cart that is not a subscription."51const createdItems = await apiPost("/vouchers/redeem", {52code: code.trim(),53project_id,54});55if (!isMounted.current) return;56setCreatedItems(createdItems);57// success!58setState("redeemed");59} catch (err) {60// The redeem failed.61setError(err.message);62setState("input"); // back to input mode63} finally {64if (!isMounted.current) return;65}66}6768return (69<Customize value={customize}>70<Head title="Redeem Voucher" />71<Layout>72<Header />73<Layout.Content74style={{75backgroundColor: "white",76}}77>78<div79style={{80width: "100%",81margin: "10vh 0",82display: "flex",83justifyContent: "center",84}}85>86{profile == null && <Loading />}87{profile != null && !profile.account_id && !signedIn && (88<Card>89<div style={{ fontSize: "75px", textAlign: "center" }} >90<Icon name="gift2"/>91</div>92<InPlaceSignInOrUp93title="Redeem Voucher"94why="to redeem a voucher"95style={{ width: "450px" }}96onSuccess={() => {97router.push("/redeem");98setSignedIn(true);99}}100/>101</Card>102)}103104{(profile?.account_id || signedIn) && (105<Card style={{ background: "#fafafa" }}>106<Space direction="vertical" align="center">107<A href="/vouchers">108<Icon name="gift2" style={{ fontSize: "75px" }} />109</A>110<h1>Enter Voucher Code</h1>111<Input112disabled={state != "input"}113allowClear114autoFocus115size="large"116value={code}117onChange={(e) => {118setCode(e.target.value);119setError("");120}}121onPressEnter={redeemCode}122style={{ width: "300px", marginBottom: "15px" }}123/>124{error && (125<Alert126type="error"127message={"Error"}128description={error}129showIcon130style={{ width: "100%", marginBottom: "30px" }}131closable132onClose={() => setError("")}133/>134)}135{state != "redeemed" ? (136<Button137disabled={code.length < 8 || state != "input" || !!error}138size="large"139type="primary"140onClick={redeemCode}141>142{state == "input" && <>Redeem</>}143{state == "redeeming" && (144<Loading delay={0}>Redeeming...</Loading>145)}146</Button>147) : (148<Alert149showIcon150message={151"Success! You redeemed the voucher, which added the following to your account:"152}153type="success"154description={155<DisplayCreatedItems156createdItems={createdItems}157project_id={project_id}158/>159}160/>161)}162{project_id && (163<Alert164showIcon165style={{ marginTop: "30px" }}166type={167{168input: "info",169redeeming: "warning",170redeemed: "success",171}[state] as "info" | "warning" | "success"172}173message={174<div style={{ maxWidth: "340px" }}>175{state == "input" && (176<>177The license provided by this voucher will be178automatically applied to your project{" "}179<Project project_id={project_id} />.180</>181)}182{state == "redeeming" && (183<>184Redeeming the voucher and applying the license it185to your project{" "}186<Project project_id={project_id} />187...188</>189)}190{state == "redeemed" && createdItems != null && (191<DisplayCreatedItems192createdItems={createdItems}193project_id={project_id}194/>195)}196</div>197}198/>199)}200{state == "redeemed" && (201<div style={{ textAlign: "center", marginTop: "15px" }}>202<Button203onClick={() => {204setState("input");205setCode("");206setError("");207setCreatedItems(null);208}}209>210Redeem Another Voucher211</Button>212</div>213)}214<Divider orientation="left" style={{ width: "400px" }}>215<A href="https://doc.cocalc.com/vouchers.html">216<Icon name="medkit" /> Vouchers217</A>218</Divider>219<div220style={{221color: "#666",222maxWidth: "450px",223}}224>225<p>226When you redeem a voucher code,{" "}227<A href="/settings/purchases" external>228money229</A>{" "}230or{" "}231<A href="/settings/licenses" external>232licenses233</A>{" "}234will be added to your account235{profile?.email_address != null ? (236<A href="/config/account/email">{` ${profile?.email_address}`}</A>237) : (238""239)}240.241</p>242<p>243Once you redeem a voucher code, you can use the244corresponding{" "}245<A href="/settings/purchases" external>246money247</A>{" "}248to make purchases, or the{" "}249<A href="/settings/licenses" external>250licenses251</A>{" "}252to{" "}253<A href="https://doc.cocalc.com/add-lic-project.html">254upgrade your projects.255</A>{" "}256If a license doesn't fit your needs, you can{" "}257<A href="/settings/licenses" external>258easily edit it here259</A>{" "}260including receiving a prorated refund so you can buy261something else, or paying a little more for a more262powerful license.263</p>264<p>265You can browse{" "}266<A href="/vouchers/redeemed">267all vouchers you have already redeemed.268</A>{" "}269If in a project's settings you click "Redeem Voucher" and270enter a voucher code you already redeemed, then the271corresponding licenses will get added to that project.272</p>273<p>274If you have any questions,{" "}275<A href="/support">contact support</A> and{" "}276<A href="https://doc.cocalc.com/vouchers.html">277read the documentation278</A>279.280</p>281282<div style={{ textAlign: "center" }}>283<A href="/vouchers">284<b>The Voucher Center</b>285</A>286</div>287</div>288</Space>289</Card>290)}291</div>292<Footer />293</Layout.Content>{" "}294</Layout>295</Customize>296);297}298299function DisplayCreatedItems({ createdItems, project_id }) {300if (createdItems == null) {301return null;302}303return (304<ol>305{createdItems.map((item, n) => (306<DisplayCreatedItem item={item} project_id={project_id} key={n} />307))}308</ol>309);310}311312function DisplayCreatedItem({ item, project_id }) {313if (item.type == "cash") {314return (315<li>316{currency(item.amount)} was credited{" "}317<A href="/settings/purchases" external>318to your account319</A>{" "}320(transaction id: {item.purchase_id})321</li>322);323} else if (item.type == "license") {324return (325<li>326The following license <License license_id={item.license_id} /> was added{" "}327<A href="/settings/licenses" external>328to your licenses329</A>330.331{!!project_id && (332<>333{" "}334This license was applied to the project{" "}335<Project project_id={project_id} />.336</>337)}338</li>339);340} else {341return (342<li>343<pre>{JSON.stringify(item)}</pre>344</li>345);346}347}348349export async function getServerSideProps(context) {350return await withCustomize({ context });351}352353354