inogen/app/components/dashboard/strategic-alignment-popup.tsx

204 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "~/components/ui/dialog";
import {
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
LabelList,
Cell,
} from "recharts";
import apiService from "~/lib/api";
import { Skeleton } from "~/components/ui/skeleton";
import { formatNumber } from "~/lib/utils";
import { ChartContainer } from "../ui/chart";
interface StrategicAlignmentData {
strategic_theme: string;
operational_fee_sum: number;
percentage?: number;
}
interface StrategicAlignmentPopupProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}
// ✅ Chart config for shadcn/ui
const chartConfig = {
percentage: {
label: "",
color: "#3AEA83",
},
};
const maxHeight = 150;
const barHeights = () => Math.floor(Math.random() * maxHeight);
const ChartSkeleton = () => (
<div className="flex justify-center h-96 w-full p-4">
{/* Chart bars */}
<div className=" w-full flex items-end gap-10">
{[...Array(9)].map((_, i) => (
<div key={i} className="flex flex-col items-center gap-1">
<Skeleton
className="w-10 bg-gray-700 rounded-md"
style={{ height: `${barHeights()}px` }}
/>
</div>
))}
</div>
{/* Left space for Y-axis label */}
<div className="flex flex-col justify-between mr-2">
<Skeleton className="h-6 w-15 bg-gray-700 rounded" />
<Skeleton className="h-6 w-15 bg-gray-700 rounded" />
<Skeleton className="h-6 w-15 bg-gray-700 rounded" />
<Skeleton className="h-6 w-15 bg-gray-700 rounded" />
<Skeleton className="h-6 w-15 bg-gray-700 rounded" />
</div>
</div>
);
export function StrategicAlignmentPopup({
open,
onOpenChange,
}: StrategicAlignmentPopupProps) {
const [data, setData] = useState<StrategicAlignmentData[]>([]);
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 (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="w-full max-w-4xl bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] text-white border-none">
<DialogHeader className="border-b-3 mb-10 py-2 w-full pb-4 border-b-2 border-gray-500/20">
<DialogTitle className="ml-auto ">میزان انطباق راهبردی</DialogTitle>
</DialogHeader>
{loading ? (
<ChartSkeleton />
) : (
<>
<ResponsiveContainer width="100%" height={400}>
<ChartContainer config={chartConfig} className="aspect-auto h-96 w-full">
<BarChart
data={data}
margin={{ left: 12, right: 12 }}
barGap={15}
barSize={30}
accessibilityLayer
>
<CartesianGrid vertical={false} stroke="#475569" />
<XAxis
dataKey="strategic_theme"
tickLine={false}
axisLine={false}
tickMargin={10}
tick={{ fill: "#94a3b8", fontSize: 12 }}
/>
<YAxis
domain={[0, 100]}
tickLine={false}
axisLine={false}
tickMargin={8}
tick={{ fill: "#94a3b8", fontSize: 12 }}
tickFormatter={(value) =>
`${formatNumber(Math.round(value))}%`
}
label={{
value: "تعداد برنامه ها" ,
angle: -90,
position: "insideLeft",
fill: "#94a3b8",
fontSize: 14,
offset: 0,
dy: 0,
style: { textAnchor: "middle" },
}}
/>
<Bar dataKey="percentage" radius={[8, 8, 0, 0]}>
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={chartConfig.percentage.color} />
))}
<LabelList
dataKey="percentage"
position="top"
style={{
fill: "#ffffff",
fontSize: "12px",
fontWeight: "bold",
}}
formatter={(v: number) => `${formatNumber(Math.round(v))}%`}
/>
</Bar>
</BarChart>
</ChartContainer>
</ResponsiveContainer>
</>
)}
</DialogContent>
</Dialog>
);
}