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 ? (
-
})
{
- // 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 ? (
+
})
{
+ // 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})}
+
+
+
+ ))}
+
+ ) : (
+
+ اطلاعات تکمیلی در دسترس نیست
+
+ )}
+
+
+ )}