payment
parent
1a50324895
commit
f646a19cdf
|
@ -152,7 +152,7 @@
|
|||
"privacyPolicyLink": "Datenschutzerklärung",
|
||||
"loginLink": "Jetzt anmelden",
|
||||
"signUpLink": "Jetzt registrieren",
|
||||
"languageSwitchTo": "Switch to",
|
||||
"languageSwitchTo": "Switch language to",
|
||||
"login": {
|
||||
"button": "Anmelden",
|
||||
"forgotPassword": "Passwort vergessen?",
|
||||
|
|
|
@ -132,7 +132,6 @@ export function App() {
|
|||
options={{
|
||||
username: appUserData.user.username,
|
||||
permissions: appUserData.permissions,
|
||||
paymentPlanStatus: appUserData.user.payment_plan_status,
|
||||
paymentPlanTrialEnd: appUserData.user.payment_plan_trial_end,
|
||||
paymentPlanCanceledAt: appUserData.user.payment_plan_canceled_at,
|
||||
}}
|
||||
|
|
|
@ -7,7 +7,6 @@ import Verification from "../../Pages/Verification";
|
|||
|
||||
// Lazy-loaded components
|
||||
const Authentication = lazy(() => import("../../Pages/Authentication"));
|
||||
const SignUp = lazy(() => import("../../Pages/Authentication/SignUp"));
|
||||
const CheckoutSuccess = lazy(() =>
|
||||
import("../../Pages/Authentication/CheckoutSuccess")
|
||||
);
|
||||
|
|
|
@ -106,9 +106,8 @@ export function SideMenuContent({
|
|||
};
|
||||
|
||||
const showPaymentPlanInfoBanner =
|
||||
sideBarContext.paymentPlanStatus === "trialing" &&
|
||||
sideBarContext.paymentPlanTrialEnd !== null &&
|
||||
sideBarContext.paymentPlanCanceledAt !== null;
|
||||
sideBarContext.paymentPlanTrialEnd !== undefined ||
|
||||
sideBarContext.paymentPlanCanceledAt !== undefined;
|
||||
|
||||
const getSecondMenuItems = () => {
|
||||
let items = [];
|
||||
|
@ -177,7 +176,8 @@ export function SideMenuContent({
|
|||
|
||||
const calculateExpiry = () => {
|
||||
const currentDate = new Date();
|
||||
const expiryDate = new Date(sideBarContext.paymentPlanTrialEnd * 1000);
|
||||
const expiryDate = new Date(sideBarContext.paymentPlanTrialEnd);
|
||||
|
||||
const diffTime = Math.abs(expiryDate - currentDate);
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
|
||||
|
|
|
@ -1,11 +1,30 @@
|
|||
import { Button, notification } from "antd";
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Space,
|
||||
Flex,
|
||||
Row,
|
||||
Col,
|
||||
Typography,
|
||||
Grid,
|
||||
Skeleton,
|
||||
notification,
|
||||
} from "antd";
|
||||
import { useAppContext } from "../../Contexts/AppContext";
|
||||
import { myFetch, showUnkownErrorNotification } from "../../utils";
|
||||
import {
|
||||
AppStyle,
|
||||
FormatDatetime,
|
||||
myFetch,
|
||||
showUnkownErrorNotification,
|
||||
} from "../../utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CreditCardOutlined } from "@ant-design/icons";
|
||||
import { useState } from "react";
|
||||
import PageInDevelopment from "../PageInDevelopment";
|
||||
// import { ChoosenProduct } from "../Authentication/SignUp";
|
||||
import { CheckOutlined, CloseOutlined } from "@ant-design/icons";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ReactComponent as RocketLaunch } from "./rocket_launch_FILL1_wght400_GRAD0_opsz24.svg";
|
||||
import { ReactComponent as Check } from "./task_alt_FILL0_wght400_GRAD0_opsz24.svg";
|
||||
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
export default function PaymentPlan() {
|
||||
const { t } = useTranslation();
|
||||
|
@ -14,13 +33,287 @@ export default function PaymentPlan() {
|
|||
const [notificationApi, notificationContextHolder] =
|
||||
notification.useNotification();
|
||||
|
||||
const [isRequestingBillingDetails, setIsRequestingBillingDetails] =
|
||||
useState(false);
|
||||
const screenBreakpoint = useBreakpoint();
|
||||
|
||||
const [isRequesting, setIsRequesting] = useState(true);
|
||||
const [requestData, setRequestData] = useState({
|
||||
payment_plan: 0,
|
||||
payment_plan_interval: 0,
|
||||
prices: [],
|
||||
payment_plan_trial_end: undefined,
|
||||
payment_plan_canceled_at: undefined,
|
||||
});
|
||||
const [requestingCheckout, setRequestingCheckout] = useState(false);
|
||||
|
||||
const [selectedBillingPeriod, setSelectedBillingPeriod] = useState(0);
|
||||
|
||||
const fetchPaymentPlan = () => {
|
||||
myFetch({
|
||||
url: "/payment",
|
||||
method: "GET",
|
||||
notificationApi,
|
||||
t,
|
||||
})
|
||||
.then((data) => {
|
||||
setRequestData(data);
|
||||
setIsRequesting(false);
|
||||
|
||||
if (data.payment_plan_interval !== null) {
|
||||
setSelectedBillingPeriod(data.payment_plan_interval);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
setIsRequesting(false);
|
||||
showUnkownErrorNotification(notificationApi, t);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => fetchPaymentPlan(), []);
|
||||
|
||||
const showAlert = () => {
|
||||
if (
|
||||
!isRequesting &&
|
||||
requestData.payment_plan === 0 &&
|
||||
requestData.payment_plan_trial_end &&
|
||||
!requestData.payment_plan_canceled_at
|
||||
) {
|
||||
return (
|
||||
<Alert
|
||||
message={
|
||||
<Typography.Text>
|
||||
Ihre Demo endet in <b>7 Tagen</b>. Jetzt einen Plan auswählen, um
|
||||
den vollen Funktionsumfang zu nutzen.
|
||||
</Typography.Text>
|
||||
}
|
||||
type="warning"
|
||||
closable
|
||||
showIcon
|
||||
style={{ marginBottom: 12 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{notificationContextHolder}
|
||||
<PageInDevelopment showBackButton={false} />
|
||||
|
||||
{showAlert()}
|
||||
|
||||
<Card title="Features & Preisübersicht">
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col xs={24} xl={12}>
|
||||
<Flex vertical>
|
||||
<Space>
|
||||
<Check />
|
||||
<Typography.Text style={{ fontSize: 20 }}>
|
||||
Unbegrenzte <b>Terminanzahl</b>
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Space>
|
||||
<Check />
|
||||
<Typography.Text style={{ fontSize: 20 }}>
|
||||
Bis zu <b>20 Mitarbeiter</b>
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Space>
|
||||
<Check />
|
||||
<Typography.Text style={{ fontSize: 20 }}>
|
||||
Buchungen bis zu <b>6 Wochen</b> im Voraus
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} xl={12}>
|
||||
<Flex justify="center" style={{ marginBottom: 12 }}>
|
||||
<Space>
|
||||
{isRequesting ? (
|
||||
<Skeleton.Button shape="round" size="default" active />
|
||||
) : (
|
||||
<Button
|
||||
type={selectedBillingPeriod === 0 ? "primary" : "default"}
|
||||
shape="round"
|
||||
icon={
|
||||
requestData.payment_plan_interval === 0 &&
|
||||
(requestData.payment_plan_canceled_at === undefined ? (
|
||||
<CheckOutlined />
|
||||
) : (
|
||||
<CloseOutlined />
|
||||
))
|
||||
}
|
||||
onClick={() => setSelectedBillingPeriod(0)}
|
||||
>
|
||||
Monatlich
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{isRequesting ? (
|
||||
<Skeleton.Button shape="round" size="default" active />
|
||||
) : (
|
||||
<Button
|
||||
type={selectedBillingPeriod === 1 ? "primary" : "default"}
|
||||
shape="round"
|
||||
icon={
|
||||
requestData.payment_plan_interval === 1 &&
|
||||
(requestData.payment_plan_canceled_at === undefined ? (
|
||||
<CheckOutlined />
|
||||
) : (
|
||||
<CloseOutlined />
|
||||
))
|
||||
}
|
||||
onClick={() => setSelectedBillingPeriod(1)}
|
||||
>
|
||||
Jährlich
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<Card style={{ backgroundColor: AppStyle.colors.primary }}>
|
||||
<Flex
|
||||
justify="space-between"
|
||||
vertical={screenBreakpoint.xs}
|
||||
align="center"
|
||||
>
|
||||
<div>
|
||||
<RocketLaunch
|
||||
style={{
|
||||
width: 62,
|
||||
height: 62,
|
||||
backgroundColor: "#fff",
|
||||
padding: 12,
|
||||
borderRadius: 12,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Flex
|
||||
vertical
|
||||
style={{ marginBottom: screenBreakpoint.xs && 12 }}
|
||||
>
|
||||
<Typography.Title
|
||||
level={screenBreakpoint.xs ? 3 : 2}
|
||||
style={{ color: "#fff" }}
|
||||
>
|
||||
Unternehmensplan
|
||||
</Typography.Title>
|
||||
|
||||
{isRequesting ? (
|
||||
<Skeleton.Button shape="round" block active />
|
||||
) : (
|
||||
<Button
|
||||
loading={requestingCheckout}
|
||||
shape="round"
|
||||
style={{ fontWeight: "bold" }}
|
||||
onClick={() => {
|
||||
setRequestingCheckout(true);
|
||||
|
||||
if (
|
||||
selectedBillingPeriod ===
|
||||
requestData.payment_plan_interval
|
||||
) {
|
||||
myFetch({
|
||||
method: "GET",
|
||||
url: "/payment/portal",
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.url) {
|
||||
window.location.href = data.url;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
setRequestingCheckout(false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
myFetch({
|
||||
method: "POST",
|
||||
url: "/payment/checkout",
|
||||
body: {
|
||||
lookupKey: `za-basic-${
|
||||
selectedBillingPeriod === 0 ? "monthly" : "yearly"
|
||||
}`,
|
||||
},
|
||||
notificationApi,
|
||||
t,
|
||||
})
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
|
||||
if (data.url) {
|
||||
window.location.href = data.url;
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.status === "ok") {
|
||||
fetchPaymentPlan();
|
||||
setRequestingCheckout(false);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
setRequestingCheckout(false);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{selectedBillingPeriod ===
|
||||
requestData.payment_plan_interval
|
||||
? "Plan verwalten"
|
||||
: "Jetzt buchen"}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Flex vertical align="center">
|
||||
{isRequesting ? (
|
||||
<Skeleton.Button shape="round" size="large" active />
|
||||
) : (
|
||||
<Typography.Title
|
||||
level={screenBreakpoint.xs ? 3 : 2}
|
||||
style={{
|
||||
color: "#fff",
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
width: 76,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{`${
|
||||
requestData.prices.find(
|
||||
(price) =>
|
||||
price.lookup_key ===
|
||||
`za-basic-${
|
||||
selectedBillingPeriod === 0 ? "monthly" : "yearly"
|
||||
}`
|
||||
).unit_amount / (selectedBillingPeriod === 0 ? 1 : 12)
|
||||
} €`}
|
||||
</Typography.Title>
|
||||
)}
|
||||
<Typography.Text
|
||||
style={{ color: "#fff", textAlign: "center" }}
|
||||
>
|
||||
/monatlich
|
||||
</Typography.Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
||||
{selectedBillingPeriod === requestData.payment_plan &&
|
||||
requestData.payment_plan_canceled_at !== undefined && (
|
||||
<Flex justify="center">
|
||||
<Typography.Text type="secondary">
|
||||
Gekündigt am{" "}
|
||||
{FormatDatetime(requestData.payment_plan_canceled_at)} Uhr
|
||||
</Typography.Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m98-537 168-168q14-14 33-20t39-2l52 11q-54 64-85 116t-60 126L98-537Zm205 91q23-72 62.5-136T461-702q88-88 201-131.5T873-860q17 98-26 211T716-448q-55 55-120 95.5T459-289L303-446Zm276-120q23 23 56.5 23t56.5-23q23-23 23-56.5T692-679q-23-23-56.5-23T579-679q-23 23-23 56.5t23 56.5ZM551-85l-64-147q74-29 126.5-60T730-377l10 52q4 20-2 39.5T718-252L551-85ZM162-318q35-35 85-35.5t85 34.5q35 35 35 85t-35 85q-25 25-83.5 43T87-74q14-103 32-161t43-83Z"/></svg>
|
After Width: | Height: | Size: 544 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q65 0 123 19t107 53l-58 59q-38-24-81-37.5T480-800q-133 0-226.5 93.5T160-480q0 133 93.5 226.5T480-160q133 0 226.5-93.5T800-480q0-18-2-36t-6-35l65-65q11 32 17 66t6 70q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm-56-216L254-466l56-56 114 114 400-401 56 56-456 457Z"/></svg>
|
After Width: | Height: | Size: 467 B |
|
@ -14,7 +14,7 @@ root.render(
|
|||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
fontFamily: "Roboto, sans-serif",
|
||||
fontFamily: "Outfit, sans-serif",
|
||||
colorPrimary: "#1395f8",
|
||||
colorInfo: "#1395f8",
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue