erp-platform/ui/src/views/project/components/ProjectView.tsx
2025-09-16 00:02:48 +03:00

997 lines
51 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react'
import { useParams, Link, useNavigate } from 'react-router-dom'
import {
FaArrowLeft,
FaEdit,
FaUser,
FaDollarSign,
FaBullseye,
FaBuilding,
FaClock,
FaFlag,
FaTasks,
FaFileAlt,
FaExclamationCircle,
FaChartLine,
FaFolder,
FaPhone,
FaEnvelope,
} from 'react-icons/fa'
import { TaskStatusEnum } from '../../../types/ps'
import { mockProjects } from '../../../mocks/mockProjects'
import { mockProjectPhases } from '../../../mocks/mockProjectPhases'
import { mockProjectTasks } from '../../../mocks/mockProjectTasks'
import dayjs from 'dayjs'
import classNames from 'classnames'
import { PriorityEnum } from '../../../types/common'
import {
getProjectStatusColor,
getProjectStatusIcon,
getProjectStatusText,
getProgressColor,
getPriorityColor,
getPriorityText,
getProjectTypeColor,
getProjectTypeText,
} from '../../../utils/erp'
import { Container } from '@/components/shared'
const ProjectView: React.FC = () => {
const { id } = useParams<{ id: string }>()
const navigate = useNavigate()
const [activeTab, setActiveTab] = useState('overview')
// Find the project by ID
const project = mockProjects.find((p) => p.id === id)
if (!project) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<FaFolder className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">Proje bulunamadı</h3>
<p className="mt-1 text-sm text-gray-500">Belirtilen ID ile proje mevcut değil.</p>
<div className="mt-6">
<Link
to="/admin/projects"
className="inline-flex items-center px-3 py-1.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm"
>
<FaArrowLeft className="mr-2 h-4 w-4" />
Projelere Dön
</Link>
</div>
</div>
</div>
)
}
const tabs = [
{ id: 'overview', name: 'Genel Bakış', icon: FaChartLine },
{ id: 'phases', name: 'Fazlar', icon: FaFlag },
{ id: 'tasks', name: 'Görevler', icon: FaTasks },
{ id: 'documents', name: 'Belgeler', icon: FaFileAlt },
{ id: 'risks', name: 'Riskler', icon: FaExclamationCircle },
{ id: 'financial', name: 'Mali Özet', icon: FaDollarSign },
]
const budgetUsagePercentage = (project.actualCost / project.budget) * 100
const timeElapsed = dayjs().diff(dayjs(project.startDate), 'day')
const totalDuration = dayjs(project.endDate).diff(dayjs(project.startDate), 'day')
const timeProgress = Math.min((timeElapsed / totalDuration) * 100, 100)
return (
<Container>
<div className="space-y-2">
{/* Header */}
<div className="bg-white shadow-sm border-b border-gray-200">
<div className="p-2">
<div className="flex items-center justify-between h-12">
<div className="flex items-center space-x-2">
<button
onClick={() => navigate('/admin/projects')}
className="flex items-center px-2 py-1.5 text-sm text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"
>
<FaArrowLeft className="w-4 h-4 mr-2" />
Geri
</button>
<div className="flex items-center space-x-2">
<div className="w-9 h-9 bg-blue-100 rounded-lg flex items-center justify-center">
<FaFolder className="w-6 h-6 text-blue-600" />
</div>
<div>
<h1 className="text-lg font-bold text-gray-900">{project.code}</h1>
<p className="text-gray-600">{project.name}</p>
</div>
</div>
</div>
<div className="flex items-center space-x-2">
<span
className={classNames(
'inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium border',
getProjectStatusColor(project.status),
)}
>
{getProjectStatusIcon(project.status)}
<span className="ml-2">{getProjectStatusText(project.status)}</span>
</span>
<Link
to={`/admin/projects/edit/${project.id}`}
className="inline-flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<FaEdit className="w-4 h-4 mr-2" />
Düzenle
</Link>
</div>
</div>
</div>
</div>
<div className="mx-auto py-2">
{/* Summary Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 mb-4">
{/* Progress Card */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-3">
<div className="flex items-center justify-between mb-2">
<div className="w-7 h-7 bg-blue-100 rounded-lg flex items-center justify-center">
<FaBullseye className="w-5 h-5 text-blue-600" />
</div>
<span className="text-lg font-bold text-blue-600">{project.progress}%</span>
</div>
<h3 className="text-sm font-medium text-gray-600 mb-1.5">İlerleme</h3>
<div className="w-full bg-gray-200 rounded-full h-1.5">
<div
className={classNames('h-1.5 rounded-full', getProgressColor(project.progress))}
style={{ width: `${project.progress}%` }}
/>
</div>
</div>
{/* Budget Card */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-3">
<div className="flex items-center justify-between mb-2">
<div className="w-7 h-7 bg-green-100 rounded-lg flex items-center justify-center">
<FaDollarSign className="w-5 h-5 text-green-600" />
</div>
<span className="text-lg font-bold text-green-600">
{budgetUsagePercentage.toFixed(0)}%
</span>
</div>
<h3 className="text-sm font-medium text-gray-600 mb-1">Bütçe Kullanımı</h3>
<div className="text-xs text-gray-500">
{project.actualCost.toLocaleString()} / {project.budget.toLocaleString()}
</div>
</div>
{/* Time Progress Card */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-3">
<div className="flex items-center justify-between mb-2">
<div className="w-7 h-7 bg-orange-100 rounded-lg flex items-center justify-center">
<FaClock className="w-5 h-5 text-orange-600" />
</div>
<span className="text-lg font-bold text-orange-600">
{timeProgress.toFixed(0)}%
</span>
</div>
<h3 className="text-sm font-medium text-gray-600 mb-1">Zaman İlerlemesi</h3>
<div className="text-xs text-gray-500">
{timeElapsed} / {totalDuration} gün
</div>
</div>
{/* Priority Card */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-3">
<div className="flex items-center justify-between mb-2">
<div className="w-7 h-7 bg-red-100 rounded-lg flex items-center justify-center">
<FaFlag className="w-5 h-5 text-red-600" />
</div>
<span
className={classNames('text-lg font-bold', getPriorityColor(project.priority))}
>
{getPriorityText(project.priority)}
</span>
</div>
<h3 className="text-sm font-medium text-gray-600 mb-1">Öncelik</h3>
<span
className={classNames(
'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium border',
getProjectTypeColor(project.projectType),
)}
>
{getProjectTypeText(project.projectType)}
</span>
</div>
</div>
{/* Tabs */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div className="border-b border-gray-200">
<nav className="flex space-x-4 px-3" aria-label="Tabs">
{tabs.map((tab) => {
const Icon = tab.icon
return (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={classNames(
'py-2 px-1 border-b-2 font-medium text-sm flex items-center space-x-2 transition-colors',
activeTab === tab.id
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300',
)}
>
<Icon className="w-4 h-4" />
<span>{tab.name}</span>
</button>
)
})}
</nav>
</div>
<div className="p-3">
{activeTab === 'overview' && (
<div className="space-y-3">
{/* Project Info Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
{/* Project Details */}
<div className="space-y-3">
<div>
<h3 className="text-base font-semibold text-gray-900 mb-2">
Proje Detayları
</h3>
<div className="bg-gray-50 rounded-lg p-2.5 space-y-1.5 text-sm">
<div className="flex justify-between items-center">
<span className="text-gray-600">Proje Kodu:</span>
<span className="font-medium text-gray-900">{project.code}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-600">Proje Türü:</span>
<span
className={classNames(
'px-2 py-0.5 rounded-full text-xs font-medium border',
getProjectTypeColor(project.projectType),
)}
>
{getProjectTypeText(project.projectType)}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-600">Başlangıç Tarihi:</span>
<span className="font-medium text-gray-900">
{dayjs(project.startDate).format('DD.MM.YYYY')}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-600">Bitiş Tarihi:</span>
<span className="font-medium text-gray-900">
{dayjs(project.endDate).format('DD.MM.YYYY')}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-600">Gerçek Başlangıç:</span>
<span className="font-medium text-gray-900">
{project.actualStartDate
? dayjs(project.actualStartDate).format('DD.MM.YYYY')
: 'Henüz başlamadı'}
</span>
</div>
</div>
</div>
{/* Description */}
{project.description && (
<div>
<h3 className="text-base font-semibold text-gray-900 mb-2">ıklama</h3>
<div className="bg-gray-50 rounded-lg p-2.5">
<p className="text-sm text-gray-700 leading-relaxed">
{project.description}
</p>
</div>
</div>
)}
</div>
{/* Stakeholders */}
<div className="space-y-3">
{/* Project Manager */}
<div>
<h3 className="text-base font-semibold text-gray-900 mb-2">
Proje Yöneticisi
</h3>
{project.projectManager && (
<div className="bg-gray-50 rounded-lg p-2.5">
<div className="flex items-center space-x-2">
<div className="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center">
<FaUser className="w-6 h-6 text-blue-600" />
</div>
<div>
<h4 className="font-medium text-gray-900">
{project.projectManager.fullName}
</h4>
<p className="text-gray-600">
{project.projectManager.jobPosition?.name || 'Proje Yöneticisi'}
</p>
<div className="flex items-center mt-1 text-sm text-gray-500">
<FaEnvelope className="w-3 h-3 mr-1" />
{project.projectManager.email}
</div>
{project.projectManager.phone && (
<div className="flex items-center mt-1 text-sm text-gray-500">
<FaPhone className="w-3 h-3 mr-1" />
{project.projectManager.phone}
</div>
)}
</div>
</div>
</div>
)}
</div>
{/* Customer */}
{project.customer && (
<div>
<h3 className="text-base font-semibold text-gray-900 mb-2">Müşteri</h3>
<div className="bg-gray-50 rounded-lg p-2.5">
<div className="flex items-center space-x-2">
<div className="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
<FaBuilding className="w-6 h-6 text-green-600" />
</div>
<div>
<h4 className="font-medium text-gray-900">
{project.customer.name}
</h4>
<p className="text-gray-600">
{project.customer.primaryContact?.firstName}{' '}
{project.customer.primaryContact?.lastName}
</p>
{project.customer.primaryContact?.email && (
<div className="flex items-center mt-1 text-sm text-gray-500">
<FaEnvelope className="w-3 h-3 mr-1" />
{project.customer.primaryContact.email}
</div>
)}
{project.customer.primaryContact?.phone && (
<div className="flex items-center mt-1 text-sm text-gray-500">
<FaPhone className="w-3 h-3 mr-1" />
{project.customer.primaryContact.phone}
</div>
)}
</div>
</div>
</div>
</div>
)}
</div>
</div>
</div>
)}
{activeTab === 'phases' && (
<div className="space-y-3">
{/* Phases List */}
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div className="divide-y divide-gray-200">
{mockProjectPhases.filter((phase) => phase.projectId === project.id).length >
0 ? (
mockProjectPhases
.filter((phase) => phase.projectId === project.id)
.map((phase) => (
<div key={phase.id} className="px-3 py-2 hover:bg-gray-50">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center space-x-2 mb-1.5">
<h4 className="text-sm font-medium text-gray-900">
{phase.name}
</h4>
<span className="inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{phase.code}
</span>
<span className="inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
{phase.status}
</span>
</div>
<p className="text-sm text-gray-600 mb-1.5">
{phase.description}
</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 mb-1.5">
<div>
<p className="text-xs font-medium text-gray-700">Başlangıç</p>
<p className="text-xs text-gray-600">
{dayjs(phase.startDate).format('DD.MM.YYYY')}
</p>
</div>
<div>
<p className="text-xs font-medium text-gray-700">Bitiş</p>
<p className="text-xs text-gray-600">
{dayjs(phase.endDate).format('DD.MM.YYYY')}
</p>
</div>
<div>
<p className="text-xs font-medium text-gray-700">Bütçe</p>
<p className="text-xs text-gray-600">
{phase.budget.toLocaleString()}
</p>
</div>
<div>
<p className="text-xs font-medium text-gray-700">İlerleme</p>
<p className="text-xs text-gray-600">%{phase.progress}</p>
</div>
</div>
<div className="w-full bg-gray-200 rounded-full h-1.5">
<div
className="bg-blue-500 h-1.5 rounded-full"
style={{ width: `${phase.progress}%` }}
/>
</div>
</div>
<div className="flex items-center space-x-1 ml-2">
<button className="p-1 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-lg">
<FaFlag className="w-4 h-4" />
</button>
</div>
</div>
</div>
))
) : (
<div className="px-3 py-5 text-center">
<FaFlag className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">Faz bulunamadı</h3>
<p className="mt-1 text-sm text-gray-500">
Bu proje için henüz faz tanımlanmamış.
</p>
</div>
)}
</div>
</div>
</div>
)}
{activeTab === 'tasks' && (
<div className="space-y-3">
{/* Tasks List */}
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div className="divide-y divide-gray-200">
{mockProjectTasks.filter((task) => task.projectId === project.id).length >
0 ? (
mockProjectTasks
.filter((task) => task.projectId === project.id)
.map((task) => (
<div key={task.id} className="px-3 py-2 hover:bg-gray-50">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center space-x-2 mb-1.5">
<h4 className="text-sm font-medium text-gray-900">
{task.name}
</h4>
<span className="inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{task.taskCode}
</span>
<span
className={`inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium ${
task.status === TaskStatusEnum.Completed
? 'bg-green-100 text-green-800'
: task.status === TaskStatusEnum.InProgress
? 'bg-yellow-100 text-yellow-800'
: task.status === TaskStatusEnum.NotStarted
? 'bg-gray-100 text-gray-800'
: 'bg-red-100 text-red-800'
}`}
>
{task.status === TaskStatusEnum.Completed
? 'Tamamlandı'
: task.status === TaskStatusEnum.InProgress
? 'Devam Ediyor'
: task.status === TaskStatusEnum.NotStarted
? 'Başlamadı'
: task.status}
</span>
<span
className={`inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium ${
task.priority === PriorityEnum.High
? 'bg-red-100 text-red-800'
: task.priority === PriorityEnum.Normal
? 'bg-orange-100 text-orange-800'
: 'bg-green-100 text-green-800'
}`}
>
{task.priority === PriorityEnum.High
? 'Yüksek'
: task.priority === PriorityEnum.Normal
? 'Normal'
: task.priority === PriorityEnum.Urgent
? 'Acil'
: 'Düşük'}
</span>
</div>
<p className="text-sm text-gray-600 mb-2">{task.description}</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-2">
<div>
<p className="text-xs font-medium text-gray-700">Başlangıç</p>
<p className="text-xs text-gray-600">
{dayjs(task.startDate).format('DD.MM.YYYY')}
</p>
</div>
<div>
<p className="text-xs font-medium text-gray-700">Bitiş</p>
<p className="text-xs text-gray-600">
{dayjs(task.endDate).format('DD.MM.YYYY')}
</p>
</div>
<div>
<p className="text-xs font-medium text-gray-700">
Tahmini Saat
</p>
<p className="text-xs text-gray-600">
{task.estimatedHours}h
</p>
</div>
<div>
<p className="text-xs font-medium text-gray-700">İlerleme</p>
<p className="text-xs text-gray-600">%{task.progress}</p>
</div>
</div>
<div className="w-full bg-gray-200 rounded-full h-1.5">
<div
className={`h-1.5 rounded-full ${
task.progress >= 90
? 'bg-green-500'
: task.progress >= 70
? 'bg-blue-500'
: task.progress >= 50
? 'bg-yellow-500'
: 'bg-red-500'
}`}
style={{ width: `${task.progress}%` }}
/>
</div>
</div>
<div className="flex items-center space-x-1 ml-2">
<button className="p-1 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-lg">
<FaTasks className="w-4 h-4" />
</button>
</div>
</div>
</div>
))
) : (
<div className="px-3 py-5 text-center">
<FaTasks className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">
Görev bulunamadı
</h3>
<p className="mt-1 text-sm text-gray-500">
Bu proje için henüz görev tanımlanmamış.
</p>
</div>
)}
</div>
</div>
</div>
)}
{activeTab === 'documents' && (
<div className="space-y-3">
{/* Documents List */}
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div className="divide-y divide-gray-200">
{project.documents.length > 0 ? (
project.documents.map((doc) => (
<div key={doc.id} className="px-3 py-2 hover:bg-gray-50">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className="w-7 h-7 bg-blue-100 rounded-lg flex items-center justify-center">
<FaFileAlt className="w-4 h-4 text-blue-600" />
</div>
<div>
<h4 className="text-sm font-medium text-gray-900">
{doc.documentName}
</h4>
<div className="flex items-center space-x-3 text-xs text-gray-500 mt-1">
<span>{doc.documentType}</span>
<span>{doc.fileSize} MB</span>
<span>{dayjs(doc.uploadedAt).format('DD.MM.YYYY')}</span>
<span>Yükleyen: {doc.uploadedBy}</span>
</div>
</div>
</div>
<div className="flex items-center space-x-1 ml-2">
<button className="p-1 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-lg">
<FaFolder className="w-4 h-4" />
</button>
</div>
</div>
</div>
))
) : (
<div className="px-3 py-5 text-center">
<FaFileAlt className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">
Belge bulunamadı
</h3>
<p className="mt-1 text-sm text-gray-500">
Bu proje için henüz belge yüklenmemiş.
</p>
</div>
)}
</div>
</div>
</div>
)}
{activeTab === 'risks' && (
<div className="space-y-3">
{/* Risks List */}
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div className="divide-y divide-gray-200">
{project.risks.length > 0 ? (
project.risks.map((risk) => (
<div key={risk.id} className="px-3 py-2 hover:bg-gray-50">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center space-x-2 mb-1.5">
<h4 className="text-sm font-medium text-gray-900">
{risk.title}
</h4>
<span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
{risk.riskLevel}
</span>
<span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{risk.status}
</span>
</div>
<p className="text-sm text-gray-600 mb-1.5">{risk.description}</p>
{risk.mitigationPlan && (
<div className="bg-gray-50 rounded-lg p-2 mb-2">
<p className="text-xs font-medium text-gray-700 mb-1">
Önlem Planı:
</p>
<p className="text-xs text-gray-600">{risk.mitigationPlan}</p>
</div>
)}
<div className="flex items-center space-x-3 text-xs text-gray-500">
<span>Risk Kodu: {risk.riskCode}</span>
<span>
Tanımlama: {dayjs(risk.identifiedDate).format('DD.MM.YYYY')}
</span>
</div>
</div>
<div className="flex items-center space-x-1 ml-2">
<button className="p-1 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-lg">
<FaExclamationCircle className="w-4 h-4" />
</button>
</div>
</div>
</div>
))
) : (
<div className="px-3 py-5 text-center">
<FaExclamationCircle className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">
Risk bulunamadı
</h3>
<p className="mt-1 text-sm text-gray-500">
Bu proje için henüz risk tanımlanmamış.
</p>
</div>
)}
</div>
</div>
</div>
)}
{/* Financial Summary Tab */}
{activeTab === 'financial' && (
<div className="space-y-3">
{/* Overview Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
<div className="bg-white rounded-lg border border-gray-200 p-3">
<div className="flex items-center">
<div className="p-1.5 bg-blue-100 rounded-lg">
<FaDollarSign className="h-5 w-5 text-blue-600" />
</div>
<div className="ml-2">
<p className="text-sm font-medium text-gray-500">Toplam Bütçe</p>
<p className="text-base font-semibold text-gray-900">
{project.budget.toLocaleString()}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg border border-gray-200 p-3">
<div className="flex items-center">
<div className="p-1.5 bg-red-100 rounded-lg">
<FaDollarSign className="h-5 w-5 text-red-600" />
</div>
<div className="ml-2">
<p className="text-sm font-medium text-gray-500">Harcanan</p>
<p className="text-base font-semibold text-gray-900">
{project.actualCost.toLocaleString()}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg border border-gray-200 p-3">
<div className="flex items-center">
<div className="p-1.5 bg-green-100 rounded-lg">
<FaDollarSign className="h-5 w-5 text-green-600" />
</div>
<div className="ml-2">
<p className="text-sm font-medium text-gray-500">Kalan Bütçe</p>
<p className="text-base font-semibold text-gray-900">
{(project.budget - project.actualCost).toLocaleString()}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg border border-gray-200 p-3">
<div className="flex items-center">
<div className="p-1.5 bg-orange-100 rounded-lg">
<FaChartLine className="h-5 w-5 text-orange-600" />
</div>
<div className="ml-2">
<p className="text-sm font-medium text-gray-500">Kullanım Oranı</p>
<p className="text-base font-semibold text-gray-900">
%{budgetUsagePercentage.toFixed(1)}
</p>
</div>
</div>
</div>
</div>
{/* Budget Progress */}
<div className="bg-white rounded-lg border border-gray-200 p-3">
<h3 className="text-base font-semibold text-gray-900 mb-2">
Bütçe Kullanım Durumu
</h3>
<div className="space-y-2">
<div className="flex justify-between text-xs">
<span>Harcanan: {project.actualCost.toLocaleString()}</span>
<span>Toplam: {project.budget.toLocaleString()}</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2.5">
<div
className={`h-2.5 rounded-full transition-all duration-300 ${
budgetUsagePercentage > 90
? 'bg-red-500'
: budgetUsagePercentage > 70
? 'bg-orange-500'
: 'bg-green-500'
}`}
style={{
width: `${Math.min(budgetUsagePercentage, 100)}%`,
}}
/>
</div>
<div
className={`text-xs font-medium ${
budgetUsagePercentage > 90
? 'text-red-600'
: budgetUsagePercentage > 70
? 'text-orange-600'
: 'text-green-600'
}`}
>
{budgetUsagePercentage > 90
? 'Kritik Seviye - Bütçe Aşımı Riski'
: budgetUsagePercentage > 70
? 'Dikkat - Yüksek Kullanım'
: 'Normal Seviye'}
</div>
</div>
</div>
{/* Phase Costs */}
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div className="px-3 py-2 border-b border-gray-200">
<h3 className="text-base font-semibold text-gray-900">
Faz Bazında Maliyet Analizi
</h3>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200 text-sm">
<thead className="bg-gray-50">
<tr>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Faz Adı
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Bütçe
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Harcanan
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Kalan
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Kullanım %
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Durum
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{mockProjectPhases
.filter((phase) => phase.projectId === project.id)
.map((phase) => {
const phaseUsage = (phase.actualCost / phase.budget) * 100
return (
<tr key={phase.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex flex-col">
<div className="text-sm font-medium text-gray-900">
{phase.name}
</div>
<div className="text-sm text-gray-500">{phase.code}</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{phase.budget.toLocaleString()}
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{phase.actualCost.toLocaleString()}
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{(phase.budget - phase.actualCost).toLocaleString()}
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center">
<div className="w-14 bg-gray-200 rounded-full h-1.5 mr-2">
<div
className={`h-1.5 rounded-full ${
phaseUsage > 90
? 'bg-red-500'
: phaseUsage > 70
? 'bg-orange-500'
: 'bg-green-500'
}`}
style={{
width: `${Math.min(phaseUsage, 100)}%`,
}}
/>
</div>
<span className="text-sm text-gray-900">
{phaseUsage.toFixed(1)}%
</span>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span
className={`inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium ${
phaseUsage > 100
? 'bg-red-100 text-red-800'
: phaseUsage > 90
? 'bg-orange-100 text-orange-800'
: phaseUsage > 70
? 'bg-yellow-100 text-yellow-800'
: 'bg-green-100 text-green-800'
}`}
>
{phaseUsage > 100
? 'Bütçe Aşımı'
: phaseUsage > 90
? 'Kritik'
: phaseUsage > 70
? 'Dikkat'
: 'Normal'}
</span>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
{/* Cost Breakdown */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
<div className="bg-white rounded-lg border border-gray-200 p-3">
<h3 className="text-base font-semibold text-gray-900 mb-2">
Maliyet Kategorileri
</h3>
<div className="space-y-2">
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">Personel Maliyeti</span>
<span className="text-sm font-medium text-gray-900">
{(project.actualCost * 0.6).toLocaleString()}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">Malzeme Maliyeti</span>
<span className="text-sm font-medium text-gray-900">
{(project.actualCost * 0.25).toLocaleString()}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">İş Merkezi Maliyeti</span>
<span className="text-sm font-medium text-gray-900">
{(project.actualCost * 0.1).toLocaleString()}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600">Diğer Giderler</span>
<span className="text-sm font-medium text-gray-900">
{(project.actualCost * 0.05).toLocaleString()}
</span>
</div>
</div>
</div>
<div className="bg-white rounded-lg border border-gray-200 p-3">
<h3 className="text-base font-semibold text-gray-900 mb-2">
Maliyet Analizi
</h3>
<div className="space-y-2">
<div>
<div className="flex justify-between mb-1">
<span className="text-sm text-gray-600">Ortalama Günlük Harcama</span>
<span className="text-sm font-medium text-gray-900">
{Math.round(
project.actualCost / Math.max(timeElapsed, 1),
).toLocaleString()}
</span>
</div>
</div>
<div>
<div className="flex justify-between mb-1">
<span className="text-sm text-gray-600">
Tahmini Proje Sonu Maliyeti
</span>
<span className="text-sm font-medium text-gray-900">
{Math.round(
(project.actualCost / Math.max(project.progress, 1)) * 100,
).toLocaleString()}
</span>
</div>
</div>
<div>
<div className="flex justify-between mb-1">
<span className="text-sm text-gray-600">Maliyet Varyansı</span>
<span
className={`text-sm font-medium ${
project.actualCost > project.budget
? 'text-red-600'
: 'text-green-600'
}`}
>
{project.actualCost > project.budget ? '+' : ''}
{(project.actualCost - project.budget).toLocaleString()}
</span>
</div>
</div>
<div>
<div className="flex justify-between mb-1">
<span className="text-sm text-gray-600">
CPI (Maliyet Performans İndeksi)
</span>
<span
className={`text-sm font-medium ${
project.budget / project.actualCost >= 1
? 'text-green-600'
: 'text-red-600'
}`}
>
{(project.budget / project.actualCost).toFixed(2)}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
)}
</div>
</div>
</div>
</div>
</Container>
)
}
export default ProjectView