marco.pms.web/src/pages/Tenant/TenantPage.jsx

191 lines
5.6 KiB
JavaScript

import React, {
useState,
createContext,
useEffect,
useContext,
useCallback,
useMemo,
} from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";
// ------ Components -------
import Breadcrumb from "../../components/common/Breadcrumb";
import TenantsList from "../../components/Tenant/TenantsList";
import TenantFilterPanel from "../../components/Tenant/TenantFilterPanel";
// ------ Context & Utils -------
import { useDebounce } from "../../utils/appUtils";
import { useFab } from "../../Context/FabContext";
import { setCurrentTenant } from "../../slices/globalVariablesSlice";
import { hasUserPermission } from "../../utils/authUtils";
// ------ Schema -------
import {
defaultFilterValues,
filterSchema,
} from "../../components/Tenant/TenantSchema";
// ------ Constants -------
import {
MANAGE_TENANTS,
SUPPER_TENANT,
VIEW_TENANTS,
} from "../../utils/constants";
import { useProfile } from "../../hooks/useProfile";
// ---------- Context ----------
export const TenantContext = createContext();
export const useTenantContext = () => {
const context = useContext(TenantContext);
if (!context) {
throw new Error(
"useTenantContext must be used within a TenantContext.Provider"
);
}
return context;
};
const TenantPage = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { profile } = useProfile();
// ---------- State ----------
const [searchText, setSearchText] = useState("");
const [isRefetching, setIsRefetching] = useState(false);
const [refetchFn, setRefetchFn] = useState(null);
const [filters, setFilters] = useState();
// ---------- Hooks ----------
const debouncedSearch = useDebounce(searchText, 500);
const { setOffcanvasContent, setShowTrigger } = useFab();
const isSuperTenant = hasUserPermission(SUPPER_TENANT);
const canManageTenants = hasUserPermission(MANAGE_TENANTS);
const isSelfTenant = hasUserPermission(VIEW_TENANTS);
const methods = useForm({
resolver: zodResolver(filterSchema),
defaultValues: defaultFilterValues,
});
const { reset } = methods;
const handleApplyFilters = useCallback((values) => {
setFilters(values);
}, []);
const filterPanelElement = useMemo(
() => <TenantFilterPanel onApply={handleApplyFilters} />,
[handleApplyFilters]
);
console.log(isSelfTenant)
// ---------- Fab Filter Panel ----------
useEffect(() => {
if (!isSuperTenant) return;
setShowTrigger(true);
setOffcanvasContent("Tenant Filters", filterPanelElement);
return () => {
setShowTrigger(false);
setOffcanvasContent("", null);
};
}, [isSuperTenant, filterPanelElement, profile]);
// ---------- Redirect for Self Tenant ----------
useEffect(() => {
if (!isSuperTenant && isSelfTenant) {
// Delay navigation to next tick to avoid "update during render" warning
setTimeout(() => {
navigate("/tenant/self");
}, 0);
}
}, [isSuperTenant, isSelfTenant, navigate]);
// ---------- Handlers ----------
const handleNewTenant = () => {
dispatch(setCurrentTenant(null));
navigate("/tenants/new-tenant");
};
// ---------- Context Value ----------
const contextValue = {};
return (
<TenantContext.Provider value={contextValue}>
<div className="container-fluid">
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Tenant", link: null },
]}
/>
{/* Super Tenant Actions */}
{isSuperTenant && (
<div className="card d-flex p-2">
<div className="row align-items-center">
{/* Search */}
<div className="col-6 col-md-6 col-lg-3 mb-md-0">
<input
type="search"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
className="form-control form-control-sm"
placeholder="Search Tenant"
/>
</div>
{/* Actions */}
<div className="col-6 col-md-6 col-lg-9 text-end">
<span
className="text-tiny text-muted p-1 border-0 bg-none lead mx-3 cursor-pointer"
disabled={isRefetching}
onClick={() => refetchFn && refetchFn()}
>
Refresh{" "}
<i
className={`bx bx-refresh ms-1 ${
isRefetching ? "bx-spin" : ""
}`}
></i>
</span>
<button
type="button"
title="Add New Tenant"
className="p-1 bg-primary rounded-circle cursor-pointer"
onClick={handleNewTenant}
>
<i className="bx bx-plus fs-4 text-white"></i>
</button>
</div>
</div>
</div>
)}
{/* Tenant List or Access Denied */}
{isSuperTenant ? (
<TenantsList
filters={filters}
searchText={debouncedSearch}
setIsRefetching={setIsRefetching}
setRefetchFn={setRefetchFn}
/>
) : !isSelfTenant ? (
<div className="card text-center my-4 p-2">
<i className="fa-solid fa-triangle-exclamation fs-5"></i>
<p>
Access Denied: You don't have permission to perform this action!
</p>
</div>
) : null}
</div>
</TenantContext.Provider>
);
};
export default TenantPage;