initially setup tenant manag. setup wizard-form and properly manage data. handle two sperate form with on wizard
This commit is contained in:
parent
5a209ae24e
commit
d48b4c4ef3
107
src/components/Tenanat/ContactInfro.jsx
Normal file
107
src/components/Tenanat/ContactInfro.jsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Label from "../common/Label";
|
||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
|
const ContactInfro = ({ onNext }) => {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
control,
|
||||||
|
trigger,
|
||||||
|
formState: { errors },
|
||||||
|
} = useFormContext();
|
||||||
|
|
||||||
|
|
||||||
|
const handleNext = async () => {
|
||||||
|
const valid = await trigger([
|
||||||
|
"firstName",
|
||||||
|
"lastName",
|
||||||
|
"email",
|
||||||
|
"contactNumber",
|
||||||
|
"billingAddress",
|
||||||
|
]);
|
||||||
|
if (valid) {
|
||||||
|
onNext(); // go to next tab
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div class="row g-6">
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="firstName" required>
|
||||||
|
First Name
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
id="firstName"
|
||||||
|
type="text"
|
||||||
|
className={`form-control form-control-sm ${errors.firstName ? "is-invalid" : ""}`}
|
||||||
|
{...register("firstName")}
|
||||||
|
/>
|
||||||
|
{errors.firstName && (
|
||||||
|
<div className="invalid-feedback">{errors.firstName.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="lastName" required>
|
||||||
|
Last Name
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
id="lastName"
|
||||||
|
type="text"
|
||||||
|
className={`form-control form-control-sm ${errors.lastName ? "is-invalid" : ""}`}
|
||||||
|
{...register("lastName")}
|
||||||
|
/>
|
||||||
|
{errors.lastName && (
|
||||||
|
<div className="invalid-feedback">{errors.lastName.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="email" required>
|
||||||
|
Email
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
className={`form-control form-control-sm ${errors.email ? "is-invalid" : ""}`}
|
||||||
|
{...register("email")}
|
||||||
|
/>
|
||||||
|
{errors.email && (
|
||||||
|
<div className="invalid-feedback">{errors.email.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="contactNumber" required>
|
||||||
|
Contact Number
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
id="contactNumber"
|
||||||
|
type="text"
|
||||||
|
className={`form-control form-control-sm ${errors.contactNumber ? "is-invalid" : ""}`}
|
||||||
|
{...register("contactNumber")}
|
||||||
|
/>
|
||||||
|
{errors.contactNumber && (
|
||||||
|
<div className="invalid-feedback">{errors.contactNumber.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="col-12">
|
||||||
|
<Label htmlFor="billingAddress" required>
|
||||||
|
Billing Address
|
||||||
|
</Label>
|
||||||
|
<textarea
|
||||||
|
id="billingAddress"
|
||||||
|
className={`form-control ${errors.billingAddress ? "is-invalid" : ""}`}
|
||||||
|
{...register("billingAddress")}
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
{errors.billingAddress && (
|
||||||
|
<div className="invalid-feedback">{errors.billingAddress.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="d-flex justify-content-end mt-3">
|
||||||
|
<button type="button" className="btn btn-primary" onClick={handleNext}>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactInfro;
|
||||||
234
src/components/Tenanat/OrganizationInfo.jsx
Normal file
234
src/components/Tenanat/OrganizationInfo.jsx
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useFormContext, Controller } from "react-hook-form";
|
||||||
|
import Label from "../common/Label";
|
||||||
|
import DatePicker from "../common/DatePicker";
|
||||||
|
|
||||||
|
const OrganizationInfo = ({ onNext, onPrev }) => {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
control,
|
||||||
|
trigger,
|
||||||
|
formState: { errors },
|
||||||
|
} = useFormContext();
|
||||||
|
|
||||||
|
const handleNext = async () => {
|
||||||
|
const valid = await trigger([
|
||||||
|
"organizationName",
|
||||||
|
"officeNumber",
|
||||||
|
"domainName",
|
||||||
|
"description",
|
||||||
|
"onBoardingDate",
|
||||||
|
"organizationSize",
|
||||||
|
"taxId",
|
||||||
|
"industryId",
|
||||||
|
"reference",
|
||||||
|
"logoImage",
|
||||||
|
]);
|
||||||
|
if (valid) onNext();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="row g-6">
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="organizationName" required>
|
||||||
|
Organization Name
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
id="organizationName"
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.organizationName ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
{...register("organizationName")}
|
||||||
|
/>
|
||||||
|
{errors.organizationName && (
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
{errors.organizationName.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="officeNumber" required>
|
||||||
|
Office Number
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
id="officeNumber"
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.officeNumber ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
{...register("officeNumber")}
|
||||||
|
/>
|
||||||
|
{errors.officeNumber && (
|
||||||
|
<div className="invalid-feedback">{errors.officeNumber.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="domainName" required>
|
||||||
|
Domain Name
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
id="domainName"
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.domainName ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
{...register("domainName")}
|
||||||
|
/>
|
||||||
|
{errors.domainName && (
|
||||||
|
<div className="invalid-feedback">{errors.domainName.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="taxId" required>
|
||||||
|
Tax ID
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
id="taxId"
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.taxId ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
{...register("taxId")}
|
||||||
|
/>
|
||||||
|
{errors.taxId && (
|
||||||
|
<div className="invalid-feedback">{errors.taxId.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="onBoardingDate" required>
|
||||||
|
Onboarding Date
|
||||||
|
</Label>
|
||||||
|
{/* this will upcomming from main */}
|
||||||
|
{/* <DatePicker
|
||||||
|
name="onBoardingDate"
|
||||||
|
control={control}
|
||||||
|
placeholder="Select onboarding date"
|
||||||
|
maxDate={new Date()}
|
||||||
|
className={errors.onBoardingDate ? "is-invalid" : ""}
|
||||||
|
/>
|
||||||
|
{errors.onBoardingDate && (
|
||||||
|
<div className="invalid-feedback">{errors.onBoardingDate.message}</div>
|
||||||
|
)} */}
|
||||||
|
|
||||||
|
<label htmlFor="onBoardingDate" className="form-label">
|
||||||
|
Onboarding Date <span className="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
id="onBoardingDate"
|
||||||
|
{...register("onBoardingDate")}
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.onBoardingDate ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
{errors.onBoardingDate && (
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
{errors.onBoardingDate.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="organizationSize" required>
|
||||||
|
Organization Size
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
id="organizationSize"
|
||||||
|
type="number"
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.organizationSize ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
{...register("organizationSize")}
|
||||||
|
/>
|
||||||
|
{errors.organizationSize && (
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
{errors.organizationSize.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="industryId" required>
|
||||||
|
Industry
|
||||||
|
</Label>
|
||||||
|
<Controller
|
||||||
|
name="industryId"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<Select
|
||||||
|
{...field}
|
||||||
|
id="industryId"
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.industryId ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
options={[
|
||||||
|
{ label: "Tech", value: 1 },
|
||||||
|
{ label: "Healthcare", value: 2 },
|
||||||
|
]} // replace with your data
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{errors.industryId && (
|
||||||
|
<div className="invalid-feedback">{errors.industryId.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<Label htmlFor="reference">Reference</Label>
|
||||||
|
<input
|
||||||
|
id="reference"
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.reference ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
{...register("reference")}
|
||||||
|
/>
|
||||||
|
{errors.reference && (
|
||||||
|
<div className="invalid-feedback">{errors.reference.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-12">
|
||||||
|
<Label htmlFor="description">Description</Label>
|
||||||
|
<textarea
|
||||||
|
id="description"
|
||||||
|
rows={3}
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.description ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
{...register("description")}
|
||||||
|
/>
|
||||||
|
{errors.description && (
|
||||||
|
<div className="invalid-feedback">{errors.description.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-12">
|
||||||
|
<Label htmlFor="logoImage">Logo Image</Label>
|
||||||
|
<input
|
||||||
|
id="logoImage"
|
||||||
|
type="file"
|
||||||
|
className={`form-control form-control-sm ${
|
||||||
|
errors.logoImage ? "is-invalid" : ""
|
||||||
|
}`}
|
||||||
|
{...register("logoImage")}
|
||||||
|
/>
|
||||||
|
{errors.logoImage && (
|
||||||
|
<div className="invalid-feedback">{errors.logoImage.message}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="d-flex justify-content-between mt-3">
|
||||||
|
<button type="button" className="btn btn-secondary" onClick={onPrev}>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
<button type="button" className="btn btn-primary" onClick={handleNext}>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OrganizationInfo;
|
||||||
9
src/components/Tenanat/SubScription.jsx
Normal file
9
src/components/Tenanat/SubScription.jsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const SubScription = () => {
|
||||||
|
return (
|
||||||
|
<div>SubScription</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubScription
|
||||||
133
src/components/Tenanat/TenantForm.jsx
Normal file
133
src/components/Tenanat/TenantForm.jsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import ContactInfro from "./ContactInfro";
|
||||||
|
import SubScription from "./SubScription";
|
||||||
|
import OrganizationInfo from "./OrganizationInfo";
|
||||||
|
import { useForm, FormProvider } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import {
|
||||||
|
getStepFields,
|
||||||
|
newTenantSchema,
|
||||||
|
subscriptionDefaultValues,
|
||||||
|
subscriptionSchema,
|
||||||
|
tenantDefaultValues,
|
||||||
|
} from "./TenantSchema";
|
||||||
|
|
||||||
|
const TenantForm = () => {
|
||||||
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
|
const [completedTabs, setCompletedTabs] = useState([]);
|
||||||
|
|
||||||
|
const tenantForm = useForm({
|
||||||
|
resolver: zodResolver(newTenantSchema),
|
||||||
|
defaultValues: tenantDefaultValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
const subscriptionForm = useForm({
|
||||||
|
resolver: zodResolver(subscriptionSchema),
|
||||||
|
defaultValues: subscriptionDefaultValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCurrentTrigger = () =>
|
||||||
|
activeTab === 2 ? subscriptionForm.trigger : tenantForm.trigger;
|
||||||
|
|
||||||
|
const handleNext = async () => {
|
||||||
|
const currentStepFields = getStepFields(activeTab);
|
||||||
|
const trigger = getCurrentTrigger();
|
||||||
|
|
||||||
|
const valid = await trigger(currentStepFields);
|
||||||
|
|
||||||
|
if (valid) {
|
||||||
|
setCompletedTabs((prev) => [...new Set([...prev, activeTab])]);
|
||||||
|
setActiveTab((prev) => Math.min(prev + 1, newTenantConfig.length - 1));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmitTenant = (data) => {
|
||||||
|
console.log(data);
|
||||||
|
};
|
||||||
|
const onSubmitSubScription = () => {
|
||||||
|
console.log(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const newTenantConfig = [
|
||||||
|
{
|
||||||
|
name: "Contact Info",
|
||||||
|
icon: "bx bx-user bx-md",
|
||||||
|
subtitle: "Provide Contact Details",
|
||||||
|
component: <ContactInfro onNext={handleNext} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Organization",
|
||||||
|
icon: "bx bx-home bx-md",
|
||||||
|
subtitle: "Organization Details",
|
||||||
|
component: <OrganizationInfo onNext={handleNext} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SubScription",
|
||||||
|
icon: "bx bx-star bx-md",
|
||||||
|
subtitle: "Select a plan",
|
||||||
|
component: <SubScription />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const isSubscriptionTab = activeTab === 2;
|
||||||
|
return (
|
||||||
|
<div id="wizard-property-listing" className="bs-stepper vertical mt-2">
|
||||||
|
<div className="bs-stepper-header border-end text-start">
|
||||||
|
{newTenantConfig.map((step, index) => {
|
||||||
|
const isActive = activeTab === index;
|
||||||
|
const isCompleted = completedTabs.includes(index);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment key={step.name}>
|
||||||
|
<div
|
||||||
|
className={`step ${isActive ? "active" : ""} ${
|
||||||
|
isCompleted ? "crossed" : ""
|
||||||
|
}`}
|
||||||
|
data-target={`#step-${index}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`step-trigger ${isActive ? "active" : ""}`}
|
||||||
|
onClick={() => setActiveTab(index)}
|
||||||
|
>
|
||||||
|
<span className="bs-stepper-circle">
|
||||||
|
{isCompleted ? (
|
||||||
|
<i className="bx bx-check"></i>
|
||||||
|
) : (
|
||||||
|
<i className={step.icon}></i>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className="bs-stepper-label">
|
||||||
|
<span className="bs-stepper-title">{step.name}</span>
|
||||||
|
<span className="bs-stepper-subtitle">{step.subtitle}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{index < newTenantConfig.length - 1 && (
|
||||||
|
<div className="line"></div>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bs-stepper-content">
|
||||||
|
{isSubscriptionTab ? (
|
||||||
|
<FormProvider {...subscriptionForm}>
|
||||||
|
<form onSubmit={subscriptionForm.handleSubmit(onSubmitTenant)}>
|
||||||
|
{newTenantConfig[activeTab].component}
|
||||||
|
</form>
|
||||||
|
</FormProvider>
|
||||||
|
) : (
|
||||||
|
<FormProvider {...tenantForm}>
|
||||||
|
<form onSubmit={tenantForm.handleSubmit(onSubmitSubScription)}>
|
||||||
|
{newTenantConfig[activeTab].component}
|
||||||
|
</form>
|
||||||
|
</FormProvider>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TenantForm;
|
||||||
100
src/components/Tenanat/TenantSchema.js
Normal file
100
src/components/Tenanat/TenantSchema.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const newTenantSchema = z.object({
|
||||||
|
firstName: z.string().nonempty("First name is required"),
|
||||||
|
lastName: z.string().nonempty("Last name is required"),
|
||||||
|
email: z.string().email("Invalid email address"),
|
||||||
|
description: z.string().nonempty("Description is required"),
|
||||||
|
domainName: z.string().nonempty("Domain name is required"),
|
||||||
|
billingAddress: z.string().nonempty("Billing address is required"),
|
||||||
|
taxId: z.string().nonempty("Tax ID is required"),
|
||||||
|
logoImage: z.string().nonempty("Logo image is required"),
|
||||||
|
organizationName: z.string().nonempty("Organization name is required"),
|
||||||
|
officeNumber: z.string().nonempty("Office number is required"),
|
||||||
|
contactNumber: z.string().nonempty("Contact number is required"),
|
||||||
|
onBoardingDate: z.coerce.date({
|
||||||
|
required_error: "Onboarding date is required",
|
||||||
|
invalid_type_error: "Invalid date format",
|
||||||
|
}),
|
||||||
|
organizationSize: z.string().nonempty("Organization size is required"),
|
||||||
|
industryId: z.string().uuid("Invalid industry ID"),
|
||||||
|
reference: z.string().nonempty("Reference is required"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const tenantDefaultValues = {
|
||||||
|
firstName: "",
|
||||||
|
lastName: "",
|
||||||
|
email: "",
|
||||||
|
description: "",
|
||||||
|
domainName: "",
|
||||||
|
billingAddress: "",
|
||||||
|
taxId: "",
|
||||||
|
logoImage: "",
|
||||||
|
organizationName: "",
|
||||||
|
officeNumber: "",
|
||||||
|
contactNumber: "",
|
||||||
|
onBoardingDate: new Date(), // or `null` if you want it empty
|
||||||
|
organizationSize: "",
|
||||||
|
industryId: "", // should be a valid UUID if pre-filled
|
||||||
|
reference: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const subscriptionSchema = z.object({
|
||||||
|
tenantId: z.string().uuid("Invalid tenant ID"),
|
||||||
|
planId: z.string().uuid("Invalid plan ID"),
|
||||||
|
currencyId: z.string().uuid("Invalid currency ID"),
|
||||||
|
maxUsers: z
|
||||||
|
.number({ invalid_type_error: "Max users must be a number" })
|
||||||
|
.min(1, "At least one user is required"),
|
||||||
|
frequency: z
|
||||||
|
.number({ invalid_type_error: "Frequency must be a number" })
|
||||||
|
.min(1, "Frequency must be at least 1"),
|
||||||
|
isTrial: z.boolean(),
|
||||||
|
autoRenew: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const subscriptionDefaultValues = {
|
||||||
|
tenantId: "", // should be a UUID
|
||||||
|
planId: "", // should be a UUID
|
||||||
|
currencyId: "", // should be a UUID
|
||||||
|
maxUsers: 1,
|
||||||
|
frequency: 1,
|
||||||
|
isTrial: false,
|
||||||
|
autoRenew: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStepFields = (stepIndex) => {
|
||||||
|
const stepFieldMap = {
|
||||||
|
0: [
|
||||||
|
"firstName",
|
||||||
|
"lastName",
|
||||||
|
"email",
|
||||||
|
"contactNumber",
|
||||||
|
"billingAddress",
|
||||||
|
],
|
||||||
|
1: [
|
||||||
|
"organizationName",
|
||||||
|
"officeNumber",
|
||||||
|
"domainName",
|
||||||
|
"description",
|
||||||
|
"onBoardingDate",
|
||||||
|
"organizationSize",
|
||||||
|
"taxId",
|
||||||
|
"industryId",
|
||||||
|
"reference",
|
||||||
|
"logoImage",
|
||||||
|
],
|
||||||
|
2: [
|
||||||
|
"tenantId",
|
||||||
|
"planId",
|
||||||
|
"currencyId",
|
||||||
|
"maxUsers",
|
||||||
|
"frequency",
|
||||||
|
"isTrial",
|
||||||
|
"autoRenew",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return stepFieldMap[stepIndex] || [];
|
||||||
|
};
|
||||||
37
src/components/Tenanat/TenantsList.jsx
Normal file
37
src/components/Tenanat/TenantsList.jsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTenants } from "../../hooks/useTenant";
|
||||||
|
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
||||||
|
|
||||||
|
const TenantsList = () => {
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const { data, isLoading, isError, isInitialLoading, error } = useTenants(
|
||||||
|
ITEMS_PER_PAGE,
|
||||||
|
currentPage
|
||||||
|
);
|
||||||
|
|
||||||
|
const paginate = (page) => {
|
||||||
|
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
|
||||||
|
setCurrentPage(page);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isInitialLoading)
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Loading...</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
if (isError) return <div>{error}</div>;
|
||||||
|
return (
|
||||||
|
<div className="card p-2">
|
||||||
|
<div className="text-end">
|
||||||
|
<button className="bt btn-sm btn-primary me-2">
|
||||||
|
<span class="icon-base bx bx-pie-chart-alt me-1"></span>Create Tenant
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-datatable text-nowrap table-responsive"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TenantsList;
|
||||||
12
src/components/common/Label.jsx
Normal file
12
src/components/common/Label.jsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const Label = ({ htmlFor, children, required = false, className = "" }) => {
|
||||||
|
return (
|
||||||
|
<label htmlFor={htmlFor} className={`form-label d-block ${className}`}>
|
||||||
|
{children}
|
||||||
|
{required && <span className="text-danger ms-1">*</span>}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Label;
|
||||||
@ -73,9 +73,9 @@
|
|||||||
"link": "",
|
"link": "",
|
||||||
"submenu": [
|
"submenu": [
|
||||||
{
|
{
|
||||||
"text": "Users",
|
"text": "Tenants",
|
||||||
"available": true,
|
"available": true,
|
||||||
"link": "/employees/"
|
"link": "/tenants"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Masters",
|
"text": "Masters",
|
||||||
|
|||||||
22
src/hooks/useTenant.js
Normal file
22
src/hooks/useTenant.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { TenantRepository } from "../repositories/TenantRepository";
|
||||||
|
|
||||||
|
export const useTenants = (
|
||||||
|
pageSize,
|
||||||
|
pageNumber,
|
||||||
|
filter,
|
||||||
|
searchString = ""
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["Tenants", pageNumber, pageSize],
|
||||||
|
queryFn: async () => {
|
||||||
|
const response = await TenantRepository.getTenanatList(
|
||||||
|
pageSize,
|
||||||
|
pageNumber,
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
keepPreviousData: true,
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
});
|
||||||
|
};
|
||||||
20
src/pages/Tenant/CreateTenant.jsx
Normal file
20
src/pages/Tenant/CreateTenant.jsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Breadcrumb from '../../components/common/Breadcrumb'
|
||||||
|
import TenantForm from '../../components/Tenanat/TenantForm'
|
||||||
|
|
||||||
|
const CreateTenant = () => {
|
||||||
|
return (
|
||||||
|
<div className='container-fluid'>
|
||||||
|
<Breadcrumb
|
||||||
|
data={[
|
||||||
|
{ label: "Home", link: "/dashboard" },
|
||||||
|
{ label: "Tenant", link: '/tenants' },
|
||||||
|
{ label: "New Tenant", link: null },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<TenantForm/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateTenant
|
||||||
21
src/pages/Tenant/TenantPage.jsx
Normal file
21
src/pages/Tenant/TenantPage.jsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Breadcrumb from '../../components/common/Breadcrumb'
|
||||||
|
import TenantsList from '../../components/Tenanat/TenantsList'
|
||||||
|
|
||||||
|
const TenantPage = () => {
|
||||||
|
return (
|
||||||
|
<div className='container-fluid'>
|
||||||
|
<Breadcrumb
|
||||||
|
data={[
|
||||||
|
{ label: "Home", link: "/dashboard" },
|
||||||
|
{ label: "Tenant", link: null },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TenantsList />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TenantPage
|
||||||
10
src/repositories/TenantRepository.jsx
Normal file
10
src/repositories/TenantRepository.jsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { api } from "../utils/axiosClient";
|
||||||
|
|
||||||
|
export const TenantRepository = {
|
||||||
|
|
||||||
|
getTenanatList:(pageSize, pageNumber, filter,searchString)=>{
|
||||||
|
const payloadJsonString = JSON.stringify(filter);
|
||||||
|
return api.get(`/api/Tenant/list?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -38,6 +38,8 @@ import LegalInfoCard from "../pages/TermsAndConditions/LegalInfoCard";
|
|||||||
import ProtectedRoute from "./ProtectedRoute";
|
import ProtectedRoute from "./ProtectedRoute";
|
||||||
import Directory from "../pages/Directory/Directory";
|
import Directory from "../pages/Directory/Directory";
|
||||||
import LoginWithOtp from "../pages/authentication/LoginWithOtp";
|
import LoginWithOtp from "../pages/authentication/LoginWithOtp";
|
||||||
|
import TenantPage from "../pages/Tenant/TenantPage";
|
||||||
|
import CreateTenant from "../pages/Tenant/CreateTenant";
|
||||||
|
|
||||||
const router = createBrowserRouter(
|
const router = createBrowserRouter(
|
||||||
[
|
[
|
||||||
@ -77,6 +79,8 @@ const router = createBrowserRouter(
|
|||||||
{ path: "/activities/reports", element: <Reports /> },
|
{ path: "/activities/reports", element: <Reports /> },
|
||||||
{ path: "/gallary", element: <ImageGallary /> },
|
{ path: "/gallary", element: <ImageGallary /> },
|
||||||
{ path: "/masters", element: <MasterPage /> },
|
{ path: "/masters", element: <MasterPage /> },
|
||||||
|
{ path: "/tenants", element: <TenantPage /> },
|
||||||
|
{ path: "/tenants/new-tenant", element: <CreateTenant /> },
|
||||||
{ path: "/help/support", element: <Support /> },
|
{ path: "/help/support", element: <Support /> },
|
||||||
{ path: "/help/docs", element: <Documentation /> },
|
{ path: "/help/docs", element: <Documentation /> },
|
||||||
{ path: "/help/connect", element: <Connect /> },
|
{ path: "/help/connect", element: <Connect /> },
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user