From 6694a48b44ae9d719c0a580cd338f1e1c885fd00 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 30 Mar 2024 20:48:49 +0100 Subject: [PATCH] payment --- public/locales/de/translation.json | 42 +++- public/locales/en/translation.json | 42 +++- src/Components/SideMenu/index.js | 19 +- src/Pages/PaymentPlan/index.js | 299 +++++++++++++++++++---------- 4 files changed, 275 insertions(+), 127 deletions(-) diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index 0745e73..c9009a7 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -20,6 +20,7 @@ "hour": "Stunde", "minutes": "Minuten", "minute": "Minute", + "daysLong": "Tagen", "days": "Tage", "day": "Tag", "separator": "und" @@ -507,13 +508,38 @@ ] }, "paymentPlan": { - "title": "Zahlungsplan", - "buttonUpdateBillingDetails": "Zahlungsdetails aktualisieren", - "plan": "Plan", - "contentsOfSubscription": { - "title": "Inhalt des Abonnements", - "maxEmployees": "Maximale Anzahl von Mitarbeitern", - "calendarMaxFutureBookingDays": "Maximale Anzahl von Tagen im Voraus" - } + "title": "Features & Preisübersicht", + "buttonMonthly": "Monatlich", + "buttonYearly": "Jährlich", + "buttonManagePlan": "Zahlungsplan verwalten", + "buttonBuyPlan": "Jetzt buchen", + "monthly": "monatlich", + "businessPlan": "Unternehmensplan", + "cancelledOn": "Gekündigt am {{date}} Uhr", + "changeToAnnualPayment": "Wechseln Sie zur jährlichen Zahlung und ", + "saveAmount": "sparen Sie {{amount}} €", + "popconfirmChangePlan": { + "title": "Zahlungsplan wechseln", + "description": "Sind Sie sicher, dass Sie Ihren Plan ändern wollen?" + }, + "alertDemoEndsIn": { + "demoEndsIn": "Die Testversion endet in", + "selectPlanToAccessFullFeatures": "Wählen Sie einen Plan, um auf alle Funktionen zuzugreifen." + }, + "alertPlanCancelled": "Ihr Plan wurde gekündigt. Am Ende des Abrechnungszeitraums müssen Sie einen neuen Plan auswählen, um unsere Dienste weiterhin nutzen zu können.", + "features": [ + { + "text": "Unbegrenzte {{bold}}", + "bold": "Terminanzahl" + }, + { + "text": "Bis zu {{bold}}", + "bold": "20 Mitarbeiter" + }, + { + "text": "Buchungen bis zu {{bold}} im Voraus", + "bold": "7 Wochen" + } + ] } } diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 12aaf72..b1d7a5a 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -20,6 +20,7 @@ "hour": "hour", "minutes": "minutes", "minute": "minute", + "daysLong": "days", "days": "days", "day": "day", "separator": "and" @@ -516,13 +517,38 @@ ] }, "paymentPlan": { - "title": "Payment plan", - "buttonUpdateBillingDetails": "Update billing details", - "plan": "Plan", - "contentsOfSubscription": { - "title": "Contents of subscription", - "maxEmployees": "Max. employees", - "calendarMaxFutureBookingDays": "Max. future booking days" - } + "title": "Features & Pricing", + "buttonMonthly": "Monthly", + "buttonYearly": "Yearly", + "buttonManagePlan": "Manage plan", + "buttonBuyPlan": "Buy now", + "monthly": "monthly", + "businessPlan": "Business Plan", + "cancelledOn": "Cancelled on {{date}} o'clock", + "changeToAnnualPayment": "Switch to annual payment and ", + "saveAmount": "save {{amount}} €", + "popconfirmChangePlan": { + "title": "Change plan", + "description": "Are you sure you want to change your plan?" + }, + "alertDemoEndsIn": { + "demoEndsIn": "Demo ends in", + "selectPlanToAccessFullFeatures": "Select a plan to access all features." + }, + "alertPlanCancelled": "Your plan has been cancelled. At the end of the billing period, you need to select a new plan to continue using our services.", + "features": [ + { + "text": "Unlimited {{bold}}", + "bold": "number of appointments" + }, + { + "text": "Up to {{bold}}", + "bold": "20 employees" + }, + { + "text": "Bookings up to {{bold}} in advance", + "bold": "7 weeks" + } + ] } } diff --git a/src/Components/SideMenu/index.js b/src/Components/SideMenu/index.js index d3a9128..9d317cf 100644 --- a/src/Components/SideMenu/index.js +++ b/src/Components/SideMenu/index.js @@ -174,17 +174,10 @@ export function SideMenuContent({ } }, [location.pathname]); - const calculateExpiry = () => { - const currentDate = new Date(); - const expiryDate = new Date(sideBarContext.paymentPlanTrialEnd); - - const diffTime = Math.abs(expiryDate - currentDate); - const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); - - return diffDays; - }; - - const accountPlanExpiry = calculateExpiry(); + const daysLeft = Math.floor( + (new Date(sideBarContext.paymentPlanTrialEnd) - new Date()) / + (1000 * 60 * 60 * 24) + ); return (
{t("sideMenu.paymentPlanTrailingDaysLeft", { - daysLeft: accountPlanExpiry, + daysLeft: daysLeft, dayUnit: - accountPlanExpiry > 1 + daysLeft > 1 ? t("common.unit.days") : t("common.unit.day"), })} diff --git a/src/Pages/PaymentPlan/index.js b/src/Pages/PaymentPlan/index.js index 6729865..fb3b667 100644 --- a/src/Pages/PaymentPlan/index.js +++ b/src/Pages/PaymentPlan/index.js @@ -10,25 +10,26 @@ import { Grid, Skeleton, notification, + Popconfirm, + Tag, } from "antd"; -import { useAppContext } from "../../Contexts/AppContext"; import { AppStyle, FormatDatetime, myFetch, showUnkownErrorNotification, } from "../../utils"; -import { useTranslation } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; 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"; +import CountUp from "react-countup"; const { useBreakpoint } = Grid; export default function PaymentPlan() { const { t } = useTranslation(); - const appContext = useAppContext(); const [notificationApi, notificationContextHolder] = notification.useNotification(); @@ -62,8 +63,7 @@ export default function PaymentPlan() { setSelectedBillingPeriod(data.payment_plan_interval); } }) - .catch((error) => { - console.log(error); + .catch(() => { setIsRequesting(false); showUnkownErrorNotification(notificationApi, t); }); @@ -71,19 +71,31 @@ export default function PaymentPlan() { useEffect(() => fetchPaymentPlan(), []); - const showAlert = () => { + const showAlertTrialEnds = () => { if ( !isRequesting && requestData.payment_plan === 0 && requestData.payment_plan_trial_end && !requestData.payment_plan_canceled_at ) { + const daysLeft = Math.floor( + (new Date(requestData.payment_plan_trial_end) - new Date()) / + (1000 * 60 * 60 * 24) + ); + return ( - Ihre Demo endet in 7 Tagen. Jetzt einen Plan auswählen, um - den vollen Funktionsumfang zu nutzen. + {t("paymentPlan.alertDemoEndsIn.demoEndsIn")}{" "} + + {daysLeft}{" "} + {daysLeft === 1 + ? t("common.unit.day") + : t("common.unit.daysLong")} + + .{" "} + {t("paymentPlan.alertDemoEndsIn.selectPlanToAccessFullFeatures")} } type="warning" @@ -95,34 +107,104 @@ export default function PaymentPlan() { } }; + const showAlertPlanCancelled = () => { + if (!isRequesting && requestData.payment_plan_canceled_at) { + return ( + + {t("paymentPlan.alertPlanCancelled", { + date: FormatDatetime(requestData.payment_plan_canceled_at), + })} + + } + type="warning" + showIcon + closable + style={{ marginBottom: 12 }} + /> + ); + } + }; + + const percentageDiscount = + requestData.prices.length > 0 + ? Math.round( + 100 - + (100 / (requestData.prices[0].unit_amount * 12)) * + requestData.prices[1].unit_amount + ) + : 0; + + const handleButtonBuyPlan = () => { + setRequestingCheckout(true); + + myFetch({ + method: "POST", + url: "/payment/checkout", + body: { + lookupKey: `za-basic-${ + selectedBillingPeriod === 0 ? "monthly" : "yearly" + }`, + }, + notificationApi, + t, + }) + .then((data) => { + if (data.url) { + window.location.href = data.url; + return; + } + + if (data.status === "ok") { + fetchPaymentPlan(); + setRequestingCheckout(false); + } + }) + .catch((error) => { + console.log(error); + setRequestingCheckout(false); + }); + }; + + const ButtonBuyPlan = ({ onClick }) => { + return ( + + ); + }; + return ( <> {notificationContextHolder} - {showAlert()} + {showAlertTrialEnds()} + {showAlertPlanCancelled()} - + - - - - Unbegrenzte Terminanzahl - - - - - - Bis zu 20 Mitarbeiter - - - - - - Buchungen bis zu 6 Wochen im Voraus - - + {t("paymentPlan.features", { returnObjects: true }).map( + (feature, index) => ( + + + + }} + /> + + + ) + )} @@ -140,12 +222,12 @@ export default function PaymentPlan() { (requestData.payment_plan_canceled_at === undefined ? ( ) : ( - + )) } onClick={() => setSelectedBillingPeriod(0)} > - Monatlich + {t("paymentPlan.buttonMonthly")} )} @@ -160,12 +242,29 @@ export default function PaymentPlan() { (requestData.payment_plan_canceled_at === undefined ? ( ) : ( - + )) } onClick={() => setSelectedBillingPeriod(1)} > - Jährlich + + {t("paymentPlan.buttonYearly")} + + {requestData.payment_plan_interval !== 1 && ( + + - + 10 + ? percentageDiscount - 10 + : 0 + } + end={percentageDiscount} + />{" "} + % + + )} + )} @@ -197,74 +296,56 @@ export default function PaymentPlan() { level={screenBreakpoint.xs ? 3 : 2} style={{ color: "#fff" }} > - Unternehmensplan + {t("paymentPlan.businessPlan")} {isRequesting ? ( ) : ( - + requestData.payment_plan_interval ? ( + + ) : requestData.payment_plan === 0 ? ( + handleButtonBuyPlan()} /> + ) : ( + handleButtonBuyPlan()} + > + + + )} + )} @@ -296,18 +377,40 @@ export default function PaymentPlan() { - /monatlich + /{t("paymentPlan.monthly")} - {selectedBillingPeriod === requestData.payment_plan && + {selectedBillingPeriod === requestData.payment_plan_interval && requestData.payment_plan_canceled_at !== undefined && ( - Gekündigt am{" "} - {FormatDatetime(requestData.payment_plan_canceled_at)} Uhr + {t("paymentPlan.cancelledOn", { + date: FormatDatetime( + requestData.payment_plan_canceled_at + ), + })} + + + )} + + {selectedBillingPeriod === 1 && + requestData.payment_plan_interval !== 1 && ( + + + {t("paymentPlan.changeToAnnualPayment")} + + {t("paymentPlan.saveAmount", { + amount: + requestData.prices[0].unit_amount * 12 - + requestData.prices[1].unit_amount, + })} + )}