tenant and subscript form full setup

This commit is contained in:
pramod mahajan 2025-08-06 19:42:40 +05:30
parent c1d3b831cc
commit 6f2ddacfd1
6 changed files with 194 additions and 66 deletions

View File

@ -24,7 +24,7 @@ const ContactInfro = ({ onNext }) => {
} }
}; };
return ( return (
<div class="row g-6"> <div className="row g-6">
<div className="col-sm-6"> <div className="col-sm-6">
<Label htmlFor="firstName" required> <Label htmlFor="firstName" required>
First Name First Name

View File

@ -6,24 +6,25 @@ useEffect(()=>{
setFrequency(selected) setFrequency(selected)
},[selected]) },[selected])
return ( return (
<div className="d-inline-flex border rounded-pill overflow-hidden shadow-sm"> <div className='text-center mt-6'>
<div className="d-inline-flex border rounded-pill overflow-hidden shadow-none">
<button <button
type="button" type="button"
className={`btn px-4 py-2 rounded-0 ${selected === 0 ? 'active btn-primary text-white' : ''}`} className={`btn px-4 py-2 rounded-0 ${selected === 0 ? 'active btn-secondary text-white' : ''}`}
onClick={() => setSelected(0)} onClick={() => setSelected(0)}
> >
Monthly Monthly
</button> </button>
<button <button
type="button" type="button"
className={`btn px-4 py-2 rounded-0 ${selected === 1? 'active btn-primary text-white' : ''}`} className={`btn px-4 py-2 rounded-0 ${selected === 1? 'active btn-secondary text-white' : ''}`}
onClick={() => setSelected(1)} onClick={() => setSelected(1)}
> >
Quaterly Quaterly
</button> </button>
<button <button
type="button" type="button"
className={`btn px-4 py-2 rounded-0 ${selected === 2 ? 'active btn-primary text-white' : ''}`} className={`btn px-4 py-2 rounded-0 ${selected === 2 ? 'active btn-secondary text-white' : ''}`}
onClick={() => setSelected(2)} onClick={() => setSelected(2)}
> >
Half-Yearly Half-Yearly
@ -31,12 +32,13 @@ useEffect(()=>{
<button <button
type="button" type="button"
className={`btn px-4 py-2 rounded-0 ${selected === 3 ? 'active btn-primary text-white' : ''}`} className={`btn px-4 py-2 rounded-0 ${selected === 3 ? 'active btn-secondary text-white' : ''}`}
onClick={() => setSelected(3)} onClick={() => setSelected(3)}
> >
Yearly Yearly
</button> </button>
</div> </div>
</div>
); );
}; };

View File

@ -1,80 +1,199 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { useSubscriptionPlan } from "../../hooks/useTenant"; import { useSubscriptionPlan } from "../../hooks/useTenant";
import SegmentedControl from "./SegmentedControl"; import SegmentedControl from "./SegmentedControl";
import { useFormContext } from "react-hook-form";
import { CONSTANT_TEXT } from "../../utils/constants";
import Label from "../common/Label";
const SubScription = ({ onSubmitSubScription }) => {
const [frequency, setFrequency] = useState(2);
const [selectedPlanId, setSelectedPlanId] = useState(null);
const {
data: plans = [],
isError,
isLoading,
} = useSubscriptionPlan(frequency);
const {
register,
setValue,
getValues,
trigger,
formState: { errors },
} = useFormContext();
const handleSubscriptionSubmit = async () => {
const isValid = await trigger([
"planId",
"currencyId",
"maxUsers",
"frequency",
"isTrial",
"autoRenew",
]);
if (isValid) {
const payload = getValues();
onSubmitSubScription(payload);
}
};
const handlePlanSelection = (plan) => {
setSelectedPlanId(plan.id);
setValue("planId", plan.id);
setValue("currencyId", plan.currency?.id);
setValue("frequency", frequency);
};
const selectedPlan = plans.find((p) => p.id === selectedPlanId);
const SubScription = () => {
const [Frequency, setFrequency] = useState(3);
const[selectedPlanId,setSelectedPlanId] = useState()
const { data, isError, isLoading } = useSubscriptionPlan(Frequency);
console.log(Frequency);
return ( return (
<div className="text-start"> <div className="text-start">
<SegmentedControl setFrequency={setFrequency} /> <SegmentedControl setFrequency={setFrequency} />
{!isLoading && !isError && data.length > 0 && ( {!isLoading && !isError && plans.length > 0 && (
<> <div className="row g-4 my-6">
<div className="row g-1 my-1"> {plans.map((plan) => {
{data.map((plan) => ( const isSelected = plan.id === selectedPlanId;
<div key={plan.id} className="col-md-6">
return (
<div key={plan.id} className="col-md-4">
<div <div
className={`card h-100 shadow-sm cursor-pointer ${ className={`card h-100 shadow-none border-1 cursor-pointer ${
selectedPlanId === plan.id ? "border-primary border-2" : "" isSelected ? "border-primary border-2" : ""
}`} }`}
onClick={() => setSelectedPlanId(plan.id)} onClick={() => handlePlanSelection(plan)}
style={{ cursor: "pointer" }}
> >
<div className="card-body d-flex flex-column"> <div className="card-body d-flex flex-column p-3">
<div className="d-flex align-items-center gap-3 mb-3"> <div className="d-flex align-items-center gap-3 mb-3">
<i <i className="bx bxs-package text-primary fs-1"></i>
className="bx bxs-package"
style={{ fontSize: "40px", color: "#6366f1" }}
></i>
<div> <div>
<h5 className="card-title fw-bold mb-1"> <p className="card-title fs-4 fw-bold mb-1">
{plan.planName} {plan.planName}
</h5> </p>
<p className="text-muted mb-0">{plan.description}</p> <p className="text-muted mb-0">{plan.description}</p>
</div> </div>
</div> </div>
<h2 className="fw-bold mt-auto mb-3"> <h4 className="fw-semibold mt-auto mb-3">
{plan.currency?.symbol} {plan.currency?.symbol} {plan.price}
{plan.price} </h4>
{/* <small className="fs-6 fw-normal">
/{selectedBilling.label.toLowerCase()}
</small> */}
</h2>
<ul className="list-unstyled mb-4"> <ul className="list-unstyled d-flex gap-4 flex-wrap mb-2">
<li className="mb-2"> <li className="d-flex align-items-center">
<i className="bx bx-check text-success me-2"></i> <i className="bx bx-check-double text-success me-1"></i>
Max Users: {plan.maxUser} Max Users {plan.maxUser}
</li> </li>
<li className="mb-2"> <li className="d-flex align-items-center">
<i className="bx bx-check text-success me-2"></i> <i className="bx bx-check-double text-success me-2"></i>
Storage: {plan.maxStorage} MB Storage {plan.maxStorage} MB
</li> </li>
<li className="mb-2"> <li className="d-flex align-items-center">
<i className="bx bx-check text-success me-2"></i> <i className="bx bx-check-double text-success me-2"></i>
Trial Days: {plan.trialDays} Trial Days {plan.trialDays}
</li> </li>
</ul> </ul>
<div>
<div className="divider my-3">
<div className="divider-text card-text text-uppercase text-muted small">
Features
</div>
</div>
{Object.entries(plan.features.modules)
.filter(([key]) => key !== "id")
.map(([key, mod]) => (
<div
key={key}
className="mb-2 d-flex align-items-center"
>
<i
className={`bx bx-check bx-xs rounded-circle text-white ${
mod.enabled ? "bg-success" : "bg-secondary"
}`}
></i>
<small className="ms-2">{mod.name}</small>
</div>
))}
</div>
<button <button
className={`btn mt-auto ${ className={`btn mt-3 ${
selectedPlanId === plan.id isSelected ? "btn-primary" : "btn-outline-primary"
? "btn-primary"
: "btn-outline-primary"
}`} }`}
onClick={() => setSelectedPlanId(plan.id)} onClick={() => handlePlanSelection(plan)}
> >
{selectedPlanId === plan.id ? "Selected" : "Get Plan"} {isSelected ? "Selected" : "Select Plan"}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
))} );
})}
{/* Form Inputs */}
<div className="row g-2 mt-3">
<div className="col-sm-4">
<Label htmlFor="maxUsers" required> Team Size</Label>
<input
type="number"
className="form-control form-control-sm"
{...register("maxUsers", {
valueAsNumber: true,
min: { value: 1, message: "Team size must be at least 1" },
})}
/>
</div>
<div className="col-12">
<div className="d-flex justify-content-between">
<label className="form-label fw-semibold d-block">
Enable auto renew
</label>
<label className="switch switch-square switch-sm">
<input
type="checkbox"
className="switch-input"
{...register("autoRenew")}
/>
<span className="switch-toggle-slider">
<span className="switch-on">
<i className="icon-base bx bx-check"></i>
</span>
<span className="switch-off">
<i className="icon-base bx bx-x"></i>
</span>
</span>
</label>
</div>
<small className="text-secondary text-tiny">
{CONSTANT_TEXT.RenewsubscriptionLabel}
</small>
</div>
</div> </div>
</>
{Object.keys(errors).length > 0 && (
<div class="alert alert-danger" role="alert">
{Object.entries(errors).map(([key, error]) => (
<div key={key} className="danger-text">
{error?.message}
</div>
))}
</div>
)}
<div className="d-flex text-center mt-4">
<button
onClick={handleSubscriptionSubmit}
className="btn btn-primary"
type="button"
>
Submit
</button>
</div>
</div>
)} )}
</div> </div>
); );

View File

@ -46,7 +46,7 @@ const TenantForm = () => {
const onSubmitTenant = (data) => { const onSubmitTenant = (data) => {
console.log(data); console.log(data);
}; };
const onSubmitSubScription = () => { const onSubmitSubScription = (data) => {
console.log(data); console.log(data);
}; };
@ -67,13 +67,13 @@ const TenantForm = () => {
name: "SubScription", name: "SubScription",
icon: "bx bx-star bx-md", icon: "bx bx-star bx-md",
subtitle: "Select a plan", subtitle: "Select a plan",
component: <SubScription />, component: <SubScription onSubmitSubScription={onSubmitSubScription} />,
}, },
]; ];
const isSubscriptionTab = activeTab === 2; const isSubscriptionTab = activeTab === 2;
return ( return (
<div id="wizard-property-listing" className="bs-stepper vertical mt-2"> <div id="wizard-property-listing" className="bs-stepper horizontically mt-2">
<div className="bs-stepper-header border-end text-start "> <div className="bs-stepper-header border-end text-start ">
{newTenantConfig.map((step, index) => { {newTenantConfig.map((step, index) => {
const isActive = activeTab === index; const isActive = activeTab === index;
@ -113,7 +113,7 @@ const TenantForm = () => {
})} })}
</div> </div>
<div className="bs-stepper-content"> <div className="bs-stepper-content py-2">
{isSubscriptionTab ? ( {isSubscriptionTab ? (
<FormProvider {...subscriptionForm}> <FormProvider {...subscriptionForm}>
<form onSubmit={subscriptionForm.handleSubmit(onSubmitTenant)}> <form onSubmit={subscriptionForm.handleSubmit(onSubmitTenant)}>

View File

@ -41,12 +41,12 @@ export const tenantDefaultValues = {
export const subscriptionSchema = z.object({ export const subscriptionSchema = z.object({
tenantId: z.string().uuid("Invalid tenant ID"), // tenantId: z.string().uuid("Invalid tenant ID"),
planId: z.string().uuid("Invalid plan ID"), planId: z.string().min(1,{message:"Please select Plan"}),
currencyId: z.string().uuid("Invalid currency ID"), currencyId: z.string().uuid("Invalid currency"),
maxUsers: z maxUsers: z
.number({ invalid_type_error: "Max users must be a number" }) .number({ invalid_type_error: " Must be a number" })
.min(1, "At least one user is required"), .min(1, "Team size is required and must be greater than zero"),
frequency: z frequency: z
.number({ invalid_type_error: "Frequency must be a number" }) .number({ invalid_type_error: "Frequency must be a number" })
.min(1, "Frequency must be at least 1"), .min(1, "Frequency must be at least 1"),
@ -55,9 +55,9 @@ export const subscriptionSchema = z.object({
}); });
export const subscriptionDefaultValues = { export const subscriptionDefaultValues = {
tenantId: "", // should be a UUID // tenantId: "",
planId: "", // should be a UUID planId: "",
currencyId: "", // should be a UUID currencyId: "",
maxUsers: 1, maxUsers: 1,
frequency: 1, frequency: 1,
isTrial: false, isTrial: false,

View File

@ -41,5 +41,12 @@ export const DIRECTORY_MANAGER = "62668630-13ce-4f52-a0f0-db38af2230c5"
export const DIRECTORY_USER = "0f919170-92d4-4337-abd3-49b66fc871bb" export const DIRECTORY_USER = "0f919170-92d4-4337-abd3-49b66fc871bb"
export const BASIC_PLAN = "08ddd43e-ca62-4cf4-8d7b-569a364307f4"
export const BASE_URL = process.env.VITE_BASE_URL; export const BASE_URL = process.env.VITE_BASE_URL;
// export const BASE_URL = "https://api.marcoaiot.com"; // export const BASE_URL = "https://api.marcoaiot.com";
export const CONSTANT_TEXT = {
RenewsubscriptionLabel : " This option; if checked, will renew your productive subscription, if the current plan expires. However, this might prevent you from"
}