From b28946333e9c80e0cfe2efc87d4d37681bcee3c2 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Mon, 27 Oct 2025 02:22:35 +0530 Subject: [PATCH] added subscription form and payment proccess page --- public/assets/css/core-extend.css | 58 +++ .../UserSubscription/ProcessedPayment.jsx | 295 ++++++++++++++ .../UserSubscription/SubscriptionForm.jsx | 376 ++++++++++++++++++ .../UserSubscription/SubscriptionLayout.jsx | 53 +++ src/hooks/useAuth.jsx | 23 +- src/hooks/useTenant.js | 4 +- src/pages/Home/HomeSchema.jsx | 2 +- src/pages/Home/LandingPage.jsx | 6 +- src/pages/Home/MakeSubscription.jsx | 309 +------------- src/pages/Home/SubscriptionPlans.jsx | 84 ++-- src/repositories/AuthRepository.jsx | 7 +- src/router/AppRoutes.jsx | 2 +- src/utils/appUtils.js | 17 +- src/utils/axiosClient.jsx | 18 +- 14 files changed, 884 insertions(+), 370 deletions(-) create mode 100644 src/components/UserSubscription/ProcessedPayment.jsx create mode 100644 src/components/UserSubscription/SubscriptionForm.jsx create mode 100644 src/components/UserSubscription/SubscriptionLayout.jsx diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css index 940f2065..32ebbf32 100644 --- a/public/assets/css/core-extend.css +++ b/public/assets/css/core-extend.css @@ -40,6 +40,64 @@ .text-royalblue{ color: #1796e3; } +.stepper-container { + position: relative; +} + +.timeline-horizontal { + position: relative; + padding: 0; + margin: 0; +} + +.timeline-item { + position: relative; + flex: 1; +} + +.timeline-point { + width: 20px; + height: 20px; + border-radius: 50%; + background: #dee2e6; + color: #6c757d; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + z-index: 2; + position: relative; + padding: 3px; +} + +.timeline-point.completed { + background-color: var(--bs-success); + color: white; +} + +.timeline-point.active { + background-color: var(--bs-info); + color: white; + transform: scale(1.1); + padding: 4px; +} + +.timeline-line-horizontal { + content: ""; + position: absolute; + top: 10px; + left: 50%; + width:100% ; + height: 2px; + background-color: #dee2e6; + z-index: 1; +} + +.timeline-item.completed ~ .timeline-line-horizontal { + background-color: #28a745; +} + + .text-md { font-size: 2rem; diff --git a/src/components/UserSubscription/ProcessedPayment.jsx b/src/components/UserSubscription/ProcessedPayment.jsx new file mode 100644 index 00000000..aabaae27 --- /dev/null +++ b/src/components/UserSubscription/ProcessedPayment.jsx @@ -0,0 +1,295 @@ +import React, { useState, useMemo } from "react"; +import { useSubscription } from "../../hooks/useAuth"; +import { useParams } from "react-router-dom"; +import { useCreateTenant, useIndustries } from "../../hooks/useTenant"; +import { frequencyLabel } from "../../utils/appUtils"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +const ProcessedPayment = () => { + const { frequency, planName } = useParams(); + const [selectedPlan, setSelectedPlan] = useState(planName); + const { + data: plans, + isError: isPlanError, + isLoading, + } = useSubscription(frequency); + const handleChange = (e) => { + setSelectedPlan(e.target.value); + }; + const visiblePlans = useMemo(() => { + if (!plans) return []; + + const planOrder = ["basic", "pro", "super"]; + const currentIndex = planOrder.indexOf(planName?.toLowerCase()); + + if (currentIndex === -1) return plans; // fallback: show all + const visibleNames = planOrder.slice(currentIndex); + + return plans.filter((p) => + visibleNames.includes(p.planName?.toLowerCase()) + ); + }, [plans, selectedPlan]); + const clients = [ + { + firstName: "Alice", + lastName: "Smith", + email: "alice.smith@example.com", + billingAddress: + "456 Elm Street, Metropolis, USA 456 Elm Street, Metropolis, USA 456 Elm Street, Metropolis, USA", + organizationName: "BetaTech Ltd.", + contactNumber: "+44 20 7946 0958", + onBoardingDate: "2025-09-15", + organizationSize: "10-50", + industryId: "c4d5e6f7-8901-2345-6789-abcdef123456", + reference: "d2e3f4a5-6789-0123-4567-89abcdef0123", + }, + ]; + return ( +
+
+
+
+
+

+ Choose the Perfect Plan for Your Organization +

+

+ Select a plan that fits your team’s needs and unlock the + features that drive productivity. +

+
+ {visiblePlans?.map((plan) => { + let colSize = "8"; // default 1 card full width + + if (visiblePlans.length === 2) colSize = "6"; + else if (visiblePlans.length === 3) colSize = "4"; + else if (visiblePlans.length >= 4) colSize = "3"; + + return ( +
+
+ +
+
+ ); + })} + {selectedPlan && ( +
+
+ {(() => { + const selected = plans?.find( + (p) => p.planName === selectedPlan + ); + if (!selected) return null; + const { + planName, + description, + price, + frequency, + trialDays, + maxUser, + maxStorage, + currency, + features, + } = selected; + + return ( + <> +
+
+
+ + Max Users: {maxUser} +
+
+
+
+ + Max Storage: {maxStorage} MB +
+
+
+
+ + Trial Days: {trialDays} +
+
+
+ +
+ Included Features +
+
+ {Object.entries(features?.modules || {}).map( + ([key, mod]) => ( +
+
+
+ + + {mod.name} + + + {mod.enabled ? "Enabled" : "Disabled"} + +
+
+
+ ) + )} +
+ +
+ Support +
+
    + {features?.supports?.emailSupport && ( +
  • + + Email Support +
  • + )} + {features?.supports?.phoneSupport && ( +
  • + + Phone Support +
  • + )} + {features?.supports?.prioritySupport && ( +
  • + + Priority Support +
  • + )} +
+ + ); + })()} +
+
+ )} +
+
+
+
Client Info
+ {clients.map((client, idx) => ( +
+
+
+ First Name: +
+
{client.firstName}
+ +
+ Last Name: +
+
{client.lastName}
+ +
+ Email: +
+
{client.email}
+ +
+ Contact Number: +
+
{client.contactNumber}
+ +
+ Organization Name: +
+
{client.organizationName}
+ +
+ Organization Size: +
+
{client.organizationSize}
+ +
+ Onboarding Date: +
+
+ {formatUTCToLocalTime(client.onBoardingDate)} +
+ +
+ Billing Address: +
+
{client.billingAddress}
+ +
+ Industry ID: +
+
{client.industryId}
+ + {/*
+ Reference: +
+
{client.reference}
*/} +
+
+ ))} +
+
+
+ +
+
+ ); +}; + +export default ProcessedPayment; diff --git a/src/components/UserSubscription/SubscriptionForm.jsx b/src/components/UserSubscription/SubscriptionForm.jsx new file mode 100644 index 00000000..d917f985 --- /dev/null +++ b/src/components/UserSubscription/SubscriptionForm.jsx @@ -0,0 +1,376 @@ +import React, { useState, useMemo } from "react"; +import { useForm } from "react-hook-form"; +import { + OrganizationDefaultValue, + OrganizationSchema, +} from "../../pages/Home/HomeSchema"; +import { zodResolver } from "@hookform/resolvers/zod"; +import Label from "../common/Label"; +import { orgSize, reference } from "../../utils/constants"; +import DatePicker from "../common/DatePicker"; +import { useCreateTenant, useIndustries } from "../../hooks/useTenant"; + + + +const SubscriptionForm = ({onNext}) => { + + + + + const { data, isError, isLoading: industryLoading } = useIndustries(); + + + + + const { + register, + handleSubmit, + control, + formState: { errors }, + reset, + } = useForm({ + resolver: zodResolver(OrganizationSchema), + defaultValues: OrganizationDefaultValue, + }); + + const { mutate: CreateTenant, isPending } = useCreateTenant(() => { + // nextstep + if (onNext) onNext(); + }); + + const onSubmit = (data) => { + CreateTenant(data) + // reset(); + }; + return ( +
+
+
+
+
+
+
+ {/* First Name */} +
+ + + {errors.firstName && ( +
+ {errors.firstName.message} +
+ )} +
+ + {/* Last Name */} +
+ + + {errors.lastName && ( +
+ {errors.lastName.message} +
+ )} +
+ + {/* Email */} +
+ + + {errors.email && ( +
{errors.email.message}
+ )} +
+ + {/* Contact Number */} +
+ + + {errors.contactNumber && ( +
+ {errors.contactNumber.message} +
+ )} +
+ + {/* Billing Address */} +
+ +