Creating a get api for tenant.

This commit is contained in:
Kartik Sharma 2025-08-04 17:58:23 +05:30
parent 6c3f64bf24
commit b96df88129
8 changed files with 287 additions and 352 deletions

View File

@ -5,6 +5,7 @@ import Breadcrumb from "../common/Breadcrumb";
import { apiTenant } from "./apiTenant";
import { useCreateTenant } from "./useTenants";
import TenantSubscription from "./TenantSubscription";
import { useQueryClient } from "@tanstack/react-query";
const defaultAvatar = "https://via.placeholder.com/100x100.png?text=Avatar";
const initialData = {
@ -22,18 +23,28 @@ const initialData = {
taxId: "",
billingAddress: "",
onBoardingDate: "",
status: "",
};
// RequiredLabel component remains the same for mandatory fields
const RequiredLabel = ({ label, name }) => (
<label htmlFor={name} className="form-label small mb-1">
{label} <span className="text-danger">*</span>
</label>
);
// Regular label for non-mandatory fields
const RegularLabel = ({ label, name }) => (
<label htmlFor={name} className="form-label small mb-1">
{label}
</label>
);
const validateForm = (form, step) => {
let errors = {};
let fieldsToValidate = [];
// Updated list of mandatory fields for each step
if (step === 1) {
fieldsToValidate = [
"firstName",
@ -45,11 +56,11 @@ const validateForm = (form, step) => {
} else if (step === 2) {
fieldsToValidate = [
"organizationName",
"onBoardingDate",
"organizationSize",
"industryId",
"reference",
"domainName",
"onBoardingDate",
"status",
];
}
@ -71,7 +82,9 @@ const CreateTenant = () => {
const navigate = useNavigate();
const { state } = useLocation();
const formData = state?.formData || null;
const { createTenant, updateTenant, loading } = useCreateTenant();
// const { createTenant, updateTenant, loading } = useCreateTenant();
const { mutate: createTenant, isPending } = useCreateTenant();
const queryClient = useQueryClient();
const [form, setForm] = useState(initialData);
const [formErrors, setFormErrors] = useState({});
@ -169,7 +182,7 @@ const CreateTenant = () => {
}
setFormErrors({});
const finalLogoImage = imageFile || (imagePreview === defaultAvatar ? null : imagePreview);
const submissionData = {
const tenantPayload = {
...form,
logoImage: finalLogoImage,
contactNumber: form.phone,
@ -177,14 +190,15 @@ const CreateTenant = () => {
};
let result;
if (formData?.id) {
result = await updateTenant(formData.id, submissionData);
// result = await updateTenant(formData.id, tenantPayload);
if (result) navigate("/tenant/profile", { state: { newTenant: result } });
} else {
result = await createTenant(submissionData);
if (result) navigate("/tenant/profile/viewtenant", { state: { formData: result } });
// result = await createTenant(submissionData);
// if (result) navigate("/tenant/profile/viewtenant", { state: { formData: result } });
createTenant(tenantPayload);
}
},
[form, imagePreview, imageFile, formData, navigate, createTenant, updateTenant]
[form, imagePreview, imageFile, formData, navigate, createTenant]
);
const renderFormStep = () => {
@ -208,7 +222,7 @@ const CreateTenant = () => {
{formErrors?.email && (<p className="text-danger small mt-1">{formErrors.email}</p>)}
</div>
<div className="col-md-6">
<RequiredLabel label="Phone" name="phone" />
<RequiredLabel label="Contact Number" 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>
@ -230,7 +244,7 @@ const CreateTenant = () => {
<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>)}
{formErrors?.organizationName && (<p className="text-danger small mt-1">{formErrors.organizationName}</p>)}
</div>
<div className="col-md-4">
<RequiredLabel label="Organization Size" name="organizationSize" />
@ -263,21 +277,34 @@ const CreateTenant = () => {
{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} />
<RequiredLabel label="Status" name="status" />
<select id="status" name="status" className="form-select form-select-sm" value={form.status} onChange={handleChange} required>
<option value="">Select</option>
<option value="Active">Active</option>
<option value="Inactive">Inactive</option>
</select>
{formErrors?.status && (<p className="text-danger small mt-1">{formErrors.status}</p>)}
</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 />
<RegularLabel label="Tax ID" name="taxId" />
<input id="taxId" type="text" name="taxId" className="form-control form-control-sm" value={form.taxId} onChange={handleChange} />
{formErrors?.taxId && (<p className="text-danger small mt-1">{formErrors.taxId}</p>)}
</div>
<div className="col-md-6">
<RegularLabel label="Domain Name" name="domainName" />
<input id="domainName" type="text" name="domainName" className="form-control form-control-sm" value={form.domainName} onChange={handleChange} />
{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>
<RegularLabel label="Landline Number" name="mobile" />
<input id="mobile" type="text" name="mobile" className="form-control form-control-sm" value={form.mobile} onChange={handleChange} />
{formErrors?.mobile && (<p className="text-danger small mt-1">{formErrors.mobile}</p>)}
</div>
<div className="col-12">
<label htmlFor="description" className="form-label small mb-1">Description</label>
<RegularLabel label="Description" name="description" />
<textarea id="description" name="description" className="form-control form-control-sm" rows="2" value={form.description} onChange={handleChange}></textarea>
{formErrors?.description && (<p className="text-danger small mt-1">{formErrors.description}</p>)}
</div>
<div className="mb-0 text-start d-flex align-items-start gap-3 position-relative">
<div style={{ position: "relative", width: "100px", height: "100px" }}>
@ -354,8 +381,8 @@ const CreateTenant = () => {
<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>)}
{step < 3 && (<button type="button" className="btn btn-sm btn-primary px-4" onClick={handleNext} disabled={isPending}>Next</button>)}
{step === 3 && (<button type="submit" className="btn btn-sm btn-primary px-4" disabled={isPending}>{isPending ? "Saving..." : "Save & Continue"}</button>)}
</div>
</form>
</div>

View File

@ -3,6 +3,7 @@ import { useNavigate, useLocation } from "react-router-dom";
import Breadcrumb from "../common/Breadcrumb";
import { useTenants } from "./useTenants";
import Avatar from "../common/Avatar";
import TenantSkeleton from "./TenantSkeleton";
const Tenant = () => {
const { tenants, loading, error } = useTenants();
@ -65,7 +66,7 @@ const Tenant = () => {
});
if (loading) {
return <div className="container mt-4">Loading tenants...</div>;
return <TenantSkeleton />;
}
if (error) {
@ -90,19 +91,14 @@ const Tenant = () => {
</button>
</div>
<div
className="table-responsive p-3 pt-3"
style={{ overflowX: "auto", whiteSpace: "nowrap" }}
>
<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 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">User</th>
<th className="px-4">Industry</th>
<th className="px-4">Actions</th>
</tr>
@ -147,22 +143,7 @@ const Tenant = () => {
<span className="ms-0">{tenant.contactName}</span>
</div>
</td>
{/* <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">
@ -172,7 +153,7 @@ const Tenant = () => {
onClick={() => handleView(tenant)}
></i>
<i
className="bx bx-edit-alt text-primary fs-5 cursor-pointer"
className="bx bx-edit text-primary fs-5 cursor-pointer"
onClick={() => handleEdit(tenant)}
></i>
<i
@ -181,7 +162,6 @@ const Tenant = () => {
></i>
</div>
</td>
</tr>
);
})}

View File

@ -0,0 +1,59 @@
import React from "react";
const SkeletonRow = () => (
<tr className="align-middle" style={{ height: "50px" }}>
{Array.from({ length: 5 }).map((_, idx) => (
<td className="px-4" key={idx}>
<div className="placeholder-glow w-100">
<span className="placeholder col-8" style={{ height: "20px", display: "inline-block" }}></span>
</div>
</td>
))}
<td className="px-4">
<div className="d-flex gap-2">
{Array.from({ length: 3 }).map((_, idx) => (
<span key={idx} className="placeholder rounded" style={{ width: "24px", height: "24px" }}></span>
))}
</div>
</td>
</tr>
);
const TenantSkeleton = ({ rows = 6 }) => {
return (
<div className="container">
<div className="card mt-3">
<div className="d-flex justify-content-between align-items-center p-3 pb-0">
<div className="placeholder-glow w-25">
<span className="placeholder col-12" style={{ height: "38px", display: "inline-block" }}></span>
</div>
<div className="placeholder-glow" style={{ width: "150px" }}>
<span className="placeholder col-12" style={{ height: "38px", display: "inline-block" }}></span>
</div>
</div>
<div className="table-responsive p-3 pt-3">
<table className="table text-start align-middle mb-0" style={{ minWidth: "1000px" }}>
<thead>
<tr className="fs-6">
<th className="px-4">Organization</th>
<th className="px-4">Name</th>
<th className="px-4">Phone</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">
{Array.from({ length: rows }).map((_, idx) => (
<SkeletonRow key={idx} />
))}
</tbody>
</table>
</div>
</div>
</div>
);
};
export default TenantSkeleton;

View File

@ -1,247 +1,31 @@
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>
);
// import { useSubscriptionPlans } from "./useTenant"; // adjust the path if needed
import { useSubscriptionPlans } from "./useTenants";
import { useSelector } from "react-redux";
const billingOptions = [
{ label: "Monthly", frequency: 0 },
{ label: "Quarterly", frequency: 1 },
{ label: "Half-Yearly", frequency: 2 },
{ label: "Yearly", frequency: 3 },
];
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 currentTenant = useSelector((state)=> state.globalVariables.currentTenant);
console.log("karti",currentTenant)
const selectedBillingKey = billingOptions[selectedBillingIndex];
const plansForBilling = pricingData[selectedBillingKey];
const currentPlan = plansForBilling ? plansForBilling[selectedPlanIndex] : null;
const selectedBilling = billingOptions[selectedBillingIndex];
const { plans, loading, error } = useSubscriptionPlans(selectedBilling?.frequency);
const currentPlan = plans?.[selectedPlanIndex] || null;
return (
<div className="container py-1">
<Breadcrumb />
{/* Breadcrumb placeholder */}
<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">
@ -253,102 +37,98 @@ const TenantSubscription = () => {
selectedBillingIndex === index ? "btn-primary text-white" : "btn-light"
}`}
style={{
borderRight:
index !== billingOptions.length - 1 ? "1px solid #dee2e6" : "none",
borderRight: index !== billingOptions.length - 1 ? "1px solid #dee2e6" : "none",
fontWeight: selectedBillingIndex === index ? "600" : "normal",
}}
onClick={() => {
setSelectedBillingIndex(index);
setSelectedPlanIndex(null);
setSelectedPlanIndex(null);
}}
>
{option}
{option.label}
</button>
))}
</div>
</div>
{plansForBilling && (
{loading && <p>Loading plans...</p>}
{error && <p className="text-danger">{error}</p>}
{plans.length > 0 && (
<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) => (
{plans.map((plan, index) => (
<button
key={index}
key={plan.id}
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",
borderRight: index !== plans.length - 1 ? "1px solid #dee2e6" : "none",
fontWeight: selectedPlanIndex === index ? "600" : "normal",
}}
onClick={() => setSelectedPlanIndex(index)}
>
{plan.name}
{plan.planName}
</button>
))}
</div>
</div>
)}
<div className="row justify-content-start mt-3">
{currentPlan && (
{currentPlan && (
<div className="row justify-content-start mt-3">
<div className="col-md-6 mb-4">
<div
className={`card h-100 shadow-sm ${
currentPlan.highlight ? "border-primary" : ""
}`}
>
<div className="card h-100 shadow-sm">
<div className="card-body position-relative">
{currentPlan.badge && (
<span className="badge bg-primary position-absolute top-0 end-0 m-2">
{currentPlan.badge}
</span>
)}
<div className="mb-3">
<i
className="bx bxs-package"
style={{ fontSize: "48px", color: "#6366f1" }}
></i>
<i className="bx bxs-package" style={{ fontSize: "48px", color: "#6366f1" }}></i>
</div>
<h5 className="card-title">{currentPlan.name}</h5>
<h5 className="card-title">{currentPlan.planName}</h5>
<p className="text-muted">{currentPlan.description}</p>
<h2 className="fw-bold">
{currentPlan.currency?.symbol}
{currentPlan.price}{" "}
<small className="fs-6">{currentPlan.per}</small>
<small className="fs-6">/{selectedBilling?.label.toLowerCase()}</small>
</h2>
{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>
))}
<li className="mb-2">
<i className="bx bx-check text-success me-2"></i>
Max Users: {currentPlan.maxUser}
</li>
<li className="mb-2">
<i className="bx bx-check text-success me-2"></i>
Storage: {currentPlan.maxStorage} MB
</li>
<li className="mb-2">
<i className="bx bx-check text-success me-2"></i>
Trial Days: {currentPlan.trialDays}
</li>
</ul>
<button className={currentPlan.buttonClass}>
{currentPlan.button}
</button>
<button className="btn btn-primary">Choose Plan</button>
</div>
</div>
</div>
)}
</div>
{/* Moved the Organization Size dropdown to appear after the details section */}
</div>
)}
<div className="my-4">
<label htmlFor="organizationSize" className="form-label mb-2">Organization Size</label>
<select
id="organizationSize"
<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>
<option key={index} value={size}>
{size}
</option>
))}
</select>
</div>
@ -356,4 +136,4 @@ const TenantSubscription = () => {
);
};
export default TenantSubscription;
export default TenantSubscription;

View File

@ -8,4 +8,8 @@ export const apiTenant = {
createTenant: (data) => api.post("api/tenant/create", data),
getSubscriptionPlansTenant: (frequency) => api.get(`api/tenant/list/subscription-plan?frequency=${frequency}`),
createSubscriptionTenant: (data) => api.post("api/tenant/add-subscription", data),
};

View File

@ -1,5 +1,10 @@
import { useCallback, useEffect, useState } from "react";
import { apiTenant } from "./apiTenant";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useDispatch } from "react-redux";
import { setCurrentTenant } from "../../slices/globalVariablesSlice";
import showToast from "../../services/toastService";
import { useNavigate } from "react-router-dom";
export const useTenants = (page = 1, pageSize = 20) => {
const [tenants, setTenants] = useState([]);
@ -28,33 +33,106 @@ export const useTenants = (page = 1, pageSize = 20) => {
};
export const useCreateTenant = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(false);
// export const useCreateTenant = () => {
// const [loading, setLoading] = useState(false);
// const [data, setData] = useState(null);
// const [error, setError] = useState(null);
// const [success, setSuccess] = useState(false);
const createTenant = useCallback(async (tenantData) => {
setLoading(true);
setError(null);
setData(null);
setSuccess(false);
// const createTenant = useCallback(async (tenantData) => {
// setLoading(true);
// setError(null);
// setData(null);
// setSuccess(false);
try {
const res = await apiTenant.createTenant(tenantData);
setData(res.data);
setSuccess(true);
console.log("Tenant created successfully:", res.data);
// try {
// const res = await apiTenant.createTenant(tenantData);
// setData(res.data);
// setSuccess(true);
// console.log("Tenant created successfully:", res.data);
// return res.data;
// } catch (err) {
// console.error("Failed to create tenant:", err);
// setError(err);
// setSuccess(false);
// return null;
// } finally {
// setLoading(false);
// }
// }, []);
// return { createTenant, loading, data, error, success };
// };
export const useCreateTenant = ()=>{
const queryClient = useQueryClient();
const dispatch = useDispatch();
const navigate = useNavigate();
return useMutation({
mutationFn: async (tenantPayload) => {
const res = await apiTenant.createTenant(tenantPayload);
return res.data;
} catch (err) {
console.error("Failed to create tenant:", err);
setError(err);
setSuccess(false);
return null;
} finally {
setLoading(false);
}
}, []);
},
onSuccess:(data,variables)=>{
dispatch(setCurrentTenant(data));
showToast("Tenant created successfully","sucess");
navigate("/tenant/profile/viewtenant", { state: { formData: result } });
},
onError: (error) => {
showToast(error.message || "Failed to mark Tenant", "error");
},
})
}
export const createSubscriptionTenant = ()=>{
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (subscriptionPayload) => {
const res = await apiTenant.createSubscriptionTenant(subscriptionPayload);
return res.data;
},
onSuccess:(data,variables)=>{
dispatch(setCurrentTenant(data));
showToast("Tenant created successfully","sucess");
},
onError: (error) => {
showToast(error.message || "Failed to mark Tenant", "error");
},
})
}
export const useSubscriptionPlans = (frequency) => {
const [plans, setPlans] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (!frequency) return;
const fetchPlans = async () => {
setLoading(true);
setError(null);
try {
const response = await apiTenant.getSubscriptionPlansTenant(frequency);
if (response?.data) {
setPlans(response.data);
} else {
setError("Failed to fetch plans.");
}
} catch (err) {
setError(err.message || "Something went wrong.");
} finally {
setLoading(false);
}
};
fetchPlans();
}, [frequency]);
return { plans, loading, error };
};
return { createTenant, loading, data, error, success };
};

View File

@ -286,6 +286,7 @@ export const useMarkAttendance = () => {
if(variables.forWhichTab !== 3) showToast("Attendance marked successfully", "success");
},
onError: (error) => {
showToast(error.message || "Failed to mark attendance", "error");
},

View File

@ -1,9 +1,10 @@
import { createSlice } from "@reduxjs/toolkit";
import { createSlice, current } from "@reduxjs/toolkit";
const globalVariablesSlice = createSlice({
name: "globalVariables",
initialState: {
loginUser:null
loginUser:null,
currentTenant:null
},
reducers: {
setGlobalVariable: (state, action) => {
@ -13,9 +14,14 @@ const globalVariablesSlice = createSlice({
setLoginUserPermmisions: ( state, action ) =>
{
state.loginUser = action.payload
},
setCurrentTenant:(state, action)=>
{
state.currentTenant = action.payload;
localStorage.setItem("currentTenant",state.currentTenant.id);
}
},
});
export const { setGlobalVariable,setLoginUserPermmisions } = globalVariablesSlice.actions;
export const { setGlobalVariable,setLoginUserPermmisions,setCurrentTenant } = globalVariablesSlice.actions;
export default globalVariablesSlice.reducer;