upgrade plane card and add discount coupon input box

This commit is contained in:
pramod.mahajan 2025-10-31 16:35:24 +05:30
parent 5690ec8a69
commit 2042e179c7
11 changed files with 420 additions and 184 deletions

View File

@ -17596,6 +17596,31 @@ html:not([dir=rtl]) .toast.bs-toast .toast-header .btn-close {
} }
} }
@media (min-width: 768px) { @media (min-width: 768px) {
/* width */
.w-md-auto {
width: auto !important;
}
.w-md-20 {
width: 20% !important;
}
.w-md-25 {
width: 25% !important;
}
.w-md-50 {
width: 50% !important;
}
.w-md-60 {
width: 60% !important;
}
.w-md-75 {
width: 75% !important;
}
.object-fit-md-contain { .object-fit-md-contain {
object-fit: contain !important; object-fit: contain !important;
} }

View File

@ -43,15 +43,14 @@ const SubScriptionHistory = ({ tenantId }) => {
); );
} }
// Format dates
const end = plan?.endDate ? new Date(plan.endDate) : null; const end = plan?.endDate ? new Date(plan.endDate) : null;
const today = new Date(); const today = new Date();
const daysLeft = end const daysLeft = end
? Math.max(0, Math.ceil((end - today) / (1000 * 60 * 60 * 24))) ? Math.max(0, Math.ceil((end - plan?.startDate) / (1000 * 60 * 60 * 24)))
: 0; : 0;
// Render logic for subscription history table
const renderSubscriptionHistory = () => { const renderSubscriptionHistory = () => {
if (!subscriptionHistory || subscriptionHistory.length === 0) { if (!subscriptionHistory || subscriptionHistory.length === 0) {
return ( return (
@ -72,23 +71,35 @@ const SubScriptionHistory = ({ tenantId }) => {
<table className="table border-top dataTable text-nowrap align-middle"> <table className="table border-top dataTable text-nowrap align-middle">
<thead className="align-middle"> <thead className="align-middle">
<tr> <tr>
<th>Date</th> <th>Invoice</th>
<th>Type</th>
<th>Amount</th> <th>Amount</th>
<th>Plan Name</th> <th>Date</th>
<th className="text-center">Action</th> <th>Status</th>
<th className="text-center actions-col">Action</th>
</tr> </tr>
</thead> </thead>
<tbody className="align-middle"> <tbody className="align-middle">
{sortedHistory.map((item) => ( {sortedHistory.map((item) => (
<tr key={item.id}> <tr key={item.id}>
<td>{formatUTCToLocalTime(item?.createdAt)}</td> <td className="fw-medium">{item.planName}</td>
<td>{SUBSCRIPTION_PLAN_FREQUENCIES[item.frequency] || "N/A"}</td>
<td> <td className="fw-medium">
{item.currency?.symbol || "₹"} {item.price} {item.currency?.symbol || "₹"} {item.price}
</td> </td>
<td>{item.planName}</td> <td>
<td className="text-center"> <span className="text-tiny small text-muted">
{formatUTCToLocalTime(item?.createdAt)} by,{" "}
{item?.createdBy?.firstName}
</span>
</td>
<td>
<div className="d-flex align-items-center">
<p className="p-1 my-0 mx-1 rounded-circle bg-success"></p>{" "}
<small className="fw-medium">Paid</small>
</div>
</td>
<td className="text-center actions-col">
<div className="dropdown"> <div className="dropdown">
<button <button
className="btn btn-icon btn-sm dropdown-toggle hide-arrow" className="btn btn-icon btn-sm dropdown-toggle hide-arrow"
@ -97,9 +108,7 @@ const SubScriptionHistory = ({ tenantId }) => {
<i className="bx bx-dots-vertical-rounded"></i> <i className="bx bx-dots-vertical-rounded"></i>
</button> </button>
<div className="dropdown-menu dropdown-menu-end"> <div className="dropdown-menu dropdown-menu-end">
<button <button className="dropdown-item py-1">
className="dropdown-item py-1"
>
<i className="bx bx-detail bx-sm"></i> View <i className="bx bx-detail bx-sm"></i> View
</button> </button>
<button <button
@ -108,7 +117,8 @@ const SubScriptionHistory = ({ tenantId }) => {
console.log("Download clicked for", item.id) console.log("Download clicked for", item.id)
} }
> >
<i className="bx bx-cloud-download bx-sm"></i> Download <i className="bx bx-cloud-download bx-sm"></i>{" "}
Download
</button> </button>
</div> </div>
</div> </div>
@ -120,100 +130,172 @@ const SubScriptionHistory = ({ tenantId }) => {
</div> </div>
{/* Card-based view for smaller screens */} {/* Card-based view for smaller screens */}
</> </>
); );
}; };
return ( return (
<div className="p-2 p-md-4"> <div className="text-start">
<div className="row g-4"> <div className="row d-flex justify-content-between py-1">
{/* Left Card: Active Subscription */} <div className="col-md-4 d-flex flex-column gap-6">
<div className="col-12 col-lg-6"> <div className="">
<div className="card shadow-sm border rounded p-3 h-100 text-start"> <p className=" d-bolck fw-semibold fs-6">Current Plan</p>
<div className="divider text-start mb-3"> <small className="d-bolck">
<div className="divider-text">Active Subscription</div> You can update your plan anytime for best benifit from the product
</div> and track your projct
</small>
<p className="text-primary fw-bold m-0 fs-4"> </div>
{plan.planName || "N/A"} <p className="fw-semibold text-primary">Switch Plan</p>
</p> </div>
{plan.description && ( <div className="col-md-6 ">
<p className="m-0 text-muted small">{plan.description}</p> <div className="row border bg-light-primary p-3">
)} <div className="col-1">
<small>
<div className="mt-2"> <i className="bx bxs-package"></i>
<h3 className="m-0">
{plan.currency?.symbol || "₹"} {plan.price}
</h3>
<small className="text-muted">
{SUBSCRIPTION_PLAN_FREQUENCIES[plan.frequency] || ""}
</small> </small>
</div> </div>
<div className="col-11">
<div className="mt-3 small text-muted"> <div className="d-flex justify-content-between">
<div> <div className="d-flex flex-row gap-4">
Activated Since:{" "} <small className="fw-bold">{plan.planName || "N/A"}</small>
{plan.startDate ? formatUTCToLocalTime(plan.startDate) : "N/A"} ( <small className="fw-bold text-primary">
{daysLeft} days left) {" "}
{plan.currency?.symbol || "₹"} {plan.price}
</small>
<small className=" text-primary">
{" "}
{SUBSCRIPTION_PLAN_FREQUENCIES[plan.frequency] || ""}
</small>
</div>
<span className=" bg-primary rounded-circle">
<i className="bx bx-md bx-check text-white"></i>
</span>
</div> </div>
<div className="mt-1"> <div className="col-12">
Ends on:{" "} <p className="m-0 text-muted text-tiny">
{plan.endDate ? formatUTCToLocalTime(plan.endDate) : "N/A"} Includes up to 100 userss, 10GB individual cloud storage and
access maximum features
</p>
<div className="mt-1">
Activated Since:{" "}
<span className="fw-semibold">
{" "}
{plan.startDate
? formatUTCToLocalTime(plan.startDate)
: "N/A"}{" "}
({daysLeft} days left)
</span>
</div>
</div> </div>
</div> </div>
{/* Features list */}
<div className="mt-4">
<h6 className="text-secondary">Features</h6>
<div className="row g-2">
{features?.modules &&
Object.entries(features.modules).map(([key, mod]) => {
if (!mod?.name) return null;
return (
<div
key={key}
className="col-12 col-sm-6 d-flex align-items-center"
>
<i
className={`fa-regular ${
mod.enabled
? "fa-circle-check text-success"
: "fa-circle-xmark text-danger"
}`}
></i>
<span className="ms-2">{mod.name}</span>
</div>
);
})}
</div>
</div>
<div className="mt-3 text-end">
<button
className="btn btn-sm btn-primary"
onClick={handleUpgradePlan}
>
Upgrade Plan
</button>
</div>
</div> </div>
</div> </div>
</div>
{/* Right Card: Subscription History */} <hr className="divider border-2 my-2" />
<div className="col-12 col-lg-6">
<div className="card shadow-sm border rounded p-3 h-100"> <div className="row d-flex justify-content-between py-1">
<div className="divider text-start mb-3"> <div className="col-md-4 d-flex flex-column gap-6">
<div className="divider-text"> <div className="">
<i className="bx bx-history"></i> <small>History</small> <p className=" d-bolck fw-semibold fs-6">Billing History</p>
</div> <small className="d-bolck">
</div> Sumary on the payment history for the subscription plan of the
{renderSubscriptionHistory()} application
</small>
</div> </div>
<p className="fw-semibold text-primary">Billing History</p>
</div> </div>
<div className="col-md-6 ">{renderSubscriptionHistory()}</div>
</div> </div>
</div> </div>
); );
}; };
export default SubScriptionHistory; export default SubScriptionHistory;
// <div className="p-2 p-md-4">
// <div className="row g-4">
// {/* Left Card: Active Subscription */}
// <div className="col-12 col-lg-6">
// <div className="card shadow-sm border rounded p-3 h-100 text-start">
// <div className="divider text-start mb-3">
// <div className="divider-text">Active Subscription</div>
// </div>
// <p className="text-primary fw-bold m-0 fs-4">
// {plan.planName || "N/A"}
// </p>
// {plan.description && (
// <p className="m-0 text-muted small">{plan.description}</p>
// )}
// <div className="mt-2">
// <h3 className="m-0">
// {plan.currency?.symbol || ""} {plan.price}
// </h3>
// <small className="text-muted">
// {SUBSCRIPTION_PLAN_FREQUENCIES[plan.frequency] || ""}
// </small>
// </div>
// <div className="mt-3 small text-muted">
// <div>
// Activated Since:{" "}
// {plan.startDate ? formatUTCToLocalTime(plan.startDate) : "N/A"} (
// {daysLeft} days left)
// </div>
// <div className="mt-1">
// Ends on:{" "}
// {plan.endDate ? formatUTCToLocalTime(plan.endDate) : "N/A"}
// </div>
// </div>
// {/* Features list */}
// <div className="mt-4">
// <h6 className="text-secondary">Features</h6>
// <div className="row g-2">
// {features?.modules &&
// Object.entries(features.modules).map(([key, mod]) => {
// if (!mod?.name) return null;
// return (
// <div
// key={key}
// className="col-12 col-sm-6 d-flex align-items-center"
// >
// <i
// className={`fa-regular ${
// mod.enabled
// ? "fa-circle-check text-success"
// : "fa-circle-xmark text-danger"
// }`}
// ></i>
// <span className="ms-2">{mod.name}</span>
// </div>
// );
// })}
// </div>
// </div>
// <div className="mt-3 text-end">
// <button
// className="btn btn-sm btn-primary"
// onClick={handleUpgradePlan}
// >
// Upgrade Plan
// </button>
// </div>
// </div>
// </div>
// <div className="col-12 col-lg-6">
// <div className="card shadow-sm border rounded p-3 h-100">
// <div className="divider text-start mb-3">
// <div className="divider-text">
// <i className="bx bx-history"></i> <small>History</small>
// </div>
// </div>
// {renderSubscriptionHistory()}
// </div>
// </div>
// </div>
// </div>

View File

@ -37,7 +37,10 @@ const ProcessedPayment = ({
const { const {
data: plans, data: plans,
isError: isPlanError, isError: isPlanError,
isLoading,isError,isRefetching,refetch isLoading,
isError,
isRefetching,
refetch,
} = useSubscription(frequency); } = useSubscription(frequency);
useEffect(() => { useEffect(() => {
if (!plans || !selectedPlanId) return; if (!plans || !selectedPlanId) return;
@ -77,7 +80,11 @@ const ProcessedPayment = ({
alert("Failed to load Razorpay SDK"); alert("Failed to load Razorpay SDK");
return; return;
} }
MakePayment({ amount: selectedPlan?.price }); let price = 0;
price =
frequencyLabel(selectedPlan?.frequency, true, true)?.planDurationInInt *
selectedPlan?.price;
MakePayment({ amount: price });
}; };
const handleRetry = () => { const handleRetry = () => {
@ -188,21 +195,27 @@ const ProcessedPayment = ({
<div className="custom-option custom-option-basic text-start w-100 bg-light-primary border border-primary shadow-md p-3 rounded-3"> <div className="custom-option custom-option-basic text-start w-100 bg-light-primary border border-primary shadow-md p-3 rounded-3">
<div className="custom-option-header d-flex justify-content-between align-items-center"> <div className="custom-option-header d-flex justify-content-between align-items-center">
<span className="h6 mb-0"> <span className="h6 mb-0">
{selectedPlan?.planName} {selectedPlan?.description}
</span> </span>
<i className="bx bx-check-circle text-primary fs-4"></i> <i className="bx bx-check-circle text-primary fs-4"></i>
</div> </div>
<div className="d-flex justify-content-between mt-1"> <div className="d-flex justify-content-between mt-1">
<small className="text-muted"> <small>
{selectedPlan?.currency?.symbol} {selectedPlan?.price}{" "} Price -{" "}
/{frequencyLabel(frequency, false)} <span className="fw-medium">
{selectedPlan.currency?.symbol} {selectedPlan.price}{" "}
per {frequencyLabel(frequency)}
</span>
</small> </small>
</div> </div>
<span className="custom-option-body d-block mt-1"> <div className="d-flex flex-row gap-3 mt-1 align-items-center">
<small>{selectedPlan?.description}</small> <small>{selectedPlan?.planName}</small>
</span> <small className="d-block mt-1 text-xs border border-primary text-center rounded text-secondary w-min text-nowrap px-1">
billed {frequencyLabel(frequency, true)}
</small>
</div>
</div> </div>
</div> </div>
)} )}
@ -307,11 +320,18 @@ const ProcessedPayment = ({
<div className="d-flex justify-content-between"> <div className="d-flex justify-content-between">
<h6 className="fs-4">Total Price</h6> <h6 className="fs-4">Total Price</h6>
<h5 className="fs-3"> <h5 className="fs-3">
{formatFigure(selectedPlan?.price, { {formatFigure(
type: "currency", frequencyLabel(
currency: selectedPlan?.frequency,
selectedPlan?.currency.currencyCode, true,
})} true
)?.planDurationInInt * price,
{
type: "currency",
currency:
selectedPlan?.currency.currencyCode,
}
)}
</h5> </h5>
</div> </div>
</div> </div>
@ -375,26 +395,28 @@ const ProcessedPayment = ({
)} )}
</div> </div>
</div> </div>
<div className="col-12 d-flex justify-content-between"> <div className="col-12 d-flex flex-column flex-md-row justify-content-between gap-2 mt-3">
<button <button
type="submit" type="submit"
className="btn btn-label-primary d-flex align-items-center me-2" className="btn btn-label-primary d-flex align-items-center justify-content-center w-md-auto"
onClick={handlPrevious} onClick={handlPrevious}
> >
<i className="bx bx-chevron-left"></i> Previous <i className="bx bx-chevron-left me-1"></i> Previous
</button> </button>
<button <button
type="submit" type="submit"
className="btn btn-label-primary d-flex align-items-center me-2" className="btn btn-label-primary d-flex align-items-center justify-content-center w-md-auto"
onClick={() => ProcessToPayment(currentPlan?.price)} onClick={() => ProcessToPayment(currentPlan?.price)}
disabled={isPending}
> >
{isPending ? ( {isPending ? (
<span> <>
<i className="bx bx-loader-alt bx-md bx-spin me-2"></i>Please <i className="bx bx-loader-alt bx-md bx-spin me-2"></i> Please
Wait... Wait...
</span> </>
) : ( ) : (
"Processed To Payment" "Proceed To Payment"
)} )}
</button> </button>
</div> </div>

View File

@ -6,9 +6,12 @@ import { formatFigure, frequencyLabel } from "../../utils/appUtils";
import { setSelfTenant } from "../../slices/localVariablesSlice"; import { setSelfTenant } from "../../slices/localVariablesSlice";
import SelectedPlanSkeleton from "./SelectedPlanSkeleton"; import SelectedPlanSkeleton from "./SelectedPlanSkeleton";
import { error } from "pdf-lib"; import { error } from "pdf-lib";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { CouponDiscount } from "../../pages/Home/HomeSchema";
const SelectPlan = ({ currentStep, setStepStatus, onNext }) => { const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
const { frequency, planName } = useParams(); const { frequency, planId } = useParams();
const [selectedFrequency, setSelectedFrequency] = useState( const [selectedFrequency, setSelectedFrequency] = useState(
parseInt(frequency) parseInt(frequency)
); );
@ -17,7 +20,7 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
const client = useSelector( const client = useSelector(
(store) => store.localVariables.selfTenant.details (store) => store.localVariables.selfTenant.details
); );
const [selectedPlan, setSelectedPlan] = useState(planName); const [selectedPlan, setSelectedPlan] = useState(planId);
const [currentPlan, setCurrentPlan] = useState(null); const [currentPlan, setCurrentPlan] = useState(null);
const [failPayment, setFailPayment] = useState(null); const [failPayment, setFailPayment] = useState(null);
@ -30,25 +33,51 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
isRefetching, isRefetching,
} = useSubscription(selectedFrequency); } = useSubscription(selectedFrequency);
const handleChange = (e) => setSelectedPlan(e.target.value); const handleChange = (e) => {
setSelectedPlan(e.target.value);
};
useEffect(() => { useEffect(() => {
if (!plans || !selectedPlan) return; if (!plans || plans.length === 0) return;
const selected = plans.find((p) => p.planName === selectedPlan);
if (selected) { // Prefer route param if exists, else default to first plan
setCurrentPlan(selected); const matchingPlan = plans.find((p) => p.planId === planId) || plans[0];
dispatch(
setSelfTenant({ planId: selected.id, frequency: selectedFrequency }) setSelectedPlan(matchingPlan.id);
); setCurrentPlan(matchingPlan);
}
}, [plans, selectedPlan, dispatch, selectedFrequency]); // Dispatch correct plan + frequency only once data is ready
dispatch(
setSelfTenant({
planId: matchingPlan.id,
frequency: selectedFrequency,
})
);
}, [plans, selectedFrequency, planId, dispatch]);
const handleNextStep = () => { const handleNextStep = () => {
dispatch(setSelfTenant({ frequency: selectedFrequency })); if (!selectedPlan) {
toast.warning("Please select a plan before continuing.");
return;
}
dispatch(
setSelfTenant({
planId: selectedPlan,
frequency: selectedFrequency,
})
);
setStepStatus((prev) => ({ ...prev, 2: "success" })); setStepStatus((prev) => ({ ...prev, 2: "success" }));
onNext(); onNext();
}; };
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ resolver: zodResolver(CouponDiscount) });
return ( return (
<div className="container text-start "> <div className="container text-start ">
<div className="row gy-5 align-items-center justify-content-around"> <div className="row gy-5 align-items-center justify-content-around">
@ -171,51 +200,59 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
<SelectedPlanSkeleton /> <SelectedPlanSkeleton />
) : ( ) : (
<> <>
<div className="row"> <div className="row">
{plans?.map((plan) => ( {plans?.map((plan) => (
<div key={plan?.id} className="col-12 col-md-4 mb-md-3 mb-2">
<div <div
className={`form-check custom-option custom-option-basic text-start w-100 bg-light-primary ${ key={plan?.id}
selectedPlan === plan?.planName className="col-12 col-md-4 mb-md-3 mb-2"
? "border border-primary shadow-md"
: ""
}`}
> >
<label <div
className="form-check-label custom-option-content w-100" className={`form-check custom-option custom-option-basic text-start w-100 bg-light-primary ${
htmlFor={`customRadioTemp${plan?.id}`} selectedPlan === plan?.id
? "border border-primary shadow-md"
: ""
}`}
> >
<input <label
name="customRadioTemp" className="form-check-label custom-option-content w-100"
className="form-check-input" htmlFor={`customRadioTemp${plan?.id}`}
type="radio" >
value={plan?.planName} <input
id={`customRadioTemp${plan?.id}`} name="customRadioTemp"
checked={selectedPlan === plan?.planName} className="form-check-input"
onChange={handleChange} type="radio"
/> value={plan?.id}
<span className="custom-option-header d-flex justify-content-between align-items-center"> id={`customRadioTemp${plan?.id}`}
<span className="h6 mb-0">{plan?.planName}</span> checked={selectedPlan === plan?.id}
<span> onChange={handleChange}
{plan.currency?.symbol} {plan.price} /{" "} />
{frequencyLabel(selectedFrequency)} <span className="custom-option-header d-flex justify-content-between align-items-center">
<span className="h6 mb-0">{plan?.description}</span>
</span> </span>
</span> <span className="custom-option-body d-block ">
<span className="custom-option-body d-block mt-1"> <small>
<small>{plan?.description}</small> Price -{" "}
</span> <span className="fw-medium">
</label> {plan.currency?.symbol} {plan.price} per{" "}
{frequencyLabel(selectedFrequency)}
</span>
</small>
<small className="d-block mt-1 text-xs border border-primary text-center rounded text-secondary w-min text-nowrap px-1">
billed {frequencyLabel(selectedFrequency, true)}
</small>
</span>
</label>
</div>
</div> </div>
</div> ))}
))} </div>
</div>
{selectedPlan && ( {selectedPlan && (
<div className="col-12 text-start"> <div className="col-12 text-start">
<div className="border-warning mt-3"> <div className="border-warning mt-3">
{(() => { {(() => {
const selected = plans?.find( const selected = plans?.find(
(p) => p.planName === selectedPlan (p) => p.id === selectedPlan
); );
if (!selected) return null; if (!selected) return null;
@ -251,7 +288,6 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
</div> </div>
</div> </div>
</div> </div>
<h6 className="fw-bold text-secondary mb-2"> <h6 className="fw-bold text-secondary mb-2">
Included Features Included Features
</h6> </h6>
@ -275,7 +311,6 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
</div> </div>
))} ))}
</div> </div>
<h6 className="fw-bold text-secondary mt-3 mb-2"> <h6 className="fw-bold text-secondary mt-3 mb-2">
Support Support
</h6> </h6>
@ -299,8 +334,29 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
</li> </li>
)} )}
</ul> </ul>
<div className="d-flex flex-row align-items-end gap-2 mt-1">
<hr className="divider border-2" /> <div className="">
<label className="form-lable ">
Apply Coupon
<input
type="text"
{...register("coupon")}
className="form-control form-control-sm mt-1"
/>
</label>
</div>
<div>
{" "}
<button className="btn btn-primary btn-sm">
Apply
</button>
</div>
</div>
{/* {errors.coupon && (<small>{error.coupon.message}</small>)} */}{" "}
<small className="m-0">
Currently, no coupon codes are available!{" "}
</small>
<hr className="divider border-2 my-1" />
<div className="d-flex flex-column co-12"> <div className="d-flex flex-column co-12">
<div className="d-flex justify-content-between"> <div className="d-flex justify-content-between">
<h6 className="fs-6 m-0">Duration</h6> <h6 className="fs-6 m-0">Duration</h6>
@ -312,10 +368,17 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
<div className="d-flex justify-content-between"> <div className="d-flex justify-content-between">
<h6 className="fs-4">Total Price</h6> <h6 className="fs-4">Total Price</h6>
<h5 className="fs-3"> <h5 className="fs-3">
{formatFigure(price, { {formatFigure(
type: "currency", frequencyLabel(
currency: currency.currencyCode, selectedFrequency,
})} true,
true
)?.planDurationInInt * price,
{
type: "currency",
currency: currency.currencyCode,
}
)}
</h5> </h5>
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@ const SubscriptionLayout = ({
}) => { }) => {
return ( return (
<div className="container-fluid stepper-container align-items-start my-4 w-100"> <div className="container-fluid stepper-container align-items-start my-4 w-100">
<ul className="timeline-horizontal list-unstyled d-flex justify-content-between align-items-center position-relative w-75"> <ul className="timeline-horizontal list-unstyled d-flex justify-content-between align-items-center position-relative w-md-75">
{configStep.map((step, index) => { {configStep.map((step, index) => {
const stepNumber = index + 1; const stepNumber = index + 1;
const status = stepStatus[stepNumber] || "pending"; const status = stepStatus[stepNumber] || "pending";

View File

@ -24,4 +24,8 @@ export const OrganizationSchema = z.object({
export const OrganizationDefaultValue = { export const OrganizationDefaultValue = {
} }
export const CouponDiscount = z.object({
coupon:z.string().optional()
})

View File

@ -8,7 +8,7 @@ import SelectPlan from "../../components/UserSubscription/SelectPlan";
import Review from "../../components/UserSubscription/Review"; import Review from "../../components/UserSubscription/Review";
const MakeSubscription = () => { const MakeSubscription = () => {
const [currentStep, setCurrentStep] = useState(1); const [currentStep, setCurrentStep] = useState(2);
const [responsePayment, setResponsePayment] = useState(null); const [responsePayment, setResponsePayment] = useState(null);
const [stepStatus, setStepStatus] = useState({ const [stepStatus, setStepStatus] = useState({

View File

@ -113,7 +113,7 @@ const SubscriptionPlans = () => {
{/* Button */} {/* Button */}
<div className="mt-auto"> <div className="mt-auto">
<Link <Link
to={`/auth/subscripe/${frequency}/${plan.planName}`} to={`/auth/subscripe/${frequency}/${plan.id}`}
className="btn btn-outline-primary w-100 fw-bold mb-2" className="btn btn-outline-primary w-100 fw-bold mb-2"
> >
Subscribe Subscribe

View File

@ -122,8 +122,8 @@ const TenantDetails = ({
/> />
)} )}
<div className="nav-align-left nav-tabs-shadow mb-6"> <div className="nav-align-top nav-tabs-shadow mb-6">
<ul className="nav nav-tabs py-2 page-min-h" role="tablist"> <ul className="nav nav-tabs " >
{tabs.map((tab, index) => ( {tabs.map((tab, index) => (
<li key={tab.id} className="nav-item"> <li key={tab.id} className="nav-item">
<button <button

View File

@ -74,7 +74,7 @@ const router = createBrowserRouter(
], ],
}, },
{ path: "/auth/switch/org", element: <TenantSelectionPage /> }, { path: "/auth/switch/org", element: <TenantSelectionPage /> },
{ path: "/auth/subscripe/:frequency/:planName", element: <MakeSubscription /> }, { path: "/auth/subscripe/:frequency/:planId", element: <MakeSubscription /> },
{ {
element: <ProtectedRoute />, element: <ProtectedRoute />,
errorElement: <ErrorPage />, errorElement: <ErrorPage />,

View File

@ -113,7 +113,7 @@ export const formatFigure = (
type = "number", type = "number",
currency = "INR", currency = "INR",
locale = "en-US", locale = "en-US",
notation = "standard", // standard or compact notation = "standard", // standard or compact
compactDisplay = "short", compactDisplay = "short",
minimumFractionDigits = 0, minimumFractionDigits = 0,
maximumFractionDigits = 2, maximumFractionDigits = 2,
@ -122,7 +122,12 @@ export const formatFigure = (
if (amount == null || isNaN(amount)) return "-"; if (amount == null || isNaN(amount)) return "-";
const formatterOptions = { const formatterOptions = {
style: type === "currency" ? "currency" : type === "percent" ? "percent" : "decimal", style:
type === "currency"
? "currency"
: type === "percent"
? "percent"
: "decimal",
notation: notation, notation: notation,
compactDisplay, compactDisplay,
minimumFractionDigits, minimumFractionDigits,
@ -136,17 +141,52 @@ export const formatFigure = (
return new Intl.NumberFormat(locale, formatterOptions).format(amount); return new Intl.NumberFormat(locale, formatterOptions).format(amount);
}; };
export const frequencyLabel = (freq, isLong = false) => { export const frequencyLabel = (
const frequency = parseInt(freq, 10); freq,
isLong = false,
isMonthRequired = false
) => {
const frequency = parseInt(freq, 10);
switch (frequency) { switch (frequency) {
case 0: case 0:
return isLong ? "1 Month" : "1 mo"; if (isLong && isMonthRequired) {
return { planDurationInString: "1 Month", planDurationInInt: 1 };
}
if (isLong) {
return "1 Month";
} else {
return "1 mon";
}
case 1: case 1:
return isLong ? "Quarterly (3 Months)" : "3 mo"; if (isLong && isMonthRequired) {
return {
planDurationInString: "Quarterly (3 Months)",
planDurationInInt: 3,
};
}
if (isLong) {
return "Quarterly (3 Months)";
} else {
return "3 mon";
}
case 2: case 2:
return isLong ? "6 Months" : "6 mo"; if (isLong && isMonthRequired) {
return { planDurationInString: "6 Month", planDurationInInt: 6 };
}
if (isLong) {
return "6 Month";
} else {
return "6 mon";
}
case 3: case 3:
return isLong ? "1 Year" : "1 yr"; if (isLong && isMonthRequired) {
return { planDurationInString: "1 Year", planDurationInInt: 12 };
}
if (isLong) {
return "1 Year";
} else {
return "1 yr";
}
default: default:
return isLong ? "Unknown" : "N/A"; return isLong ? "Unknown" : "N/A";
} }