diff --git a/app/components/dashboard/d3-image-info.tsx b/app/components/dashboard/d3-image-info.tsx
index 4463bda..0f9b786 100644
--- a/app/components/dashboard/d3-image-info.tsx
+++ b/app/components/dashboard/d3-image-info.tsx
@@ -5,13 +5,17 @@ export type CompanyInfo = {
id: string;
imageUrl: string;
name: string;
- costReduction: number; // absolute value
+ costReduction: number;
revenue?: number;
capacity?: number;
+ costI : number,
+ capacityI : number,
+ revenueI : number,
+ cost : number | string,
};
export type D3ImageInfoProps = {
- companies: CompanyInfo[]; // exactly 6 items
+ companies: CompanyInfo[];
width?: number;
height?: number;
};
@@ -59,7 +63,7 @@ export function D3ImageInfo({ companies }: D3ImageInfoProps) {
{displayCompanies.map((company, index) => {
- const gp = gridPositions.find(v => v.name === company.name) || { col: (index % 5) + 1, row: Math.floor(index / 5) + 1 };
+ const gp = gridPositions.find(v => v.name === company.name) ;
return (
<>
-
+
>);
})}
diff --git a/app/components/dashboard/dashboard-home.tsx b/app/components/dashboard/dashboard-home.tsx
index 81d6f95..940ba71 100644
--- a/app/components/dashboard/dashboard-home.tsx
+++ b/app/components/dashboard/dashboard-home.tsx
@@ -49,7 +49,9 @@ export function DashboardHome() {
const [error, setError] = useState
(null);
// Chart and schematic data from select API
const [companyChartData, setCompanyChartData] = useState<
- { category: string; capacity: number; revenue: number; cost: number }[]
+ { category: string; capacity: number; revenue: number; cost: number , costI : number,
+ capacityI : number,
+ revenueI : number }[]
>([]);
const [totalIncreasedCapacity, setTotalIncreasedCapacity] = useState(0);
@@ -138,7 +140,6 @@ export function DashboardHome() {
const capacityPct = preCap > 0 ? (incCap / preCap) * 100 : 0;
const revenuePct = preInc > 0 ? (incInc / preInc) * 100 : 0;
const costPct = preFee > 0 ? (costRed / preFee) * 100 : 0;
- console.log(costRed)
return {
category: rel,
capacity: isFinite(capacityPct) ? capacityPct : 0,
diff --git a/app/components/dashboard/layout.tsx b/app/components/dashboard/layout.tsx
index af82e01..0d12e7f 100644
--- a/app/components/dashboard/layout.tsx
+++ b/app/components/dashboard/layout.tsx
@@ -2,6 +2,7 @@ import { useState } from "react";
import { cn } from "~/lib/utils";
import { Sidebar } from "./sidebar";
import { Header } from "./header";
+import { StrategicAlignmentPopup } from "./strategic-alignment-popup";
interface DashboardLayoutProps {
children: React.ReactNode;
@@ -16,6 +17,7 @@ export function DashboardLayout({
}: DashboardLayoutProps) {
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
+ const [isStrategicAlignmentPopupOpen, setIsStrategicAlignmentPopupOpen] = useState(false);
const toggleSidebarCollapse = () => {
setIsSidebarCollapsed(!isSidebarCollapsed);
@@ -55,6 +57,7 @@ export function DashboardLayout({
isCollapsed={isSidebarCollapsed}
onToggleCollapse={toggleSidebarCollapse}
className="h-full flex-shrink-0 relative z-10"
+ onStrategicAlignmentClick={() => setIsStrategicAlignmentPopupOpen(true)}
/>
@@ -79,6 +82,7 @@ export function DashboardLayout({
+
);
}
diff --git a/app/components/dashboard/sidebar.tsx b/app/components/dashboard/sidebar.tsx
index 43a0878..4a90072 100644
--- a/app/components/dashboard/sidebar.tsx
+++ b/app/components/dashboard/sidebar.tsx
@@ -25,18 +25,20 @@ interface SidebarProps {
isCollapsed?: boolean;
onToggleCollapse?: () => void;
className?: string;
+ onStrategicAlignmentClick?: () => void;
}
interface MenuItem {
id: string;
label: string;
- icon: React.ComponentType<{ className?: string }>;
+ icon: React.ComponentType<{ className?: string }> | null;
href?: string;
children?: MenuItem[];
badge?: string | number;
}
const menuItems: MenuItem[] = [
+
{
id: "dashboard",
label: "صفحه اصلی",
@@ -104,6 +106,12 @@ const menuItems: MenuItem[] = [
icon: Star,
href: "/dashboard/top-innovations",
},
+ {
+ id: "strategic-alignment",
+ label: "میزان انطباق راهبردی",
+ icon: null,
+ href: "#", // This is not a route, it opens a popup
+ },
];
const bottomMenuItems: MenuItem[] = [
@@ -125,6 +133,7 @@ export function Sidebar({
isCollapsed = false,
onToggleCollapse,
className,
+ onStrategicAlignmentClick,
}: SidebarProps) {
const location = useLocation();
const [expandedItems, setExpandedItems] = useState([]);
@@ -196,16 +205,42 @@ export function Sidebar({
const ItemIcon = item.icon;
const handleClick = () => {
- if (item.id === "logout") {
+ if (item.id === "strategic-alignment") {
+ onStrategicAlignmentClick?.();
+ } else if (item.id === "logout") {
logout();
} else if (hasChildren) {
toggleExpanded(item.id);
}
};
+ if (item.id === "strategic-alignment") {
+return (
+
+)
+
+ }
+
return (
- {item.href && item.id !== "logout" ? (
+ {item.href && item.href !== "#" ? (
void;
+}
+
+// ✅ Chart config for shadcn/ui
+const chartConfig = {
+ percentage: {
+ label: "",
+ color: "#3AEA83",
+ },
+};
+
+ const maxHeight = 150;
+ const barHeights = () => Math.floor(Math.random() * maxHeight);
+
+const ChartSkeleton = () => (
+
+
+ {/* Chart bars */}
+
+ {[...Array(9)].map((_, i) => (
+
+
+
+ ))}
+
+ {/* Left space for Y-axis label */}
+
+
+
+
+
+
+
+
+);
+export function StrategicAlignmentPopup({
+ open,
+ onOpenChange,
+}: StrategicAlignmentPopupProps) {
+ const [data, setData] = useState
([]);
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ if (open) {
+ fetchData();
+ }
+ }, [open]);
+
+ const fetchData = async () => {
+ setLoading(true);
+ try {
+ const response = await apiService.select({
+ ProcessName: "project",
+ OutputFields: [
+ "strategic_theme",
+ "sum(operational_fee) as operational_fee_sum",
+ ],
+ GroupBy: ["strategic_theme"],
+ });
+
+ const responseData =
+ typeof response.data === "string"
+ ? JSON.parse(response.data)
+ : response.data;
+
+ const processedData = responseData
+ .map((item: any) => ({
+ strategic_theme: item.strategic_theme || "N/A",
+ operational_fee_sum: Math.max(0, Number(item.operational_fee_sum)),
+ }))
+ .filter((item: StrategicAlignmentData) => item.strategic_theme !== "");
+
+ const total = processedData.reduce(
+ (acc: number, item: StrategicAlignmentData) =>
+ acc + item.operational_fee_sum,
+ 0
+ );
+
+ const dataWithPercentage = processedData.map(
+ (item: StrategicAlignmentData) => ({
+ ...item,
+ percentage:
+ total > 0
+ ? Math.round((item.operational_fee_sum / total) * 100)
+ : 0,
+ })
+ );
+ setData(dataWithPercentage || []);
+ } catch (error) {
+ console.error("Error fetching strategic alignment data:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/app/components/ui/skeleton.tsx b/app/components/ui/skeleton.tsx
new file mode 100644
index 0000000..1e29d7a
--- /dev/null
+++ b/app/components/ui/skeleton.tsx
@@ -0,0 +1,13 @@
+import { cn } from "~/lib/utils"
+
+function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export { Skeleton }
diff --git a/app/components/ui/tooltip.tsx b/app/components/ui/tooltip.tsx
new file mode 100644
index 0000000..fc97fa5
--- /dev/null
+++ b/app/components/ui/tooltip.tsx
@@ -0,0 +1,61 @@
+"use client"
+
+import * as React from "react"
+import * as TooltipPrimitive from "@radix-ui/react-tooltip"
+
+import { cn } from "~/lib/utils"
+
+function TooltipProvider({
+ delayDuration = 0,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function Tooltip({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function TooltipTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function TooltipContent({
+ className,
+ sideOffset = 0,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+ {children}
+
+
+
+ )
+}
+
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
diff --git a/package.json b/package.json
index df3ac08..e855d04 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.1.13",
+ "@radix-ui/react-tooltip": "^1.2.8",
"@react-router/node": "^7.7.0",
"@react-router/serve": "^7.7.1",
"@types/d3": "^7.4.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ec1bd4e..a0691b8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -32,6 +32,9 @@ importers:
'@radix-ui/react-tabs':
specifier: ^1.1.13
version: 1.1.13(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-tooltip':
+ specifier: ^1.2.8
+ version: 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@react-router/node':
specifier: ^7.7.0
version: 7.8.2(react-router@7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(typescript@5.9.2)
@@ -755,6 +758,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-tooltip@1.2.8':
+ resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-use-callback-ref@1.1.1':
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
peerDependencies:
@@ -3119,6 +3135,26 @@ snapshots:
'@types/react': 19.1.12
'@types/react-dom': 19.1.9(@types/react@19.1.12)
+ '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.1)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.1)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.1)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ optionalDependencies:
+ '@types/react': 19.1.12
+ '@types/react-dom': 19.1.9(@types/react@19.1.12)
+
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.12)(react@19.1.1)':
dependencies:
react: 19.1.1