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