Creating a Component for Tenant.
This commit is contained in:
parent
74f532799a
commit
c672e0cea0
333
src/components/Tenant/CreateTenant.jsx
Normal file
333
src/components/Tenant/CreateTenant.jsx
Normal 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;
|
||||||
210
src/components/Tenant/Tenant.jsx
Normal file
210
src/components/Tenant/Tenant.jsx
Normal 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;
|
||||||
114
src/components/Tenant/TenantSubscription.jsx
Normal file
114
src/components/Tenant/TenantSubscription.jsx
Normal 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;
|
||||||
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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 /> },
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user