WIP: Stripe integration
This commit is contained in:
parent
7007c0a0bd
commit
01fd4754f9
20 changed files with 557 additions and 43 deletions
|
@ -171,10 +171,28 @@ const Stats = () => {
|
|||
const { t } = useTranslation();
|
||||
const { account } = useContext(AccountContext);
|
||||
const [upgradeDialogOpen, setUpgradeDialogOpen] = useState(false);
|
||||
|
||||
if (!account) {
|
||||
return <></>;
|
||||
}
|
||||
const normalize = (value, max) => Math.min(value / max * 100, 100);
|
||||
|
||||
const normalize = (value, max) => {
|
||||
return Math.min(value / max * 100, 100);
|
||||
};
|
||||
|
||||
const handleManageBilling = async () => {
|
||||
try {
|
||||
const response = await accountApi.createBillingPortalSession();
|
||||
window.location.href = response.redirect_url;
|
||||
} catch (e) {
|
||||
console.log(`[Account] Error changing password`, e);
|
||||
if ((e instanceof UnauthorizedError)) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
}
|
||||
// TODO show error
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card sx={{p: 3}} aria-label={t("account_usage_title")}>
|
||||
<Typography variant="h5" sx={{marginBottom: 2}}>
|
||||
|
@ -201,12 +219,20 @@ const Stats = () => {
|
|||
>{t("account_usage_tier_upgrade_button")}</Button>
|
||||
}
|
||||
{config.enable_payments && account.role === "user" && account.tier?.paid &&
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={() => setUpgradeDialogOpen(true)}
|
||||
sx={{ml: 1}}
|
||||
>{t("account_usage_tier_change_button")}</Button>
|
||||
<>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={() => setUpgradeDialogOpen(true)}
|
||||
sx={{ml: 1}}
|
||||
>{t("account_usage_tier_change_button")}</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={handleManageBilling}
|
||||
sx={{ml: 1}}
|
||||
>Manage billing</Button>
|
||||
</>
|
||||
}
|
||||
<UpgradeDialog
|
||||
open={upgradeDialogOpen}
|
||||
|
|
|
@ -501,7 +501,7 @@ const Reservations = () => {
|
|||
const handleDialogSubmit = async (reservation) => {
|
||||
setDialogOpen(false);
|
||||
try {
|
||||
await accountApi.upsertAccess(reservation.topic, reservation.everyone);
|
||||
await accountApi.upsertReservation(reservation.topic, reservation.everyone);
|
||||
await accountApi.sync();
|
||||
console.debug(`[Preferences] Added topic reservation`, reservation);
|
||||
} catch (e) {
|
||||
|
@ -557,7 +557,7 @@ const ReservationsTable = (props) => {
|
|||
const handleDialogSubmit = async (reservation) => {
|
||||
setDialogOpen(false);
|
||||
try {
|
||||
await accountApi.upsertAccess(reservation.topic, reservation.everyone);
|
||||
await accountApi.upsertReservation(reservation.topic, reservation.everyone);
|
||||
await accountApi.sync();
|
||||
console.debug(`[Preferences] Added topic reservation`, reservation);
|
||||
} catch (e) {
|
||||
|
@ -568,7 +568,7 @@ const ReservationsTable = (props) => {
|
|||
|
||||
const handleDeleteClick = async (reservation) => {
|
||||
try {
|
||||
await accountApi.deleteAccess(reservation.topic);
|
||||
await accountApi.deleteReservation(reservation.topic);
|
||||
await accountApi.sync();
|
||||
console.debug(`[Preferences] Deleted topic reservation`, reservation);
|
||||
} catch (e) {
|
||||
|
|
|
@ -110,7 +110,7 @@ const SubscribePage = (props) => {
|
|||
if (session.exists() && baseUrl === config.base_url && reserveTopicVisible) {
|
||||
console.log(`[SubscribeDialog] Reserving topic ${topic} with everyone access ${everyone}`);
|
||||
try {
|
||||
await accountApi.upsertAccess(topic, everyone);
|
||||
await accountApi.upsertReservation(topic, everyone);
|
||||
// Account sync later after it was added
|
||||
} catch (e) {
|
||||
console.log(`[SubscribeDialog] Error reserving topic`, e);
|
||||
|
|
|
@ -37,9 +37,9 @@ const SubscriptionSettingsDialog = (props) => {
|
|||
|
||||
// Reservation
|
||||
if (reserveTopicVisible) {
|
||||
await accountApi.upsertAccess(subscription.topic, everyone);
|
||||
await accountApi.upsertReservation(subscription.topic, everyone);
|
||||
} else if (!reserveTopicVisible && subscription.reservation) { // Was removed
|
||||
await accountApi.deleteAccess(subscription.topic);
|
||||
await accountApi.deleteReservation(subscription.topic);
|
||||
}
|
||||
|
||||
// Sync account
|
||||
|
|
|
@ -2,28 +2,83 @@ import * as React from 'react';
|
|||
import Dialog from '@mui/material/Dialog';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import {useMediaQuery} from "@mui/material";
|
||||
import {CardActionArea, CardContent, useMediaQuery} from "@mui/material";
|
||||
import theme from "./theme";
|
||||
import DialogFooter from "./DialogFooter";
|
||||
import Button from "@mui/material/Button";
|
||||
import accountApi, {TopicReservedError, UnauthorizedError} from "../app/AccountApi";
|
||||
import session from "../app/Session";
|
||||
import routes from "./routes";
|
||||
import {useContext, useState} from "react";
|
||||
import Card from "@mui/material/Card";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import {AccountContext} from "./App";
|
||||
|
||||
const UpgradeDialog = (props) => {
|
||||
const { account } = useContext(AccountContext);
|
||||
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const [selected, setSelected] = useState(account?.tier?.code || null);
|
||||
const [errorText, setErrorText] = useState("");
|
||||
|
||||
const handleSuccess = async () => {
|
||||
// TODO
|
||||
const handleCheckout = async () => {
|
||||
try {
|
||||
const response = await accountApi.createCheckoutSession(selected);
|
||||
if (response.redirect_url) {
|
||||
window.location.href = response.redirect_url;
|
||||
} else {
|
||||
await accountApi.sync();
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log(`[UpgradeDialog] Error creating checkout session`, e);
|
||||
if ((e instanceof UnauthorizedError)) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
}
|
||||
// FIXME show error
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
||||
<Dialog open={props.open} onClose={props.onCancel} maxWidth="md" fullScreen={fullScreen}>
|
||||
<DialogTitle>Upgrade to Pro</DialogTitle>
|
||||
<DialogContent>
|
||||
Content
|
||||
<div style={{
|
||||
display: "flex",
|
||||
flexDirection: "row"
|
||||
}}>
|
||||
<TierCard code={null} name={"Free"} selected={selected === null} onClick={() => setSelected(null)}/>
|
||||
<TierCard code="starter" name={"Starter"} selected={selected === "starter"} onClick={() => setSelected("starter")}/>
|
||||
<TierCard code="pro" name={"Pro"} selected={selected === "pro"} onClick={() => setSelected("pro")}/>
|
||||
<TierCard code="business" name={"Business"} selected={selected === "business"} onClick={() => setSelected("business")}/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
Footer
|
||||
<DialogFooter status={errorText}>
|
||||
<Button onClick={handleCheckout}>Checkout</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
const TierCard = (props) => {
|
||||
const cardStyle = (props.selected) ? {
|
||||
border: "1px solid red",
|
||||
|
||||
} : {};
|
||||
return (
|
||||
<Card sx={{ m: 1, maxWidth: 345 }}>
|
||||
<CardActionArea>
|
||||
<CardContent sx={{...cardStyle}} onClick={props.onClick}>
|
||||
<Typography gutterBottom variant="h5" component="div">
|
||||
{props.name}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Lizards are a widespread group of squamate reptiles, with over 6,000
|
||||
species, ranging across all continents except Antarctica
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default UpgradeDialog;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue