changed ui for org
This commit is contained in:
parent
1372f9870a
commit
32f16092db
@ -19,7 +19,6 @@ import ExpenseStatus from "./ExpenseStatus";
|
||||
import ExpenseByProject from "./ExpenseByProject";
|
||||
|
||||
const Dashboard = () => {
|
||||
const { projectsCardData } = useDashboardProjectsCardData();
|
||||
const { teamsCardData } = useDashboardTeamsCardData();
|
||||
const { tasksCardData } = useDashboardTasksCardData();
|
||||
|
||||
@ -32,7 +31,7 @@ const Dashboard = () => {
|
||||
<div className="row gy-4">
|
||||
{isAllProjectsSelected && (
|
||||
<div className="col-sm-6 col-lg-4">
|
||||
<Projects projectsCardData={projectsCardData} />
|
||||
<Projects />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
54
src/components/Dashboard/DashboardSkeleton.jsx
Normal file
54
src/components/Dashboard/DashboardSkeleton.jsx
Normal file
@ -0,0 +1,54 @@
|
||||
import React from "react";
|
||||
|
||||
const SkeletonLine = ({ height = 20, width = "100%", className = "" }) => (
|
||||
<div
|
||||
className={`skeleton ${className}`}
|
||||
style={{
|
||||
height,
|
||||
width,
|
||||
borderRadius: "4px",
|
||||
background: "linear-gradient(90deg, #eee, #f5f5f5, #eee)",
|
||||
backgroundSize: "200% 100%",
|
||||
animation: "skeleton-loading 1.5s infinite",
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
|
||||
const skeletonStyle = `
|
||||
@keyframes skeleton-loading {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
`;
|
||||
|
||||
export const ProjectCardSkeleton = () => {
|
||||
return (
|
||||
<>
|
||||
{/* Inject animation CSS once */}
|
||||
<style>{skeletonStyle}</style>
|
||||
|
||||
<div className="card p-3 h-100 text-center d-flex justify-content-between">
|
||||
{/* Header */}
|
||||
<div className="d-flex justify-content-start align-items-center mb-3">
|
||||
<h5 className="fw-bold mb-0 ms-2">
|
||||
<i className="rounded-circle bx bx-building-house text-primary"></i>{" "}
|
||||
Projects
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
{/* Skeleton body */}
|
||||
<div className="d-flex justify-content-around align-items-start mt-n2 w-100">
|
||||
<div className="text-center">
|
||||
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
|
||||
<SkeletonLine height={14} width="40px" className="mx-auto" />
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
|
||||
<SkeletonLine height={14} width="40px" className="mx-auto" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useDashboardProjectsCardData } from "../../hooks/useDashboard_Data";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import ProjectInfra from "../Project/ProjectInfra";
|
||||
import { ProjectCardSkeleton } from "./DashboardSkeleton";
|
||||
|
||||
const Projects = () => {
|
||||
const {
|
||||
@ -23,7 +25,7 @@ const Projects = () => {
|
||||
|
||||
const totalProjects = projectsCardData?.totalProjects ?? 0;
|
||||
const ongoingProjects = projectsCardData?.ongoingProjects ?? 0;
|
||||
|
||||
if(isLoading) return <ProjectCardSkeleton/>
|
||||
return (
|
||||
<div className="card p-3 h-100 text-center d-flex justify-content-between">
|
||||
<div className="d-flex justify-content-start align-items-center mb-3">
|
||||
|
||||
@ -93,10 +93,12 @@ const OrganizationsList = ({searchText}) => {
|
||||
if (isError) return <div>{error?.message || "Something went wrong"}</div>;
|
||||
|
||||
return (
|
||||
<div className="card px-0 px-sm-4 pb-12 pt-5">
|
||||
<div className="card-datatable table-responsive" id="horizontal-example">
|
||||
<div className="dataTables_wrapper no-footer px-2">
|
||||
<table className="table border-top dataTable text-nowrap">
|
||||
<div
|
||||
className="card-datatable table-responsive overflow-auto"
|
||||
id="horizontal-example"
|
||||
>
|
||||
<div className="dataTables_wrapper no-footer px-2 ">
|
||||
<table className="table border-top dataTable text-nowrap">
|
||||
<thead>
|
||||
<tr className="table_header_border">
|
||||
{organizationsColumns.map((col) => (
|
||||
@ -157,7 +159,6 @@ const OrganizationsList = ({searchText}) => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ const TenantsList = ({
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<div className="p-2 mt-3">
|
||||
<div className=" mt-3">
|
||||
<div className=" text-nowrap table-responsive">
|
||||
<table className="table border-top dataTable text-nowrap">
|
||||
<thead>
|
||||
|
||||
@ -151,7 +151,7 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
||||
if (isError) return <p>{error.message}</p>;
|
||||
|
||||
return (
|
||||
<div className="card ">
|
||||
<div className="card px-sm-4 px-0">
|
||||
<div
|
||||
className="card-datatable table-responsive page-min-h"
|
||||
id="horizontal-example"
|
||||
|
||||
@ -4,7 +4,6 @@ import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||
const ViewGallery = ({ batch, index }) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [currentIndex, setCurrentIndex] = useState(index);
|
||||
console.log(batch);
|
||||
useEffect(() => {
|
||||
setCurrentIndex(index);
|
||||
}, [index, batch]);
|
||||
|
||||
@ -13,7 +13,7 @@ const OrganizationPage = () => {
|
||||
<Breadcrumb
|
||||
data={[{ label: "Home", link: "/" }, { label: "Organizations" }]}
|
||||
/>
|
||||
<div className="card my-3 px-sm-2 px-0">
|
||||
<div className="card my-3 px-sm-4 px-0">
|
||||
<div className="card-body py-2 px-3">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-6 d-flex ">
|
||||
@ -42,9 +42,13 @@ const OrganizationPage = () => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<OrganizationsList searchText={searchText} />
|
||||
<div className="card page-min-h px-sm-4">
|
||||
<OrganizationsList searchText={searchText} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -121,7 +121,7 @@ const TenantPage = () => {
|
||||
{ label: "Tenant", link: null },
|
||||
]}
|
||||
/>
|
||||
<div className="card text-center my-4 p-5 pb-10">
|
||||
<div className="card text-center my-4 p-md-5 px-1 py-3 pb-10">
|
||||
{/* Super Tenant Actions */}
|
||||
{isSuperTenant && (
|
||||
<div className="p-0">
|
||||
|
||||
@ -8,35 +8,43 @@ const TenantSelectionPage = () => {
|
||||
const [pendingTenant, setPendingTenant] = useState(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { data, isLoading, isError, error } = useTenants();
|
||||
const { data, isLoading, isError } = useTenants();
|
||||
const { mutate: chooseTenant, isPending } = useSelectTenant(() => {
|
||||
navigate("/dashboard");
|
||||
});
|
||||
const handleTenantselection = (tenantId) => {
|
||||
const { mutate: handleLogout, isPending: isLogouting } = useLogout();
|
||||
|
||||
const handleTenantSelection = (tenantId) => {
|
||||
setPendingTenant(tenantId);
|
||||
localStorage.setItem("ctnt", tenantId);
|
||||
chooseTenant(tenantId);
|
||||
};
|
||||
|
||||
const {mutate:handleLogout,isPending:isLogouting} = useLogout()
|
||||
|
||||
|
||||
// Auto-select if already stored
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("ctnt")) {
|
||||
chooseTenant(localStorage.getItem("ctnt"))
|
||||
const storedTenant = localStorage.getItem("ctnt");
|
||||
if (storedTenant) {
|
||||
chooseTenant(storedTenant);
|
||||
}
|
||||
}, [navigate]);
|
||||
}, []);
|
||||
|
||||
// Auto-select if only one tenant
|
||||
useEffect(() => {
|
||||
if (!isLoading && data?.data?.length === 1) {
|
||||
const tenant = data.data[0];
|
||||
handleTenantselection(tenant.id);
|
||||
handleTenantSelection(tenant.id);
|
||||
}
|
||||
}, [isLoading, data]);
|
||||
|
||||
if (isLoading) return <Loader />;
|
||||
|
||||
if (isLoading) {
|
||||
// Show loader if:
|
||||
// - initial loading
|
||||
// - only one tenant (auto-selecting)
|
||||
// - user manually selecting
|
||||
if (
|
||||
isLoading ||
|
||||
isPending ||
|
||||
(data?.data?.length === 1 && pendingTenant !== null)
|
||||
) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
@ -48,7 +56,6 @@ const TenantSelectionPage = () => {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
{/* Logo */}
|
||||
@ -65,20 +72,24 @@ const TenantSelectionPage = () => {
|
||||
<div className="text-center mb-4">
|
||||
<p className="fs-4 fw-bold mb-1">Welcome</p>
|
||||
<p className="fs-6 fs-md-5">
|
||||
Please select which dashboard you want to explore!!!
|
||||
Please select which dashboard you want to explore!
|
||||
</p>
|
||||
<div onClick={()=>handleLogout()}>
|
||||
{isLogouting ? "Please Wait...":<span className="fs-6 fw-semibold cursor-pointer text-decoration-underline"><i className='bx bx-log-out'></i>SignOut</span>}
|
||||
<div onClick={() => handleLogout()}>
|
||||
{isLogouting ? (
|
||||
"Please Wait..."
|
||||
) : (
|
||||
<span className="fs-6 fw-semibold cursor-pointer text-decoration-underline">
|
||||
<i className="bx bx-log-out"></i> Sign Out
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Card Section */}
|
||||
<div className="row justify-content-center g-4 ">
|
||||
{/* Tenant Cards */}
|
||||
<div className="row justify-content-center g-4">
|
||||
{data?.data.map((tenant) => (
|
||||
<div key={tenant.id} className="col-12 col-md-10 col-lg-8">
|
||||
<div className="d-flex flex-column flex-md-row gap-4 align-items-center align-items-md-start p-3 border rounded shadow-sm bg-white h-100">
|
||||
|
||||
{/* Image */}
|
||||
<div className="flex-shrink-0 text-center">
|
||||
<img
|
||||
@ -95,12 +106,10 @@ const TenantSelectionPage = () => {
|
||||
|
||||
{/* Content */}
|
||||
<div className="d-flex flex-column text-start gap-2 w-100">
|
||||
{/* Title */}
|
||||
<p className="fs-5 fs-md-4 text-dark fw-semibold mb-1">
|
||||
{tenant?.name}
|
||||
</p>
|
||||
|
||||
{/* Industry */}
|
||||
<div className="d-flex flex-wrap gap-2 align-items-center">
|
||||
<p className="fw-semibold m-0">Industry:</p>
|
||||
<p className="m-0 text-muted">
|
||||
@ -108,21 +117,19 @@ const TenantSelectionPage = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
{tenant?.description && (
|
||||
<p className="text-start text-wrap text-muted small m-0">
|
||||
{tenant?.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Button */}
|
||||
<button
|
||||
className="btn btn-primary btn-sm mt-2 align-self-start"
|
||||
onClick={() => handleTenantselection(tenant?.id)}
|
||||
onClick={() => handleTenantSelection(tenant?.id)}
|
||||
disabled={pendingTenant === tenant.id && isPending}
|
||||
>
|
||||
{isPending && pendingTenant === tenant.id
|
||||
? "Please Wait.."
|
||||
{pendingTenant === tenant.id && isPending
|
||||
? "Please Wait..."
|
||||
: "Go To Dashboard"}
|
||||
</button>
|
||||
</div>
|
||||
@ -131,8 +138,8 @@ const TenantSelectionPage = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default TenantSelectionPage;
|
||||
export default TenantSelectionPage;
|
||||
|
||||
|
||||
@ -82,18 +82,29 @@ const CollectionPage = () => {
|
||||
const handleMarkedPayment = (payload) => {
|
||||
MarkedReceived(payload);
|
||||
};
|
||||
if (isAdmin === undefined ||
|
||||
canAddPayment === undefined ||
|
||||
canEditCollection === undefined ||
|
||||
canViewCollection === undefined ||
|
||||
if (
|
||||
isAdmin === undefined ||
|
||||
canAddPayment === undefined ||
|
||||
canEditCollection === undefined ||
|
||||
canViewCollection === undefined ||
|
||||
canCreate === undefined
|
||||
) {
|
||||
return <div className="text-center py-5">Checking access...</div>;
|
||||
}
|
||||
) {
|
||||
return <div className="text-center py-5">Checking access...</div>;
|
||||
}
|
||||
|
||||
if (!isAdmin && !canAddPayment && !canEditCollection && !canViewCollection && !canCreate) {
|
||||
return <AccessDenied data={[{ label: "Home", link: "/" }, { label: "Collection" }]} />;
|
||||
}
|
||||
if (
|
||||
!isAdmin &&
|
||||
!canAddPayment &&
|
||||
!canEditCollection &&
|
||||
!canViewCollection &&
|
||||
!canCreate
|
||||
) {
|
||||
return (
|
||||
<AccessDenied
|
||||
data={[{ label: "Home", link: "/" }, { label: "Collection" }]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<CollectionContext.Provider value={contextMassager}>
|
||||
<div className="container-fluid">
|
||||
@ -127,28 +138,31 @@ if (!isAdmin && !canAddPayment && !canEditCollection && !canViewCollection && !c
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-6 d-flex justify-content-end gap-4">
|
||||
<div className=" w-md-auto">
|
||||
<div className="col-12 col-md-6 d-flex justify-content-between justify-content-md-end gap-4">
|
||||
<div className="w-md-auto">
|
||||
{" "}
|
||||
<input
|
||||
type="search"
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
placeholder="search Collection"
|
||||
placeholder="Search Collection"
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
</div>
|
||||
{ (canCreate || isAdmin) && (
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
type="button"
|
||||
onClick={() => setCollection({ isOpen: true, invoiceId: null })}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
<span className="d-none d-md-inline-block">Add New Collection</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{(canCreate || isAdmin) && (
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setCollection({ isOpen: true, invoiceId: null })
|
||||
}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
<span className="d-none d-md-inline-block">
|
||||
Add New Collection
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user