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";
|
import ExpenseByProject from "./ExpenseByProject";
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const { projectsCardData } = useDashboardProjectsCardData();
|
|
||||||
const { teamsCardData } = useDashboardTeamsCardData();
|
const { teamsCardData } = useDashboardTeamsCardData();
|
||||||
const { tasksCardData } = useDashboardTasksCardData();
|
const { tasksCardData } = useDashboardTasksCardData();
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ const Dashboard = () => {
|
|||||||
<div className="row gy-4">
|
<div className="row gy-4">
|
||||||
{isAllProjectsSelected && (
|
{isAllProjectsSelected && (
|
||||||
<div className="col-sm-6 col-lg-4">
|
<div className="col-sm-6 col-lg-4">
|
||||||
<Projects projectsCardData={projectsCardData} />
|
<Projects />
|
||||||
</div>
|
</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 React, { useEffect } from "react";
|
||||||
import { useDashboardProjectsCardData } from "../../hooks/useDashboard_Data";
|
import { useDashboardProjectsCardData } from "../../hooks/useDashboard_Data";
|
||||||
import eventBus from "../../services/eventBus";
|
import eventBus from "../../services/eventBus";
|
||||||
|
import ProjectInfra from "../Project/ProjectInfra";
|
||||||
|
import { ProjectCardSkeleton } from "./DashboardSkeleton";
|
||||||
|
|
||||||
const Projects = () => {
|
const Projects = () => {
|
||||||
const {
|
const {
|
||||||
@ -23,7 +25,7 @@ const Projects = () => {
|
|||||||
|
|
||||||
const totalProjects = projectsCardData?.totalProjects ?? 0;
|
const totalProjects = projectsCardData?.totalProjects ?? 0;
|
||||||
const ongoingProjects = projectsCardData?.ongoingProjects ?? 0;
|
const ongoingProjects = projectsCardData?.ongoingProjects ?? 0;
|
||||||
|
if(isLoading) return <ProjectCardSkeleton/>
|
||||||
return (
|
return (
|
||||||
<div className="card p-3 h-100 text-center d-flex justify-content-between">
|
<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">
|
<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>;
|
if (isError) return <div>{error?.message || "Something went wrong"}</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card px-0 px-sm-4 pb-12 pt-5">
|
<div
|
||||||
<div className="card-datatable table-responsive" id="horizontal-example">
|
className="card-datatable table-responsive overflow-auto"
|
||||||
<div className="dataTables_wrapper no-footer px-2">
|
id="horizontal-example"
|
||||||
<table className="table border-top dataTable text-nowrap">
|
>
|
||||||
|
<div className="dataTables_wrapper no-footer px-2 ">
|
||||||
|
<table className="table border-top dataTable text-nowrap">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="table_header_border">
|
<tr className="table_header_border">
|
||||||
{organizationsColumns.map((col) => (
|
{organizationsColumns.map((col) => (
|
||||||
@ -157,7 +159,6 @@ const OrganizationsList = ({searchText}) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -136,7 +136,7 @@ const TenantsList = ({
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="p-2 mt-3">
|
<div className=" mt-3">
|
||||||
<div className=" text-nowrap table-responsive">
|
<div className=" text-nowrap table-responsive">
|
||||||
<table className="table border-top dataTable text-nowrap">
|
<table className="table border-top dataTable text-nowrap">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@ -151,7 +151,7 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
|||||||
if (isError) return <p>{error.message}</p>;
|
if (isError) return <p>{error.message}</p>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card ">
|
<div className="card px-sm-4 px-0">
|
||||||
<div
|
<div
|
||||||
className="card-datatable table-responsive page-min-h"
|
className="card-datatable table-responsive page-min-h"
|
||||||
id="horizontal-example"
|
id="horizontal-example"
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
|||||||
const ViewGallery = ({ batch, index }) => {
|
const ViewGallery = ({ batch, index }) => {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [currentIndex, setCurrentIndex] = useState(index);
|
const [currentIndex, setCurrentIndex] = useState(index);
|
||||||
console.log(batch);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentIndex(index);
|
setCurrentIndex(index);
|
||||||
}, [index, batch]);
|
}, [index, batch]);
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const OrganizationPage = () => {
|
|||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
data={[{ label: "Home", link: "/" }, { label: "Organizations" }]}
|
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="card-body py-2 px-3">
|
||||||
<div className="row align-items-center">
|
<div className="row align-items-center">
|
||||||
<div className="col-6 d-flex ">
|
<div className="col-6 d-flex ">
|
||||||
@ -42,9 +42,13 @@ const OrganizationPage = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<OrganizationsList searchText={searchText} />
|
<div className="card page-min-h px-sm-4">
|
||||||
|
<OrganizationsList searchText={searchText} />
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -121,7 +121,7 @@ const TenantPage = () => {
|
|||||||
{ label: "Tenant", link: null },
|
{ 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 */}
|
{/* Super Tenant Actions */}
|
||||||
{isSuperTenant && (
|
{isSuperTenant && (
|
||||||
<div className="p-0">
|
<div className="p-0">
|
||||||
|
|||||||
@ -8,35 +8,43 @@ const TenantSelectionPage = () => {
|
|||||||
const [pendingTenant, setPendingTenant] = useState(null);
|
const [pendingTenant, setPendingTenant] = useState(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { data, isLoading, isError, error } = useTenants();
|
const { data, isLoading, isError } = useTenants();
|
||||||
const { mutate: chooseTenant, isPending } = useSelectTenant(() => {
|
const { mutate: chooseTenant, isPending } = useSelectTenant(() => {
|
||||||
navigate("/dashboard");
|
navigate("/dashboard");
|
||||||
});
|
});
|
||||||
const handleTenantselection = (tenantId) => {
|
const { mutate: handleLogout, isPending: isLogouting } = useLogout();
|
||||||
|
|
||||||
|
const handleTenantSelection = (tenantId) => {
|
||||||
setPendingTenant(tenantId);
|
setPendingTenant(tenantId);
|
||||||
localStorage.setItem("ctnt", tenantId);
|
localStorage.setItem("ctnt", tenantId);
|
||||||
chooseTenant(tenantId);
|
chooseTenant(tenantId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const {mutate:handleLogout,isPending:isLogouting} = useLogout()
|
// Auto-select if already stored
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localStorage.getItem("ctnt")) {
|
const storedTenant = localStorage.getItem("ctnt");
|
||||||
chooseTenant(localStorage.getItem("ctnt"))
|
if (storedTenant) {
|
||||||
|
chooseTenant(storedTenant);
|
||||||
}
|
}
|
||||||
}, [navigate]);
|
}, []);
|
||||||
|
|
||||||
|
// Auto-select if only one tenant
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoading && data?.data?.length === 1) {
|
if (!isLoading && data?.data?.length === 1) {
|
||||||
const tenant = data.data[0];
|
const tenant = data.data[0];
|
||||||
handleTenantselection(tenant.id);
|
handleTenantSelection(tenant.id);
|
||||||
}
|
}
|
||||||
}, [isLoading, data]);
|
}, [isLoading, data]);
|
||||||
|
|
||||||
if (isLoading) return <Loader />;
|
// Show loader if:
|
||||||
|
// - initial loading
|
||||||
if (isLoading) {
|
// - only one tenant (auto-selecting)
|
||||||
|
// - user manually selecting
|
||||||
|
if (
|
||||||
|
isLoading ||
|
||||||
|
isPending ||
|
||||||
|
(data?.data?.length === 1 && pendingTenant !== null)
|
||||||
|
) {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +56,6 @@ const TenantSelectionPage = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
@ -65,20 +72,24 @@ const TenantSelectionPage = () => {
|
|||||||
<div className="text-center mb-4">
|
<div className="text-center mb-4">
|
||||||
<p className="fs-4 fw-bold mb-1">Welcome</p>
|
<p className="fs-4 fw-bold mb-1">Welcome</p>
|
||||||
<p className="fs-6 fs-md-5">
|
<p className="fs-6 fs-md-5">
|
||||||
Please select which dashboard you want to explore!!!
|
Please select which dashboard you want to explore!
|
||||||
</p>
|
</p>
|
||||||
<div onClick={()=>handleLogout()}>
|
<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>}
|
{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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Card Section */}
|
{/* Tenant Cards */}
|
||||||
<div className="row justify-content-center g-4 ">
|
<div className="row justify-content-center g-4">
|
||||||
{data?.data.map((tenant) => (
|
{data?.data.map((tenant) => (
|
||||||
<div key={tenant.id} className="col-12 col-md-10 col-lg-8">
|
<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">
|
<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 */}
|
{/* Image */}
|
||||||
<div className="flex-shrink-0 text-center">
|
<div className="flex-shrink-0 text-center">
|
||||||
<img
|
<img
|
||||||
@ -95,12 +106,10 @@ const TenantSelectionPage = () => {
|
|||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="d-flex flex-column text-start gap-2 w-100">
|
<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">
|
<p className="fs-5 fs-md-4 text-dark fw-semibold mb-1">
|
||||||
{tenant?.name}
|
{tenant?.name}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Industry */}
|
|
||||||
<div className="d-flex flex-wrap gap-2 align-items-center">
|
<div className="d-flex flex-wrap gap-2 align-items-center">
|
||||||
<p className="fw-semibold m-0">Industry:</p>
|
<p className="fw-semibold m-0">Industry:</p>
|
||||||
<p className="m-0 text-muted">
|
<p className="m-0 text-muted">
|
||||||
@ -108,21 +117,19 @@ const TenantSelectionPage = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
|
||||||
{tenant?.description && (
|
{tenant?.description && (
|
||||||
<p className="text-start text-wrap text-muted small m-0">
|
<p className="text-start text-wrap text-muted small m-0">
|
||||||
{tenant?.description}
|
{tenant?.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Button */}
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary btn-sm mt-2 align-self-start"
|
className="btn btn-primary btn-sm mt-2 align-self-start"
|
||||||
onClick={() => handleTenantselection(tenant?.id)}
|
onClick={() => handleTenantSelection(tenant?.id)}
|
||||||
disabled={pendingTenant === tenant.id && isPending}
|
disabled={pendingTenant === tenant.id && isPending}
|
||||||
>
|
>
|
||||||
{isPending && pendingTenant === tenant.id
|
{pendingTenant === tenant.id && isPending
|
||||||
? "Please Wait.."
|
? "Please Wait..."
|
||||||
: "Go To Dashboard"}
|
: "Go To Dashboard"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -131,8 +138,8 @@ const TenantSelectionPage = () => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TenantSelectionPage;
|
export default TenantSelectionPage;
|
||||||
|
|
||||||
|
|||||||
@ -82,18 +82,29 @@ const CollectionPage = () => {
|
|||||||
const handleMarkedPayment = (payload) => {
|
const handleMarkedPayment = (payload) => {
|
||||||
MarkedReceived(payload);
|
MarkedReceived(payload);
|
||||||
};
|
};
|
||||||
if (isAdmin === undefined ||
|
if (
|
||||||
|
isAdmin === undefined ||
|
||||||
canAddPayment === undefined ||
|
canAddPayment === undefined ||
|
||||||
canEditCollection === undefined ||
|
canEditCollection === undefined ||
|
||||||
canViewCollection === undefined ||
|
canViewCollection === undefined ||
|
||||||
canCreate === 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) {
|
if (
|
||||||
return <AccessDenied data={[{ label: "Home", link: "/" }, { label: "Collection" }]} />;
|
!isAdmin &&
|
||||||
}
|
!canAddPayment &&
|
||||||
|
!canEditCollection &&
|
||||||
|
!canViewCollection &&
|
||||||
|
!canCreate
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<AccessDenied
|
||||||
|
data={[{ label: "Home", link: "/" }, { label: "Collection" }]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<CollectionContext.Provider value={contextMassager}>
|
<CollectionContext.Provider value={contextMassager}>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
@ -127,28 +138,31 @@ if (!isAdmin && !canAddPayment && !canEditCollection && !canViewCollection && !c
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 col-md-6 d-flex justify-content-end gap-4">
|
<div className="col-12 col-md-6 d-flex justify-content-between justify-content-md-end gap-4">
|
||||||
<div className=" w-md-auto">
|
<div className="w-md-auto">
|
||||||
{" "}
|
{" "}
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
value={searchText}
|
value={searchText}
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
placeholder="search Collection"
|
placeholder="Search Collection"
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{ (canCreate || isAdmin) && (
|
{(canCreate || isAdmin) && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-primary"
|
className="btn btn-sm btn-primary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setCollection({ isOpen: true, invoiceId: null })}
|
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>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user