Creating a Component for Tenant.

This commit is contained in:
Kartik Sharma 2025-07-31 18:05:36 +05:30
parent 74f532799a
commit c672e0cea0
5 changed files with 673 additions and 0 deletions

View File

@ -0,0 +1,333 @@
import React, { useEffect, useState } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import Breadcrumb from "../common/Breadcrumb"; // Adjust the path if needed
import { Modal } from "react-bootstrap"; // Ensure you have react-bootstrap installed
const defaultAvatar = "https://via.placeholder.com/100x100.png?text=Avatar";
const initialData = {
firstName: "",
lastName: "",
email: "",
phone: "",
domain: "",
organization: "",
description: "",
size: "",
industry: "",
reference: "",
taxId: "",
billingAddress: "",
};
const CreateTenant = () => {
const navigate = useNavigate();
const location = useLocation();
const formData = location.state?.formData || null;
const [form, setForm] = useState(initialData);
const [imagePreview, setImagePreview] = useState(defaultAvatar);
const [imageFile, setImageFile] = useState(null);
const [showImageModal, setShowImageModal] = useState(false);
useEffect(() => {
if (formData) {
setForm({ ...initialData, ...formData });
// Load profileImage preview if available
if (formData.profileImage) {
setImagePreview(formData.profileImage);
}
}
}, [formData]);
const handleChange = (e) => {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
};
const handleImageChange = (e) => {
const file = e.target.files[0];
if (file && file.size <= 800 * 1024) {
setImageFile(file);
const reader = new FileReader();
reader.onloadend = () => {
setImagePreview(reader.result);
};
reader.readAsDataURL(file);
} else {
alert("File must be JPG/PNG/GIF and less than 800KB");
}
};
const handleImageReset = () => {
setImageFile(null);
setImagePreview(defaultAvatar);
};
// const handleSubmit = (e) => {
// e.preventDefault();
// const submissionData = {
// ...form,
// profileImage: imagePreview, // Save base64/URL of image
// };
// console.log("Form submitted:", submissionData);
// navigate("/tenant/profile/subscription", { state: { formData: submissionData } });
// };
const handleSubmit = (e) => {
e.preventDefault();
const submissionData = {
...form,
profileImage: imagePreview, // Save base64/URL of image
};
if (formData?.id) {
navigate("/tenant/profile");
} else {
navigate("/tenant/profile/subscription", { state: { formData: submissionData } });
}
};
const RequiredLabel = ({ label }) => (
<label className="form-label small mb-1">
{label} <span className="text-danger">*</span>
</label>
);
return (
<div className="container py-3">
{/* ✅ Breadcrumb */}
<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}>
{/* 👤 Image Upload */}
<div className="mb-4 text-start d-flex align-items-start gap-3">
<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>
<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>
<button
type="button"
className="btn btn-sm btn-secondary"
onClick={handleImageReset}
>
Reset
</button>
</div>
<small className="text-muted">Allowed JPG, GIF or PNG. Max size of 800K</small>
</div>
</div>
{/* 🧾 Form Fields */}
<div className="row g-4 text-start">
<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-6">
<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-6">
<RequiredLabel label="Billing Address" />
<input
type="text"
name="billingAddress"
className="form-control form-control-sm"
value={form.billingAddress}
onChange={handleChange}
required
/>
</div>
<div className="col-md-6">
<RequiredLabel label="Organization" />
<input
type="text"
name="organization"
className="form-control form-control-sm"
value={form.organization}
onChange={handleChange}
required
/>
</div>
<div className="col-md-4">
<RequiredLabel label="Size" />
<select
name="size"
className="form-select form-select-sm"
value={form.size}
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="industry"
className="form-select form-select-sm"
value={form.industry}
onChange={handleChange}
required
>
<option value="">Select</option>
<option>Technology</option>
<option>Finance</option>
<option>Healthcare</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>
</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}
required
/>
</div>
<div className="col-md-6">
<label className="form-label small mb-1">Domain Name</label>
<input
type="text"
name="domain"
className="form-control form-control-sm"
value={form.domain}
onChange={handleChange}
/>
</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>
{/* 🔘 Buttons */}
<div className="mt-4 text-center">
<button type="submit" className="btn btn-sm btn-primary px-4">
{formData?.id ? "Update" : "Save & Continue"}
</button>
<button
type="button"
className="btn btn-sm btn-secondary ms-2 px-4"
onClick={() => navigate("/tenant")}
>
Cancel
</button>
</div>
</form>
</div>
</div>
{/* 🔍 Image Preview Modal */}
<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" }}
/>
</Modal.Body>
</Modal>
</div>
);
};
export default CreateTenant;

View File

@ -0,0 +1,210 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import Breadcrumb from "../common/Breadcrumb";
const Tenant = () => {
const [tenants, setTenants] = useState([
{
id: 1,
firstName: "Albert",
lastName: "Cook",
email: "albert.cook@example.com",
phone: "+1 (555) 123-4567",
organization: "Innovate Corp",
size: "101-500",
industry: "Technology",
domain: "innovate.com",
description: "Innovative solutions for businesses",
},
{
id: 2,
firstName: "Barry",
lastName: "Hunter",
email: "barry.hunter@example.com",
phone: "+1 (555) 987-6543",
organization: "Creative Solutions",
size: "51-100",
industry: "Marketing",
domain: "creatives.com",
description: "Creative marketing strategies",
},
{
id: 3,
firstName: "Sophia",
lastName: "Johnson",
email: "sophia.johnson@example.com",
phone: "+1 (555) 678-9012",
organization: "TechWorld Inc",
size: "501-1000",
industry: "IT Services",
domain: "techworld.com",
description: "Cutting-edge tech services",
},
{
id: 4,
firstName: "Daniel",
lastName: "Lee",
email: "daniel.lee@example.com",
phone: "+1 (555) 345-6789",
organization: "EduPro",
size: "101-500",
industry: "Education",
domain: "edupro.org",
description: "Smart learning platforms",
},
{
id: 5,
firstName: "Emily",
lastName: "Davis",
email: "emily.davis@example.com",
phone: "+1 (555) 765-4321",
organization: "GreenEarth Solutions",
size: "51-100",
industry: "Environmental",
domain: "greenearth.com",
description: "Eco-friendly innovations",
},
{
id: 6,
firstName: "Michael",
lastName: "Brown",
email: "michael.brown@example.com",
phone: "+1 (555) 888-1212",
organization: "FinanceLink",
size: "1000+",
industry: "Finance",
domain: "financelink.net",
description: "Reliable financial services",
},
{
id: 7,
firstName: "Olivia",
lastName: "Taylor",
email: "olivia.taylor@example.com",
phone: "+1 (555) 222-3344",
organization: "HealthPlus",
size: "201-500",
industry: "Healthcare",
domain: "healthplus.com",
description: "Comprehensive health care solutions",
},
]);
const navigate = useNavigate();
const location = useLocation();
// Handle form submission result
useEffect(() => {
const newTenant = location.state?.newTenant;
if (newTenant) {
if (newTenant.id) {
// Update existing tenant
setTenants((prev) =>
prev.map((t) => (t.id === newTenant.id ? newTenant : t))
);
} else {
// Add new tenant
setTenants((prev) => [...prev, { ...newTenant, id: Date.now() }]);
}
}
}, [location.state]);
const handleCreate = () => {
navigate("/tenant/profile/create");
};
const handleEdit = (tenant) => {
navigate("/tenant/profile/create", { state: { formData: tenant } });
};
const handleView = (tenant) => {
alert(
`Tenant Info:\n\nName: ${tenant.firstName} ${tenant.lastName}\nEmail: ${tenant.email}\nPhone: ${tenant.phone}\nOrganization: ${tenant.organization}`
);
};
const handleDelete = (id) => {
if (window.confirm("Are you sure to delete this tenant?")) {
setTenants((prev) => prev.filter((t) => t.id !== id));
}
};
return (
<div className="container">
{/* ✅ Breadcrumb added */}
<Breadcrumb data={[{ label: "Home", link: "/" }, { label: "Tenant" }]} />
<div className="card mt-3">
<div className="d-flex justify-content-end p-3 pb-0">
<button className="btn btn-sm btn-primary" onClick={handleCreate}>
<i className="bx bx-plus-circle me-2"></i> Create
</button>
</div>
<div className="table-responsive p-3">
<table className="table text-start align-middle">
<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>
</tr>
</thead>
<tbody className="table-border-bottom-0">
{tenants.map((tenant) => (
<tr key={tenant.id} style={{ height: "50px" }} className="align-middle">
<td>{tenant.firstName} {tenant.lastName}</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.phone}</td>
<td><i className="bx bx-globe text-info me-2"></i>{tenant.domain}</td>
<td><i className="bx bx-building text-secondary me-2"></i>{tenant.organization}</td>
<td><i className="bx bx-group text-warning me-2"></i>{tenant.size}</td>
<td><i className="bx bx-briefcase-alt text-dark me-2"></i>{tenant.industry}</td>
<td>
<div className="d-flex gap-2">
<button
className="btn btn-sm text-secondary p-0"
title="View"
onClick={() => handleView(tenant)}
>
<i className="bx bx-show fs-5"></i>
</button>
<button
className="btn btn-sm text-primary p-0"
title="Edit"
onClick={() => handleEdit(tenant)}
>
<i className="bx bx-edit-alt fs-5"></i>
</button>
<button
className="btn btn-sm text-danger p-0"
title="Delete"
onClick={() => handleDelete(tenant.id)}
>
<i className="bx bx-trash fs-5"></i>
</button>
</div>
</td>
</tr>
))}
{tenants.length === 0 && (
<tr>
<td colSpan="8" className="text-center py-3">
No tenants found.
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
);
};
export default Tenant;

View File

@ -0,0 +1,114 @@
import React from "react";
import Breadcrumb from "../common/Breadcrumb"; // Adjust path if needed
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 TenantSubscription = () => {
return (
<div className="container py-1">
{/* ✅ Breadcrumb */}
<Breadcrumb
data={[
{ label: "Home", link: "/" },
{ label: "Tenant", link: "/tenant/profile" },
{ label: "Subscription" },
]}
/>
<div className="row justify-content-center mt-3">
{plans.map((plan, idx) => (
<div key={idx} className="col-md-4 mb-4">
<div
className={`card h-100 shadow-sm ${
plan.highlight ? "border-primary" : ""
}`}
>
<div className="card-body text-center position-relative">
{plan.badge && (
<span className="badge bg-primary position-absolute top-0 end-0 m-2">
{plan.badge}
</span>
)}
<div className="mb-3">
<i
className="bx bxs-package"
style={{ fontSize: "48px", color: "#6366f1" }}
></i>
</div>
<h5 className="card-title">{plan.name}</h5>
<p className="text-muted">{plan.description}</p>
<h2 className="fw-bold">
{plan.price} <small className="fs-6">{plan.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) => (
<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>
</div>
</div>
</div>
))}
</div>
</div>
);
};
export default TenantSubscription;

View File

@ -81,6 +81,16 @@
"text": "Masters", "text": "Masters",
"available": true, "available": true,
"link": "/masters" "link": "/masters"
},
{
"text": "Manage Subscription",
"available": true,
"link": "/tenant/manage"
},
{
"text": "Manage Tenants",
"available": true,
"link": "/tenant/profile"
} }
] ]
}, },

View File

@ -38,6 +38,9 @@ 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 Tenant from "../components/Tenant/Tenant";
import CreateTenant from "../components/Tenant/CreateTenant";
import TenantSubscription from "../components/Tenant/TenantSubscription";
const router = createBrowserRouter( const router = createBrowserRouter(
[ [
@ -77,6 +80,9 @@ 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: "/tenant/profile", element: <Tenant /> },
{ path: "/tenant/profile/create", element: <CreateTenant /> },
{ path: "/tenant/profile/subscription", element: <TenantSubscription /> },
{ 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 /> },