diff --git a/.gitignore b/.gitignore index 9b7c041..7a00baa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ # React Router /.react-router/ /build/ + +.env diff --git a/app/components/common/not-found.tsx b/app/components/common/not-found.tsx index ab06dac..380ab1d 100644 --- a/app/components/common/not-found.tsx +++ b/app/components/common/not-found.tsx @@ -24,7 +24,10 @@ export function NotFound({ }; return ( -
+
{/* 404 Illustration */}
@@ -50,10 +53,10 @@ export function NotFound({ {/* Error Message */}
-

+

{title}

-

+

{message}

diff --git a/app/components/dashboard/dashboard-home.tsx b/app/components/dashboard/dashboard-home.tsx new file mode 100644 index 0000000..19b57a6 --- /dev/null +++ b/app/components/dashboard/dashboard-home.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { DashboardLayout } from "./layout"; +import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; + +export function DashboardHome() { + return ( + +
+ {/* Main Content Area - Empty for now */} +
+ + + +
+
+
+ + + +
+

+ صفحه در دست ساخت +

+

+ محتوای این بخش به زودی اضافه خواهد شد +

+
+
+
+
+
+
+
+ ); +} + +export default DashboardHome; diff --git a/app/components/dashboard/header.tsx b/app/components/dashboard/header.tsx new file mode 100644 index 0000000..c88eb06 --- /dev/null +++ b/app/components/dashboard/header.tsx @@ -0,0 +1,131 @@ +import { useEffect, useState } from "react"; +import { useAuth } from "~/contexts/auth-context"; +import { Link } from "react-router"; +import { cn } from "~/lib/utils"; +import { Button } from "~/components/ui/button"; +import { + Search, + Bell, + Settings, + User, + Moon, + Sun, + Menu, + ChevronDown, + Globe, + HelpCircle, +} from "lucide-react"; + +interface HeaderProps { + onToggleSidebar?: () => void; + className?: string; + title?: string; +} + +export function Header({ + onToggleSidebar, + className, + title = "داشبورد", +}: HeaderProps) { + const { user } = useAuth(); + const [isProfileMenuOpen, setIsProfileMenuOpen] = useState(false); + const [isNotificationOpen, setIsNotificationOpen] = useState(false); + + return ( +
+ {/* Left Section */} +
+ {/* Mobile Menu Toggle */} + {onToggleSidebar && ( + + )} + + {/* Page Title */} +

{title}

+
+ + {/* Right Section */} +
+ {/* User Menu */} +
+ + + {/* Profile Dropdown */} + {isProfileMenuOpen && ( +
+
+
+ {user?.name} {user?.family} +
+
+ {user?.email} +
+
+
+ setIsProfileMenuOpen(false)} + > + + پروفایل کاربری + + setIsProfileMenuOpen(false)} + > + + تنظیمات + +
+
+ )} +
+
+ + {/* Click outside to close dropdowns */} + {(isProfileMenuOpen || isNotificationOpen) && ( +
{ + setIsProfileMenuOpen(false); + setIsNotificationOpen(false); + }} + /> + )} +
+ ); +} + +export default Header; diff --git a/app/components/dashboard/layout.tsx b/app/components/dashboard/layout.tsx new file mode 100644 index 0000000..b310a11 --- /dev/null +++ b/app/components/dashboard/layout.tsx @@ -0,0 +1,86 @@ +import { useState } from "react"; +import { cn } from "~/lib/utils"; +import { Sidebar } from "./sidebar"; +import { Header } from "./header"; + +interface DashboardLayoutProps { + children: React.ReactNode; + className?: string; + title?: string; +} + +export function DashboardLayout({ + children, + className, + title, +}: DashboardLayoutProps) { + const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); + const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false); + + const toggleSidebarCollapse = () => { + setIsSidebarCollapsed(!isSidebarCollapsed); + }; + + const toggleMobileSidebar = () => { + setIsMobileSidebarOpen(!isMobileSidebarOpen); + }; + + return ( +
+ {/* Gradient overlay */} +
+ {/* Mobile sidebar overlay */} + {isMobileSidebarOpen && ( +
setIsMobileSidebarOpen(false)} + > +
+
+ )} + + {/* Sidebar */} +
+ +
+ + {/* Main content area */} +
+ {/* Header */} +
+ + {/* Main content */} +
+
+ {children} +
+
+
+
+ ); +} + +export default DashboardLayout; diff --git a/app/components/dashboard/project-management/project-management-page.tsx b/app/components/dashboard/project-management/project-management-page.tsx new file mode 100644 index 0000000..6125475 --- /dev/null +++ b/app/components/dashboard/project-management/project-management-page.tsx @@ -0,0 +1,521 @@ +import { useState, useEffect, useCallback, useRef } from "react"; +import { DashboardLayout } from "../layout"; +import { Card, CardContent } from "~/components/ui/card"; +import { Button } from "~/components/ui/button"; +import { Badge } from "~/components/ui/badge"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "~/components/ui/table"; +import { + ChevronUp, + ChevronDown, + RefreshCw, + Calendar, + DollarSign, + Users, + Target, +} from "lucide-react"; +import apiService from "~/lib/api"; +import toast from "react-hot-toast"; + +interface ProjectData { + WorkflowID: number; + ValueP1215S1887ValueID: number; + ValueP1215S1887StageID: number; + project_no: 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 SortConfig { + field: string; + direction: "asc" | "desc"; +} + +const columns = [ + { key: "project_no", label: "شماره پروژه", sortable: true, width: "120px" }, + { key: "title", label: "عنوان پروژه", sortable: true, width: "200px" }, + { + 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: "start_date", + label: "تاریخ شروع", + sortable: true, + width: "120px", + }, + { + key: "end_date", + label: "تاریخ پایان نهایی", + sortable: true, + width: "140px", + }, + { + key: "done_date", + label: "تاریخ انجام نهایی", + sortable: true, + width: "140px", + }, + { + key: "approved_budget", + label: "بودجه مصوب", + sortable: true, + width: "150px", + }, + { + key: "budget_spent", + label: "بودجه هزینه شده", + sortable: true, + width: "150px", + }, +]; + +export function ProjectManagementPage() { + const [projects, setProjects] = useState([]); + const [loading, setLoading] = useState(false); + const [loadingMore, setLoadingMore] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const [pageSize] = useState(20); + const [hasMore, setHasMore] = useState(true); + const [totalCount, setTotalCount] = useState(0); + const [actualTotalCount, setActualTotalCount] = useState(0); + const [sortConfig, setSortConfig] = useState({ + field: "start_date", + direction: "asc", + }); + const observerRef = useRef(null); + + const fetchProjects = async (reset = false) => { + try { + if (reset) { + setLoading(true); + setCurrentPage(1); + } else { + setLoadingMore(true); + } + + const pageToFetch = reset ? 1 : currentPage; + + const response = await apiService.select({ + ProcessName: "project", + OutputFields: [ + "project_no", + "title", + "strategic_theme", + "value_technology_and_innovation", + "type_of_innovation", + "innovation", + "person_executing", + "excellent_observer", + "observer", + "moderator", + "start_date", + "end_date", + "done_date", + "approved_budget", + "budget_spent", + ], + Pagination: { PageNumber: pageToFetch, PageSize: pageSize }, + 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); + setTotalCount(parsedData.length); + } else { + setProjects((prev) => [...prev, ...parsedData]); + setTotalCount((prev) => prev + parsedData.length); + } + + // Check if there are more items to load + setHasMore(parsedData.length === pageSize); + } else { + if (reset) { + setProjects([]); + setTotalCount(0); + } + setHasMore(false); + } + } catch (parseError) { + console.error("Error parsing project data:", parseError); + if (reset) { + setProjects([]); + setTotalCount(0); + } + setHasMore(false); + } + } else { + if (reset) { + setProjects([]); + setTotalCount(0); + } + setHasMore(false); + } + } else { + toast.error(response.message || "خطا در دریافت اطلاعات پروژه‌ها"); + if (reset) { + setProjects([]); + setTotalCount(0); + } + setHasMore(false); + } + } catch (error) { + console.error("Error fetching projects:", error); + toast.error("خطا در دریافت اطلاعات پروژه‌ها"); + if (reset) { + setProjects([]); + setTotalCount(0); + } + setHasMore(false); + } finally { + setLoading(false); + setLoadingMore(false); + } + }; + + const loadMore = useCallback(() => { + if (!loadingMore && hasMore) { + setCurrentPage((prev) => prev + 1); + } + }, [loadingMore, hasMore]); + + useEffect(() => { + fetchProjects(true); + fetchTotalCount(); + }, [sortConfig]); + + useEffect(() => { + if (currentPage > 1) { + fetchProjects(false); + } + }, [currentPage]); + + // Infinite scroll observer + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && hasMore && !loadingMore) { + loadMore(); + } + }, + { threshold: 0.1 }, + ); + + if (observerRef.current) { + observer.observe(observerRef.current); + } + + return () => { + if (observerRef.current) { + observer.unobserve(observerRef.current); + } + }; + }, [loadMore, hasMore, loadingMore]); + + const handleSort = (field: string) => { + setSortConfig((prev) => ({ + field, + direction: + prev.field === field && prev.direction === "asc" ? "desc" : "asc", + })); + setCurrentPage(1); + setProjects([]); + setHasMore(true); + }; + + const fetchTotalCount = async () => { + try { + const response = await apiService.select({ + ProcessName: "project", + OutputFields: ["count(project_no)"], + 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].project_no_count || 0); + } + } catch (parseError) { + console.error("Error parsing count data:", parseError); + } + } + } + } catch (error) { + console.error("Error fetching total count:", error); + } + }; + + const handleRefresh = () => { + setCurrentPage(1); + setProjects([]); + setHasMore(true); + fetchProjects(true); + fetchTotalCount(); + }; + + const formatCurrency = (amount: string | number) => { + if (!amount) return "0 ریال"; + // Remove commas and convert to number + const numericAmount = + typeof amount === "string" + ? parseFloat(amount.replace(/,/g, "")) + : amount; + if (isNaN(numericAmount)) return "0 ریال"; + return new Intl.NumberFormat("fa-IR").format(numericAmount) + " ریال"; + }; + + const formatDate = (dateString: string | null) => { + if (!dateString || dateString === "null" || dateString.trim() === "") + return "-"; + try { + return new Intl.DateTimeFormat("fa-IR").format(new Date(dateString)); + } catch { + return "-"; + } + }; + + const renderCellContent = (item: ProjectData, column: any) => { + const value = item[column.key as keyof ProjectData]; + + switch (column.key) { + case "approved_budget": + case "budget_spent": + return ( + + {formatCurrency(String(value))} + + ); + case "start_date": + case "end_date": + case "done_date": + return ( + {formatDate(String(value))} + ); + case "project_no": + return ( + + {String(value)} + + ); + case "title": + return {String(value)}; + default: + return {String(value) || "-"}; + } + }; + + const totalPages = Math.ceil(totalCount / pageSize); + + return ( + +
+ {/* Actions */} +
+ +
+ {/* Data Table */} + + +
+
+ + + + {columns.map((column) => ( + + {column.sortable ? ( + + ) : ( + column.label + )} + + ))} + + + + {loading ? ( + + +
+ + + در حال بارگذاری... + +
+
+
+ ) : projects.length === 0 ? ( + + + + هیچ پروژه‌ای یافت نشد + + + + ) : ( + projects.map((project, index) => ( + + {columns.map((column) => ( + + {renderCellContent(project, column)} + + ))} + + )) + )} +
+
+
+
+ + {/* Infinite scroll trigger */} +
+ {loadingMore && ( +
+ + + در حال بارگذاری... + +
+ )} + {!hasMore && projects.length > 0 && ( +
+ + همه داده‌ها نمایش داده شد + +
+ )} +
+
+ + {/* Footer */} +
+
+ + نمایش {projects.length} از {actualTotalCount} پروژه + + کل پروژه‌ها: {actualTotalCount} +
+
+
+
+
+ ); +} diff --git a/app/components/dashboard/projects/project-detail.tsx b/app/components/dashboard/projects/project-detail.tsx new file mode 100644 index 0000000..e7c6802 --- /dev/null +++ b/app/components/dashboard/projects/project-detail.tsx @@ -0,0 +1,354 @@ +import React from "react"; +import { DashboardLayout } from "../layout"; +import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; +import { Button } from "~/components/ui/button"; +import { Badge } from "~/components/ui/badge"; +import { + ArrowRight, + Calendar, + User, + Users, + DollarSign, + Clock, + FileText, + Edit, + Trash2, +} from "lucide-react"; + +interface ProjectDetailProps { + projectId: string; +} + +// Mock project data +const mockProject = { + id: 1, + name: "پروژه توسعه اپلیکیشن موبایل", + manager: "علی احمدی", + team: "تیم توسعه موبایل", + status: "در حال انجام", + priority: "بالا", + startDate: "1403/01/15", + endDate: "1403/06/30", + budget: "500,000,000", + progress: 65, + description: "این پروژه شامل توسعه یک اپلیکیشن موبایل کراس پلتفرم برای مدیریت پروژه‌ها و وظایف می‌باشد. اپلیکیشن باید قابلیت‌های مختلفی از جمله مدیریت کاربران، گزارش‌گیری و نوتیفیکیشن را داشته باشد.", + teamMembers: [ + { id: 1, name: "علی احمدی", role: "مدیر پروژه", avatar: "AA" }, + { id: 2, name: "سارا کریمی", role: "توسعه‌دهنده فرانت‌اند", avatar: "SK" }, + { id: 3, name: "محمد رضایی", role: "توسعه‌دهنده بک‌اند", avatar: "MR" }, + { id: 4, name: "فاطمه موسوی", role: "طراح UI/UX", avatar: "FM" }, + ], + milestones: [ + { id: 1, title: "تحلیل نیازمندی‌ها", status: "تکمیل شده", date: "1403/01/30" }, + { id: 2, title: "طراحی رابط کاربری", status: "تکمیل شده", date: "1403/02/15" }, + { id: 3, title: "توسعه بک‌اند", status: "در حال انجام", date: "1403/04/01" }, + { id: 4, title: "توسعه فرانت‌اند", status: "در حال انجام", date: "1403/05/01" }, + { id: 5, title: "تست و رفع باگ", status: "در انتظار", date: "1403/06/01" }, + { id: 6, title: "انتشار نهایی", status: "در انتظار", date: "1403/06/30" }, + ], + tasks: [ + { id: 1, title: "پیاده‌سازی سیستم احراز هویت", assignee: "محمد رضایی", status: "در حال انجام", priority: "بالا" }, + { id: 2, title: "طراحی صفحه داشبورد", assignee: "سارا کریمی", status: "تکمیل شده", priority: "متوسط" }, + { id: 3, title: "توسعه API گزارش‌گیری", assignee: "محمد رضایی", status: "در انتظار", priority: "بالا" }, + { id: 4, title: "طراحی آیکون‌های اپلیکیشن", assignee: "فاطمه موسوی", status: "در حال انجام", priority: "پایین" }, + ], +}; + +const statusColors = { + "در حال انجام": "info", + "تکمیل شده": "success", + "در انتظار": "warning", + "لغو شده": "destructive", +} as const; + +const priorityColors = { + "بالا": "destructive", + "متوسط": "warning", + "پایین": "secondary", +} as const; + +export function ProjectDetail({ projectId }: ProjectDetailProps) { + const project = mockProject; // In real app, fetch by projectId + + return ( + +
+ {/* Breadcrumb */} +
+ + + + جزئیات پروژه + +
+ + {/* Project Header */} +
+
+

+ {project.name} +

+

+ {project.description} +

+
+ +
+ + +
+
+ + {/* Project Stats */} +
+ + +
+
+ +
+
+

تاریخ شروع

+

+ {project.startDate} +

+
+
+
+
+ + + +
+
+ +
+
+

تاریخ پایان

+

+ {project.endDate} +

+
+
+
+
+ + + +
+
+ +
+
+

بودجه

+

+ {project.budget} ریال +

+
+
+
+
+ + + +
+
+ +
+
+

پیشرفت

+

+ {project.progress}% +

+
+
+
+
+
+ +
+ {/* Project Details */} +
+ {/* Progress Bar */} + + + پیشرفت پروژه + + +
+
+ + {project.progress}% تکمیل شده + +
+ + {project.status} + + + اولویت {project.priority} + +
+
+
+
+
+
+
+
+ + {/* Milestones */} + + + مراحل پروژه + + +
+ {project.milestones.map((milestone) => ( +
+
+
+
+

+ {milestone.title} +

+

+ {milestone.date} +

+
+
+ + {milestone.status} + +
+ ))} +
+
+
+ + {/* Recent Tasks */} + + + وظایف اخیر + + +
+ {project.tasks.map((task) => ( +
+
+

+ {task.title} +

+

+ مسئول: {task.assignee} +

+
+
+ + {task.status} + + + {task.priority} + +
+
+ ))} +
+
+
+
+ + {/* Sidebar */} +
+ {/* Project Info */} + + + اطلاعات پروژه + + +
+ +
+

مدیر پروژه

+

+ {project.manager} +

+
+
+ +
+ +
+

تیم

+

+ {project.team} +

+
+
+ +
+ +
+

مدت زمان

+

+ {project.startDate} تا {project.endDate} +

+
+
+
+
+ + {/* Team Members */} + + + اعضای تیم + + +
+ {project.teamMembers.map((member) => ( +
+
+ {member.avatar} +
+
+

+ {member.name} +

+

+ {member.role} +

+
+
+ ))} +
+
+
+
+
+
+
+ ); +} + +export default ProjectDetail; diff --git a/app/components/dashboard/projects/projects-page.tsx b/app/components/dashboard/projects/projects-page.tsx new file mode 100644 index 0000000..fd75675 --- /dev/null +++ b/app/components/dashboard/projects/projects-page.tsx @@ -0,0 +1,663 @@ +import React, { useState } from "react"; +import { DashboardLayout } from "../layout"; +import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; +import { Button } from "~/components/ui/button"; +import { Input } from "~/components/ui/input"; +import { Badge } from "~/components/ui/badge"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "~/components/ui/table"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "~/components/ui/dropdown-menu"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "~/components/ui/dialog"; +import { Label } from "~/components/ui/label"; +import { Textarea } from "~/components/ui/textarea"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "~/components/ui/select"; +import { + Plus, + Search, + Filter, + MoreHorizontal, + Edit, + Trash2, + Eye, + Calendar, + User, + DollarSign, + Clock, +} from "lucide-react"; + +// Mock data for projects +const mockProjects = [ + { + id: 1, + name: "پروژه توسعه اپلیکیشن موبایل", + manager: "علی احمدی", + team: "تیم توسعه موبایل", + status: "در حال انجام", + priority: "بالا", + startDate: "1403/01/15", + endDate: "1403/06/30", + budget: "500,000,000", + progress: 65, + description: "توسعه اپلیکیشن موبایل برای مدیریت پروژه‌ها", + }, + { + id: 2, + name: "پیاده‌سازی سیستم مدیریت محتوا", + manager: "فاطمه کریمی", + team: "تیم بک‌اند", + status: "تکمیل شده", + priority: "متوسط", + startDate: "1402/10/01", + endDate: "1403/02/15", + budget: "750,000,000", + progress: 100, + description: "توسعه سیستم مدیریت محتوای وب", + }, + { + id: 3, + name: "بهینه‌سازی پایگاه داده", + manager: "محمد رضایی", + team: "تیم دیتابیس", + status: "در انتظار", + priority: "بالا", + startDate: "1403/03/01", + endDate: "1403/05/30", + budget: "300,000,000", + progress: 0, + description: "بهینه‌سازی عملکرد پایگاه داده‌های موجود", + }, + { + id: 4, + name: "راه‌اندازی سیستم مانیتورینگ", + manager: "سارا موسوی", + team: "تیم DevOps", + status: "در حال انجام", + priority: "متوسط", + startDate: "1403/02/01", + endDate: "1403/04/15", + budget: "400,000,000", + progress: 30, + description: "پیاده‌سازی سیستم نظارت و مانیتورینگ", + }, + { + id: 5, + name: "توسعه پنل مدیریت", + manager: "رضا نوری", + team: "تیم فرانت‌اند", + status: "لغو شده", + priority: "پایین", + startDate: "1402/12/01", + endDate: "1403/03/01", + budget: "200,000,000", + progress: 25, + description: "توسعه پنل مدیریت برای ادمین‌ها", + }, +]; + +const statusColors = { + "در حال انجام": "info", + "تکمیل شده": "success", + "در انتظار": "warning", + "لغو شده": "destructive", +} as const; + +const priorityColors = { + بالا: "destructive", + متوسط: "warning", + پایین: "secondary", +} as const; + +export function ProjectsPage() { + const [projects, setProjects] = useState(mockProjects); + const [searchTerm, setSearchTerm] = useState(""); + const [filterStatus, setFilterStatus] = useState("همه"); + const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); + const [editingProject, setEditingProject] = useState(null); + const [newProject, setNewProject] = useState({ + name: "", + manager: "", + team: "", + status: "در انتظار", + priority: "متوسط", + startDate: "", + endDate: "", + budget: "", + description: "", + }); + + const filteredProjects = projects.filter((project) => { + const matchesSearch = + project.name.toLowerCase().includes(searchTerm.toLowerCase()) || + project.manager.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesStatus = + filterStatus === "همه" || project.status === filterStatus; + return matchesSearch && matchesStatus; + }); + + const handleAddProject = () => { + const id = Math.max(...projects.map((p) => p.id)) + 1; + setProjects([...projects, { ...newProject, id, progress: 0 }]); + setNewProject({ + name: "", + manager: "", + team: "", + status: "در انتظار", + priority: "متوسط", + startDate: "", + endDate: "", + budget: "", + description: "", + }); + setIsAddDialogOpen(false); + }; + + const handleEditProject = (project: any) => { + setEditingProject(project); + setNewProject(project); + setIsAddDialogOpen(true); + }; + + const handleUpdateProject = () => { + setProjects( + projects.map((p) => + p.id === editingProject.id + ? { + ...newProject, + id: editingProject.id, + progress: editingProject.progress, + } + : p, + ), + ); + setEditingProject(null); + setNewProject({ + name: "", + manager: "", + team: "", + status: "در انتظار", + priority: "متوسط", + startDate: "", + endDate: "", + budget: "", + description: "", + }); + setIsAddDialogOpen(false); + }; + + const handleDeleteProject = (id: number) => { + setProjects(projects.filter((p) => p.id !== id)); + }; + + return ( + +
+ {/* Page Header */} +
+
+

+ مدیریت پروژه‌ها +

+

+ مدیریت و پیگیری پروژه‌های فناوری و نوآوری +

+
+ + + + + + + + + {editingProject ? "ویرایش پروژه" : "پروژه جدید"} + + + {editingProject + ? "اطلاعات پروژه را ویرایش کنید." + : "اطلاعات پروژه جدید را وارد کنید."} + + + +
+
+ + + setNewProject({ ...newProject, name: e.target.value }) + } + className="font-persian" + placeholder="نام پروژه را وارد کنید" + /> +
+ +
+ + + setNewProject({ ...newProject, manager: e.target.value }) + } + className="font-persian" + placeholder="نام مدیر پروژه" + /> +
+ +
+ + + setNewProject({ ...newProject, team: e.target.value }) + } + className="font-persian" + placeholder="نام تیم" + /> +
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+ + + setNewProject({ + ...newProject, + startDate: e.target.value, + }) + } + className="font-persian" + placeholder="1403/01/01" + /> +
+ +
+ + + setNewProject({ + ...newProject, + endDate: e.target.value, + }) + } + className="font-persian" + placeholder="1403/06/01" + /> +
+
+ +
+ + + setNewProject({ ...newProject, budget: e.target.value }) + } + className="font-persian" + placeholder="500,000,000" + /> +
+ +
+ +