payment plan settings

master
alex 2024-02-24 07:33:05 +01:00
parent cf0b173ab7
commit dac70b358a
10 changed files with 102 additions and 38 deletions

View File

@ -126,7 +126,9 @@
}, },
"support": "Unterstützung", "support": "Unterstützung",
"feedback": "Feedback", "feedback": "Feedback",
"paymentPlan": "Zahlungsplan" "paymentPlan": "Zahlungsplan",
"paymentPlanTrailingDaysLeft": "{{daysLeft}} {{dayUnit}} verbleibend",
"paymentPlanUpgradeButton": "Jetzt upgraden"
}, },
"employees": { "employees": {
"pageTitle": "Mitarbeiter", "pageTitle": "Mitarbeiter",
@ -184,7 +186,6 @@
"choosenProduct": { "choosenProduct": {
"title": "Ausgewähltes Produkt", "title": "Ausgewähltes Produkt",
"products": [ "products": [
{},
{ {
"price": "9,99 € / Monat", "price": "9,99 € / Monat",
"featuresCount": 9, "featuresCount": 9,

View File

@ -20,8 +20,8 @@
"hour": "hour", "hour": "hour",
"minutes": "minutes", "minutes": "minutes",
"minute": "minute", "minute": "minute",
"days": "Days", "days": "days",
"day": "Day", "day": "day",
"separator": "and" "separator": "and"
}, },
"failed": "Failed", "failed": "Failed",
@ -126,7 +126,9 @@
}, },
"support": "Support", "support": "Support",
"feedback": "Feedback", "feedback": "Feedback",
"paymentPlan": "Payment plan" "paymentPlan": "Payment plan",
"paymentPlanTrailingDaysLeft": "{{daysLeft}} {{dayUnit}} left",
"paymentPlanUpgradeButton": "Upgrade now"
}, },
"employees": { "employees": {
"pageTitle": "Employees", "pageTitle": "Employees",
@ -184,7 +186,6 @@
"choosenProduct": { "choosenProduct": {
"title": "Choosen product", "title": "Choosen product",
"products": [ "products": [
{},
{ {
"price": "9,99 € / month", "price": "9,99 € / month",
"featuresCount": 9, "featuresCount": 9,

View File

@ -132,6 +132,9 @@ export function App() {
options={{ options={{
username: appUserData.user.username, username: appUserData.user.username,
permissions: appUserData.permissions, permissions: appUserData.permissions,
paymentPlanStatus: appUserData.user.payment_plan_status,
paymentPlanTrialEnd: appUserData.user.payment_plan_trial_end,
paymentPlanCanceledAt: appUserData.user.payment_plan_canceled_at,
}} }}
> >
<UserProfileProvider> <UserProfileProvider>
@ -139,6 +142,7 @@ export function App() {
<AppProvider <AppProvider
options={{ options={{
paymentPlan: appUserData.user.payment_plan, paymentPlan: appUserData.user.payment_plan,
paymentPlanSettings: appUserData.paymentPlanSettings,
}} }}
> >
<StoresProvider options={{ stores: appUserData.stores }}> <StoresProvider options={{ stores: appUserData.stores }}>

View File

@ -134,10 +134,7 @@ export function MyCalendarMaxFutureBookingDaysFormInput({
<MyFormInput <MyFormInput
formItemName={formItemName} formItemName={formItemName}
minLength={Constants.GLOBALS.MIN_CALENDAR_FUTURE_BOOKING_DAYS} minLength={Constants.GLOBALS.MIN_CALENDAR_FUTURE_BOOKING_DAYS}
maxLength={ maxLength={appContext.paymentPlanSettings.maxEmployees}
Constants.PAYMENT_PLAN[appContext.paymentPlan]
.calendarMaxFutureBookingDays
}
label={t("common.calendarMaxFutureBookingDays")} label={t("common.calendarMaxFutureBookingDays")}
ruleMessageValueRequired={t( ruleMessageValueRequired={t(
"common.inputRules.calendarMaxFutureBookingDaysRequired" "common.inputRules.calendarMaxFutureBookingDaysRequired"

View File

@ -10,7 +10,7 @@ import {
UserOutlined, UserOutlined,
WalletOutlined, WalletOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Divider, Menu } from "antd"; import { Button, Card, Divider, Flex, Menu, Typography } from "antd";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { BreakpointLgWidth, Constants, isDevelopmentEnv } from "../../utils"; import { BreakpointLgWidth, Constants, isDevelopmentEnv } from "../../utils";
@ -100,6 +100,12 @@ export function SideMenuContent({
return items; return items;
}; };
const showPaymentPlanInfoBanner =
sideBarContext.permissions.includes("paymentPlan") &&
sideBarContext.paymentPlanStatus === "trialing" &&
sideBarContext.paymentPlanTrialEnd !== null &&
sideBarContext.paymentPlanCanceledAt !== null;
const getSecondMenuItems = () => { const getSecondMenuItems = () => {
let items = []; let items = [];
@ -118,7 +124,7 @@ export function SideMenuContent({
); );
} }
if (sideBarContext.permissions.includes("paymentPlan")) { if (!showPaymentPlanInfoBanner) {
items.push({ items.push({
label: t("sideMenu.paymentPlan"), label: t("sideMenu.paymentPlan"),
icon: <WalletOutlined />, icon: <WalletOutlined />,
@ -162,6 +168,17 @@ export function SideMenuContent({
} }
}, [location.pathname]); }, [location.pathname]);
const calculateExpiry = () => {
const currentDate = new Date();
const expiryDate = new Date(sideBarContext.paymentPlanTrialEnd * 1000);
const diffTime = Math.abs(expiryDate - currentDate);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
};
const accountPlanExpiry = calculateExpiry();
return ( return (
<div <div
style={{ style={{
@ -216,6 +233,36 @@ export function SideMenuContent({
<div> <div>
<Divider style={{ margin: 0 }} /> <Divider style={{ margin: 0 }} />
{showPaymentPlanInfoBanner && (
<Card
style={{ backgroundColor: "#6878d6", margin: 8 }}
styles={{ body: { padding: 10 } }}
>
<Flex justify="center" align="center">
<Typography.Title
level={5}
style={{ color: "#fff", textAlign: "center" }}
>
{t("sideMenu.paymentPlanTrailingDaysLeft", {
daysLeft: accountPlanExpiry,
dayUnit:
accountPlanExpiry > 1
? t("common.unit.days")
: t("common.unit.day"),
})}
</Typography.Title>
</Flex>
<Button
block
style={{ fontWeight: "bold", fontSize: 13 }}
onClick={() => navigate(Constants.ROUTE_PATHS.PAYMENT_PLAN)}
>
{t("sideMenu.paymentPlanUpgradeButton")}
</Button>
</Card>
)}
<Menu <Menu
selectable={true} selectable={true}
selectedKeys={[selectedKeys]} selectedKeys={[selectedKeys]}

View File

@ -3,6 +3,12 @@ import { createContext, useContext, useState } from "react";
const preview = { const preview = {
paymentPlan: "", paymentPlan: "",
setPaymentPlan: () => {}, setPaymentPlan: () => {},
paymentPlanSettings: {
name: "",
maxEmployees: 0,
calendarMaxFutureBookingDays: 0,
},
setPaymentPlanSettings: () => {},
}; };
const AppContext = createContext(preview); const AppContext = createContext(preview);
@ -11,12 +17,17 @@ export const useAppContext = () => useContext(AppContext);
export function AppProvider({ children, options }) { export function AppProvider({ children, options }) {
const [paymentPlan, setPaymentPlan] = useState(options.paymentPlan); const [paymentPlan, setPaymentPlan] = useState(options.paymentPlan);
const [paymentPlanSettings, setPaymentPlanSettings] = useState(
options.paymentPlanSettings
);
return ( return (
<AppContext.Provider <AppContext.Provider
value={{ value={{
paymentPlan, paymentPlan,
setPaymentPlan, setPaymentPlan,
paymentPlanSettings,
setPaymentPlanSettings,
}} }}
> >
{children} {children}

View File

@ -5,8 +5,12 @@ const preview = {
setUsername: () => {}, setUsername: () => {},
permissions: [], permissions: [],
setPermissions: () => {}, setPermissions: () => {},
accountPlanExpiry: "", paymentPlanStatus: "",
setAccountPlanExpiry: () => {}, setPaymentPlanStatus: () => {},
paymentPlanTrialEnd: "",
setPaymentPlanTrialEnd: () => {},
paymentPlanCanceledAt: "",
setPaymentPlanCanceledAt: () => {},
}; };
const SideBarContext = createContext(preview); const SideBarContext = createContext(preview);
@ -16,8 +20,14 @@ export const useSideBarContext = () => useContext(SideBarContext);
export default function SideBarProvider({ children, options }) { export default function SideBarProvider({ children, options }) {
const [username, setUsername] = useState(options.username); const [username, setUsername] = useState(options.username);
const [permissions, setPermissions] = useState(options.permissions); const [permissions, setPermissions] = useState(options.permissions);
const [accountPlanExpiry, setAccountPlanExpiry] = useState( const [paymentPlanStatus, setPaymentPlanStatus] = useState(
options.accountPlanExpiry options.paymentPlanStatus
);
const [paymentPlanTrialEnd, setPaymentPlanTrialEnd] = useState(
options.paymentPlanTrialEnd
);
const [paymentPlanCanceledAt, setPaymentPlanCanceledAt] = useState(
options.paymentPlanCanceledAt
); );
return ( return (
@ -27,8 +37,12 @@ export default function SideBarProvider({ children, options }) {
setUsername, setUsername,
permissions, permissions,
setPermissions, setPermissions,
accountPlanExpiry, paymentPlanStatus,
setAccountPlanExpiry, setPaymentPlanStatus,
paymentPlanTrialEnd,
setPaymentPlanTrialEnd,
paymentPlanCanceledAt,
setPaymentPlanCanceledAt,
}} }}
> >
{children} {children}

View File

@ -34,6 +34,7 @@ import { MyRecaptcha, PrivacyPolicyCheckbox } from ".";
import MyAppLogo from "../../Components/MyAppLogo"; import MyAppLogo from "../../Components/MyAppLogo";
import { CheckOutlined } from "@ant-design/icons"; import { CheckOutlined } from "@ant-design/icons";
import { RequestState } from "../../Components/MyRequestStateItem"; import { RequestState } from "../../Components/MyRequestStateItem";
import { useAppContext } from "../../Contexts/AppContext";
/* /*
const SignUpStep = { const SignUpStep = {
SIGN_UP: 1, SIGN_UP: 1,
@ -131,6 +132,10 @@ function AccountDetails({ t, form }) {
} }
export function ChoosenProduct({ t, paymentPlan, extra }) { export function ChoosenProduct({ t, paymentPlan, extra }) {
const appContext = useAppContext();
console.log(appContext.paymentPlanSettings);
return ( return (
<Card <Card
title={t("authentication.signUp.choosenProduct.title")} title={t("authentication.signUp.choosenProduct.title")}
@ -138,7 +143,7 @@ export function ChoosenProduct({ t, paymentPlan, extra }) {
extra={extra} extra={extra}
> >
<Typography.Title level={2}> <Typography.Title level={2}>
{Constants.APP_NAME} - {Constants.PAYMENT_PLAN[paymentPlan].name} {Constants.APP_NAME} - {appContext.paymentPlanSettings.name}
</Typography.Title> </Typography.Title>
<Divider /> <Divider />
@ -173,6 +178,7 @@ export function ChoosenProduct({ t, paymentPlan, extra }) {
function CostSummary({ notificationApi, paymentPlan, form }) { function CostSummary({ notificationApi, paymentPlan, form }) {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const appContext = useAppContext();
const [isRequesting, setIsRequesting] = useState(RequestState.INIT); const [isRequesting, setIsRequesting] = useState(RequestState.INIT);
const recaptchaRef = useRef(null); const recaptchaRef = useRef(null);
@ -290,7 +296,7 @@ function CostSummary({ notificationApi, paymentPlan, form }) {
marginBottom: 12, marginBottom: 12,
}} }}
> >
{Constants.APP_NAME} - {Constants.PAYMENT_PLAN[paymentPlan].name} {Constants.APP_NAME} - {appContext.paymentPlanSettings.name}
</div> </div>
<Flex justify="space-between"> <Flex justify="space-between">
<Typography.Text style={{ fontSize: 16 }}> <Typography.Text style={{ fontSize: 16 }}>

View File

@ -183,7 +183,7 @@ export default function StoreEmployees() {
setModalOptions={setAddEditEmployeeModalOptions} setModalOptions={setAddEditEmployeeModalOptions}
fetchEmployees={fetchEmployees} fetchEmployees={fetchEmployees}
disabled={ disabled={
Constants.PAYMENT_PLAN[appContext.paymentPlan].maxEmployees <= appContext.paymentPlanSettings.maxEmployees <=
requestData.employees.length requestData.employees.length
} }
/> />

View File

@ -110,23 +110,6 @@ export const Constants = {
INIT_PAYMENT: 5, INIT_PAYMENT: 5,
}, },
SUPPORT_EMAIL: process.env.REACT_APP_SUPPORT_EMAIL, SUPPORT_EMAIL: process.env.REACT_APP_SUPPORT_EMAIL,
PAYMENT_PLAN: [
{
name: "Demo",
maxEmployees: 5,
calendarMaxFutureBookingDays: 14,
},
{
name: "Basic",
maxEmployees: 15,
calendarMaxFutureBookingDays: 60,
},
{
name: "Premium",
maxEmployees: 50, // temporary
calendarMaxFutureBookingDays: 365, // temporary
},
],
}; };
export function isEmailValid(email) { export function isEmailValid(email) {