diff --git a/app/components/dashboard/project-management/product-innovation-page.tsx b/app/components/dashboard/project-management/product-innovation-page.tsx index 16d1f11..da18932 100644 --- a/app/components/dashboard/project-management/product-innovation-page.tsx +++ b/app/components/dashboard/project-management/product-innovation-page.tsx @@ -23,6 +23,12 @@ import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; import { Checkbox } from "~/components/ui/checkbox"; import { Bar, BarChart, LabelList } from "recharts" +import { + Popover, + PopoverTrigger, + PopoverContent, +} from "~/components/ui/popover" + import { FunnelChart } from "~/components/ui/funnel-chart"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from "recharts"; import { @@ -43,9 +49,10 @@ import { TableRow, } from "~/components/ui/table"; import apiService from "~/lib/api"; -import { formatNumber } from "~/lib/utils"; +import { formatNumber, handleDataValue } from "~/lib/utils"; import { DashboardLayout } from "../layout"; import { Skeleton } from "~/components/ui/skeleton"; +import { Tooltip as TooltipSh, TooltipTrigger, TooltipContent } from "~/components/ui/tooltip"; moment.loadPersian({ usePersianDigits: true }); @@ -130,7 +137,57 @@ const columns = [ { key: "details", label: "جزئیات پروژه", sortable: false, width: "140px" }, ]; + +export default function Timeline() { + const stages = ["تجاری سازی", "توسعه", "تحلیل بازار", "ثبت ایده"]; + const currentStage = 1; // index of current stage + + return ( +
+ {/* Year labels */} +
+ ۱۴۰۷ + ۱۴۰۶ + ۱۴۰۵ + ۱۴۰۴ +
+ + {/* Timeline bar */} +
+ {stages.map((stage, index) => ( +
+ + +
+ {stage} +
+
+
+
+ ))} + + {/* Vertical line showing current position */} +
+
وضعیت فعلی
+
+
+ ); +} + + + export function ProductInnovationPage() { + const [showPopup, setShowPopup] = useState(false); const [projects, setProjects] = useState([]); const [loading, setLoading] = useState(false); const [loadingMore, setLoadingMore] = useState(false); @@ -202,6 +259,7 @@ export function ProductInnovationPage() { const handleProjectDetails = async (project: ProductInnovationData) => { setSelectedProjectDetails(project); + console.log(project) setDetailsDialogOpen(true); await fetchPopupData(project); }; @@ -220,7 +278,7 @@ export function ProductInnovationPage() { if (statsResponse.state === 0) { const statsData = JSON.parse(statsResponse.data); if (statsData.innovation_product_popup_function1 && statsData.innovation_product_popup_function1[0]) { - setPopupStats(statsData.innovation_product_popup_function1[0]); + setPopupStats(JSON.parse(statsData.innovation_product_popup_function1)[0]); } } @@ -234,7 +292,6 @@ export function ProductInnovationPage() { ], GroupBy: ["product_title", "full_season"] }); - if (chartResponse.state === 0) { const chartData = JSON.parse(chartResponse.data); if (Array.isArray(chartData)) { @@ -865,177 +922,244 @@ export function ProductInnovationPage() { {/* Project Details Dialog */} - + - - جزئیات پروژه + + شرح پروژه -
- - {/* Right Column - Stats Cards and Details */} -
+
+ {/* right Column - Stats Cards and Details */} +
{/* Stats Cards */}
-
-

میزان صادارت محصول جدید

- {popupLoading ? ( -
-
-
-
-
- ) : ( - <> -
- {formatNumber(popupStats.new_products_export)} -
-
میلیون ریال
-
- {formatNumber(popupStats.new_products_export_percent)}% -
-
درصد به کل صادرات
- - )} -
- -
-

تاثیر در واردات

- {popupLoading ? ( -
-
-
-
-
- ) : ( - <> -
- {formatNumber(popupStats.import_impact)} -
-
میلیون ریال
-
- {formatNumber(popupStats.import_impact_percent)}% -
-
درصد صرفه جویی
- - )} -
+

{selectedProjectDetails?.title}

+

{selectedProjectDetails?.project_description}

+ {/* Technical Knowledge */} -
-

دانش فنی محصول جدید

-
-
- - توسعه درونزا -
-
- - همکاری فناورانه -
-
- - سایر -
-
+
+

دانش فنی محصول جدید

+
+
+ توسعه درونزا + + +
+ +
+ همکاری فناورانه + + +
+ +
+ سایر + +
+
{/* Standards */} -
-

استانداردهای ملی و بین المللی اخذ شده

-
-
-
- استاندارد ملی شماره یک -
-
-
- استاندارد بین المللی شماره یک -
-
-
- استاندارد ملی شماره یک -
-
-
- استاندارد ملی شماره یک -
-
-
+
+

+ استانداردهای ملی و بین‌المللی اخذ شده +

+ + {selectedProjectDetails?.obtained_standard_title && selectedProjectDetails?.obtained_standard_title.length > 0 ? ( +
+ {(Array.isArray(selectedProjectDetails?.obtained_standard_title) + ? selectedProjectDetails?.obtained_standard_title + : [selectedProjectDetails?.obtained_standard_title] + ).map((standard, index) => ( +
+
+ {standard} +
+ ))} +
+ ) : ( +

+ هیچ استانداردی ثبت نشده است. +

+ )} +
{/* Knowledge-based Certificate Button */} -
- -
+
+ {selectedProjectDetails?.knowledge_based_certificate_obtained === "خیر" ? ( +
+ +
+ ) : ( + + + + + + + + +
+

+ شماره گواهی: + {selectedProjectDetails?.knowledge_based_certificate_number || + "—"} +

+

+ تاریخ اخذ: + {handleDataValue(selectedProjectDetails?.certificate_obtain_date) || "—"} +

+

+ مرجع صادرکننده: + {selectedProjectDetails?.issuing_authority || "—"} +

+
+
+
+
+
+ )} +
{/* Left Column - Project Description and Charts */} -
+ {popupLoading ? ( +
+
+ + + + + + + + + + +
+
+ + +
+
+ + +
+
+ ) : ( +
{/* Project Description */} -
-

- {selectedProjectDetails?.title} -

-

- {selectedProjectDetails?.project_description || "-"} -

- - {/* Project Timeline */} -
-
- ۱۴۰۴ - ۱۴۰۵ - ۱۴۰۶ - ۱۴۰۷ -
-
-
-
- ثبت ایده -
-
-
-
- تحلیل بازار -
-
-
-
- توسعه -
-
-
-
- تجاری سازی -
-
-
وضعیت فعلی: تحلیل بازار
-
+
+ + +
+
+

+ میزان صادارت محصول جدید +

+ +
+ +
+

+ {formatNumber(Math.round(popupStats?.new_products_export > 0 ? popupStats?.new_products_export : 0)) || formatNumber(0)} +

+

+ میلیون ریال +

+
+ / +
+

+ {formatNumber(Math.round(popupStats?.new_products_export_percent > 0 ? popupStats?.new_products_export_percent : 0)) || formatNumber(0)}% +

+

+ درصد به کل صادرات +

+
+
+
+
+ + + +
+
+

+ تاثیر در واردات +

+ +
+ +
+

+ {formatNumber(Math.round(popupStats?.import_impact > 0 ? popupStats?.import_impact : 0)) || formatNumber(0)} +

+

+ میلیون ریال +

+
+ / +
+

+ {formatNumber(Math.round(popupStats?.import_impact_percent > 0 ? popupStats?.import_impact_percent : 0)) || formatNumber(0)}% +

+

+ درصد صرفه جویی +

+
+
+
+
+ + +
{/* Export Revenue Bar Chart */} -
-

ظرفیت صادر شده

-
+
+

ظرفیت صادر شده

+
{popupLoading ? (
-
در حال بارگذاری...
+
در حال بارگذاری...
) : exportChartData.length > 0 ? ( - + {/* Export Revenue Line Chart */} -
-

ظرفیت صادر شده

-
+
+

ظرفیت صادر شده

+
{popupLoading ? ( -
-
در حال بارگذاری...
+
+
در حال بارگذاری...
) : allExportData.length > 0 ? ( @@ -1176,6 +1300,7 @@ export function ProductInnovationPage() {
+ )}
diff --git a/app/components/ui/popover.tsx b/app/components/ui/popover.tsx new file mode 100644 index 0000000..9be5957 --- /dev/null +++ b/app/components/ui/popover.tsx @@ -0,0 +1,48 @@ +"use client" + +import * as React from "react" +import * as PopoverPrimitive from "@radix-ui/react-popover" + +import { cn } from "~/lib/utils" + +function Popover({ + ...props +}: React.ComponentProps) { + return +} + +function PopoverTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function PopoverContent({ + className, + align = "center", + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function PopoverAnchor({ + ...props +}: React.ComponentProps) { + return +} + +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } diff --git a/app/lib/utils.ts b/app/lib/utils.ts index dc79b88..2bc7fa1 100644 --- a/app/lib/utils.ts +++ b/app/lib/utils.ts @@ -1,5 +1,6 @@ import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; +import moment from "moment-jalaali"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -20,3 +21,23 @@ export const formatCurrency = (amount: string | number) => { if (isNaN(numericAmount)) return "0 ریال"; return new Intl.NumberFormat("fa-IR").format(numericAmount) + " ریال"; }; + + + +export const handleDataValue = (val: any): any => { +moment.loadPersian({ usePersianDigits: true }); + if (val == null) return val; + if ( + typeof val === "string" && + /^\d{4}[-/]\d{2}[-/]\d{2}( \d{2}:\d{2}(:\d{2})?)?$/.test(val) + ) { + return moment(val, "YYYY-MM-DD HH:mm:ss").format("YYYY/MM/DD"); + } + if ( + typeof val === "number" || + (typeof val === "string" && /^-?\d+$/.test(val)) + ) { + return val.toString().replace(/\d/g, (d) => "۰۱۲۳۴۵۶۷۸۹"[+d]); + } + return val; +} diff --git a/package.json b/package.json index e855d04..fcd6efb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-slot": "^1.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0691b8..b6aaa68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@radix-ui/react-label': specifier: ^2.0.2 version: 2.1.7(@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-popover': + specifier: ^1.1.15 + version: 1.1.15(@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-progress': specifier: ^1.1.7 version: 1.1.7(@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) @@ -645,6 +648,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} + 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-popper@1.2.8': resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: @@ -3009,6 +3025,29 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-popover@1.1.15(@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-focus-guards': 1.1.3(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.7(@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) + aria-hidden: 1.2.6 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@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)': dependencies: '@floating-ui/react-dom': 2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)