changed ui for org

This commit is contained in:
pramod.mahajan 2025-10-24 17:29:38 +05:30
parent 1372f9870a
commit 32f16092db
11 changed files with 147 additions and 67 deletions

View File

@ -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>
)} )}

View 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>
</>
);
};

View File

@ -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">

View File

@ -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>
); );
}; };

View File

@ -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>

View File

@ -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"

View File

@ -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]);

View File

@ -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>
); );

View File

@ -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">

View File

@ -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;

View File

@ -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>