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, TableCell, TableHead, TableHeader, TableRow, } from "~/components/ui/table"; import apiService from "~/lib/api"; import { formatCurrency, formatNumber } from "~/lib/utils"; import { DashboardLayout } from "../layout"; 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; direction: "asc" | "desc"; } type ColumnDef = { key: string; label: string; sortable: boolean; width: string; }; const columns: ColumnDef[] = [ { 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 [ideas, setIdeas] = useState([]); const [loading, setLoading] = useState(false); const [loadingMore, setLoadingMore] = useState(false); const [currentPage, setCurrentPage] = useState(1); 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 fetchIdeas = async (reset = false) => { if (fetchingRef.current) { return; } try { fetchingRef.current = true; if (reset) { setLoading(true); setCurrentPage(1); } else { setLoadingMore(true); } const pageToFetch = reset ? 1 : currentPage; const response = await apiService.select({ ProcessName: "idea", 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: [[sortConfig.field, sortConfig.direction]], Conditions: [], }); if (response.state === 0) { const dataString = response.data; if (dataString && typeof dataString === "string") { try { const parsedData = JSON.parse(dataString); if (Array.isArray(parsedData)) { if (reset) { setIdeas(parsedData); setTotalCount(parsedData.length); } else { setIdeas((prev) => [...prev, ...parsedData]); setTotalCount((prev) => prev + parsedData.length); } setHasMore(parsedData.length === pageSize); } else { if (reset) { setIdeas([]); setTotalCount(0); } setHasMore(false); } } catch (parseError) { console.error("Error parsing idea data:", parseError); if (reset) { setIdeas([]); setTotalCount(0); } setHasMore(false); } } else { if (reset) { setIdeas([]); setTotalCount(0); } setHasMore(false); } } else { toast.error(response.message || "خطا در دریافت اطلاعات ایده‌ها"); if (reset) { setIdeas([]); setTotalCount(0); } setHasMore(false); } } catch (error) { console.error("Error fetching ideas:", error); toast.error("خطا در دریافت اطلاعات ایده‌ها"); if (reset) { setIdeas([]); setTotalCount(0); } setHasMore(false); } finally { setLoading(false); setLoadingMore(false); fetchingRef.current = false; } }; const loadMore = useCallback(() => { if (hasMore && !loading && !loadingMore && !fetchingRef.current) { setCurrentPage((prev) => prev + 1); } }, [hasMore, loading, loadingMore]); useEffect(() => { fetchIdeas(true); fetchTotalCount(); fetchPeopleRanking(); }, [sortConfig]); useEffect(() => { if (currentPage > 1) { fetchIdeas(false); } }, [currentPage]); // Infinite scroll observer with debouncing useEffect(() => { const scrollContainer = scrollContainerRef.current; const handleScroll = () => { if (!scrollContainer || !hasMore || loadingMore || fetchingRef.current) return; if (scrollTimeoutRef.current) { clearTimeout(scrollTimeoutRef.current); } scrollTimeoutRef.current = setTimeout(() => { const { scrollTop, scrollHeight, clientHeight } = scrollContainer; const scrollPercentage = (scrollTop + clientHeight) / scrollHeight; if (scrollPercentage >= 0.95) { loadMore(); } }, 150); }; if (scrollContainer) { scrollContainer.addEventListener("scroll", handleScroll, { passive: true }); } return () => { if (scrollContainer) { scrollContainer.removeEventListener("scroll", handleScroll); } if (scrollTimeoutRef.current) { clearTimeout(scrollTimeoutRef.current); } }; }, [loadMore, hasMore, loadingMore]); const handleSort = (field: string) => { fetchingRef.current = false; setSortConfig((prev) => ({ field, direction: prev.field === field && prev.direction === "asc" ? "desc" : "asc", })); setCurrentPage(1); setIdeas([]); setHasMore(true); }; const fetchTotalCount = async () => { try { const response = await apiService.select({ ProcessName: "idea", OutputFields: ["count(idea_title)"], Conditions: [], }); if (response.state === 0) { const dataString = response.data; if (dataString && typeof dataString === "string") { try { const parsedData = JSON.parse(dataString); if (Array.isArray(parsedData) && parsedData[0]) { setActualTotalCount(parsedData[0].idea_title_count || 0); } } catch (parseError) { console.error("Error parsing count data:", parseError); } } } } catch (error) { console.error("Error fetching total count:", error); } }; 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 = { "0": "۰", "1": "۱", "2": "۲", "3": "۳", "4": "۴", "5": "۵", "6": "۶", "7": "۷", "8": "۸", "9": "۹", }; return str.replace(/[0-9]/g, (d) => map[d] ?? d); }; const formatDate = (dateString: string | null) => { if (!dateString || dateString === "null" || dateString.trim() === "") { return "-"; } const raw = String(dateString).trim(); const jalaliPattern = /^(\d{4})[\/](\d{1,2})[\/](\d{1,2})$/; const jalaliMatch = raw.match(jalaliPattern); if (jalaliMatch) { const [, y, m, d] = jalaliMatch; const mm = m.padStart(2, "0"); const dd = d.padStart(2, "0"); return toPersianDigits(`${y}/${mm}/${dd}`); } try { const parsed = new Date(raw); if (isNaN(parsed.getTime())) return "-"; return new Intl.DateTimeFormat("fa-IR-u-ca-persian", { year: "numeric", month: "2-digit", day: "2-digit", }).format(parsed); } catch { return "-"; } }; // 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 handleShowDetails = (idea: IdeaData) => { setSelectedIdea(idea); setIsDetailsOpen(true); }; const renderCellContent = (item: IdeaData, column: ColumnDef) => { const value = (item as any)[column.key]; switch (column.key) { case "idea_title": return ( {String(value)} ); case "idea_registration_date": return ( {formatDate(String(value))} ); case "idea_status": return ( {!!value ? String(value) : "-"} ); case "increased_revenue": return ( {formatCurrency(String(value || "0")).replace("ریال" , "")} ); case "details": return ( ); default: return ( {(value && String(value)) || "-"} ); } }; return (
{/* People Ranking Table */}

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

رتبه ایده پرداز امتیاز {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) => ( ))}
)) )}
کل افراد: {toPersianDigits(peopleRanking.length)}
{/* 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)}

)}
); }