added skeleton and refresh button
This commit is contained in:
parent
4bff065153
commit
25f5ff27b1
97
src/components/Tenanat/TenanatSkeleton.jsx
Normal file
97
src/components/Tenanat/TenanatSkeleton.jsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const SkeletonCell = ({ width = "100%", height = 20, style = {} }) => (
|
||||||
|
<div
|
||||||
|
className="skeleton"
|
||||||
|
style={{
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
borderRadius: 4,
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TenantTableSkeleton = ({ columns, rows = 5 }) => {
|
||||||
|
return (
|
||||||
|
<div className="card p-2 mt-3">
|
||||||
|
<div className="card-datatable text-nowrap table-responsive">
|
||||||
|
<table className="table border-top dataTable text-nowrap">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{columns.map((col) => (
|
||||||
|
<th key={col.key} className="sorting d-table-cell">
|
||||||
|
<div className={col.align}>{col.label}</div>
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{[...Array(rows)].map((_, rowIdx) => (
|
||||||
|
<tr key={rowIdx}>
|
||||||
|
{columns.map((col, colIdx) => (
|
||||||
|
<td
|
||||||
|
key={col.key || colIdx}
|
||||||
|
className={`d-table-cell px-3 py-2 align-middle ${
|
||||||
|
col.align ?? ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{/* Icon + text skeleton for first few columns */}
|
||||||
|
{col.key === "name" && (
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<div
|
||||||
|
className="me-2"
|
||||||
|
style={{
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
borderRadius: "5px",
|
||||||
|
background: "#ddd",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SkeletonCell width="120px" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{col.key === "domainName" && (
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<div
|
||||||
|
className="me-2"
|
||||||
|
style={{
|
||||||
|
width: 14,
|
||||||
|
height: 14,
|
||||||
|
borderRadius: "50%",
|
||||||
|
background: "#ddd",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SkeletonCell width="140px" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{col.key === "contactName" && (
|
||||||
|
<div className="d-flex align-items-center ">
|
||||||
|
<div
|
||||||
|
className="me-2"
|
||||||
|
style={{
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
borderRadius: "50%",
|
||||||
|
background: "#ddd",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SkeletonCell width="100px" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{col.key === "contactNumber" && (
|
||||||
|
<SkeletonCell width="100px"/>
|
||||||
|
)}
|
||||||
|
{col.key === "status" && (
|
||||||
|
<SkeletonCell width="60px" height={24} />
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,31 +1,38 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTenants } from "../../hooks/useTenant";
|
import { useTenants } from "../../hooks/useTenant";
|
||||||
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
||||||
import { getTenantStatus } from "../../utils/dateUtils";
|
import { getTenantStatus } from "../../utils/dateUtils";
|
||||||
import IconButton from "../common/IconButton";
|
import IconButton from "../common/IconButton";
|
||||||
import Pagination from "../common/Pagination";
|
import Pagination from "../common/Pagination";
|
||||||
|
import { TenantTableSkeleton } from "./TenanatSkeleton";
|
||||||
|
import { useTenantContext } from "../../pages/Tenant/TenantPage";
|
||||||
|
|
||||||
const TenantsList = ({searchText}) => {
|
const TenantsList = ({searchText,setIsRefetching, setRefetchFn}) => {
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const { data, isLoading, isError, isInitialLoading, error } = useTenants(
|
const { data, isLoading, isError, isInitialLoading, error,refetch, isFetching } = useTenants(
|
||||||
currentPage,
|
currentPage,
|
||||||
{},
|
{},
|
||||||
searchText,
|
searchText,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const {setRefetching} = useTenantContext()
|
||||||
|
|
||||||
const paginate = (page) => {
|
const paginate = (page) => {
|
||||||
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
|
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
|
||||||
setCurrentPage(page);
|
setCurrentPage(page);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isInitialLoading)
|
|
||||||
return (
|
// Pass the refetch function to parent when component mounts
|
||||||
<div>
|
useEffect(() => {
|
||||||
<p>Loading...</p>
|
setRefetchFn(() => refetch); // store in parent
|
||||||
</div>
|
}, [setRefetchFn, refetch]);
|
||||||
);
|
|
||||||
if (isError) return <div>{error.message}</div>;
|
// Sync fetching status with parent
|
||||||
|
useEffect(() => {
|
||||||
|
setIsRefetching(isFetching);
|
||||||
|
}, [isFetching, setIsRefetching]);
|
||||||
|
|
||||||
const TenantColumns = [
|
const TenantColumns = [
|
||||||
{
|
{
|
||||||
@ -60,7 +67,7 @@ const TenantsList = ({searchText}) => {
|
|||||||
key: "contactName",
|
key: "contactName",
|
||||||
label: "Contact Person",
|
label: "Contact Person",
|
||||||
getValue: (t) => (
|
getValue: (t) => (
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center text-start">
|
||||||
<i className="bx bx-sm bx-user me-1" />
|
<i className="bx bx-sm bx-user me-1" />
|
||||||
{t.contactName || "N/A"}
|
{t.contactName || "N/A"}
|
||||||
</div>
|
</div>
|
||||||
@ -86,16 +93,21 @@ const TenantsList = ({searchText}) => {
|
|||||||
{t.tenantStatus?.name || "Unknown"}
|
{t.tenantStatus?.name || "Unknown"}
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
|
align:"text-center"
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
if (isInitialLoading)
|
||||||
|
return (
|
||||||
|
<TenantTableSkeleton columns={TenantColumns} rows={13} />
|
||||||
|
);
|
||||||
|
if (isError) return <div>{error.message}</div>;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="card p-2 mt-3">
|
<div className="card p-2 mt-3">
|
||||||
<div className="card-datatable text-nowrap table-responsive">
|
<div className="card-datatable text-nowrap table-responsive">
|
||||||
<table className="table border-top dataTable text-nowrap">
|
<table className="table border-top dataTable text-nowrap">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr className="shadow-sm">
|
||||||
{TenantColumns.map((col) => (
|
{TenantColumns.map((col) => (
|
||||||
<th key={col.key} className="sorting d-table-cell">
|
<th key={col.key} className="sorting d-table-cell">
|
||||||
<div className={col.align}>{col.label}</div>
|
<div className={col.align}>{col.label}</div>
|
||||||
@ -125,7 +137,7 @@ const TenantsList = ({searchText}) => {
|
|||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
colSpan={TenantColumns.length + 1}
|
colSpan={TenantColumns.length + 1}
|
||||||
className="text-center py-4"
|
className="text-center py-4 border-0"
|
||||||
>
|
>
|
||||||
No Tenants Found
|
No Tenants Found
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, createContext, useEffect } from "react";
|
import React, { useState, createContext, useEffect, useContext } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
// ------Components-------
|
// ------Components-------
|
||||||
@ -9,10 +9,12 @@ import { useDebounce } from "../../utils/appUtils";
|
|||||||
import { useFab } from "../../Context/FabContext";
|
import { useFab } from "../../Context/FabContext";
|
||||||
|
|
||||||
//---------- Schema and defaultValues----
|
//---------- Schema and defaultValues----
|
||||||
import { defaultFilterValues, filterSchema } from "../../components/Tenanat/TenantSchema";
|
import {
|
||||||
|
defaultFilterValues,
|
||||||
|
filterSchema,
|
||||||
|
} from "../../components/Tenanat/TenantSchema";
|
||||||
import TenantFilterPanel from "../../components/Tenanat/TenantFilterPanel";
|
import TenantFilterPanel from "../../components/Tenanat/TenantFilterPanel";
|
||||||
|
|
||||||
|
|
||||||
// This is context that wrapping all components tenant releated , but must pass inside 'TenantContext.Provider'
|
// This is context that wrapping all components tenant releated , but must pass inside 'TenantContext.Provider'
|
||||||
export const TenantContext = createContext();
|
export const TenantContext = createContext();
|
||||||
export const useTenantContext = () => {
|
export const useTenantContext = () => {
|
||||||
@ -25,22 +27,20 @@ export const useTenantContext = () => {
|
|||||||
|
|
||||||
const TenantPage = () => {
|
const TenantPage = () => {
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
const [isRefetching,setRefetching] = useState(false)
|
||||||
|
const [refetchFn, setRefetchFn] = useState(null);
|
||||||
const debouncedSearch = useDebounce(searchText, 500);
|
const debouncedSearch = useDebounce(searchText, 500);
|
||||||
const contextValue = {};
|
const contextValue = {
|
||||||
|
};
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
||||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||||
|
|
||||||
|
|
||||||
// This Hook allow us to right-side bar for filter Tenants
|
// This Hook allow us to right-side bar for filter Tenants
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowTrigger(true);
|
setShowTrigger(true);
|
||||||
setOffcanvasContent(
|
setOffcanvasContent("Tenant Filters", <TenantFilterPanel />);
|
||||||
"Expense Filters",
|
|
||||||
<TenantFilterPanel/>
|
|
||||||
);
|
|
||||||
return () => {
|
return () => {
|
||||||
setShowTrigger(false);
|
setShowTrigger(false);
|
||||||
setOffcanvasContent("", null);
|
setOffcanvasContent("", null);
|
||||||
@ -63,13 +63,17 @@ const TenantPage = () => {
|
|||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
value={searchText}
|
value={searchText}
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
onChange={(e)=>setSearchText(e.target.value)}
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-6 col-md-6 col-lg-9 text-end">
|
<div className="col-6 col-md-6 col-lg-9 text-end cursor-pointer">
|
||||||
|
<span className="text-tiny text-muted p-1 border-0 bg-none lead mx-3 " disabled={isRefetching} onClick={() => refetchFn && refetchFn()}>
|
||||||
|
Refresh <i className={`bx bx-refresh ms-1 ${isRefetching ? "bx-spin":""}`}></i>
|
||||||
|
</span>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-bs-toggle="tooltip"
|
data-bs-toggle="tooltip"
|
||||||
@ -86,7 +90,9 @@ const TenantPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TenantsList searchText={debouncedSearch} />
|
<TenantsList searchText={debouncedSearch} setIsRefetching={setRefetching}
|
||||||
|
setRefetchFn={setRefetchFn}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TenantContext.Provider>
|
</TenantContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export const THRESH_HOLD = 48; // hours
|
export const THRESH_HOLD = 48; // hours
|
||||||
export const DURATION_TIME = 20; // minutes
|
export const DURATION_TIME = 10; // minutes
|
||||||
export const ITEMS_PER_PAGE = 10;
|
export const ITEMS_PER_PAGE = 20;
|
||||||
export const OTP_EXPIRY_SECONDS = 600 // OTP time
|
export const OTP_EXPIRY_SECONDS = 600 // OTP time
|
||||||
|
|
||||||
export const MANAGE_MASTER = "588a8824-f924-4955-82d8-fc51956cf323";
|
export const MANAGE_MASTER = "588a8824-f924-4955-82d8-fc51956cf323";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user