From 39e1ae4e814accf8657c2558a15c8a01b1db5a0e Mon Sep 17 00:00:00 2001 From: Saeed Abadiyan Date: Sat, 4 Oct 2025 02:21:11 +0330 Subject: [PATCH] add loading in graph ,also fix the graph --- .../mange-ideas-tech-page.tsx | 79 ++++++- app/components/ecosystem/info-panel.tsx | 8 +- app/components/ecosystem/network-graph.tsx | 28 ++- app/routes/ecosystem.tsx | 214 +++++++++++------- 4 files changed, 223 insertions(+), 106 deletions(-) diff --git a/app/components/dashboard/project-management/mange-ideas-tech-page.tsx b/app/components/dashboard/project-management/mange-ideas-tech-page.tsx index 12aba32..e451903 100644 --- a/app/components/dashboard/project-management/mange-ideas-tech-page.tsx +++ b/app/components/dashboard/project-management/mange-ideas-tech-page.tsx @@ -29,7 +29,7 @@ import { } from "~/components/ui/chart"; import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, CartesianGrid, LabelList, Cell, RadialBarChart, PolarGrid, RadialBar, PolarRadiusAxis } from "recharts"; import { BaseCard } from "~/components/ui/base-card"; -import { Label } from "~/components/ui/label"; +import { Label } from "recharts" import { MetricCard } from "~/components/ui/metric-card"; @@ -605,9 +605,34 @@ export function ManageIdeasTechPage() { const VerticalBarChart = () => { if (loadingChart) { return ( -
-
-
+
+ {/* Chart title skeleton */} +
+ + {/* Chart area skeleton */} +
+ {/* Y-axis labels */} +
+ {Array.from({ length: 4 }).map((_, i) => ( +
+ ))} +
+ + {/* Bars skeleton */} +
+ {Array.from({ length: 4 }).map((_, i) => ( +
+ {/* Bar */} +
+ {/* X-axis label */} +
+
+ ))} +
+
); } @@ -921,6 +946,23 @@ style: { textAnchor: "middle" },
+ {loadingStats ? ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+ )} - + {loadingStats ? ( +
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : ( + + )}
diff --git a/app/components/ecosystem/info-panel.tsx b/app/components/ecosystem/info-panel.tsx index 89c547d..c8fcf8a 100644 --- a/app/components/ecosystem/info-panel.tsx +++ b/app/components/ecosystem/info-panel.tsx @@ -256,7 +256,7 @@ export function InfoPanel({ selectedCompany }: InfoPanelProps) { {Array.from({ length: 4 }).map((_, i) => (
-
+
{/* Bar Chart Skeleton */} @@ -362,7 +362,7 @@ export function InfoPanel({ selectedCompany }: InfoPanelProps) { {Array.from({ length: 4 }).map((_, i) => (
-
+
diff --git a/app/components/ecosystem/network-graph.tsx b/app/components/ecosystem/network-graph.tsx index ac04955..15b7e44 100644 --- a/app/components/ecosystem/network-graph.tsx +++ b/app/components/ecosystem/network-graph.tsx @@ -43,6 +43,7 @@ export interface CompanyDetails { export interface NetworkGraphProps { onNodeClick?: (node: CompanyDetails) => void; + onLoadingChange?: (loading: boolean) => void; } function parseApiResponse(raw: any): any[] { @@ -58,7 +59,7 @@ function isBrowser(): boolean { return typeof window !== "undefined"; } -export function NetworkGraph({ onNodeClick }: NetworkGraphProps) { +export function NetworkGraph({ onNodeClick, onLoadingChange }: NetworkGraphProps) { const svgRef = useRef(null); const [nodes, setNodes] = useState([]); const [links, setLinks] = useState([]); @@ -441,6 +442,19 @@ export function NetworkGraph({ onNodeClick }: NetworkGraphProps) { if (d.isCenter) return; if (onNodeClick && d.stageid) { + // Open dialog immediately with basic info + const basicDetails: CompanyDetails = { + id: d.id, + label: d.label, + category: d.category, + stageid: d.stageid, + fields: [], + }; + onNodeClick(basicDetails); + + // Start loading + onLoadingChange?.(true); + try { const res = await callAPI(d.stageid); const responseData = JSON.parse(res.data); @@ -473,14 +487,10 @@ export function NetworkGraph({ onNodeClick }: NetworkGraphProps) { onNodeClick(companyDetails); } catch (error) { console.error("Failed to fetch company details:", error); - const basicDetails: CompanyDetails = { - id: d.id, - label: d.label, - category: d.category, - stageid: d.stageid, - fields: [], - }; - onNodeClick(basicDetails); + // Keep the basic details already shown + } finally { + // Stop loading + onLoadingChange?.(false); } } }); diff --git a/app/routes/ecosystem.tsx b/app/routes/ecosystem.tsx index 696ced1..ff96122 100644 --- a/app/routes/ecosystem.tsx +++ b/app/routes/ecosystem.tsx @@ -56,10 +56,20 @@ function handleValue(val: any): any { export default function EcosystemPage() { const [selectedCompany, setSelectedCompany] = React.useState(null); + const [isDialogLoading, setIsDialogLoading] = React.useState(false); const { token } = useAuth(); const closeDialog = () => { setSelectedCompany(null); + setIsDialogLoading(false); + }; + + const handleNodeClick = (company: CompanyDetails) => { + setSelectedCompany(company); + }; + + const handleLoadingChange = (loading: boolean) => { + setIsDialogLoading(loading); }; // Construct image URL @@ -79,7 +89,7 @@ export default function EcosystemPage() {
- +
@@ -91,7 +101,7 @@ export default function EcosystemPage() { open={!!selectedCompany} onOpenChange={(open) => !open && closeDialog()} > - + معرفی @@ -99,98 +109,136 @@ export default function EcosystemPage() { -
- {/* Right Column - Description */} -
- {/* Company Image */} -
- {selectedCompany?.label || ""} - {selectedCompany?.stageid && token?.accessToken ? ( - {selectedCompany?.label { - // Hide image and show fallback on error - e.currentTarget.style.display = "none"; - if (e.currentTarget.nextSibling) { - ( - e.currentTarget.nextSibling as HTMLElement - ).style.display = "flex"; - } - }} - /> - ) : null} -
- - - + {isDialogLoading ? ( +
+ {/* Right Column - Loading Skeleton */} +
+ {/* Company Image & Title Skeleton */} +
+
+
+
+ {/* Description Skeleton */} +
+
+
+
+
- {selectedCompany?.description ? ( -
-

- {selectedCompany.description} -

-
- ) : ( -
- توضیحات در دسترس نیست -
- )} -
- {/* Left Column - Company Fields */} -
-

- اطلاعات - {selectedCompany?.category} -

- {selectedCompany?.fields && - selectedCompany.fields.length > 0 ? ( + {/* Left Column - Loading Skeleton */} +
+
- {selectedCompany.fields.map((field, index) => ( + {Array.from({ length: 6 }).map((_, index) => (
- - - {field.N}: - - - - {handleValue(field.V)} - {field.U && ({field.U})} - - +
+
+
+
+
))}
- ) : ( -
- اطلاعات تکمیلی در دسترس نیست -
- )} +
-
+ ) : ( +
+ {/* Right Column - Description */} +
+ {/* Company Image */} +
+ {selectedCompany?.label || ""} + {selectedCompany?.stageid && token?.accessToken ? ( + {selectedCompany?.label { + // Hide image and show fallback on error + e.currentTarget.style.display = "none"; + if (e.currentTarget.nextSibling) { + ( + e.currentTarget.nextSibling as HTMLElement + ).style.display = "flex"; + } + }} + /> + ) : null} +
+ + + +
+
+ {selectedCompany?.description ? ( +
+

+ {selectedCompany.description} +

+
+ ) : ( +
+ توضیحات در دسترس نیست +
+ )} +
+ {/* Left Column - Company Fields */} +
+

+ اطلاعات + {selectedCompany?.category} +

+ {selectedCompany?.fields && + selectedCompany.fields.length > 0 ? ( +
+ {selectedCompany.fields.map((field, index) => ( +
+ + + {field.N}: + + + + {handleValue(field.V)} + {field.U && ({field.U})} + + +
+ ))} +
+ ) : ( +
+ اطلاعات تکمیلی در دسترس نیست +
+ )} +
+
+ )}