From 31955592dd1e1e5c337ba18f074590e6cb53bc5e Mon Sep 17 00:00:00 2001 From: Saeed Abadiyan Date: Sun, 28 Sep 2025 19:07:15 +0330 Subject: [PATCH] refactor: the component ,feat: add tables in componente --- .../mange-ideas-tech-page.tsx | 1049 +++++++++-------- app/components/dashboard/sidebar.tsx | 13 +- app/components/ui/table.tsx | 2 +- app/routes/ecosystem.tsx | 2 +- 4 files changed, 554 insertions(+), 512 deletions(-) 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 e5500f5..aef763f 100644 --- a/app/components/dashboard/project-management/mange-ideas-tech-page.tsx +++ b/app/components/dashboard/project-management/mange-ideas-tech-page.tsx @@ -1,8 +1,15 @@ -import { ChevronDown, ChevronUp, RefreshCw } from "lucide-react"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { ChevronDown, ChevronUp, RefreshCw, Eye, Star } from "lucide-react"; +import { useCallback, useEffect, useRef, useState, useMemo } from "react"; import toast from "react-hot-toast"; import { Badge } from "~/components/ui/badge"; +import { Button } from "~/components/ui/button"; import { Card, CardContent } from "~/components/ui/card"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "~/components/ui/dialog"; import { Table, TableBody, @@ -15,240 +22,78 @@ import apiService from "~/lib/api"; import { formatCurrency, formatNumber } from "~/lib/utils"; import { DashboardLayout } from "../layout"; -interface ProjectData { - WorkflowID: number; - ValueP1215S1887ValueID: number; - ValueP1215S1887StageID: number; - project_no: string; - importance_project: string; - title: string; - strategic_theme: string; - value_technology_and_innovation: string; - type_of_innovation: string; - innovation: string; - person_executing: string; - excellent_observer: string; - observer: string; - moderator: string; - start_date: string; - end_date: string | null; - done_date: string | null; - approved_budget: string; - budget_spent: string; +interface IdeaData { + idea_title: string; + idea_registration_date: string; + idea_status: string; + increased_revenue: string; + full_name: string; + personnel_number: string; + management: string; + deputy: string; + innovator_team_members: string; + innovation_type: string; + idea_originality: string; + idea_axis: string; + idea_description: string; + idea_current_status_description: string; + idea_execution_benefits: string; + process_improvements: string; +} + +interface PersonRanking { + full_name: string; + full_name_count: number; + ranking: number; + stars: number; } interface SortConfig { - field: string; // uses column.key + field: string; direction: "asc" | "desc"; } type ColumnDef = { - key: string; // UI key + key: string; label: string; sortable: boolean; width: string; - apiField?: string; // API field name; defaults to key - computed?: boolean; // not fetched from API }; -// const columns: ColumnDef[] = [ -// { key: "idea_title", label: "عنوان پروژه", sortable: true, width: "200px" }, -// { -// key: "idea_status", -// label: "میزان اهمیت", -// sortable: true, -// width: "150px", -// }, -// { -// key: "strategic_theme", -// label: "مضمون راهبردی", -// sortable: true, -// width: "160px", -// }, -// { -// key: "value_technology_and_innovation", -// label: "ارزش فناوری و نوآوری", -// sortable: true, -// width: "200px", -// }, -// { -// key: "type_of_innovation", -// label: "انواع نوآوری", -// sortable: true, -// width: "140px", -// }, -// { key: "innovation", label: "میزان نوآوری", sortable: true, width: "120px" }, -// { -// key: "person_executing", -// label: "مسئول اجرا", -// sortable: true, -// width: "140px", -// }, -// { -// key: "excellent_observer", -// label: "ناطر عالی", -// sortable: true, -// width: "140px", -// }, -// { key: "observer", label: "ناظر پروژه", sortable: true, width: "140px" }, -// { key: "moderator", label: "مجری", sortable: true, width: "140px" }, -// { -// key: "executive_phase", -// label: "فاز اجرایی", -// sortable: true, -// width: "140px", -// }, -// { key: "start_date", label: "تاریخ شروع", sortable: true, width: "120px" }, -// { -// key: "remaining_time", -// label: "زمان باقی مانده", -// sortable: true, -// width: "140px", -// computed: true, -// }, -// { -// key: "end_date", -// label: "تاریخ پایان (برنامه‌ریزی)", -// sortable: true, -// width: "160px", -// }, -// { -// key: "renewed_duration", -// label: "مدت زمان تمدید", -// sortable: true, -// width: "140px", -// }, -// { -// key: "done_date", -// label: "تاریخ پایان (واقعی)", -// sortable: true, -// width: "160px", -// }, -// { -// key: "deviation_from_program", -// label: "متوسط انحراف برنامه‌ای", -// sortable: true, -// width: "160px", -// }, -// { -// key: "approved_budget", -// label: "بودجه مصوب", -// sortable: true, -// width: "150px", -// }, -// { -// key: "budget_spent", -// label: "بودجه صرف شده", -// sortable: true, -// width: "150px", -// }, -// { -// key: "cost_deviation", -// label: "متوسط انحراف هزینه‌ای", -// sortable: true, -// width: "160px", -// }, -// ]; - const columns: ColumnDef[] = [ - { key: "idea_title", label: "عنوان ایده", sortable: true, width: "200px" }, - { - key: "idea_status", - label: "وضعیت ایده", - sortable: true, - width: "260px", - }, - { key: "idea_axis", label: "محور ایده", sortable: true, width: "160px" }, - { - key: "idea_current_status_description", - label: "توضیح وضعیت فعلی ایده", - sortable: true, - width: "220px", - }, - { - key: "idea_description", - label: "شرح ایده", - sortable: true, - width: "200px", - }, - { - key: "full_name", - label: "نام و نام خانوادگی", - sortable: true, - width: "160px", - }, - { - key: "personnel_number", - label: "شماره پرسنلی", - sortable: true, - width: "140px", - }, - { - key: "innovator_team_members", - label: "اعضای تیم نوآور", - sortable: true, - width: "200px", - }, - { - key: "idea_registration_date", - label: "تاریخ ثبت ایده", - sortable: true, - width: "160px", - }, - { key: "deputy", label: "معاونت مربوطه", sortable: true, width: "160px" }, - { key: "management", label: "مدیریت", sortable: true, width: "140px" }, - { - key: "idea_execution_benefits", - label: "مزایای اجرای ایده", - sortable: true, - width: "220px", - }, - { - key: "innovation_type", - label: "نوع نوآوری", - sortable: true, - width: "160px", - }, - { - key: "idea_originality", - label: "میزان اصالت ایده", - sortable: true, - width: "160px", - }, - { - key: "idea_income", - label: "درآمد حاصل از ایده", - sortable: true, - width: "160px", - }, - { - key: "process_improvements", - label: "بهبودهای فرآیندی", - sortable: true, - width: "180px", - }, + { key: "idea_title", label: "عنوان ایده", sortable: true, width: "250px" }, + { key: "idea_registration_date", label: "تاریخ ثبت ایده", sortable: true, width: "180px" }, + { key: "idea_status", label: "وضعیت ایده", sortable: true, width: "150px" }, + { key: "increased_revenue", label: "درآمد حاصل از ایده", sortable: true, width: "180px" }, + { key: "details", label: "جزئیات بیشتر", sortable: false, width: "120px" }, ]; export function ManageIdeasTechPage() { - const [projects, setProjects] = useState([]); + const [ideas, setIdeas] = useState([]); const [loading, setLoading] = useState(false); const [loadingMore, setLoadingMore] = useState(false); const [currentPage, setCurrentPage] = useState(1); - const [pageSize] = useState(25); + const [pageSize] = useState(10); const [hasMore, setHasMore] = useState(true); const [totalCount, setTotalCount] = useState(0); const [actualTotalCount, setActualTotalCount] = useState(0); + const [selectedIdea, setSelectedIdea] = useState(null); + const [isDetailsOpen, setIsDetailsOpen] = useState(false); const [sortConfig, setSortConfig] = useState({ field: "idea_title", direction: "asc", }); + + // People ranking state + const [peopleRanking, setPeopleRanking] = useState([]); + const [loadingPeople, setLoadingPeople] = useState(false); + const observerRef = useRef(null); const fetchingRef = useRef(false); const scrollTimeoutRef = useRef(null); - const scrollContainerRef = useRef(null); + const scrollContainerRef = useRef(null); - const fetchProjects = async (reset = false) => { - // Prevent concurrent API calls + const fetchIdeas = async (reset = false) => { if (fetchingRef.current) { return; } @@ -265,73 +110,80 @@ export function ManageIdeasTechPage() { const pageToFetch = reset ? 1 : currentPage; - const fetchableColumns = columns.filter((c) => !c.computed); - const outputFields = fetchableColumns.map((c) => c.apiField ?? c.key); - const sortCol = columns.find((c) => c.key === sortConfig.field); - const sortField = sortCol?.computed - ? undefined - : (sortCol?.apiField ?? sortCol?.key); - const response = await apiService.select({ ProcessName: "idea", - OutputFields: outputFields, + OutputFields: [ + "idea_title", + "idea_registration_date", + "idea_status", + "increased_revenue", + "full_name", + "personnel_number", + "management", + "deputy", + "innovator_team_members", + "innovation_type", + "idea_originality", + "idea_axis", + "idea_description", + "idea_current_status_description", + "idea_execution_benefits", + "process_improvements", + ], Pagination: { PageNumber: pageToFetch, PageSize: pageSize }, - Sorts: sortField ? [[sortField, sortConfig.direction]] : [], + Sorts: [[sortConfig.field, sortConfig.direction]], Conditions: [], }); if (response.state === 0) { - // Parse the JSON string from the API response const dataString = response.data; if (dataString && typeof dataString === "string") { try { const parsedData = JSON.parse(dataString); if (Array.isArray(parsedData)) { if (reset) { - setProjects(parsedData); + setIdeas(parsedData); setTotalCount(parsedData.length); } else { - setProjects((prev) => [...prev, ...parsedData]); + setIdeas((prev) => [...prev, ...parsedData]); setTotalCount((prev) => prev + parsedData.length); } - - // Check if there are more items to load setHasMore(parsedData.length === pageSize); } else { if (reset) { - setProjects([]); + setIdeas([]); setTotalCount(0); } setHasMore(false); } } catch (parseError) { - console.error("Error parsing project data:", parseError); + console.error("Error parsing idea data:", parseError); if (reset) { - setProjects([]); + setIdeas([]); setTotalCount(0); } setHasMore(false); } } else { if (reset) { - setProjects([]); + setIdeas([]); setTotalCount(0); } setHasMore(false); } } else { - toast.error(response.message || "خطا در دریافت اطلاعات پروژه‌ها"); + toast.error(response.message || "خطا در دریافت اطلاعات ایده‌ها"); if (reset) { - setProjects([]); + setIdeas([]); setTotalCount(0); } setHasMore(false); } } catch (error) { - console.error("Error fetching projects:", error); - toast.error("خطا در دریافت اطلاعات پروژه‌ها"); + console.error("Error fetching ideas:", error); + toast.error("خطا در دریافت اطلاعات ایده‌ها"); if (reset) { - setProjects([]); + setIdeas([]); setTotalCount(0); } setHasMore(false); @@ -349,13 +201,14 @@ export function ManageIdeasTechPage() { }, [hasMore, loading, loadingMore]); useEffect(() => { - fetchProjects(true); + fetchIdeas(true); fetchTotalCount(); + fetchPeopleRanking(); }, [sortConfig]); useEffect(() => { if (currentPage > 1) { - fetchProjects(false); + fetchIdeas(false); } }, [currentPage]); @@ -366,17 +219,14 @@ export function ManageIdeasTechPage() { const handleScroll = () => { if (!scrollContainer || !hasMore || loadingMore || fetchingRef.current) return; - // Clear previous timeout if (scrollTimeoutRef.current) { clearTimeout(scrollTimeoutRef.current); } - // Debounce scroll events scrollTimeoutRef.current = setTimeout(() => { const { scrollTop, scrollHeight, clientHeight } = scrollContainer; const scrollPercentage = (scrollTop + clientHeight) / scrollHeight; - // Trigger load more when scrolled to 95% of the container if (scrollPercentage >= 0.95) { loadMore(); } @@ -398,14 +248,14 @@ export function ManageIdeasTechPage() { }, [loadMore, hasMore, loadingMore]); const handleSort = (field: string) => { - fetchingRef.current = false; // Reset fetching state on sort + fetchingRef.current = false; setSortConfig((prev) => ({ field, direction: prev.field === field && prev.direction === "asc" ? "desc" : "asc", })); setCurrentPage(1); - setProjects([]); + setIdeas([]); setHasMore(true); }; @@ -435,6 +285,71 @@ export function ManageIdeasTechPage() { } }; + const fetchPeopleRanking = async () => { + try { + setLoadingPeople(true); + + const response = await apiService.select({ + ProcessName: "idea", + OutputFields: ["full_name", "count(full_name)"], + GroupBy: ["full_name"], + }); + + if (response.state === 0) { + const dataString = response.data; + if (dataString && typeof dataString === "string") { + try { + const parsedData = JSON.parse(dataString); + if (Array.isArray(parsedData)) { + // Calculate rankings and stars + const counts = parsedData.map(item => item.full_name_count); + const maxCount = Math.max(...counts); + const minCount = Math.min(...counts); + + // Sort by count first (highest first) + const sortedData = parsedData.sort((a, b) => b.full_name_count - a.full_name_count); + + const rankedPeople = []; + let currentRank = 1; + let sum = 1; + + for (let i = 0; i < sortedData.length; i++) { + const item = sortedData[i]; + + // If this is not the first person and their count is different from previous + if (i > 0 && sortedData[i - 1].full_name_count !== item.full_name_count) { + currentRank = sum + 1; // New rank based on position + sum++; + } + const normalizedScore = maxCount === minCount + ? 1 + : (item.full_name_count - minCount) / (maxCount - minCount); + const stars = Math.max(1, Math.round(normalizedScore * 5)); + + rankedPeople.push({ + full_name: item.full_name, + full_name_count: item.full_name_count, + ranking: currentRank, + stars: stars, + }); + } + setPeopleRanking(rankedPeople); + } + } catch (parseError) { + console.error("Error parsing people ranking data:", parseError); + } + } + } else { + toast.error(response.message || "خطا در دریافت اطلاعات رتبه‌بندی افراد"); + } + } catch (error) { + console.error("Error fetching people ranking:", error); + toast.error("خطا در دریافت اطلاعات رتبه‌بندی افراد"); + } finally { + setLoadingPeople(false); + } + }; + const toPersianDigits = (input: string | number): string => { const str = String(input); const map: Record = { @@ -452,85 +367,11 @@ export function ManageIdeasTechPage() { return str.replace(/[0-9]/g, (d) => map[d] ?? d); }; - // ----- Jalali <-> Gregorian conversion helpers (lightweight, local) ----- - const isJalaliDateString = (raw: string): boolean => { - return /^(\d{4})[\/](\d{1,2})[\/](\d{1,2})$/.test(raw.trim()); - }; - - const div = (a: number, b: number) => ~~(a / b); - - const jalaliToJDN = (jy: number, jm: number, jd: number): number => { - jy = jy - (jy >= 0 ? 474 : 473); - const cycle = 1029983; - const yCycle = 474 + (jy % 2820); - const jdn = - jd + - (jm <= 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186) + - div((yCycle * 682 - 110) as number, 2816) + - (yCycle - 1) * 365 + - div(jy, 2820) * cycle + - (1948320 - 1); - return jdn; - }; - - const jdnToGregorian = (jdn: number): [number, number, number] => { - let j = jdn + 32044; - const g = div(j, 146097); - const dg = j % 146097; - const c = div((div(dg, 36524) + 1) * 3, 4); - const dc = dg - c * 36524; - const b = div(dc, 1461); - const db = dc % 1461; - const a = div((div(db, 365) + 1) * 3, 4); - const da = db - a * 365; - const y = g * 400 + c * 100 + b * 4 + a; - const m = div(da * 5 + 308, 153) - 2; - const d = da - div((m + 4) * 153, 5) + 122; - const year = y - 4800 + div(m + 2, 12); - const month = ((m + 2) % 12) + 1; - const day = d + 1; - return [year, month, day]; - }; - - const jalaliToGregorianDate = (jy: number, jm: number, jd: number): Date => { - const jdn = jalaliToJDN(jy, jm, jd); - const [gy, gm, gd] = jdnToGregorian(jdn); - return new Date(gy, gm - 1, gd); - }; - - const parseToDate = (value: string | null): Date | null => { - if (!value) return null; - const raw = String(value).trim(); - if (isJalaliDateString(raw)) { - const [jy, jm, jd] = raw.split("/").map((s) => Number(s)); - if ([jy, jm, jd].some((n) => Number.isNaN(n))) return null; - return jalaliToGregorianDate(jy, jm, jd); - } - const tryDate = new Date(raw); - return Number.isNaN(tryDate.getTime()) ? null : tryDate; - }; - - const getTodayMidnight = (): Date => { - const now = new Date(); - return new Date(now.getFullYear(), now.getMonth(), now.getDate()); - }; - - const calculateRemainingDays = (end: string | null): number | null => { - if (!end) return null; // if either missing - const endDate = parseToDate(end); - if (!endDate) return null; - const today = getTodayMidnight(); - const MS_PER_DAY = 24 * 60 * 60 * 1000; - const diff = Math.round((endDate.getTime() - today.getTime()) / MS_PER_DAY); - return diff; - }; - const formatDate = (dateString: string | null) => { if (!dateString || dateString === "null" || dateString.trim() === "") { return "-"; } - // If API already returns Jalali like 1404/05/30, just convert digits const raw = String(dateString).trim(); const jalaliPattern = /^(\d{4})[\/](\d{1,2})[\/](\d{1,2})$/; const jalaliMatch = raw.match(jalaliPattern); @@ -541,7 +382,6 @@ export function ManageIdeasTechPage() { return toPersianDigits(`${y}/${mm}/${dd}`); } - // Otherwise, try to parse and render Persian calendar try { const parsed = new Date(raw); if (isNaN(parsed.getTime())) return "-"; @@ -555,228 +395,435 @@ export function ManageIdeasTechPage() { } }; - const phaseColors: Record = { - "تحقیق و توسعه": "#FFD700", // Yellow - آزمایش: "#1E90FF", // Blue - تولید: "#32CD32", // Green - default: "#ccc", // Fallback gray + // Color palette for idea status + const statusColorPalette = ["#3AEA83", "#69C8EA", "#F76276", "#FFD700", "#A757FF", "#E884CE", "#C3BF8B", "#FB7185"]; + + // Build a mapping of status value -> color based on loaded ideas + const statusColorMap = useMemo(() => { + const map: Record = {}; + const seenStatuses = new Set(); + + ideas.forEach((idea) => { + const status = String(idea.idea_status || "").trim(); + if (status && !seenStatuses.has(status)) { + seenStatuses.add(status); + } + }); + + const statusArray = Array.from(seenStatuses).sort(); + statusArray.forEach((status, index) => { + map[status] = statusColorPalette[index % statusColorPalette.length]; + }); + + return map; + }, [ideas]); + + const getStatusColor = (status: string) => { + const statusValue = String(status || "").trim(); + return statusColorMap[statusValue] || "#6B7280"; }; - const getImportanceColor = (importance: string) => { - switch (importance?.toLowerCase()) { - case "تایید شده": - return "var(--color-pr-green)"; // green - case "در حال بررسی": - return "var(--info)"; // آبی - case "رد شده": - return "#F76276"; // red - case "اجرا شده": - return "#69C8EA"; - default: - return "var(--muted)"; // خاکستری پیش‌فرض - } + const handleShowDetails = (idea: IdeaData) => { + setSelectedIdea(idea); + setIsDetailsOpen(true); }; - // idea_income , idea_registration_date , idea_originality , personnel_number - - const renderCellContent = (item: ProjectData, column: ColumnDef) => { - const apiField = column.apiField ?? column.key; - const value = (item as any)[apiField]; + const renderCellContent = (item: IdeaData, column: ColumnDef) => { + const value = (item as any)[column.key]; switch (column.key) { - case "remaining_time": { - const days = calculateRemainingDays(item.end_date); - if (days == null) { - return -; - } - const color = - days > 0 - ? "var(--success)" - : days < 0 - ? "var(--destructive)" - : undefined; + case "idea_title": return ( - - روز {toPersianDigits(days)} - - ); - } - case "idea_income": - return ( - - {formatCurrency(String(value))} - - ); - case "personnel_number": - // case "idea_originality": - return ( - - {toPersianDigits(value as any)}{" "} - + {String(value)} ); case "idea_registration_date": return ( - + {formatDate(String(value))} ); - case "project_no": - return ( - - {String(value)} - - ); - case "idea_title": - return ( - {String(value)} - ); case "idea_status": return ( - - {String(value)} - + + + {!!value ? String(value) : "-"} + + + ); + case "increased_revenue": + return ( + + {formatCurrency(String(value || "0")).replace("ریال" , "")} + + ); + case "details": + return ( + ); default: return ( - + {(value && String(value)) || "-"} ); } }; - const totalPages = Math.ceil(totalCount / pageSize); - return ( -
- {/* Data Table */} - - -
- - - - {columns.map((column) => ( - - {column.sortable ? ( - - ) : ( - column.label - )} - - ))} - - - - {loading ? ( - // Skeleton loading rows (compact) - Array.from({ length: 20 }).map((_, index) => ( - - {columns.map((column) => ( - -
-
-
-
- - ))} +
+
+ {/* People Ranking Table */} +
+

+ رتبه بندی نوآوران +

+ + +
+
+ + + + رتبه + + + ایده پرداز + + + امتیاز + - )) - ) : projects.length === 0 ? ( - - - - هیچ پروژه‌ای یافت نشد - - - - ) : ( - projects.map((project, index) => ( - - {columns.map((column) => ( - - {renderCellContent(project, column)} + + + {loadingPeople ? ( + Array.from({ length: 10 }).map((_, index) => ( + + +
+ + +
+ + +
+ {Array.from({ length: 5 }).map((_, starIndex) => ( +
+ ))} +
+ + + )) + ) : peopleRanking.length === 0 ? ( + + + + هیچ داده‌ای یافت نشد + - ))} - - )) - )} - -
-
+ + ) : ( + peopleRanking.map((person) => ( + + +
+ {toPersianDigits(person.ranking)} +
+
+ + + {person.full_name} + + + +
+ {Array.from({ length: 5 }).map((_, starIndex) => ( + + ))} +
+
+
+ )) + )} + + +
- {/* Infinite scroll trigger */} -
- {loadingMore && ( -
-
- - +
+
+ کل افراد: {toPersianDigits(peopleRanking.length)}
- )} -
- - - {/* Footer */} -
-
- کل پروژه‌ها: {formatNumber(actualTotalCount)} -
+ +
- + + {/* Main Ideas Table */} +
+

+ لیست ایده ها +

+ + +
+ + + + {columns.map((column) => ( + + {column.sortable ? ( + + ) : ( + column.label + )} + + ))} + + + + {loading ? ( + Array.from({ length: 20 }).map((_, index) => ( + + {columns.map((column) => ( + +
+
+
+
+ + ))} + + )) + ) : ideas.length === 0 ? ( + + + + هیچ ایده‌ای یافت نشد + + + + ) : ( + ideas.map((idea, index) => ( + + {columns.map((column) => ( + + {renderCellContent(idea, column)} + + ))} + + )) + )} + +
+
+ + {/* Infinite scroll trigger */} +
+ {loadingMore && ( +
+
+ + + +
+
+ )} +
+
+ + {/* Footer */} +
+
+ کل ایده‌ها: {toPersianDigits(actualTotalCount)} +
+
+
+
+
+ + {/* Details Dialog */} + + + + + جزئیات ایده: {selectedIdea?.idea_title} + + + + {selectedIdea && ( +
+
+
+
+ +

{selectedIdea.full_name || "-"}

+
+ +
+ +

{toPersianDigits(selectedIdea.personnel_number) || "-"}

+
+ +
+ +

{selectedIdea.management || "-"}

+
+ +
+ +

{selectedIdea.deputy || "-"}

+
+ +
+ +

{selectedIdea.innovation_type || "-"}

+
+ +
+ +

{selectedIdea.idea_originality || "-"}

+
+ +
+ +

{selectedIdea.idea_axis || "-"}

+
+
+ +
+
+ +

{selectedIdea.innovator_team_members || "-"}

+
+ +
+ +

+ {selectedIdea.idea_description || "-"} +

+
+ +
+ +

+ {selectedIdea.idea_current_status_description || "-"} +

+
+ +
+ +

+ {selectedIdea.idea_execution_benefits || "-"} +

+
+ +
+ +

+ {selectedIdea.process_improvements || "-"} +

+
+ +
+ +

+ {formatCurrency(selectedIdea.increased_revenue)} +

+
+
+
+
+ )} +
+
); diff --git a/app/components/dashboard/sidebar.tsx b/app/components/dashboard/sidebar.tsx index ab0f8c1..183f916 100644 --- a/app/components/dashboard/sidebar.tsx +++ b/app/components/dashboard/sidebar.tsx @@ -19,7 +19,8 @@ import { House, ListTodo, LightbulbIcon, - Radar + Radar, + LucideLightbulb } from "lucide-react"; import React, { useState } from "react"; import { Link, useLocation } from "react-router"; @@ -108,16 +109,10 @@ const menuItems: MenuItem[] = [ { id: "ideas", label: "ایده‌های فناوری و نوآوری", - icon: House, + icon: LucideLightbulb, href: "/dashboard/manage-ideas-tech", }, - { - id: "top-innovations", - label: "نوآور برتر", - icon: Star, - href: "/dashboard/top-innovations", - }, - { + { id: "strategic-alignment", label: "میزان انطباق راهبردی", icon: null, diff --git a/app/components/ui/table.tsx b/app/components/ui/table.tsx index 7aa9a12..6626be6 100644 --- a/app/components/ui/table.tsx +++ b/app/components/ui/table.tsx @@ -4,7 +4,7 @@ import { cn } from "~/lib/utils" interface TableProps extends React.HTMLAttributes { containerClassName?: string - containerRef?: React.RefObject + containerRef?: React.RefObject } const Table = React.forwardRef( diff --git a/app/routes/ecosystem.tsx b/app/routes/ecosystem.tsx index 87009ca..696ced1 100644 --- a/app/routes/ecosystem.tsx +++ b/app/routes/ecosystem.tsx @@ -70,7 +70,7 @@ export default function EcosystemPage() { return ( -
+