Design changes in Create Tenant form.

This commit is contained in:
Kartik Sharma 2025-08-02 16:18:26 +05:30
parent de854e87f3
commit 6c3f64bf24
5 changed files with 658 additions and 660 deletions

View File

@ -1,12 +1,12 @@
import React, { useEffect, useState, useCallback } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import Breadcrumb from "../common/Breadcrumb";
import { Modal } from "react-bootstrap";
import Breadcrumb from "../common/Breadcrumb";
import { apiTenant } from "./apiTenant";
import { useCreateTenant } from "./useTenants";
import TenantSubscription from "./TenantSubscription";
const defaultAvatar = "https://via.placeholder.com/100x100.png?text=Avatar";
const initialData = {
firstName: "",
lastName: "",
@ -24,64 +24,84 @@ const initialData = {
onBoardingDate: "",
};
const RequiredLabel = ({ label, name }) => (
<label htmlFor={name} className="form-label small mb-1">
{label} <span className="text-danger">*</span>
</label>
);
const validateForm = (form, step) => {
let errors = {};
let fieldsToValidate = [];
if (step === 1) {
fieldsToValidate = [
"firstName",
"lastName",
"email",
"phone",
"billingAddress",
];
} else if (step === 2) {
fieldsToValidate = [
"organizationName",
"organizationSize",
"industryId",
"reference",
"domainName",
"onBoardingDate",
];
}
fieldsToValidate.forEach((field) => {
if (!form?.[field] || String(form?.[field]).trim() === "") {
const fieldName = field.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
errors[field] = `${fieldName} is required.`;
}
});
if (step === 1 && form.phone && !/^[0-9]{10}$/.test(form.phone)) {
errors.phone = "Phone number must be a 10-digit number.";
}
return errors;
};
const CreateTenant = () => {
const navigate = useNavigate();
const location = useLocation();
const formData = location.state?.formData || null;
const { createTenant, updateTenant, loading, error, success } = useCreateTenant();
const { state } = useLocation();
const formData = state?.formData || null;
const { createTenant, updateTenant, loading } = useCreateTenant();
const [form, setForm] = useState(initialData);
const [formErrors, setFormErrors] = useState({});
const [imagePreview, setImagePreview] = useState(defaultAvatar);
const [imageFile, setImageFile] = useState(null);
const [showImageModal, setShowImageModal] = useState(false);
const [showImageSizeModal, setShowImageSizeModal] = useState(false);
const [industryOptions, setIndustryOptions] = useState([]);
const [step, setStep] = useState(1);
// Load form data if it's passed via location state
useEffect(() => {
if (formData) {
const { contactName, contactNumber, logoImage, ...rest } = formData;
let firstName = "";
let lastName = "";
if (contactName) {
const nameParts = contactName.trim().split(" ");
firstName = nameParts.shift() || "";
lastName = nameParts.join(" ") || "";
}
setForm({
...initialData,
...rest,
firstName,
lastName,
phone: contactNumber || "",
});
if (logoImage) {
setImagePreview(logoImage);
}
const [firstName, ...lastNameParts] = (contactName || "").trim().split(" ");
const lastName = lastNameParts.join(" ");
setForm({ ...initialData, ...rest, firstName, lastName, phone: contactNumber || "" });
if (logoImage) setImagePreview(logoImage);
}
}, [formData]);
// Load industry options from the API when the component mounts
useEffect(() => {
const fetchIndustries = async () => {
try {
const res = await apiTenant.getIndustries();
if (Array.isArray(res.data)) {
setIndustryOptions(res.data);
const { data } = await apiTenant.getIndustries();
if (Array.isArray(data)) {
setIndustryOptions(data);
if (formData?.industry) {
const matchedIndustry = res.data.find(
(industry) => industry.name === formData.industry.name
);
if (matchedIndustry) {
setForm((prev) => ({ ...prev, industryId: matchedIndustry.id }));
}
const matchedIndustry = data.find((i) => i.name === formData.industry.name);
if (matchedIndustry) setForm((prev) => ({ ...prev, industryId: matchedIndustry.id }));
}
} else {
console.error("Unexpected response format for industries", res);
}
} catch (err) {
console.error("Failed to load industries:", err);
@ -92,23 +112,28 @@ const CreateTenant = () => {
const handleChange = (e) => {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
const sanitizedValue = name === "phone" ? value.replace(/\D/g, "") : value;
setForm((prev) => ({ ...prev, [name]: sanitizedValue }));
if (formErrors?.[name]) {
setFormErrors((prev) => {
const newErrors = { ...prev };
delete newErrors?.[name];
return newErrors;
});
}
};
const handleImageChange = (e) => {
const file = e.target.files[0];
if (file) {
if (file.size <= 200 * 1024) {
setImageFile(file);
const reader = new FileReader();
reader.onloadend = () => {
setImagePreview(reader.result);
};
reader.readAsDataURL(file);
} else {
setShowImageSizeModal(true);
}
const file = e.target.files?.[0];
if (!file) return;
if (file.size > 200 * 1024) {
setShowImageSizeModal(true);
return;
}
setImageFile(file);
const reader = new FileReader();
reader.onloadend = () => setImagePreview(reader.result);
reader.readAsDataURL(file);
};
const handleImageReset = () => {
@ -118,355 +143,234 @@ const CreateTenant = () => {
const handleClearForm = () => {
setForm(initialData);
setImagePreview(defaultAvatar);
setImageFile(null);
setFormErrors({});
handleImageReset();
setStep(1);
};
const handleNext = () => {
const errors = validateForm(form, step);
if (Object.keys(errors).length > 0) {
setFormErrors(errors);
return;
}
setFormErrors({});
setStep((prev) => prev + 1);
};
const handleSubmit = useCallback(
async (e) => {
e.preventDefault();
// Determine the image to send to the API
// If there's a new file selected (imageFile), use it.
// If the image was reset (imagePreview is defaultAvatar), send null.
// Otherwise, use the existing logo image (imagePreview).
const finalLogoImage = imageFile
? imageFile
: imagePreview === defaultAvatar
? null
: imagePreview;
const errors = validateForm(form, 2);
if (Object.keys(errors).length > 0) {
setFormErrors(errors);
setStep(2);
return;
}
setFormErrors({});
const finalLogoImage = imageFile || (imagePreview === defaultAvatar ? null : imagePreview);
const submissionData = {
...form,
logoImage: finalLogoImage,
contactNumber: form.phone,
contactName: `${form.firstName} ${form.lastName}`.trim(),
};
let result;
if (formData?.id) {
result = await updateTenant(formData.id, submissionData);
if (result) {
alert("Tenant updated successfully!");
navigate("/tenant/profile", { state: { newTenant: result } });
} else {
alert("Failed to update tenant. Please check the form and try again.");
}
if (result) navigate("/tenant/profile", { state: { newTenant: result } });
} else {
result = await createTenant(submissionData);
if (result) {
alert("Tenant created successfully!");
navigate("/tenant/profile/subscription", { state: { formData: result } });
} else {
alert("Failed to create tenant. Please check the form and try again.");
}
if (result) navigate("/tenant/profile/viewtenant", { state: { formData: result } });
}
},
[form, imagePreview, imageFile, formData, navigate, createTenant, updateTenant]
);
const RequiredLabel = ({ label }) => (
<label className="form-label small mb-1">
{label} <span className="text-danger">*</span>
</label>
);
const renderFormStep = () => {
switch (step) {
case 1:
return (
<>
<div className="col-md-6">
<RequiredLabel label="First Name" name="firstName" />
<input id="firstName" type="text" name="firstName" className="form-control form-control-sm" value={form.firstName} onChange={handleChange} required />
{formErrors?.firstName && (<p className="text-danger small mt-1">{formErrors.firstName}</p>)}
</div>
<div className="col-md-6">
<RequiredLabel label="Last Name" name="lastName" />
<input id="lastName" type="text" name="lastName" className="form-control form-control-sm" value={form.lastName} onChange={handleChange} required />
{formErrors?.lastName && (<p className="text-danger small mt-1">{formErrors.lastName}</p>)}
</div>
<div className="col-md-6">
<RequiredLabel label="Email" name="email" />
<input id="email" type="email" name="email" className="form-control form-control-sm" value={form.email} onChange={handleChange} required />
{formErrors?.email && (<p className="text-danger small mt-1">{formErrors.email}</p>)}
</div>
<div className="col-md-6">
<RequiredLabel label="Phone" name="phone" />
<input id="phone" type="text" name="phone" className="form-control form-control-sm" value={form.phone} onChange={handleChange} required maxLength="10" />
{formErrors?.phone && (<p className="text-danger small mt-1">{formErrors.phone}</p>)}
</div>
<div className="col-12">
<RequiredLabel label="Billing Address" name="billingAddress" />
<textarea id="billingAddress" name="billingAddress" className="form-control form-control-sm" rows="2" value={form.billingAddress} onChange={handleChange} required></textarea>
{formErrors?.billingAddress && (<p className="text-danger small mt-1">{formErrors.billingAddress}</p>)}
</div>
</>
);
case 2:
return (
<>
<div className="col-md-6">
<RequiredLabel label="Onboarding Date" name="onBoardingDate" />
<input id="onBoardingDate" type="date" name="onBoardingDate" className="form-control form-control-sm" value={form.onBoardingDate} onChange={handleChange} required />
{formErrors?.onBoardingDate && (<p className="text-danger small mt-1">{formErrors.onBoardingDate}</p>)}
</div>
<div className="col-md-6">
<RequiredLabel label="Organization Name" name="organizationName" />
<input id="organizationName" type="text" name="organizationName" className="form-control form-control-sm" value={form.organizationName} onChange={handleChange} required />
{formErrors?.organizationName && (<p className="text-danger small mt-1">{form.organizationName}</p>)}
</div>
<div className="col-md-4">
<RequiredLabel label="Organization Size" name="organizationSize" />
<select id="organizationSize" name="organizationSize" className="form-select form-select-sm" value={form.organizationSize} onChange={handleChange} required>
<option value="">Select</option>
<option>1-50</option>
<option>51-100</option>
<option>101-500</option>
<option>500+</option>
</select>
{formErrors?.organizationSize && (<p className="text-danger small mt-1">{formErrors.organizationSize}</p>)}
</div>
<div className="col-md-4">
<RequiredLabel label="Industry" name="industryId" />
<select id="industryId" name="industryId" className="form-select form-select-sm" value={form.industryId} onChange={handleChange} required>
<option value="">Select</option>
{industryOptions.map((industry) => (<option key={industry.id} value={industry.id}>{industry.name}</option>))}
</select>
{formErrors?.industryId && (<p className="text-danger small mt-1">{formErrors.industryId}</p>)}
</div>
<div className="col-md-4">
<RequiredLabel label="Reference" name="reference" />
<select id="reference" name="reference" className="form-select form-select-sm" value={form.reference} onChange={handleChange} required>
<option value="">Select</option>
<option>Google</option>
<option>Friend</option>
<option>Advertisement</option>
<option>Root Tenant</option>
</select>
{formErrors?.reference && (<p className="text-danger small mt-1">{formErrors.reference}</p>)}
</div>
<div className="col-md-6">
<label htmlFor="taxId" className="form-label small mb-1">Tax ID</label>
<input id="taxId" type="text" name="taxId" className="form-control form-control-sm" value={form.taxId} onChange={handleChange} />
</div>
<div className="col-md-6">
<RequiredLabel label="Domain Name" name="domainName" />
<input id="domainName" type="text" name="domainName" className="form-control form-control-sm" value={form.domainName} onChange={handleChange} required />
{formErrors?.domainName && (<p className="text-danger small mt-1">{formErrors.domainName}</p>)}
</div>
<div className="col-md-6">
<label htmlFor="mobile" className="form-label small mb-1">Landline Number</label>
<input id="mobile" type="text" name="mobile" className="form-control form-control-sm" value={form.mobile} onChange={handleChange} />
</div>
<div className="col-12">
<label htmlFor="description" className="form-label small mb-1">Description</label>
<textarea id="description" name="description" className="form-control form-control-sm" rows="2" value={form.description} onChange={handleChange}></textarea>
</div>
<div className="mb-0 text-start d-flex align-items-start gap-3 position-relative">
<div style={{ position: "relative", width: "100px", height: "100px" }}>
<img src={imagePreview} alt="Profile Preview" onClick={() => setShowImageModal(true)} style={{ width: "100px", height: "100px", objectFit: "cover", borderRadius: "8px", border: "1px solid #ccc", cursor: "pointer", }} />
<button type="button" className="btn btn-sm btn-light position-absolute" onClick={handleImageReset} style={{ top: "-10px", right: "-10px", padding: "0.25rem 0.5rem", borderRadius: "50%", boxShadow: "0 0 3px rgba(0,0,0,0.3)", }} title="Delete Photo"><i className="bx bx-trash text-danger"></i></button>
</div>
<div>
<div className="mb-2">
<input type="file" accept="image/png, image/jpeg, image/gif" onChange={handleImageChange} style={{ display: "none" }} id="upload-photo" />
<label htmlFor="upload-photo" className="btn btn-sm btn-primary me-2">Upload Logo</label>
</div>
<small className="text-muted">Allowed JPG, GIF or PNG. Max size of 200KB</small>
</div>
</div>
</>
);
case 3:
return (
<div className="col-12">
<TenantSubscription />
</div>
);
default:
return null;
}
};
const steps = [
{ label: "Contact Info", subtitle: "Provide contact details", icon: "bx-user" },
{ label: "Organization Details", subtitle: "Enter organization info", icon: "bx-buildings" },
{ label: "Subscription", subtitle: "Select a plan", icon: "bx-credit-card" },
];
return (
<div className="container py-3">
<Breadcrumb
data={[
{ label: "Home", link: "/" },
{ label: "Tenant", link: "/tenant/profile" },
{ label: formData?.id ? "Update Tenant" : "Create Tenant" },
]}
/>
<Breadcrumb data={[{ label: "Home", link: "/" }, { label: "Tenant", link: "/tenant/profile" }, { label: formData?.id ? "Update Tenant" : "Create Tenant" }]} />
<div className="card rounded-3 shadow-sm mt-3">
<div className="card-body p-3 mx-6">
<h5 className="text-start mb-3">
{formData?.id ? "Update Tenant" : "Create Tenant"}
</h5>
<form onSubmit={handleSubmit}>
<div className="row g-4 text-start">
{/* Form fields */}
<div className="col-md-6">
<RequiredLabel label="First Name" />
<input
type="text"
name="firstName"
className="form-control form-control-sm"
value={form.firstName}
onChange={handleChange}
required
/>
</div>
<div className="col-md-6">
<RequiredLabel label="Last Name" />
<input
type="text"
name="lastName"
className="form-control form-control-sm"
value={form.lastName}
onChange={handleChange}
required
/>
</div>
<div className="col-md-6">
<RequiredLabel label="Email" />
<input
type="email"
name="email"
className="form-control form-control-sm"
value={form.email}
onChange={handleChange}
required
/>
</div>
<div className="col-md-3">
<RequiredLabel label="Phone" />
<input
type="text"
name="phone"
className="form-control form-control-sm"
value={form.phone}
onChange={handleChange}
required
/>
</div>
<div className="col-md-3">
<label className="form-label small mb-1">Landline Number</label>
<input
type="text"
name="mobile"
className="form-control form-control-sm"
value={form.mobile}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<RequiredLabel label="Onboarding Date" />
<input
type="date"
name="onBoardingDate"
className="form-control form-control-sm"
value={form.onBoardingDate}
onChange={handleChange}
required
/>
</div>
<div className="col-md-6">
<RequiredLabel label="Organization Name" />
<input
type="text"
name="organizationName"
className="form-control form-control-sm"
value={form.organizationName}
onChange={handleChange}
required
/>
</div>
<div className="col-md-4">
<RequiredLabel label="Organization Size" />
<select
name="organizationSize"
className="form-select form-select-sm"
value={form.organizationSize}
onChange={handleChange}
required
>
<option value="">Select</option>
<option>1-50</option>
<option>51-100</option>
<option>101-500</option>
<option>500+</option>
</select>
</div>
<div className="col-md-4">
<RequiredLabel label="Industry" />
<select
name="industryId"
className="form-select form-select-sm"
value={form.industryId}
onChange={handleChange}
required
>
<option value="">Select</option>
{industryOptions.map((industry) => (
<option key={industry.id} value={industry.id}>
{industry.name}
</option>
))}
</select>
</div>
<div className="col-md-4">
<RequiredLabel label="Reference" />
<select
name="reference"
className="form-select form-select-sm"
value={form.reference}
onChange={handleChange}
required
>
<option value="">Select</option>
<option>Google</option>
<option>Friend</option>
<option>Advertisement</option>
<option>Root Tenant</option>
</select>
</div>
<div className="col-md-6">
<label className="form-label small mb-1">Tax ID</label>
<input
type="text"
name="taxId"
className="form-control form-control-sm"
value={form.taxId}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<RequiredLabel label="Domain Name" />
<input
type="text"
name="domainName"
className="form-control form-control-sm"
value={form.domainName}
onChange={handleChange}
required
/>
</div>
<div className="col-12">
<RequiredLabel label="Billing Address" />
<textarea
name="billingAddress"
className="form-control form-control-sm"
rows="2"
value={form.billingAddress}
onChange={handleChange}
required
></textarea>
</div>
<div className="col-12">
<label className="form-label small mb-1">Description</label>
<textarea
name="description"
className="form-control form-control-sm"
rows="2"
value={form.description}
onChange={handleChange}
></textarea>
</div>
<div className="mb-0 text-start d-flex align-items-start gap-3 position-relative">
<div style={{ position: "relative", width: "100px", height: "100px" }}>
<img
src={imagePreview}
alt="Profile Preview"
onClick={() => setShowImageModal(true)}
style={{
width: "100px",
height: "100px",
objectFit: "cover",
borderRadius: "8px",
border: "1px solid #ccc",
cursor: "pointer",
}}
/>
<div className="card-body">
<div className="d-flex justify-content-between align-items-center mb-3">
<h5 className="text-start mb-0">{formData?.id ? "Update Tenant" : "Create Tenant"}</h5>
{step !== 3 && (<button type="button" className="btn btn-sm btn-warning" onClick={handleClearForm}>Clear</button>)}
</div>
<div className="d-flex flex-column flex-md-row align-items-start">
{/* Steps Container with vertical divider */}
<div className="d-flex flex-column align-items-start me-md-4 mb-3 mb-md-0 pe-md-4 border-end">
{steps.map((s, index) => (
<div key={index} className="d-flex align-items-center position-relative py-2 py-md-3">
<button
type="button"
className="btn btn-sm btn-light position-absolute"
onClick={handleImageReset}
style={{
top: "-10px",
right: "-10px",
padding: "0.25rem 0.5rem",
borderRadius: "50%",
boxShadow: "0 0 3px rgba(0,0,0,0.3)",
}}
title="Delete Photo"
className={`btn btn-link p-0 text-start ${step >= index + 1 ? 'text-primary' : 'text-dark'}`}
style={{ cursor: step >= index + 1 ? 'pointer' : 'default', textDecoration: 'none' }}
onClick={() => setStep(index + 1)}
disabled={step < index + 1}
>
<i className="bx bx-trash text-danger"></i>
<div className="d-flex align-items-center">
<div
className={`d-flex align-items-center justify-content-center me-3 ${step === index + 1 ? 'bg-primary text-white' : 'bg-light text-secondary'} ${step > index + 1 ? 'bg-white text-primary border border-primary' : ''}`}
style={{ width: '40px', height: '40px', borderRadius: '8px' }}
>
<i className={`bx ${s.icon}`}></i>
</div>
<div className="d-flex flex-column text-start">
<span className={`small ${step === index + 1 ? 'fw-semibold' : 'text-muted'}`}>{s.label}</span>
<span className="text-muted small">{s.subtitle}</span>
</div>
</div>
</button>
</div>
<div>
<div className="mb-2">
<input
type="file"
accept="image/png, image/jpeg, image/gif"
onChange={handleImageChange}
style={{ display: "none" }}
id="upload-photo"
/>
<label htmlFor="upload-photo" className="btn btn-sm btn-primary me-2">
Upload New Photo
</label>
</div>
<small className="text-muted">Allowed JPG, GIF or PNG. Max size of 200KB</small>
))}
</div>
{/* Form Content */}
<div className="tab-content w-100 p-md-3 pt-md-0">
<form onSubmit={handleSubmit} noValidate>
<div className="row g-4 text-start">{renderFormStep()}</div>
<div className="mt-4 d-flex justify-content-end">
{step > 1 && (<button type="button" className="btn btn-sm btn-secondary me-2 px-4" onClick={() => setStep((prev) => prev - 1)}>Previous</button>)}
{step < 3 && (<button type="button" className="btn btn-sm btn-primary px-4" onClick={handleNext} disabled={loading}>Next</button>)}
{step === 3 && (<button type="submit" className="btn btn-sm btn-primary px-4" disabled={loading}>{loading ? "Saving..." : "Save & Continue"}</button>)}
</div>
</div>
</form>
</div>
<div className="mt-4 text-center">
<button
type="submit"
className="btn btn-sm btn-primary px-4"
disabled={loading}
>
{loading ? "Saving..." : formData?.id ? "Update" : "Save & Continue"}
</button>
{!formData?.id && (
<button
type="button"
className="btn btn-sm btn-warning ms-2 px-4"
onClick={handleClearForm}
>
Clear
</button>
)}
{formData?.id && (
<button
type="button"
className="btn btn-sm btn-secondary ms-2 px-4"
onClick={() => navigate("/tenant/profile")}
>
Cancel
</button>
)}
</div>
</form>
</div>
</div>
</div>
<Modal show={showImageModal} onHide={() => setShowImageModal(false)} centered size="lg">
<Modal.Body className="text-center">
<img
src={imagePreview}
alt="Preview"
style={{ width: "100%", height: "auto", borderRadius: "8px" }}
/>
<img src={imagePreview} alt="Preview" style={{ width: "100%", height: "auto", borderRadius: "8px" }} />
</Modal.Body>
</Modal>
<Modal show={showImageSizeModal} onHide={() => setShowImageSizeModal(false)} centered>
<Modal.Header closeButton>
<Modal.Title>Image Size Warning</Modal.Title>
</Modal.Header>
<Modal.Body>
The selected image file must be less than 200KB. Please choose a smaller file.
</Modal.Body>
<Modal.Footer>
<button className="btn btn-primary" onClick={() => setShowImageSizeModal(false)}>
Close
</button>
</Modal.Footer>
<Modal.Header closeButton><Modal.Title>Image Size Warning</Modal.Title></Modal.Header>
<Modal.Body>The selected image file must be less than 200KB. Please choose a smaller file.</Modal.Body>
<Modal.Footer><button className="btn btn-primary" onClick={() => setShowImageSizeModal(false)}>Close</button></Modal.Footer>
</Modal>
</div>
);

View File

@ -11,25 +11,20 @@ const Tenant = () => {
const navigate = useNavigate();
const location = useLocation();
// This useEffect syncs the fetched tenants with the local state.
useEffect(() => {
if (tenants) {
setLocalTenants(tenants);
}
}, [tenants]);
// This useEffect handles adding or updating a tenant from the location state.
useEffect(() => {
const newTenant = location.state?.newTenant;
if (newTenant) {
setLocalTenants((prev) => {
// Find if the tenant already exists by ID
const tenantIndex = prev.findIndex((t) => t.id === newTenant.id);
if (tenantIndex > -1) {
// If exists, update the tenant
return prev.map((t) => (t.id === newTenant.id ? newTenant : t));
} else {
// If not, add a new tenant with a generated ID
return [...prev, { ...newTenant, id: newTenant.id || Date.now() }];
}
});
@ -48,17 +43,14 @@ const Tenant = () => {
navigate("/tenant/profile/viewtenant", { state: { formData: tenant } });
};
// This function handles tenant deletion from the local state.
const handleDelete = (id) => {
if (window.confirm("Are you sure you want to delete this tenant?")) {
setLocalTenants((prev) => prev.filter((t) => t.id !== id));
}
};
// Utility function for case-insensitive and whitespace-insensitive search
const normalize = (str) => (str?.toLowerCase().replace(/\s+/g, "") || "");
// Filters the tenants based on the search term
const filteredTenants = localTenants.filter((tenant) => {
const term = normalize(searchTerm);
return (
@ -67,7 +59,7 @@ const Tenant = () => {
normalize(tenant.contactNumber).includes(term) ||
normalize(tenant.domainName).includes(term) ||
normalize(tenant.name).includes(term) ||
normalize(tenant.oragnizationSize).includes(term) ||
normalize(tenant.organizationSize).includes(term) ||
normalize(tenant.industry?.name).includes(term)
);
});
@ -98,18 +90,21 @@ const Tenant = () => {
</button>
</div>
<div className="table-responsive p-3 pt-3">
<table className="table text-start align-middle">
<div
className="table-responsive p-3 pt-3"
style={{ overflowX: "auto", whiteSpace: "nowrap" }}
>
<table className="table text-start align-middle mb-0" style={{ minWidth: "1000px" }}>
<thead>
<tr className="fs-6">
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Domain</th>
<th>Organization</th>
<th>Size</th>
<th>Industry</th>
<th>Actions</th>
<th className="px-4">Organization</th>
<th className="px-4">Name</th>
{/* <th className="px-4">Email</th> */}
<th className="px-4">Phone</th>
{/* <th className="px-4">Domain</th> */}
<th className="px-4">Size</th>
<th className="px-4">Industry</th>
<th className="px-4">Actions</th>
</tr>
</thead>
<tbody className="table-border-bottom-0">
@ -120,7 +115,8 @@ const Tenant = () => {
return (
<tr key={tenant.id} style={{ height: "50px" }} className="align-middle">
<td>
<td className="px-4"><i className="bx bx-building text-secondary me-2"></i>{tenant.name}</td>
<td className="px-4">
<div className={`d-flex align-items-center ${tenant.logoImage ? "gap-2" : ""}`}>
{tenant.logoImage ? (
<div
@ -152,39 +148,37 @@ const Tenant = () => {
</div>
</td>
<td><i className="bx bx-envelope text-primary me-2"></i>{tenant.email}</td>
<td><i className="bx bx-phone text-success me-2"></i>{tenant.contactNumber}</td>
<td><i className="bx bx-globe text-info me-2"></i>{tenant.domainName}</td>
<td><i className="bx bx-building text-secondary me-2"></i>{tenant.name}</td>
<td><i className="bx bx-group text-warning me-2"></i>{tenant.oragnizationSize}</td>
<td><i className="bx bx-briefcase-alt text-dark me-2"></i>{tenant.industry?.name}</td>
<td>
<div className="dropdown">
<button
className="btn btn-sm text-secondary"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i className="bx bx-dots-horizontal-rounded fs-4"></i>
</button>
<ul className="dropdown-menu">
<li>
<button className="dropdown-item" onClick={() => handleView(tenant)}>
<i className="bx bx-show me-2 text-secondary"></i> View
</button>
</li>
<li>
<button className="dropdown-item" onClick={() => handleEdit(tenant)}>
<i className="bx bx-edit-alt me-2 text-primary"></i> Edit
</button>
</li>
<li>
<button className="dropdown-item" onClick={() => handleDelete(tenant.id)}>
<i className="bx bx-trash me-2 text-danger"></i> Delete
</button>
</li>
</ul>
{/* <td className="px-4"><i className="bx bx-envelope text-primary me-2"></i>{tenant.email}</td> */}
<td className="px-4"><i className="bx bx-phone text-success me-2"></i>{tenant.contactNumber}</td>
{/* <td className="px-4"><i className="bx bx-globe text-info me-2"></i>{tenant.domainName}</td> */}
{/* <td className="px-4">
{tenant.domainName ? (
<>
<i className="bx bx-globe text-info me-2"></i>
{tenant.domainName}
</>
) : (
<span className="d-block text-center w-50">--</span>
)}
</td> */}
<td className="px-4"><i className="bx bx-group text-warning me-2"></i>{tenant.organizationSize}</td>
<td className="px-4"><i className="bx bx-briefcase-alt text-dark me-2"></i>{tenant.industry?.name}</td>
<td className="px-4">
<div className="d-flex gap-2">
<i
className="bx bx-show text-secondary fs-5 cursor-pointer"
onClick={() => handleView(tenant)}
></i>
<i
className="bx bx-edit-alt text-primary fs-5 cursor-pointer"
onClick={() => handleEdit(tenant)}
></i>
<i
className="bx bx-trash text-danger fs-5 cursor-pointer"
onClick={() => handleDelete(tenant.id)}
></i>
</div>
</td>

View File

@ -1,84 +1,310 @@
import React from "react";
import Breadcrumb from "../common/Breadcrumb"; // Adjust path if needed
import React, { useState } from "react";
// Assuming Breadcrumb is a custom component and pricingData is defined globally or imported
// For this example, we'll define pricingData and a mock Breadcrumb component
const Breadcrumb = () => (
<nav aria-label="breadcrumb">
{/* <ol className="breadcrumb">
<li className="breadcrumb-item"><a href="#">Home</a></li>
<li className="breadcrumb-item"><a href="#">Subscriptions</a></li>
<li className="breadcrumb-item active" aria-current="page">Pricing</li>
</ol> */}
</nav>
);
const plans = [
{
name: "Basic",
price: "$0",
per: "/month",
description: "A simple start for everyone",
features: [
"100 responses a month",
"Unlimited forms and surveys",
"Unlimited fields",
"Basic form creation tools",
"Up to 2 subdomains",
],
button: "Your Current Plan",
buttonClass: "btn btn-success disabled",
highlight: false,
},
{
name: "Standard",
price: "$40",
per: "/month",
description: "For small to medium businesses",
subText: "USD 480/year",
features: [
"Unlimited responses",
"Unlimited forms and surveys",
"Instagram profile page",
"Google Docs integration",
'Custom "Thank you" page',
],
button: "Upgrade",
buttonClass: "btn btn-primary",
highlight: true,
badge: "Popular",
},
{
name: "Enterprise",
price: "$80",
per: "/month",
description: "Solution for big organizations",
subText: "USD 960/year",
features: [
"PayPal payments",
"Logic Jumps",
"File upload with 5GB storage",
"Custom domain support",
"Stripe integration",
],
button: "Buy Now",
buttonClass: "btn btn-warning text-white fw-semibold shadow",
highlight: false,
},
];
const pricingData = {
"Monthly": [
{
name: "Basic",
price: "$0",
per: "/month",
description: "A simple start for everyone",
features: [
"100 responses a month",
"Unlimited forms and surveys",
"Unlimited fields",
"Basic form creation tools",
"Up to 2 subdomains",
],
button: "Your Current Plan",
buttonClass: "btn btn-success disabled",
highlight: false,
},
{
name: "Standard",
price: "$45",
per: "/month",
description: "For small to medium businesses",
subText: "Billed monthly",
features: [
"Unlimited responses",
"Unlimited forms and surveys",
"Instagram profile page",
"Google Docs integration",
'Custom "Thank you" page',
],
button: "Upgrade",
buttonClass: "btn btn-primary",
highlight: true,
badge: "Popular",
},
{
name: "Enterprise",
price: "$90",
per: "/month",
description: "Solution for big organizations",
subText: "Billed monthly",
features: [
"PayPal payments",
"Logic Jumps",
"File upload with 5GB storage",
"Custom domain support",
"Stripe integration",
],
button: "Buy Now",
buttonClass: "btn btn-warning text-white fw-semibold shadow",
highlight: false,
},
],
"Quarterly": [
{
name: "Basic",
price: "$0",
per: "/quarter",
description: "A simple start for everyone",
features: [
"100 responses a month",
"Unlimited forms and surveys",
"Unlimited fields",
"Basic form creation tools",
"Up to 2 subdomains",
],
button: "Your Current Plan",
buttonClass: "btn btn-success disabled",
highlight: false,
},
{
name: "Standard",
price: "$120",
per: "/quarter",
description: "For small to medium businesses",
subText: "Billed quarterly",
features: [
"Unlimited responses",
"Unlimited forms and surveys",
"Instagram profile page",
"Google Docs integration",
'Custom "Thank you" page',
],
button: "Upgrade",
buttonClass: "btn btn-primary",
highlight: true,
badge: "Popular",
},
{
name: "Enterprise",
price: "$240",
per: "/quarter",
description: "Solution for big organizations",
subText: "Billed quarterly",
features: [
"PayPal payments",
"Logic Jumps",
"File upload with 5GB storage",
"Custom domain support",
"Stripe integration",
],
button: "Buy Now",
buttonClass: "btn btn-warning text-white fw-semibold shadow",
highlight: false,
},
],
"Half-Yearly": [
{
name: "Basic",
price: "$0",
per: "/6 months",
description: "A simple start for everyone",
features: [
"100 responses a month",
"Unlimited forms and surveys",
"Unlimited fields",
"Basic form creation tools",
"Up to 2 subdomains",
],
button: "Your Current Plan",
buttonClass: "btn btn-success disabled",
highlight: false,
},
{
name: "Standard",
price: "$220",
per: "/6 months",
description: "For small to medium businesses",
subText: "USD 220 every 6 months",
features: [
"Unlimited responses",
"Unlimited forms and surveys",
"Instagram profile page",
"Google Docs integration",
'Custom "Thank you" page',
],
button: "Upgrade",
buttonClass: "btn btn-primary",
highlight: true,
badge: "Popular",
},
{
name: "Enterprise",
price: "$440",
per: "/6 months",
description: "Solution for big organizations",
subText: "USD 440 every 6 months",
features: [
"PayPal payments",
"Logic Jumps",
"File upload with 5GB storage",
"Custom domain support",
"Stripe integration",
],
button: "Buy Now",
buttonClass: "btn btn-warning text-white fw-semibold shadow",
highlight: false,
},
],
"Yearly": [
{
name: "Basic",
price: "$0",
per: "/year",
description: "A simple start for everyone",
features: [
"100 responses a month",
"Unlimited forms and surveys",
"Unlimited fields",
"Basic form creation tools",
"Up to 2 subdomains",
],
button: "Your Current Plan",
buttonClass: "btn btn-success disabled",
highlight: false,
},
{
name: "Standard",
price: "$400",
per: "/year",
description: "For small to medium businesses",
subText: "USD 400/year",
features: [
"Unlimited responses",
"Unlimited forms and surveys",
"Instagram profile page",
"Google Docs integration",
'Custom "Thank you" page',
],
button: "Upgrade",
buttonClass: "btn btn-primary",
highlight: true,
badge: "Popular",
},
{
name: "Enterprise",
price: "$800",
per: "/year",
description: "Solution for big organizations",
subText: "USD 800/year",
features: [
"PayPal payments",
"Logic Jumps",
"File upload with 5GB storage",
"Custom domain support",
"Stripe integration",
],
button: "Buy Now",
buttonClass: "btn btn-warning text-white fw-semibold shadow",
highlight: false,
},
],
};
const billingOptions = ["Monthly", "Quarterly", "Half-Yearly", "Yearly"];
const organizationSizes = ["1-10", "11-50", "51-200", "201-500", "501+"];
const TenantSubscription = () => {
const [selectedBillingIndex, setSelectedBillingIndex] = useState(null);
const [selectedPlanIndex, setSelectedPlanIndex] = useState(null);
const [selectedOrgSize, setSelectedOrgSize] = useState("");
const selectedBillingKey = billingOptions[selectedBillingIndex];
const plansForBilling = pricingData[selectedBillingKey];
const currentPlan = plansForBilling ? plansForBilling[selectedPlanIndex] : null;
return (
<div className="container py-1">
{/* ✅ Breadcrumb */}
<Breadcrumb
data={[
{ label: "Home", link: "/" },
{ label: "Tenant", link: "/tenant/profile" },
{ label: "Subscription" },
]}
/>
<Breadcrumb />
<div className="my-4">
<p className="mb-2">Choose your billing cycle:</p>
<div className="d-inline-flex border rounded-pill overflow-hidden shadow-sm">
{billingOptions.map((option, index) => (
<button
key={index}
type="button"
className={`btn px-4 py-2 rounded-0 ${
selectedBillingIndex === index ? "btn-primary text-white" : "btn-light"
}`}
style={{
borderRight:
index !== billingOptions.length - 1 ? "1px solid #dee2e6" : "none",
fontWeight: selectedBillingIndex === index ? "600" : "normal",
}}
onClick={() => {
setSelectedBillingIndex(index);
setSelectedPlanIndex(null);
}}
>
{option}
</button>
))}
</div>
</div>
{plansForBilling && (
<div className="my-4">
<p className="mb-2">Select a plan:</p>
<div className="d-inline-flex border rounded-pill overflow-hidden shadow-sm">
{plansForBilling.map((plan, index) => (
<button
key={index}
type="button"
className={`btn px-4 py-2 rounded-0 ${
selectedPlanIndex === index ? "btn-primary text-white" : "btn-light"
}`}
style={{
borderRight:
index !== plansForBilling.length - 1 ? "1px solid #dee2e6" : "none",
fontWeight: selectedPlanIndex === index ? "600" : "normal",
}}
onClick={() => setSelectedPlanIndex(index)}
>
{plan.name}
</button>
))}
</div>
</div>
)}
<div className="row justify-content-center mt-3">
{plans.map((plan, idx) => (
<div key={idx} className="col-md-4 mb-4">
<div className="row justify-content-start mt-3">
{currentPlan && (
<div className="col-md-6 mb-4">
<div
className={`card h-100 shadow-sm ${
plan.highlight ? "border-primary" : ""
currentPlan.highlight ? "border-primary" : ""
}`}
>
<div className="card-body text-center position-relative">
{plan.badge && (
<div className="card-body position-relative">
{currentPlan.badge && (
<span className="badge bg-primary position-absolute top-0 end-0 m-2">
{plan.badge}
{currentPlan.badge}
</span>
)}
<div className="mb-3">
@ -87,28 +313,47 @@ const TenantSubscription = () => {
style={{ fontSize: "48px", color: "#6366f1" }}
></i>
</div>
<h5 className="card-title">{plan.name}</h5>
<p className="text-muted">{plan.description}</p>
<h5 className="card-title">{currentPlan.name}</h5>
<p className="text-muted">{currentPlan.description}</p>
<h2 className="fw-bold">
{plan.price} <small className="fs-6">{plan.per}</small>
{currentPlan.price}{" "}
<small className="fs-6">{currentPlan.per}</small>
</h2>
{plan.subText && <p className="text-muted">{plan.subText}</p>}
<ul className="list-unstyled text-start mt-4 mb-4">
{plan.features.map((feat, i) => (
{currentPlan.subText && <p className="text-muted">{currentPlan.subText}</p>}
<ul className="list-unstyled mt-4 mb-4">
{currentPlan.features.map((feat, i) => (
<li key={i} className="mb-2">
<i className="bx bx-check text-success me-2"></i>
{feat}
</li>
))}
</ul>
<button className={plan.buttonClass}>{plan.button}</button>
<button className={currentPlan.buttonClass}>
{currentPlan.button}
</button>
</div>
</div>
</div>
))}
)}
</div>
{/* Moved the Organization Size dropdown to appear after the details section */}
<div className="my-4">
<label htmlFor="organizationSize" className="form-label mb-2">Organization Size</label>
<select
id="organizationSize"
className="form-select w-auto shadow-sm"
value={selectedOrgSize}
onChange={(e) => setSelectedOrgSize(e.target.value)}
>
<option value="" disabled>Select a size</option>
{organizationSizes.map((size, index) => (
<option key={index} value={size}>{size}</option>
))}
</select>
</div>
</div>
);
};
export default TenantSubscription;
export default TenantSubscription;

View File

@ -46,7 +46,7 @@ const ViewTenant = () => {
aria-controls="profile-details"
aria-selected={activeTab === "profile"}
>
<i className="bx bx-user me-1"></i>Profile Details
<i className="bx bx-user me-1"></i>Tenant Profile
</button>
</li>
<li className="nav-item" role="presentation">
@ -79,7 +79,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "190px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-user bx-xs me-2 mt-1"></i>
<i className="bx bx-user bx-xs me-2 mt-0"></i>
<strong>Contact Name</strong>
</span>
<span style={{ marginLeft: "28px" }}>:</span>
@ -90,7 +90,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "190px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-envelope bx-xs me-2 mt-1"></i>
<i className="bx bx-envelope bx-xs me-2 mt-0"></i>
<strong>Email</strong>
</span>
<span style={{ marginLeft: "84px" }}>:</span>
@ -101,7 +101,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "190px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-phone bx-xs me-2 mt-1"></i>
<i className="bx bx-phone bx-xs me-2 mt-0"></i>
<strong>Phone</strong>
</span>
<span style={{ marginLeft: "80px" }}>:</span>
@ -112,7 +112,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "190px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-globe bx-xs me-2 mt-1"></i>
<i className="bx bx-globe bx-xs me-2 mt-0"></i>
<strong>Domain</strong>
</span>
<span style={{ marginLeft: "72px" }}>:</span>
@ -123,7 +123,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "190px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-building bx-xs me-2 mt-1"></i>
<i className="bx bx-building bx-xs me-2 mt-0"></i>
<strong>Organization</strong>
</span>
<span style={{ marginLeft: "38px" }}>:</span>
@ -134,18 +134,18 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "190px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-group bx-xs me-2 mt-1"></i>
<i className="bx bx-group bx-xs me-2 mt-0"></i>
<strong>Size</strong>
</span>
<span style={{ marginLeft: "92px" }}>:</span>
</div>
<span>{tenant.oragnizationSize || "N/A"}</span>
<span>{tenant.organizationSize || "N/A"}</span>
</div>
{/* Industry */}
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "190px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-briefcase bx-xs me-2 mt-1"></i>
<i className="bx bx-briefcase bx-xs me-2 mt-0"></i>
<strong>Industry</strong>
</span>
<span style={{ marginLeft: "65px" }}>:</span>
@ -156,7 +156,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "190px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-check-circle bx-xs me-2 mt-1"></i>
<i className="bx bx-check-circle bx-xs me-2 mt-0"></i>
<strong>Status</strong>
</span>
<span style={{ marginLeft: "77px" }}>:</span>
@ -182,7 +182,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "170px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-credit-card-alt bx-xs me-2 mt-1"></i>
<i className="bx bx-credit-card-alt bx-xs me-2 mt-0"></i>
<strong>Plan</strong>
</span>
<span style={{ marginLeft: "72px" }}>:</span>
@ -193,7 +193,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "170px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-calendar bx-xs me-2 mt-1"></i>
<i className="bx bx-calendar bx-xs me-2 mt-0"></i>
<strong>Start Date</strong>
</span>
<span style={{ marginLeft: "35px" }}>:</span>
@ -204,7 +204,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "170px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-calendar-x bx-xs me-2 mt-1"></i>
<i className="bx bx-calendar-x bx-xs me-2 mt-0"></i>
<strong>End Date</strong>
</span>
<span style={{ marginLeft: "44px" }}>:</span>
@ -215,7 +215,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "170px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-check-circle bx-xs me-2 mt-1"></i>
<i className="bx bx-check-circle bx-xs me-2 mt-0"></i>
<strong>Status</strong>
</span>
<span style={{ marginLeft: "59px" }}>:</span>
@ -226,7 +226,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "170px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-chair bx-xs me-2 mt-1"></i>
<i className="bx bx-chair bx-xs me-2 mt-0"></i>
<strong>Seats</strong>
</span>
<span style={{ marginLeft: "65px" }}>:</span>
@ -237,7 +237,7 @@ const ViewTenant = () => {
<div className="d-flex mb-5 align-items-start">
<div className="d-flex" style={{ minWidth: "170px" }}>
<span className="d-flex align-items-center">
<i className="bx bx-money bx-xs me-2 mt-1"></i>
<i className="bx bx-money bx-xs me-2 mt-0"></i>
<strong>Billing</strong>
</span>
<span style={{ marginLeft: "61px" }}>:</span>

View File

@ -234,58 +234,7 @@ const EmployeeList = () => {
return (
<>
{EmpForManageRole && (
<GlobalModel
isOpen={EmpForManageRole}
closeModal={() => setEmpForManageRole(null)}
>
<ManageEmp
employeeId={EmpForManageRole}
onClosed={() => setEmpForManageRole(null)}
/>
</GlobalModel>
)}
{showModal && (
<GlobalModel
isOpen={showModal}
size="lg"
closeModal={() => setShowModal(false)}
>
<ManageEmployee
employeeId={selectedEmployeeId}
onClosed={() => setShowModal(false)}
IsAllEmployee={showAllEmployees}
/>
</GlobalModel>
)}
{IsDeleteModalOpen && (
<div
className={`modal fade ${IsDeleteModalOpen ? "show" : ""}`}
tabIndex="-1"
role="dialog"
style={{
display: IsDeleteModalOpen ? "block" : "none",
backgroundColor: IsDeleteModalOpen
? "rgba(0,0,0,0.5)"
: "transparent",
}}
aria-hidden="false"
>
<ConfirmModal
type={"delete"}
header={"Suspend Employee"}
message={"Are you sure you want delete?"}
onSubmit={suspendEmployee}
onClose={() => setIsDeleteModalOpen(false)}
loading={employeeLodaing}
paramData={selectedEmpFordelete}
/>
</div>
)}
<div className="container-fluid">
<div className="container-fluid">
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
@ -306,44 +255,10 @@ const EmployeeList = () => {
{/* Switches: All Employees + Inactive */}
<div className="d-flex flex-wrap align-items-center gap-3">
{/* All Employees Switch */}
{ViewAllEmployee && (
<div className="form-check form-switch text-start">
<input
type="checkbox"
className="form-check-input"
role="switch"
id="allEmployeesCheckbox"
checked={showAllEmployees}
onChange={handleAllEmployeesToggle}
/>
<label
className="form-check-label ms-0"
htmlFor="allEmployeesCheckbox"
>
All Employees
</label>
</div>
)}
{/* Show Inactive Employees Switch */}
{showAllEmployees && (
<div className="form-check form-switch text-start">
<input
type="checkbox"
className="form-check-input"
role="switch"
id="inactiveEmployeesCheckbox"
checked={showInactive}
onChange={(e)=> setShowInactive(e.target.checked)}
/>
<label
className="form-check-label ms-0"
htmlFor="inactiveEmployeesCheckbox"
>
Show Inactive Employees
</label>
</div>
)}
</div>
{/* Right side: Search + Export + Add Employee */}
@ -363,69 +278,9 @@ const EmployeeList = () => {
</div>
{/* Export Dropdown */}
<div className="dropdown">
<button
aria-label="Click me"
className="btn btn-sm btn-label-secondary dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i className="bx bx-export me-2 bx-sm"></i>Export
</button>
<ul className="dropdown-menu">
<li>
<a
className="dropdown-item"
href="#"
onClick={() => handleExport("print")}
>
<i className="bx bx-printer me-1"></i> Print
</a>
</li>
<li>
<a
className="dropdown-item"
href="#"
onClick={() => handleExport("csv")}
>
<i className="bx bx-file me-1"></i> CSV
</a>
</li>
<li>
<a
className="dropdown-item"
href="#"
onClick={() => handleExport("excel")}
>
<i className="bx bxs-file-export me-1"></i> Excel
</a>
</li>
<li>
<a
className="dropdown-item"
href="#"
onClick={() => handleExport("pdf")}
>
<i className="bx bxs-file-pdf me-1"></i> PDF
</a>
</li>
</ul>
</div>
{/* Add Employee Button */}
{Manage_Employee && (
<button
className="btn btn-sm btn-primary"
type="button"
onClick={() => handleEmployeeModel(null)}
>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">
Add New Employee
</span>
</button>
)}
</div>
</div>