diff --git a/src/components/Tenanat/ContactInfro.jsx b/src/components/Tenanat/ContactInfro.jsx
new file mode 100644
index 00000000..34da10c3
--- /dev/null
+++ b/src/components/Tenanat/ContactInfro.jsx
@@ -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 (
+
+
+
+
+ {errors.firstName && (
+
{errors.firstName.message}
+ )}
+
+
+
+
+ {errors.lastName && (
+
{errors.lastName.message}
+ )}
+
+
+
+
+ {errors.email && (
+
{errors.email.message}
+ )}
+
+
+
+
+ {errors.contactNumber && (
+
{errors.contactNumber.message}
+ )}
+
+
+
+
+ {errors.billingAddress && (
+
{errors.billingAddress.message}
+ )}
+
+
+
+
+
+ );
+};
+
+export default ContactInfro;
diff --git a/src/components/Tenanat/OrganizationInfo.jsx b/src/components/Tenanat/OrganizationInfo.jsx
new file mode 100644
index 00000000..b332d3a0
--- /dev/null
+++ b/src/components/Tenanat/OrganizationInfo.jsx
@@ -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 (
+
+
+
+
+
+ {errors.organizationName && (
+
+ {errors.organizationName.message}
+
+ )}
+
+
+
+
+
+ {errors.officeNumber && (
+
{errors.officeNumber.message}
+ )}
+
+
+
+
+
+ {errors.domainName && (
+
{errors.domainName.message}
+ )}
+
+
+
+
+
+ {errors.taxId && (
+
{errors.taxId.message}
+ )}
+
+
+
+
+ {/* this will upcomming from main */}
+ {/*
+{errors.onBoardingDate && (
+
{errors.onBoardingDate.message}
+)} */}
+
+
+
+ {errors.onBoardingDate && (
+
+ {errors.onBoardingDate.message}
+
+ )}
+
+
+
+
+
+ {errors.organizationSize && (
+
+ {errors.organizationSize.message}
+
+ )}
+
+
+
+
+
(
+
+ )}
+ />
+ {errors.industryId && (
+ {errors.industryId.message}
+ )}
+
+
+
+
+
+ {errors.reference && (
+
{errors.reference.message}
+ )}
+
+
+
+
+
+ {errors.description && (
+
{errors.description.message}
+ )}
+
+
+
+
+
+ {errors.logoImage && (
+
{errors.logoImage.message}
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+export default OrganizationInfo;
diff --git a/src/components/Tenanat/SubScription.jsx b/src/components/Tenanat/SubScription.jsx
new file mode 100644
index 00000000..287e38de
--- /dev/null
+++ b/src/components/Tenanat/SubScription.jsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+const SubScription = () => {
+ return (
+
+
+ {newTenantConfig.map((step, index) => {
+ const isActive = activeTab === index;
+ const isCompleted = completedTabs.includes(index);
+
+ return (
+
+
+
+
+ {index < newTenantConfig.length - 1 && (
+
+ )}
+
+ );
+ })}
+
+
+
+ {isSubscriptionTab ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+ );
+};
+
+export default TenantForm;
diff --git a/src/components/Tenanat/TenantSchema.js b/src/components/Tenanat/TenantSchema.js
new file mode 100644
index 00000000..14a01eae
--- /dev/null
+++ b/src/components/Tenanat/TenantSchema.js
@@ -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] || [];
+};
diff --git a/src/components/Tenanat/TenantsList.jsx b/src/components/Tenanat/TenantsList.jsx
new file mode 100644
index 00000000..89782fd6
--- /dev/null
+++ b/src/components/Tenanat/TenantsList.jsx
@@ -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 (
+