This commit is contained in:
MehrdadAdabi 2025-10-08 18:48:54 +03:30
commit 584450550b
9 changed files with 42 additions and 37 deletions

View File

@ -594,7 +594,7 @@ export function DashboardHome() {
<p className="p-6 font-persian font-semibold text-lg "> <p className="p-6 font-persian font-semibold text-lg ">
تحقق ارزش ها تحقق ارزش ها
</p> </p>
<TabsList className="bg-transparent py-2 border m-6 border-gray-600"> <TabsList className="bg-transparent py-2 m-6 border-[1px] border-[#5F6284]">
<TabsTrigger value="canvas" className="cursor-pointer"> <TabsTrigger value="canvas" className="cursor-pointer">
شماتیک شماتیک
</TabsTrigger> </TabsTrigger>

View File

@ -932,15 +932,7 @@ export function ManageIdeasTechPage() {
{/* Infinite scroll trigger */} {/* Infinite scroll trigger */}
<div ref={observerRef} className="h-auto"> <div ref={observerRef} className="h-auto">
{loadingMore && (
<div className="flex items-center justify-center py-2">
<div className="flex items-center gap-2">
<RefreshCw className="w-4 h-4 animate-spin text-success" />
<span className="font-persian text-muted-foreground text-sm">
</span>
</div>
</div>
)}
</div> </div>
</CardContent> </CardContent>
@ -951,6 +943,15 @@ export function ManageIdeasTechPage() {
</div> </div>
</div> </div>
</Card> </Card>
{loadingMore && (
<div className="flex items-center justify-center py-2">
<div className="flex items-center gap-2">
<RefreshCw className="w-4 h-4 animate-spin text-success" />
<span className="font-persian text-muted-foreground text-sm">
</span>
</div>
</div>
)}
</div> </div>
{/* Chart Section */} {/* Chart Section */}
<BaseCard icon={TrendingUp} className="col-span-1 mt-12 row-start-2 col-start-3 row-span-1" title="نمودار ایده‌ها"> <BaseCard icon={TrendingUp} className="col-span-1 mt-12 row-start-2 col-start-3 row-span-1" title="نمودار ایده‌ها">

View File

@ -237,11 +237,11 @@ export function ProductInnovationPage() {
revenueNewProducts: { revenueNewProducts: {
id: "revenueNewProducts", id: "revenueNewProducts",
title: "سهم از درآمد برای محصولات جدید", title: "سهم از درآمد برای محصولات جدید",
value: "0", value: 0,
description: "میلیون ریال", description: "میلیون ریال",
descriptionPercent: "درصد به کل درآمد", descriptionPercent: "درصد به کل درآمد",
color: "text-[#3AEA83]", color: "text-[#3AEA83]",
percent : "0" percent :0
}, },
newProductExports: { newProductExports: {
id: "newProductExports", id: "newProductExports",
@ -466,8 +466,8 @@ export function ProductInnovationPage() {
...prev, ...prev,
revenueNewProducts: { revenueNewProducts: {
...prev.revenueNewProducts, ...prev.revenueNewProducts,
value: formatNumber(normalized?.new_products_revenue_share), value: normalized.new_products_revenue_share,
percent: formatNumber(normalized?.new_products_revenue_share_percent), percent: normalized.new_products_revenue_share_percent,
}, },
impactOnImports: { impactOnImports: {
...prev.impactOnImports, ...prev.impactOnImports,
@ -716,8 +716,8 @@ export function ProductInnovationPage() {
<div className="col-span-2"> <div className="col-span-2">
<MetricCard <MetricCard
title={stateCard.revenueNewProducts.title} title={stateCard.revenueNewProducts.title}
value={formatNumber(stateCard.revenueNewProducts.value)} value={stateCard.revenueNewProducts.value}
percentValue={formatNumber(stateCard.revenueNewProducts.percent)} percentValue={stateCard.revenueNewProducts.percent}
valueLabel={stateCard.revenueNewProducts.description} valueLabel={stateCard.revenueNewProducts.description}
percentLabel={stateCard.revenueNewProducts.descriptionPercent} percentLabel={stateCard.revenueNewProducts.descriptionPercent}
/> />
@ -756,7 +756,7 @@ export function ProductInnovationPage() {
</div> </div>
{/* Funnel Chart */} {/* Funnel Chart */}
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] h-full backdrop-blur-sm rounded-2xl w-full overflow-hidden"> <Card className=" bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] h-full 2xl:h-auto pb-8 backdrop-blur-sm rounded-2xl w-full overflow-hidden">
<CardContent className="px-0 py-4"> <CardContent className="px-0 py-4">
<FunnelChart <FunnelChart
title="قيف فرآیند پروژه ها" title="قيف فرآیند پروژه ها"

View File

@ -24,7 +24,7 @@ import { TruncatedText } from "../ui/truncatedText";
interface StrategicAlignmentData { interface StrategicAlignmentData {
strategic_theme: string; strategic_theme: string;
operational_fee_sum: number; operational_fee_count: number;
percentage?: number; percentage?: number;
} }
@ -138,7 +138,7 @@ export function StrategicAlignmentPopup({
ProcessName: "project", ProcessName: "project",
OutputFields: [ OutputFields: [
"strategic_theme", "strategic_theme",
"sum(operational_fee) as operational_fee_sum", "count(operational_fee)",
], ],
GroupBy: ["strategic_theme"], GroupBy: ["strategic_theme"],
}); });
@ -168,7 +168,7 @@ export function StrategicAlignmentPopup({
ProcessName: "project", ProcessName: "project",
OutputFields: [ OutputFields: [
"value_technology_and_innovation", "value_technology_and_innovation",
"sum(operational_fee)", "count(operational_fee)",
], ],
Conditions: [["strategic_theme", "=", item]], Conditions: [["strategic_theme", "=", item]],
GroupBy: ["value_technology_and_innovation"], GroupBy: ["value_technology_and_innovation"],
@ -233,13 +233,13 @@ export function StrategicAlignmentPopup({
.map((item: any) => ({ .map((item: any) => ({
strategic_theme: strategic_theme:
item.strategic_theme || item.value_technology_and_innovation || "N/A", item.strategic_theme || item.value_technology_and_innovation || "N/A",
operational_fee_sum: Math.max(0, Number(item.operational_fee_sum)), operational_fee_count: Math.max(0, Number(item.operational_fee_count)),
})) }))
.filter((item: StrategicAlignmentData) => item.strategic_theme !== ""); .filter((item: StrategicAlignmentData) => item.strategic_theme !== "");
const total = processedData.reduce( const total = processedData.reduce(
(acc: number, item: StrategicAlignmentData) => (acc: number, item: StrategicAlignmentData) =>
acc + item.operational_fee_sum, acc + item.operational_fee_count,
0 0
); );
@ -247,7 +247,7 @@ export function StrategicAlignmentPopup({
(item: StrategicAlignmentData) => ({ (item: StrategicAlignmentData) => ({
...item, ...item,
percentage: percentage:
total > 0 ? Math.round((item.operational_fee_sum / total) * 100) : 0, total > 0 ? Math.round((item.operational_fee_count / total) * 100) : 0,
}) })
); );
setData(dataWithPercentage || []); setData(dataWithPercentage || []);

View File

@ -305,7 +305,7 @@ export function NetworkGraph({ onNodeClick, onLoadingChange }: NetworkGraphProps
.enter() .enter()
.append("g") .append("g")
.attr("class", "node") .attr("class", "node")
.style("cursor", "pointer"); .style("cursor", d => d.stageid === -1 ? "default" : "pointer");
const drag = d3 const drag = d3
.drag<SVGGElement, Node>() .drag<SVGGElement, Node>()
@ -437,9 +437,12 @@ export function NetworkGraph({ onNodeClick, onLoadingChange }: NetworkGraphProps
.attr("stroke-width", 3); .attr("stroke-width", 3);
}); });
nodeGroup.on("click", async function (event, d) { nodeGroup.on("click", async function (event, d) {
event.stopPropagation(); event.stopPropagation();
if (d.isCenter) return;
// جلوگیری از کلیک روی مرکز و دسته‌بندی‌ها
if (d.isCenter || d.stageid === -1) return;
if (onNodeClick && d.stageid) { if (onNodeClick && d.stageid) {
// Open dialog immediately with basic info // Open dialog immediately with basic info

View File

@ -13,6 +13,7 @@ interface FunnelChartProps {
title?: string; title?: string;
className?: string; className?: string;
} }
const greenColors = ["#3C9F71","#3BC47A","#3BC47A","#3BD77E","#3AEA83"]
export function FunnelChart({ data, title, className = "" }: FunnelChartProps) { export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
const maxValue = Math.max(...data.map(d => d.value)); const maxValue = Math.max(...data.map(d => d.value));
@ -50,15 +51,15 @@ export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
return ( return (
<div key={index} className="grid grid-cols-[6rem_1fr] gap-2 w-full"> <div key={index} className="grid grid-cols-[6rem_1fr] gap-2 w-full">
<div className="text-sm font-light text-white cols-start-1 justify-self-start font-thin min-w-[max-content] text-center"> <div className="text-sm font-light text-white font-persian cols-start-1 justify-self-start min-w-[max-content] text-center">
{item.label} {item.label}
</div> </div>
<div className="flex items-center gap-10 w-full cols-start-2 flex items-center justify-center w-full"> <div className="flex items-center gap-10 w-full cols-start-2 justify-center">
<div className="flex items-center w-full"> <div className="flex items-center w-full">
<div style={{ width: `${(100 - barWidth) / 2}%` }} /> <div style={{ width: `${(100 - barWidth) / 2}%` }} />
<div <div
className="bg-[#3BC47A] h-8 rounded-2xl flex items-center justify-center text-lg relative" className="bg-[#3BC47A] h-8 rounded-2xl flex items-center justify-center text-lg relative"
style={{ width: `${barWidth}%` }} style={{ width: `${barWidth}%` ,backgroundColor : `${greenColors[index]}`}}
> >
<span className="text-pr-gray text-base font-semibold"> <span className="text-pr-gray text-base font-semibold">
{item.value.toLocaleString('fa-IR')} {item.value.toLocaleString('fa-IR')}

View File

@ -19,7 +19,7 @@ const Progress = React.forwardRef<
<span className="w-full right-0 text-sm absolute z-10 px-2 text-[#5F6284]" <span className="w-full right-0 text-sm absolute z-10 px-2 text-[#5F6284]"
>{formatNumber(Math.ceil(value || 0 * 10) / 10)}%</span> >{formatNumber(Math.ceil(value || 0 * 10) / 10)}%</span>
<ProgressPrimitive.Indicator <ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all" className="h-full w-full flex-1 bg-pr-green transition-all"
style={{ transform: `translateX(-${15 - (value || 0)}%)` }} style={{ transform: `translateX(-${15 - (value || 0)}%)` }}
/> />
</ProgressPrimitive.Root> </ProgressPrimitive.Root>

View File

@ -81,7 +81,7 @@ export function TabsTrigger({
className={cn( className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
isActive isActive
? "bg-gray-700 text-foreground shadow-sm" ? "bg-pr-gray text-foreground shadow-sm"
: "hover:bg-muted/50", : "hover:bg-muted/50",
className, className,
)} )}