2666 lines
119 KiB
TypeScript
2666 lines
119 KiB
TypeScript
import React, { useState } from 'react'
|
||
import {
|
||
FaUsers,
|
||
FaEye,
|
||
FaEdit,
|
||
FaPlus,
|
||
FaChartBar,
|
||
FaTimes,
|
||
FaSave,
|
||
FaPlay,
|
||
FaPoll,
|
||
} from 'react-icons/fa'
|
||
import {
|
||
HrEvaluation360,
|
||
CampaignStatusEnum,
|
||
AssessorTypeEnum,
|
||
ParticipantStatusEnum,
|
||
QuestionTypeEnum,
|
||
ResultStatusEnum,
|
||
HrEvaluation360Result,
|
||
HrGroupScore,
|
||
HrAssessorTypeScore,
|
||
} from '../../../types/hr'
|
||
import DataTable, { Column } from '../../../components/common/DataTable'
|
||
import { mockEvaluation360 } from '../../../mocks/mockEvaluation360'
|
||
import { mockEvaluation360Results } from '../../../mocks/mockEvaluation360Results'
|
||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||
import { mockDepartments } from '../../../mocks/mockDepartments'
|
||
import { mockEvaluation360Templates } from '../../../mocks/mockEvaluation360Templates'
|
||
import { mockBusinessParties } from '../../../mocks/mockBusinessParties'
|
||
import Widget from '../../../components/common/Widget'
|
||
import {
|
||
getAssessorTypeDescription,
|
||
getAssessorTypeText,
|
||
getParticipantStatusColor,
|
||
getParticipantStatusText,
|
||
getCampaignStatusColor,
|
||
getCampaignStatusText,
|
||
} from '../../../utils/erp'
|
||
import { Container } from '@/components/shared'
|
||
|
||
const Degree360Evaluation: React.FC = () => {
|
||
const [activeTab, setActiveTab] = useState<'campaigns' | 'results'>('campaigns')
|
||
const [selectedStatus, setSelectedStatus] = useState<string>('all')
|
||
const [selectedEmployee, setSelectedEmployee] = useState<string>('all')
|
||
const [selectedDepartment, setSelectedDepartment] = useState<string>('all')
|
||
|
||
// Results tab filters
|
||
const [selectedResultsStatus, setSelectedResultsStatus] = useState<string>('all')
|
||
const [selectedResultsEmployee, setSelectedResultsEmployee] = useState<string>('all')
|
||
const [selectedResultsDepartment, setSelectedResultsDepartment] = useState<string>('all')
|
||
const [selectedResultsCampaign, setSelectedResultsCampaign] = useState<string>('all')
|
||
|
||
// Modal states
|
||
const [showCampaignModal, setShowCampaignModal] = useState(false)
|
||
const [showViewModal, setShowViewModal] = useState(false)
|
||
const [showEvaluationModal, setShowEvaluationModal] = useState(false)
|
||
const [showResultDetailModal, setShowResultDetailModal] = useState(false)
|
||
const [showResultEditModal, setShowResultEditModal] = useState(false)
|
||
const [selectedCampaign, setSelectedCampaign] = useState<HrEvaluation360 | null>(null)
|
||
const [selectedResult, setSelectedResult] = useState<{
|
||
resultId: string
|
||
evaluatedEmployeeName: string
|
||
evaluatorName: string
|
||
campaignName: string
|
||
overallScore: number
|
||
scorePercentage: number
|
||
evaluatedEmployeeId: string
|
||
evaluatorId: string
|
||
campaignId: string
|
||
evaluatorType: AssessorTypeEnum
|
||
status: ParticipantStatusEnum
|
||
completedDate?: Date
|
||
} | null>(null)
|
||
|
||
// Evaluation states
|
||
const [evaluationTarget, setEvaluationTarget] = useState<string>('')
|
||
const [evaluationEvaluator, setEvaluationEvaluator] = useState<string>('')
|
||
const [selectedAssessorType, setSelectedAssessorType] = useState<AssessorTypeEnum | null>(null)
|
||
const [externalEvaluatorName, setExternalEvaluatorName] = useState<string>('')
|
||
const [externalEvaluatorEmail, setExternalEvaluatorEmail] = useState<string>('')
|
||
const [selectedCustomerId, setSelectedCustomerId] = useState<string>('')
|
||
const [evaluatorSearchTerm, setEvaluatorSearchTerm] = useState<string>('')
|
||
const [currentCampaignForEvaluation, setCurrentCampaignForEvaluation] =
|
||
useState<HrEvaluation360 | null>(null)
|
||
|
||
// Evaluation responses state
|
||
const [evaluationResponses, setEvaluationResponses] = useState<Record<string, string | number>>(
|
||
{},
|
||
)
|
||
|
||
// Edit modal states
|
||
const [editResponses, setEditResponses] = useState<Record<string, string | number>>({})
|
||
const [editManagerComments, setEditManagerComments] = useState<string>('')
|
||
const [editHrComments, setEditHrComments] = useState<string>('')
|
||
const [editResultStatus, setEditResultStatus] = useState<string>('PENDING')
|
||
|
||
// Form states
|
||
const [campaignFormData, setCampaignFormData] = useState({
|
||
name: '',
|
||
description: '',
|
||
templateId: '',
|
||
departmentId: '',
|
||
targetEmployees: [] as string[],
|
||
startDate: '',
|
||
endDate: '',
|
||
})
|
||
|
||
// Event handlers
|
||
const handleCloseModal = () => {
|
||
setShowCampaignModal(false)
|
||
setShowViewModal(false)
|
||
setShowEvaluationModal(false)
|
||
setShowResultDetailModal(false)
|
||
setShowResultEditModal(false)
|
||
setSelectedCampaign(null)
|
||
setSelectedResult(null)
|
||
setEvaluationTarget('')
|
||
setEvaluationEvaluator('')
|
||
setSelectedAssessorType(null)
|
||
setExternalEvaluatorName('')
|
||
setExternalEvaluatorEmail('')
|
||
setSelectedCustomerId('')
|
||
setEvaluatorSearchTerm('')
|
||
setCurrentCampaignForEvaluation(null)
|
||
setEvaluationResponses({})
|
||
setEditResponses({})
|
||
setEditManagerComments('')
|
||
setEditHrComments('')
|
||
setEditResultStatus('PENDING')
|
||
setCampaignFormData({
|
||
name: '',
|
||
description: '',
|
||
templateId: '',
|
||
departmentId: '',
|
||
targetEmployees: [],
|
||
startDate: '',
|
||
endDate: '',
|
||
})
|
||
}
|
||
|
||
// Response handling functions
|
||
const handleResponseChange = (questionId: string, value: string | number) => {
|
||
setEvaluationResponses((prev) => ({
|
||
...prev,
|
||
[questionId]: value,
|
||
}))
|
||
}
|
||
|
||
// Edit response handling function
|
||
const handleEditResponseChange = (questionId: string, value: string | number) => {
|
||
setEditResponses((prev) => ({
|
||
...prev,
|
||
[questionId]: value,
|
||
}))
|
||
}
|
||
|
||
// Evaluation kaydetme fonksiyonu
|
||
const handleSaveEvaluation = () => {
|
||
if (!currentCampaignForEvaluation || !evaluationTarget || !evaluationEvaluator) {
|
||
alert('Tüm alanları doldurun!')
|
||
return
|
||
}
|
||
|
||
const template = mockEvaluation360Templates.find(
|
||
(t) => t.id === currentCampaignForEvaluation.templateId,
|
||
)
|
||
|
||
if (!template) {
|
||
alert('Template bulunamadı!')
|
||
return
|
||
}
|
||
|
||
// Responses oluştur
|
||
const responses = Object.entries(evaluationResponses).map(([questionId, responseValue]) => {
|
||
const question = template.questionGroups
|
||
.flatMap((g) => g.questions)
|
||
.find((q) => q.id === questionId)
|
||
|
||
let score = 0
|
||
if (question) {
|
||
if (question.questionType === QuestionTypeEnum.Rating) {
|
||
score = Number(responseValue)
|
||
} else if (question.questionType === QuestionTypeEnum.YesNo) {
|
||
score = responseValue === 'yes' ? question.maxRating || 5 : question.minRating || 1
|
||
} else if (question.questionType === QuestionTypeEnum.MultipleChoice) {
|
||
const option = question.options?.find((o) => o.value === Number(responseValue))
|
||
score = option?.value || 0
|
||
}
|
||
}
|
||
|
||
return {
|
||
id: `response-${Date.now()}-${Math.random()}`,
|
||
participantId: `participant-${Date.now()}`,
|
||
questionId,
|
||
question,
|
||
responseValue,
|
||
responseText: typeof responseValue === 'string' ? responseValue : undefined,
|
||
score,
|
||
submittedDate: new Date(),
|
||
}
|
||
})
|
||
|
||
// Puanları hesapla
|
||
const totalScore = responses.reduce((sum, response) => sum + response.score, 0)
|
||
const maxScore = template.questionGroups
|
||
.flatMap((g) => g.questions)
|
||
.reduce((sum, q) => sum + (q.maxRating || 5), 0)
|
||
|
||
const scorePercentage = maxScore > 0 ? (totalScore / maxScore) * 100 : 0
|
||
|
||
// Grup skorlarını hesapla
|
||
const groupScores: HrGroupScore[] = template.questionGroups.map((group) => {
|
||
const groupResponses = responses.filter((r) =>
|
||
group.questions.some((q) => q.id === r.questionId),
|
||
)
|
||
const groupScore = groupResponses.reduce((sum, r) => sum + r.score, 0)
|
||
const groupMaxScore = group.questions.reduce((sum, q) => sum + (q.maxRating || 5), 0)
|
||
|
||
return {
|
||
groupId: group.id,
|
||
groupName: group.groupName,
|
||
score: groupScore,
|
||
maxScore: groupMaxScore,
|
||
percentage: groupMaxScore > 0 ? (groupScore / groupMaxScore) * 100 : 0,
|
||
responseCount: groupResponses.length,
|
||
}
|
||
})
|
||
|
||
// Assessor type skorlarını hesapla
|
||
const assessorTypeScores: HrAssessorTypeScore[] = [
|
||
{
|
||
assessorType: selectedAssessorType!,
|
||
assessorCount: 1,
|
||
averageScore: totalScore,
|
||
maxScore: maxScore,
|
||
percentage: scorePercentage,
|
||
},
|
||
]
|
||
|
||
// Evaluation360Result oluştur
|
||
const evaluationResult: HrEvaluation360Result = {
|
||
id: `result-${Date.now()}`,
|
||
campaignId: currentCampaignForEvaluation.id,
|
||
employeeId: evaluationTarget,
|
||
employee: mockEmployees.find((e) => e.id === evaluationTarget),
|
||
participants: [
|
||
{
|
||
id: `participant-${Date.now()}`,
|
||
campaignId: currentCampaignForEvaluation.id,
|
||
evaluatedEmployeeId: evaluationTarget,
|
||
evaluatedEmployee: mockEmployees.find((e) => e.id === evaluationTarget),
|
||
evaluatorId: evaluationEvaluator,
|
||
evaluator:
|
||
selectedAssessorType === AssessorTypeEnum.External ||
|
||
selectedAssessorType === AssessorTypeEnum.Customer
|
||
? undefined // External ve Customer için Employee bilgisi yok
|
||
: mockEmployees.find((e) => e.id === evaluationEvaluator),
|
||
evaluatorType: selectedAssessorType!,
|
||
status: ParticipantStatusEnum.Completed,
|
||
invitedDate: new Date(),
|
||
startedDate: new Date(),
|
||
completedDate: new Date(),
|
||
responses,
|
||
overallScore: totalScore,
|
||
notes: '',
|
||
},
|
||
],
|
||
overallScore: totalScore,
|
||
maxPossibleScore: maxScore,
|
||
scorePercentage,
|
||
groupScores,
|
||
assessorTypeScores,
|
||
strengths: ['Güçlü iletişim', 'Takım çalışması'], // Örnek veriler
|
||
developmentAreas: ['Zaman yönetimi', 'Teknik beceriler'], // Örnek veriler
|
||
actionPlan: ['Zaman yönetimi kursu alma', 'Teknik eğitimlere katılma'], // Örnek veriler
|
||
managerComments: '',
|
||
hrComments: '',
|
||
status: ResultStatusEnum.Pending,
|
||
generatedDate: new Date(),
|
||
approvedBy: undefined,
|
||
approvedDate: undefined,
|
||
}
|
||
|
||
// Console'a yazdır
|
||
console.log('Evaluation360Result:', evaluationResult)
|
||
|
||
alert('Değerlendirme başarıyla kaydedildi! Sonuç konsola yazdırıldı.')
|
||
handleCloseModal()
|
||
}
|
||
|
||
const handleNewCampaign = () => {
|
||
setSelectedCampaign(null)
|
||
setShowCampaignModal(true)
|
||
}
|
||
|
||
const handleEditCampaign = (campaign: HrEvaluation360 | null) => {
|
||
if (campaign) {
|
||
setSelectedCampaign(campaign)
|
||
setCampaignFormData({
|
||
name: campaign.name,
|
||
description: campaign.description,
|
||
templateId: campaign.templateId,
|
||
departmentId: campaign.departmentId || '',
|
||
targetEmployees: campaign.targetEmployees,
|
||
startDate: campaign.startDate.toISOString().split('T')[0],
|
||
endDate: campaign.endDate.toISOString().split('T')[0],
|
||
})
|
||
setShowCampaignModal(true)
|
||
}
|
||
}
|
||
|
||
const handleViewCampaign = (campaign: HrEvaluation360) => {
|
||
setSelectedCampaign(campaign)
|
||
setShowViewModal(true)
|
||
}
|
||
|
||
const handleSaveCampaign = () => {
|
||
console.log('Değerlendirme kaydediliyor:', campaignFormData)
|
||
handleCloseModal()
|
||
}
|
||
|
||
const handleDepartmentChange = (departmentId: string) => {
|
||
setSelectedDepartment(departmentId)
|
||
setCampaignFormData({
|
||
...campaignFormData,
|
||
departmentId,
|
||
targetEmployees: [],
|
||
})
|
||
}
|
||
|
||
// Değerlendirme yapma handler'ı
|
||
const handleStartEvaluation = () => {
|
||
setCurrentCampaignForEvaluation(null)
|
||
setEvaluationTarget('')
|
||
setEvaluationEvaluator('')
|
||
setShowEvaluationModal(true)
|
||
}
|
||
|
||
// Otomatik değerlendirici belirleme fonksiyonu (360° metodolojisi)
|
||
const getEvaluatorsByType = (targetEmployeeId: string, assessorType: AssessorTypeEnum) => {
|
||
const targetEmployee = mockEmployees.find((emp) => emp.id === targetEmployeeId)
|
||
if (!targetEmployee) return []
|
||
|
||
switch (assessorType) {
|
||
case AssessorTypeEnum.Self: {
|
||
return [
|
||
{
|
||
id: targetEmployee.id,
|
||
name: targetEmployee.fullName,
|
||
title: targetEmployee.jobPosition?.name,
|
||
department:
|
||
mockDepartments.find((d) => d.id === targetEmployee.departmentId)?.name || '',
|
||
type: 'employee',
|
||
},
|
||
]
|
||
}
|
||
|
||
case AssessorTypeEnum.Manager: {
|
||
const managers = mockEmployees.filter(
|
||
(emp) =>
|
||
emp.departmentId === targetEmployee.departmentId &&
|
||
(typeof emp.jobPosition?.level === 'number' &&
|
||
typeof targetEmployee.jobPosition?.level === 'number'
|
||
? emp.jobPosition?.level > targetEmployee.jobPosition?.level
|
||
: emp.jobPosition?.name.includes('Müdür') ||
|
||
emp.jobPosition?.name.includes('Manager')),
|
||
)
|
||
return managers.map((emp) => ({
|
||
id: emp.id,
|
||
name: emp.fullName,
|
||
title: emp.jobPosition?.name,
|
||
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
||
type: 'employee',
|
||
}))
|
||
}
|
||
|
||
case AssessorTypeEnum.Peer: {
|
||
const peers = mockEmployees.filter(
|
||
(emp) =>
|
||
emp.id !== targetEmployeeId &&
|
||
emp.departmentId === targetEmployee.departmentId &&
|
||
emp.jobPosition?.level === targetEmployee.jobPosition?.level,
|
||
)
|
||
return peers.map((emp) => ({
|
||
id: emp.id,
|
||
name: emp.fullName,
|
||
title: emp.jobPosition?.name,
|
||
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
||
type: 'employee',
|
||
}))
|
||
}
|
||
|
||
case AssessorTypeEnum.Subordinate: {
|
||
const subordinates = mockEmployees.filter(
|
||
(emp) =>
|
||
emp.departmentId === targetEmployee.departmentId &&
|
||
(typeof emp.jobPosition?.level === 'number' &&
|
||
typeof targetEmployee.jobPosition?.level === 'number'
|
||
? emp.jobPosition?.level < targetEmployee.jobPosition?.level
|
||
: !emp.jobPosition?.name.includes('Müdür') &&
|
||
!emp.jobPosition?.name.includes('Manager')),
|
||
)
|
||
return subordinates.map((emp) => ({
|
||
id: emp.id,
|
||
name: emp.fullName,
|
||
title: emp.jobPosition?.name,
|
||
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
||
type: 'employee',
|
||
}))
|
||
}
|
||
|
||
case AssessorTypeEnum.Customer: {
|
||
return mockBusinessParties.map((customer) => ({
|
||
id: customer.id,
|
||
name: customer.name,
|
||
title: 'Müşteri Temsilcisi',
|
||
department: 'Müşteri',
|
||
type: 'customer',
|
||
}))
|
||
}
|
||
|
||
case AssessorTypeEnum.External: {
|
||
return [
|
||
{
|
||
id: 'external',
|
||
name: 'Dış Değerlendirici',
|
||
title: 'Harici',
|
||
department: 'Dış Paydaş',
|
||
type: 'external',
|
||
},
|
||
]
|
||
}
|
||
|
||
case AssessorTypeEnum.HRUpperManagement: {
|
||
const hrEmployees = mockEmployees.filter(
|
||
(emp) =>
|
||
emp.id !== targetEmployeeId && (emp.departmentId === 'hr' || emp.departmentId === '3'), // HR departmanı
|
||
)
|
||
return hrEmployees.map((emp) => ({
|
||
id: emp.id,
|
||
name: emp.fullName,
|
||
title: emp.jobPosition?.name,
|
||
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
||
type: 'employee',
|
||
}))
|
||
}
|
||
|
||
case AssessorTypeEnum.OtherDepartment: {
|
||
const otherDepartmentEmployees = mockEmployees.filter(
|
||
(emp) => emp.id !== targetEmployeeId && emp.departmentId !== targetEmployee.departmentId,
|
||
)
|
||
return otherDepartmentEmployees.map((emp) => ({
|
||
id: emp.id,
|
||
name: emp.fullName,
|
||
title: emp.jobPosition?.name,
|
||
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
||
type: 'employee',
|
||
}))
|
||
}
|
||
|
||
default:
|
||
return []
|
||
}
|
||
}
|
||
|
||
// Değerlendiren bilgisini getiren fonksiyon
|
||
const getEvaluatorInfo = () => {
|
||
if (evaluationEvaluator === 'external') {
|
||
return externalEvaluatorName
|
||
}
|
||
|
||
if (selectedAssessorType === AssessorTypeEnum.Customer) {
|
||
const customer = mockBusinessParties.find((c) => c.id === selectedCustomerId)
|
||
return customer ? customer.name : 'Müşteri Bulunamadı'
|
||
}
|
||
|
||
const employee = mockEmployees.find((e) => e.id === evaluationEvaluator)
|
||
return employee ? employee.fullName : 'Çalışan Bulunamadı'
|
||
}
|
||
|
||
// Template'deki izin verilen assessor tiplerini getir
|
||
const getAllowedAssessorTypes = () => {
|
||
if (!currentCampaignForEvaluation) return []
|
||
|
||
const template = mockEvaluation360Templates.find(
|
||
(t) => t.id === currentCampaignForEvaluation.templateId,
|
||
)
|
||
|
||
return template?.assessorTypes || []
|
||
}
|
||
|
||
// Helper function to get participants for a campaign from results
|
||
const getCampaignParticipants = (campaignId: string) => {
|
||
return mockEvaluation360Results
|
||
.filter((result) => result.campaignId === campaignId)
|
||
.flatMap((result) => result.participants)
|
||
}
|
||
|
||
// Filtered data
|
||
const filteredCampaigns = mockEvaluation360.filter((campaign) => {
|
||
if (selectedStatus !== 'all' && campaign.status !== selectedStatus) return false
|
||
if (selectedDepartment !== 'all' && campaign.departmentId !== selectedDepartment) return false
|
||
if (selectedEmployee !== 'all') {
|
||
const participants = getCampaignParticipants(campaign.id)
|
||
const hasEmployee = participants.some(
|
||
(p) => p.evaluatedEmployeeId === selectedEmployee || p.evaluatorId === selectedEmployee,
|
||
)
|
||
if (!hasEmployee) return false
|
||
}
|
||
return true
|
||
})
|
||
|
||
const filteredEmployeesByDepartment = mockEmployees.filter(
|
||
(emp) => selectedDepartment === 'all' || emp.departmentId === selectedDepartment,
|
||
)
|
||
|
||
// Statistics
|
||
const totalActiveCampaigns = mockEvaluation360.filter(
|
||
(c) => c.status === CampaignStatusEnum.Active,
|
||
).length
|
||
|
||
const allParticipants = mockEvaluation360Results.flatMap((result) => result.participants)
|
||
|
||
const totalCompletedEvaluations = allParticipants.filter(
|
||
(p) => p.status === ParticipantStatusEnum.Completed,
|
||
).length
|
||
|
||
const totalPendingEvaluations = allParticipants.filter(
|
||
(p) => p.status === ParticipantStatusEnum.Invited || p.status === ParticipantStatusEnum.Started,
|
||
).length
|
||
|
||
const totalParticipants = allParticipants.length
|
||
|
||
// Results helper functions
|
||
// Participant evaluation helper functions
|
||
const handleViewEvaluationDetail = (result: {
|
||
resultId: string
|
||
evaluatedEmployeeName: string
|
||
evaluatorName: string
|
||
campaignName: string
|
||
overallScore: number
|
||
scorePercentage: number
|
||
evaluatedEmployeeId: string
|
||
evaluatorId: string
|
||
campaignId: string
|
||
evaluatorType: AssessorTypeEnum
|
||
status: ParticipantStatusEnum
|
||
completedDate?: Date
|
||
}) => {
|
||
// Değerlendirme detaylarını görüntüle
|
||
console.log('Viewing evaluation detail:', result)
|
||
setSelectedResult(result)
|
||
setShowResultDetailModal(true)
|
||
}
|
||
|
||
const handleEditEvaluationDetail = (result: {
|
||
resultId: string
|
||
evaluatedEmployeeName: string
|
||
evaluatorName: string
|
||
campaignName: string
|
||
overallScore: number
|
||
scorePercentage: number
|
||
evaluatedEmployeeId: string
|
||
evaluatorId: string
|
||
campaignId: string
|
||
evaluatorType: AssessorTypeEnum
|
||
status: ParticipantStatusEnum
|
||
completedDate?: Date
|
||
}) => {
|
||
// Değerlendirme detaylarını düzenle
|
||
console.log('Editing evaluation detail:', result)
|
||
|
||
// Mevcut evaluation result'ını bul
|
||
const evaluationResult = mockEvaluation360Results.find((r) => r.id === result.resultId)
|
||
if (evaluationResult) {
|
||
// Mevcut cevapları yükle
|
||
const currentResponses: Record<string, string | number> = {}
|
||
evaluationResult.participants.forEach((participant) => {
|
||
participant.responses.forEach((response) => {
|
||
currentResponses[response.questionId] = response.responseValue
|
||
})
|
||
})
|
||
|
||
setEditResponses(currentResponses)
|
||
setEditManagerComments(evaluationResult.managerComments || '')
|
||
setEditHrComments(evaluationResult.hrComments || '')
|
||
setEditResultStatus(evaluationResult.status)
|
||
}
|
||
|
||
setSelectedResult(result)
|
||
setShowResultEditModal(true)
|
||
}
|
||
|
||
const getAssessorTypeLabel = (assessorType: AssessorTypeEnum): string => {
|
||
return getAssessorTypeText(assessorType)
|
||
}
|
||
|
||
const getAllEvaluationParticipants = () => {
|
||
// Tüm sonuçlardan participant verilerini al
|
||
const allParticipants = mockEvaluation360Results.flatMap((result) =>
|
||
result.participants.map((participant) => ({
|
||
...participant,
|
||
resultId: result.id,
|
||
evaluatedEmployeeName:
|
||
mockEmployees.find((e) => e.id === participant.evaluatedEmployeeId)?.fullName ||
|
||
'Bilinmiyor',
|
||
evaluatorName:
|
||
mockEmployees.find((e) => e.id === participant.evaluatorId)?.fullName || 'Bilinmiyor',
|
||
campaignName:
|
||
mockEvaluation360.find((c) => c.id === participant.campaignId)?.name || 'Bilinmiyor',
|
||
overallScore: result.overallScore,
|
||
scorePercentage: result.scorePercentage,
|
||
result: result,
|
||
})),
|
||
)
|
||
|
||
// Filtreleme işlemi
|
||
return allParticipants.filter((participant) => {
|
||
// Durum filtresi
|
||
if (selectedResultsStatus !== 'all' && participant.status !== selectedResultsStatus) {
|
||
return false
|
||
}
|
||
|
||
// Kampanya filtresi
|
||
if (selectedResultsCampaign !== 'all' && participant.campaignId !== selectedResultsCampaign) {
|
||
return false
|
||
}
|
||
|
||
// Değerlendirilecek kişi filtresi
|
||
if (
|
||
selectedResultsEmployee !== 'all' &&
|
||
participant.evaluatedEmployeeId !== selectedResultsEmployee
|
||
) {
|
||
return false
|
||
}
|
||
|
||
// Departman filtresi
|
||
if (selectedResultsDepartment !== 'all') {
|
||
const evaluatedEmployee = mockEmployees.find(
|
||
(e) => e.id === participant.evaluatedEmployeeId,
|
||
)
|
||
if (!evaluatedEmployee || evaluatedEmployee.departmentId !== selectedResultsDepartment) {
|
||
return false
|
||
}
|
||
}
|
||
|
||
return true
|
||
})
|
||
}
|
||
|
||
// Columns for campaigns table
|
||
const campaignColumns: Column<HrEvaluation360>[] = [
|
||
{
|
||
key: 'name',
|
||
header: 'Değerlendirme Adı',
|
||
sortable: true,
|
||
},
|
||
{
|
||
key: 'departmentId',
|
||
header: 'Departman',
|
||
render: (item: HrEvaluation360) => {
|
||
const department = mockDepartments.find((d) => d.id === item.departmentId)
|
||
return department?.name || 'Tüm Departmanlar'
|
||
},
|
||
},
|
||
{
|
||
key: 'templateId',
|
||
header: 'Şablon',
|
||
render: (item: HrEvaluation360) => {
|
||
const template = mockEvaluation360Templates.find((t) => t.id === item.templateId)
|
||
return template?.name || 'Bilinmiyor'
|
||
},
|
||
},
|
||
{
|
||
key: 'status',
|
||
header: 'Durum',
|
||
render: (item: HrEvaluation360) => (
|
||
<span
|
||
className={`px-2 py-1 text-xs font-medium rounded-full ${getCampaignStatusColor(
|
||
item.status,
|
||
)}`}
|
||
>
|
||
{getCampaignStatusText(item.status)}
|
||
</span>
|
||
),
|
||
},
|
||
{
|
||
key: 'startDate',
|
||
header: 'Başlangıç',
|
||
render: (item: HrEvaluation360) => item.startDate.toLocaleDateString('tr-TR'),
|
||
},
|
||
{
|
||
key: 'endDate',
|
||
header: 'Bitiş',
|
||
render: (item: HrEvaluation360) => item.endDate.toLocaleDateString('tr-TR'),
|
||
},
|
||
{
|
||
key: 'participants',
|
||
header: 'Katılımcı',
|
||
render: (item: HrEvaluation360) => {
|
||
const participants = getCampaignParticipants(item.id)
|
||
return `${participants.length} kişi`
|
||
},
|
||
},
|
||
{
|
||
key: 'id',
|
||
header: 'İşlemler',
|
||
render: (item: HrEvaluation360) => (
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={() => handleViewCampaign(item)}
|
||
className="p-1 text-blue-600 hover:text-blue-800"
|
||
title="Görüntüle"
|
||
>
|
||
<FaEye className="w-4 h-4" />
|
||
</button>
|
||
<button
|
||
onClick={() => handleEditCampaign(item)}
|
||
className="p-1 text-green-600 hover:text-green-800"
|
||
title="Düzenle"
|
||
>
|
||
<FaEdit className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
),
|
||
},
|
||
]
|
||
|
||
return (
|
||
<Container>
|
||
<div className="space-y-2">
|
||
{/* Header */}
|
||
<div className="flex justify-between items-center">
|
||
<div>
|
||
<h2 className="text-2xl font-bold text-gray-900">360° Değerlendirme Sistemi</h2>
|
||
<p className="text-gray-600">Çok yönlü performans değerlendirme sistemi</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Statistics Cards */}
|
||
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
|
||
<Widget
|
||
title="Aktif Değerlendirmeler"
|
||
value={totalActiveCampaigns}
|
||
color="blue"
|
||
icon="FaPlay"
|
||
/>
|
||
|
||
<Widget
|
||
title="Tamamlanan Değerlendirmeler"
|
||
value={totalCompletedEvaluations}
|
||
color="green"
|
||
icon="FaCheckCircle"
|
||
/>
|
||
|
||
<Widget
|
||
title="Bekleyen Değerlendirmeler"
|
||
value={totalPendingEvaluations}
|
||
color="yellow"
|
||
icon="FaPoll"
|
||
/>
|
||
|
||
<Widget title="Katılımcılar" value={totalParticipants} color="purple" icon="FaUsers" />
|
||
</div>
|
||
|
||
{/* Tabs */}
|
||
<div className="bg-white rounded-lg shadow-sm border">
|
||
<div className="border-b border-gray-200">
|
||
<nav className="-mb-px flex space-x-2 px-2">
|
||
{[
|
||
{
|
||
key: 'campaigns',
|
||
label: 'Değerlendirme Kampanyaları',
|
||
icon: FaUsers,
|
||
},
|
||
{
|
||
key: 'results',
|
||
label: 'Değerlendirme Sonuçları',
|
||
icon: FaChartBar,
|
||
},
|
||
].map(({ key, label, icon: Icon }) => (
|
||
<button
|
||
key={key}
|
||
onClick={() => setActiveTab(key as 'campaigns' | 'results')}
|
||
className={`flex items-center gap-2 py-1.5 px-2.5 border-b-2 font-medium text-sm ${
|
||
activeTab === key
|
||
? '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" />
|
||
{label}
|
||
</button>
|
||
))}
|
||
</nav>
|
||
</div>
|
||
|
||
<div className="p-3">
|
||
{activeTab === 'campaigns' && (
|
||
<div className="space-y-3">
|
||
{/* Filters and Actions */}
|
||
<div className="flex flex-col sm:flex-row gap-3 justify-between">
|
||
<div className="flex flex-col sm:flex-row gap-3">
|
||
<select
|
||
value={selectedStatus}
|
||
onChange={(e) => setSelectedStatus(e.target.value)}
|
||
className="border border-gray-300 rounded-md px-2.5 py-1 text-sm"
|
||
>
|
||
<option value="all">Tüm Durumlar</option>
|
||
{Object.values(CampaignStatusEnum).map((status) => (
|
||
<option key={status} value={status}>
|
||
{getCampaignStatusText(status)}
|
||
</option>
|
||
))}
|
||
</select>
|
||
|
||
<select
|
||
value={selectedDepartment}
|
||
onChange={(e) => setSelectedDepartment(e.target.value)}
|
||
className="border border-gray-300 rounded-md px-2.5 py-1 text-sm"
|
||
>
|
||
<option value="all">Tüm Departmanlar</option>
|
||
{mockDepartments.map((department) => (
|
||
<option key={department.id} value={department.id}>
|
||
{department.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
|
||
<select
|
||
value={selectedEmployee}
|
||
onChange={(e) => setSelectedEmployee(e.target.value)}
|
||
className="border border-gray-300 rounded-md px-2.5 py-1 text-sm"
|
||
>
|
||
<option value="all">Tüm Personel</option>
|
||
{mockEmployees.map((employee) => (
|
||
<option key={employee.id} value={employee.id}>
|
||
{employee.fullName}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={handleNewCampaign}
|
||
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 text-sm"
|
||
>
|
||
<FaPlus className="w-4 h-4" />
|
||
Yeni Değerlendirme
|
||
</button>
|
||
<button
|
||
onClick={handleStartEvaluation}
|
||
className="flex items-center gap-2 px-3 py-1.5 bg-green-600 text-white rounded-md hover:bg-green-700 text-sm"
|
||
>
|
||
<FaPoll className="w-4 h-4" />
|
||
Değerlendirme Yap
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Campaigns Table */}
|
||
<DataTable data={filteredCampaigns} columns={campaignColumns} />
|
||
</div>
|
||
)}
|
||
|
||
{activeTab === 'results' && (
|
||
<div className="space-y-3">
|
||
{/* Results Filters */}
|
||
<div className="flex flex-col sm:flex-row gap-3 justify-between">
|
||
<div className="flex flex-col sm:flex-row gap-3">
|
||
<select
|
||
value={selectedResultsCampaign}
|
||
onChange={(e) => setSelectedResultsCampaign(e.target.value)}
|
||
className="border border-gray-300 rounded-md px-2.5 py-1 text-sm"
|
||
>
|
||
<option value="all">Tüm Değerlendirmeler</option>
|
||
{mockEvaluation360.map((campaign) => (
|
||
<option key={campaign.id} value={campaign.id}>
|
||
{campaign.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
|
||
<select
|
||
value={selectedResultsStatus}
|
||
onChange={(e) => setSelectedResultsStatus(e.target.value)}
|
||
className="border border-gray-300 rounded-md px-2.5 py-1 text-sm"
|
||
>
|
||
<option value="all">Tüm Durumlar</option>
|
||
{Object.values(ParticipantStatusEnum).map((status) => (
|
||
<option key={status} value={status}>
|
||
{getParticipantStatusText(status)}
|
||
</option>
|
||
))}
|
||
</select>
|
||
|
||
<select
|
||
value={selectedResultsDepartment}
|
||
onChange={(e) => setSelectedResultsDepartment(e.target.value)}
|
||
className="border border-gray-300 rounded-md px-2.5 py-1 text-sm"
|
||
>
|
||
<option value="all">Tüm Departmanlar</option>
|
||
{mockDepartments.map((department) => (
|
||
<option key={department.id} value={department.id}>
|
||
{department.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
|
||
<select
|
||
value={selectedResultsEmployee}
|
||
onChange={(e) => setSelectedResultsEmployee(e.target.value)}
|
||
className="border border-gray-300 rounded-md px-2.5 py-1 text-sm"
|
||
>
|
||
<option value="all">Tüm Personel</option>
|
||
{mockEmployees.map((employee) => (
|
||
<option key={employee.id} value={employee.id}>
|
||
{employee.firstName} {employee.lastName}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Results Summary */}
|
||
|
||
<div className="bg-white rounded-lg border overflow-hidden">
|
||
<div className="overflow-x-auto">
|
||
<table className="min-w-full divide-y divide-gray-200">
|
||
<thead className="bg-gray-50">
|
||
<tr>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Değerlendirme Adı
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Değerlendirilecek Kişi
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Değerlendiren
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Toplam İlerleme
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Değerlendirme Durumu
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Toplam Puan
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Puan Yüzdesi
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
İşlemler
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="bg-white divide-y divide-gray-200">
|
||
{getAllEvaluationParticipants().map((result) => (
|
||
<tr
|
||
key={`${result.resultId}-${result.evaluatedEmployeeId}-${result.evaluatorId}`}
|
||
className="hover:bg-gray-50 text-xs"
|
||
>
|
||
<td className="px-3 py-2 whitespace-nowrap text-gray-500">
|
||
<div className="flex flex-col">
|
||
<span className="font-medium text-gray-900">
|
||
{result.campaignName}
|
||
</span>
|
||
<span className="text-xs text-gray-500">
|
||
{result.completedDate
|
||
? new Date(result.completedDate).toLocaleDateString('tr-TR')
|
||
: '-'}
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-gray-500">
|
||
{result.evaluatedEmployeeName}
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-gray-500">
|
||
<div className="flex flex-col">
|
||
<span className="font-medium text-gray-900">
|
||
{result.evaluatorName}
|
||
</span>
|
||
<span className="text-xs text-gray-500">
|
||
{getAssessorTypeLabel(result.evaluatorType)}
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm">
|
||
<div className="flex items-center">
|
||
<div className="w-16 bg-gray-200 rounded-full h-2 mr-2">
|
||
<div
|
||
className="bg-blue-600 h-2 rounded-full"
|
||
style={{
|
||
width: `${
|
||
result.status === ParticipantStatusEnum.Completed ? 100 : 50
|
||
}%`,
|
||
}}
|
||
></div>
|
||
</div>
|
||
<span className="text-sm text-gray-600">
|
||
{result.status === ParticipantStatusEnum.Completed ? 100 : 50}%
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||
<span
|
||
className={`inline-block px-2 py-1 text-xs rounded-full ${getParticipantStatusColor(
|
||
result.status,
|
||
)}`}
|
||
>
|
||
{getParticipantStatusText(result.status)}
|
||
</span>
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||
{result.overallScore}/100
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||
%{result.scorePercentage}
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm font-medium">
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={() => handleViewEvaluationDetail(result)}
|
||
className="text-blue-600 hover:text-blue-900"
|
||
title="Değerlendirme Detayını Görüntüle"
|
||
>
|
||
<FaEye className="w-4 h-4" />
|
||
</button>
|
||
<button
|
||
onClick={() => handleEditEvaluationDetail(result)}
|
||
className="text-green-600 hover:text-green-900"
|
||
title="Değerlendirme Detayını Düzenle"
|
||
>
|
||
<FaEdit className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Campaign Modal */}
|
||
{showCampaignModal && (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-2">
|
||
<div className="bg-white rounded-lg w-full max-w-lg max-h-[90vh] overflow-y-auto">
|
||
<div className="flex justify-between items-center p-2.5 border-b">
|
||
<h2 className="text-base font-semibold">
|
||
{selectedCampaign ? 'Değerlendirme Düzenle' : 'Yeni Değerlendirme'}
|
||
</h2>
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="p-1.5 text-gray-400 hover:text-gray-600"
|
||
>
|
||
<FaTimes className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
<div className="p-3 space-y-2.5">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Değerlendirme Adı
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={campaignFormData.name}
|
||
onChange={(e) =>
|
||
setCampaignFormData({
|
||
...campaignFormData,
|
||
name: e.target.value,
|
||
})
|
||
}
|
||
className="w-full border border-gray-300 rounded-md px-2 py-1 text-sm"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama</label>
|
||
<textarea
|
||
value={campaignFormData.description}
|
||
onChange={(e) =>
|
||
setCampaignFormData({
|
||
...campaignFormData,
|
||
description: e.target.value,
|
||
})
|
||
}
|
||
rows={1}
|
||
className="w-full border border-gray-300 rounded-md px-2 py-1 text-sm"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Değerlendirme Şablonu
|
||
</label>
|
||
<select
|
||
value={campaignFormData.templateId}
|
||
onChange={(e) =>
|
||
setCampaignFormData({
|
||
...campaignFormData,
|
||
templateId: e.target.value,
|
||
})
|
||
}
|
||
className="w-full border border-gray-300 rounded-md px-2 py-1 text-sm"
|
||
>
|
||
<option value="">Şablon Seçin</option>
|
||
{mockEvaluation360Templates.map((template) => (
|
||
<option key={template.id} value={template.id}>
|
||
{template.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Departman</label>
|
||
<select
|
||
value={campaignFormData.departmentId}
|
||
onChange={(e) => handleDepartmentChange(e.target.value)}
|
||
className="w-full border border-gray-300 rounded-md px-2 py-1 text-sm"
|
||
>
|
||
<option value="">Departman Seçin</option>
|
||
{mockDepartments.map((department) => (
|
||
<option key={department.id} value={department.id}>
|
||
{department.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{campaignFormData.departmentId && (
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Hedef Personel
|
||
</label>
|
||
<div className="border border-gray-300 rounded-md p-1.5 max-h-28 overflow-y-auto">
|
||
{filteredEmployeesByDepartment.map((employee) => (
|
||
<label key={employee.id} className="flex items-center mb-2">
|
||
<input
|
||
type="checkbox"
|
||
checked={campaignFormData.targetEmployees.includes(employee.id)}
|
||
onChange={(e) => {
|
||
if (e.target.checked) {
|
||
setCampaignFormData({
|
||
...campaignFormData,
|
||
targetEmployees: [...campaignFormData.targetEmployees, employee.id],
|
||
})
|
||
} else {
|
||
setCampaignFormData({
|
||
...campaignFormData,
|
||
targetEmployees: campaignFormData.targetEmployees.filter(
|
||
(id) => id !== employee.id,
|
||
),
|
||
})
|
||
}
|
||
}}
|
||
className="mr-2"
|
||
/>
|
||
{employee.firstName} {employee.lastName} -{' '}
|
||
{employee.jobPosition?.name || 'Pozisyon Belirtilmemiş'}
|
||
</label>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="grid grid-cols-2 gap-2.5">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Başlangıç Tarihi
|
||
</label>
|
||
<input
|
||
type="date"
|
||
value={campaignFormData.startDate}
|
||
onChange={(e) =>
|
||
setCampaignFormData({
|
||
...campaignFormData,
|
||
startDate: e.target.value,
|
||
})
|
||
}
|
||
className="w-full border border-gray-300 rounded-md px-2 py-1 text-sm"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Bitiş Tarihi
|
||
</label>
|
||
<input
|
||
type="date"
|
||
value={campaignFormData.endDate}
|
||
onChange={(e) =>
|
||
setCampaignFormData({
|
||
...campaignFormData,
|
||
endDate: e.target.value,
|
||
})
|
||
}
|
||
className="w-full border border-gray-300 rounded-md px-2 py-1 text-sm"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex justify-end gap-2 p-2.5 border-t">
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="px-2.5 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
onClick={handleSaveCampaign}
|
||
className="flex items-center gap-2 px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||
>
|
||
<FaSave className="w-4 h-4" />
|
||
{selectedCampaign ? 'Güncelle' : 'Kaydet'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* View Modal */}
|
||
{showViewModal && selectedCampaign && (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-2">
|
||
<div className="bg-white rounded-lg w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
||
<div className="flex justify-between items-center p-2.5 border-b">
|
||
<h2 className="text-base font-semibold">{selectedCampaign.name}</h2>
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="p-1.5 text-gray-400 hover:text-gray-600"
|
||
>
|
||
<FaTimes className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
<div className="p-3 space-y-3">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||
Değerlendirme Bilgileri
|
||
</h3>
|
||
<div className="space-y-2">
|
||
<div>
|
||
<span className="text-sm font-medium text-gray-600">Adı:</span>
|
||
<p className="text-sm text-gray-900">{selectedCampaign.name}</p>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm font-medium text-gray-600">Açıklama:</span>
|
||
<p className="text-sm text-gray-900">{selectedCampaign.description}</p>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm font-medium text-gray-600">Durum:</span>
|
||
<span
|
||
className={`ml-2 px-3 py-1 text-xs font-medium rounded-full ${getCampaignStatusColor(
|
||
selectedCampaign.status,
|
||
)}`}
|
||
>
|
||
{getCampaignStatusText(selectedCampaign.status)}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm font-medium text-gray-600">Tarih Aralığı:</span>
|
||
<p className="text-sm text-gray-900">
|
||
{selectedCampaign.startDate.toLocaleDateString('tr-TR')} -{' '}
|
||
{selectedCampaign.endDate.toLocaleDateString('tr-TR')}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||
Katılımcı İstatistikleri
|
||
</h3>
|
||
<div className="space-y-2">
|
||
{(() => {
|
||
const participants = getCampaignParticipants(selectedCampaign.id)
|
||
const completed = participants.filter(
|
||
(p) => p.status === ParticipantStatusEnum.Completed,
|
||
)
|
||
const started = participants.filter(
|
||
(p) => p.status === ParticipantStatusEnum.Started,
|
||
)
|
||
const invited = participants.filter(
|
||
(p) => p.status === ParticipantStatusEnum.Invited,
|
||
)
|
||
|
||
return (
|
||
<>
|
||
<div className="flex justify-between">
|
||
<span className="text-sm text-gray-600">Toplam:</span>
|
||
<span className="text-sm font-medium text-gray-900">
|
||
{participants.length}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-sm text-gray-600">Tamamlanan:</span>
|
||
<span className="text-sm font-medium text-green-600">
|
||
{completed.length}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-sm text-gray-600">Devam Eden:</span>
|
||
<span className="text-sm font-medium text-blue-600">
|
||
{started.length}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-sm text-gray-600">Bekleyen:</span>
|
||
<span className="text-sm font-medium text-yellow-600">
|
||
{invited.length}
|
||
</span>
|
||
</div>
|
||
</>
|
||
)
|
||
})()}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">Katılımcı Detayları</h3>
|
||
<div className="overflow-x-auto">
|
||
<table className="min-w-full divide-y divide-gray-200">
|
||
<thead className="bg-gray-50">
|
||
<tr>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Hedef Çalışan
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Değerlendiren
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Tip
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Durum
|
||
</th>
|
||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||
Tarih
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="bg-white divide-y divide-gray-200">
|
||
{getCampaignParticipants(selectedCampaign.id).map((participant) => {
|
||
const targetEmployee = mockEmployees.find(
|
||
(emp) => emp.id === participant.evaluatedEmployeeId,
|
||
)
|
||
const assessorEmployee = mockEmployees.find(
|
||
(emp) => emp.id === participant.evaluatorId,
|
||
)
|
||
|
||
return (
|
||
<tr key={participant.id}>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||
{targetEmployee
|
||
? `${targetEmployee.firstName} ${targetEmployee.lastName}`
|
||
: 'Bilinmiyor'}
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||
{assessorEmployee
|
||
? `${assessorEmployee.firstName} ${assessorEmployee.lastName}`
|
||
: 'Bilinmiyor'}
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-500">
|
||
{getAssessorTypeText(participant.evaluatorType)}
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap">
|
||
<span
|
||
className={`px-2 py-1 text-xs font-medium rounded-full ${getParticipantStatusColor(
|
||
participant.status,
|
||
)}`}
|
||
>
|
||
{getParticipantStatusText(participant.status)}
|
||
</span>
|
||
</td>
|
||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-500">
|
||
{participant.completedDate
|
||
? participant.completedDate.toLocaleDateString('tr-TR')
|
||
: participant.startedDate
|
||
? `Başladı: ${participant.startedDate.toLocaleDateString(
|
||
'tr-TR',
|
||
)}`
|
||
: `Davet: ${participant.invitedDate.toLocaleDateString('tr-TR')}`}
|
||
</td>
|
||
</tr>
|
||
)
|
||
})}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex justify-end gap-2 p-2.5 border-t">
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="px-2.5 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
|
||
>
|
||
Kapat
|
||
</button>
|
||
<button
|
||
onClick={() => {
|
||
handleCloseModal()
|
||
handleEditCampaign(selectedCampaign)
|
||
}}
|
||
className="flex items-center gap-2 px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||
>
|
||
<FaEdit className="w-4 h-4" />
|
||
Düzenle
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Evaluation Modal */}
|
||
{showEvaluationModal && (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-2">
|
||
<div className="bg-white rounded-lg w-full max-w-lg max-h-[90vh] overflow-y-auto">
|
||
<div className="flex justify-between items-center p-2.5 border-b">
|
||
<h2 className="text-base font-semibold">
|
||
{!currentCampaignForEvaluation
|
||
? 'Değerlendirme Yap'
|
||
: currentCampaignForEvaluation.name}
|
||
</h2>
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="p-1.5 text-gray-400 hover:text-gray-600"
|
||
>
|
||
<FaTimes className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
<div className="p-3 space-y-2.5">
|
||
{!currentCampaignForEvaluation ? (
|
||
/* Adım 0: Değerlendirme seçimi */
|
||
<div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">Değerlendirme Seçin</h3>
|
||
<div className="space-y-2">
|
||
{mockEvaluation360
|
||
.filter((campaign) => campaign.status === CampaignStatusEnum.Active)
|
||
.map((campaign) => (
|
||
<div
|
||
key={campaign.id}
|
||
className="border rounded-lg p-2.5 hover:bg-gray-50 cursor-pointer"
|
||
onClick={() => setCurrentCampaignForEvaluation(campaign)}
|
||
>
|
||
<div className="flex justify-between items-center">
|
||
<div>
|
||
<h4 className="font-medium text-gray-900">{campaign.name}</h4>
|
||
<p className="text-gray-600">{campaign.description}</p>
|
||
<div className="flex items-center gap-2 mt-2">
|
||
<span
|
||
className={`inline-block px-2 py-1 text-xs rounded-full ${getCampaignStatusColor(
|
||
campaign.status,
|
||
)}`}
|
||
>
|
||
{getCampaignStatusText(campaign.status)}
|
||
</span>
|
||
<span className="text-xs text-gray-500">
|
||
{campaign.startDate.toLocaleDateString()} -{' '}
|
||
{campaign.endDate.toLocaleDateString()}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<FaPlay className="w-4 h-4 text-gray-400" />
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
{mockEvaluation360.filter(
|
||
(campaign) => campaign.status === CampaignStatusEnum.Active,
|
||
).length === 0 && (
|
||
<div className="text-center py-8 text-gray-500">
|
||
<FaPoll className="w-12 h-12 mx-auto mb-3 text-gray-300" />
|
||
<p>Aktif değerlendirme bulunmuyor</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : !evaluationTarget ? (
|
||
/* Adım 1: Değerlendirilecek kişi seçimi */
|
||
<div>
|
||
<div className="mb-4">
|
||
<button
|
||
onClick={() => setCurrentCampaignForEvaluation(null)}
|
||
className="text-blue-600 hover:text-blue-800 text-sm"
|
||
>
|
||
← Değerlendirme Seçimine Dön
|
||
</button>
|
||
</div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||
1. Adım: Değerlendirilecek Kişiyi Seçin
|
||
</h3>
|
||
<div className="bg-blue-50 p-2.5 rounded-lg mb-3">
|
||
<p className="text-sm text-blue-800">
|
||
<strong>Seçili Değerlendirme:</strong> {currentCampaignForEvaluation.name}
|
||
</p>
|
||
</div>
|
||
<div className="space-y-3">
|
||
{mockEmployees
|
||
.filter((emp) =>
|
||
currentCampaignForEvaluation.targetEmployees.includes(emp.id),
|
||
)
|
||
.map((employee) => (
|
||
<div
|
||
key={employee.id}
|
||
className="border rounded-lg p-2.5 hover:bg-gray-50 cursor-pointer"
|
||
onClick={() => setEvaluationTarget(employee.id)}
|
||
>
|
||
<div className="flex justify-between items-center">
|
||
<div>
|
||
<h4 className="font-medium text-gray-900">{employee.fullName}</h4>
|
||
<p className="text-gray-600">{employee.jobPosition?.name}</p>
|
||
<p className="text-gray-600">
|
||
{mockDepartments.find((d) => d.id === employee.departmentId)?.name}
|
||
</p>
|
||
</div>
|
||
<FaUsers className="w-4 h-4 text-gray-400" />
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
{mockEmployees.filter((emp) =>
|
||
currentCampaignForEvaluation.targetEmployees.includes(emp.id),
|
||
).length === 0 && (
|
||
<div className="text-center py-8 text-gray-500">
|
||
<FaUsers className="w-12 h-12 mx-auto mb-3 text-gray-300" />
|
||
<p>Bu değerlendirmede hedef çalışan bulunmuyor</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : !selectedAssessorType ? (
|
||
/* Adım 2a: Değerlendirici Tipi Seçimi */
|
||
<div>
|
||
<div className="mb-4">
|
||
<button
|
||
onClick={() => setEvaluationTarget('')}
|
||
className="text-blue-600 hover:text-blue-800 text-sm"
|
||
>
|
||
← Geri Dön
|
||
</button>
|
||
</div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||
2. Adım: Değerlendirici Tipi Seçin
|
||
</h3>
|
||
<div className="bg-blue-50 p-2.5 rounded-lg mb-3">
|
||
<p className="text-sm text-blue-800">
|
||
<strong>Değerlendirilecek:</strong>{' '}
|
||
{mockEmployees.find((e) => e.id === evaluationTarget)?.fullName}
|
||
</p>
|
||
</div>
|
||
|
||
{/* Değerlendirici tipi seçimi */}
|
||
<div className="space-y-2">
|
||
{getAllowedAssessorTypes().map((assessorType) => (
|
||
<div
|
||
key={assessorType}
|
||
className="border rounded-lg p-2.5 hover:bg-gray-50 cursor-pointer"
|
||
onClick={() => {
|
||
setSelectedAssessorType(assessorType)
|
||
setEvaluatorSearchTerm('')
|
||
}}
|
||
>
|
||
<div className="flex justify-between items-center">
|
||
<div>
|
||
<h4 className="font-medium text-gray-900">
|
||
{getAssessorTypeText(assessorType)}
|
||
</h4>
|
||
<p className="text-gray-600">
|
||
{getAssessorTypeDescription(assessorType)}
|
||
</p>
|
||
{getEvaluatorsByType(evaluationTarget, assessorType).length > 0 && (
|
||
<span className="inline-block mt-1 px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-800">
|
||
{getEvaluatorsByType(evaluationTarget, assessorType).length} seçenek
|
||
mevcut
|
||
</span>
|
||
)}
|
||
</div>
|
||
<FaUsers className="w-4 h-4 text-gray-400" />
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
) : !evaluationEvaluator ? (
|
||
/* Adım 2b: Belirli tip değerlendirici seçimi */
|
||
<div>
|
||
<div className="mb-4">
|
||
<button
|
||
onClick={() => {
|
||
setSelectedAssessorType(null)
|
||
setEvaluationEvaluator('')
|
||
setSelectedCustomerId('')
|
||
setExternalEvaluatorName('')
|
||
setExternalEvaluatorEmail('')
|
||
setEvaluatorSearchTerm('')
|
||
}}
|
||
className="text-blue-600 hover:text-blue-800 text-sm"
|
||
>
|
||
← Geri Dön
|
||
</button>
|
||
</div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||
2. Adım: {getAssessorTypeText(selectedAssessorType)} Seçin
|
||
</h3>
|
||
<div className="bg-blue-50 p-2.5 rounded-lg mb-3">
|
||
<p className="text-sm text-blue-800">
|
||
<strong>Değerlendirilecek:</strong>{' '}
|
||
{mockEmployees.find((e) => e.id === evaluationTarget)?.fullName}
|
||
<br />
|
||
<strong>Değerlendirici Tipi:</strong>{' '}
|
||
{getAssessorTypeText(selectedAssessorType)}
|
||
</p>
|
||
</div>
|
||
|
||
{selectedAssessorType === AssessorTypeEnum.External ? (
|
||
/* Dış değerlendirici için form */
|
||
<div className="space-y-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Değerlendirici Adı *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={externalEvaluatorName}
|
||
onChange={(e) => setExternalEvaluatorName(e.target.value)}
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
placeholder="Değerlendirici adını girin"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
E-posta Adresi *
|
||
</label>
|
||
<input
|
||
type="email"
|
||
value={externalEvaluatorEmail}
|
||
onChange={(e) => setExternalEvaluatorEmail(e.target.value)}
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
placeholder="E-posta adresini girin"
|
||
/>
|
||
</div>
|
||
{externalEvaluatorName && externalEvaluatorEmail && (
|
||
<button
|
||
onClick={() => setEvaluationEvaluator('external')}
|
||
className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 text-sm"
|
||
>
|
||
Dış Değerlendiriciyi Seç
|
||
</button>
|
||
)}
|
||
</div>
|
||
) : selectedAssessorType === AssessorTypeEnum.Customer ? (
|
||
/* Müşteri seçimi */
|
||
<div className="space-y-2">
|
||
{/* Arama kutusu */}
|
||
<div>
|
||
<input
|
||
type="text"
|
||
value={evaluatorSearchTerm}
|
||
onChange={(e) => setEvaluatorSearchTerm(e.target.value)}
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
placeholder="Müşteri adında ara..."
|
||
/>
|
||
</div>
|
||
{getEvaluatorsByType(evaluationTarget, selectedAssessorType)
|
||
.filter((evaluator) =>
|
||
evaluator.name.toLowerCase().includes(evaluatorSearchTerm.toLowerCase()),
|
||
)
|
||
.map((evaluator) => (
|
||
<div
|
||
key={evaluator.id}
|
||
className="border rounded-lg p-2.5 hover:bg-gray-50 cursor-pointer"
|
||
onClick={() => {
|
||
setSelectedCustomerId(evaluator.id)
|
||
setEvaluationEvaluator(evaluator.id)
|
||
}}
|
||
>
|
||
<div className="flex justify-between items-center">
|
||
<div>
|
||
<h4 className="font-medium text-gray-900">{evaluator.name}</h4>
|
||
<p className="text-gray-600">{evaluator.title}</p>
|
||
<span className="inline-block mt-1 px-2 py-1 text-xs rounded-full bg-purple-100 text-purple-800">
|
||
{evaluator.department}
|
||
</span>
|
||
</div>
|
||
<FaUsers className="w-4 h-4 text-gray-400" />
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
) : (
|
||
/* Diğer tipler için çalışan seçimi */
|
||
<div className="space-y-2">
|
||
{/* Arama kutusu */}
|
||
<div>
|
||
<input
|
||
type="text"
|
||
value={evaluatorSearchTerm}
|
||
onChange={(e) => setEvaluatorSearchTerm(e.target.value)}
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
placeholder="Çalışan adında ara..."
|
||
/>
|
||
</div>
|
||
{getEvaluatorsByType(evaluationTarget, selectedAssessorType)
|
||
.filter((evaluator) =>
|
||
evaluator.name.toLowerCase().includes(evaluatorSearchTerm.toLowerCase()),
|
||
)
|
||
.map((evaluator) => (
|
||
<div
|
||
key={evaluator.id}
|
||
className="border rounded-lg p-2.5 hover:bg-gray-50 cursor-pointer"
|
||
onClick={() => setEvaluationEvaluator(evaluator.id)}
|
||
>
|
||
<div className="flex justify-between items-center">
|
||
<div>
|
||
<h4 className="font-medium text-gray-900">{evaluator.name}</h4>
|
||
<p className="text-gray-600">{evaluator.title}</p>
|
||
<span className="inline-block mt-1 px-2 py-1 text-xs rounded-full bg-purple-100 text-purple-800">
|
||
{evaluator.department}
|
||
</span>
|
||
</div>
|
||
<FaUsers className="w-4 h-4 text-gray-400" />
|
||
</div>
|
||
</div>
|
||
))}
|
||
{getEvaluatorsByType(evaluationTarget, selectedAssessorType).length === 0 && (
|
||
<div className="text-center py-8 text-gray-500">
|
||
<FaUsers className="w-12 h-12 mx-auto mb-3 text-gray-300" />
|
||
<p>Bu kategoride değerlendirici bulunmuyor</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : (
|
||
/* Adım 3: Değerlendirme formu */
|
||
<div>
|
||
<div className="mb-4">
|
||
<button
|
||
onClick={() => {
|
||
setSelectedAssessorType(null)
|
||
setEvaluationEvaluator('')
|
||
setSelectedCustomerId('')
|
||
setExternalEvaluatorName('')
|
||
setExternalEvaluatorEmail('')
|
||
setEvaluatorSearchTerm('')
|
||
}}
|
||
className="text-blue-600 hover:text-blue-800 text-sm"
|
||
>
|
||
← Geri Dön
|
||
</button>
|
||
</div>
|
||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||
3. Adım: Değerlendirme Formu
|
||
</h3>
|
||
<div className="bg-green-50 p-2.5 rounded-lg mb-3">
|
||
<p className="text-sm text-green-800">
|
||
<strong>Değerlendirilen:</strong>{' '}
|
||
{mockEmployees.find((e) => e.id === evaluationTarget)?.fullName}
|
||
<br />
|
||
<strong>Değerlendiren:</strong> {getEvaluatorInfo()}
|
||
</p>
|
||
</div>
|
||
|
||
{/* Burada şablonun sorularını göstereceğiz */}
|
||
<div className="space-y-3">
|
||
{currentCampaignForEvaluation &&
|
||
mockEvaluation360Templates
|
||
.find((t) => t.id === currentCampaignForEvaluation.templateId)
|
||
?.questionGroups.map((group) => (
|
||
<div key={group.id} className="border rounded-lg p-2.5">
|
||
<h4 className="font-medium text-gray-900 mb-2">{group.groupName}</h4>
|
||
<p className="text-sm text-gray-600 mb-3">{group.description}</p>
|
||
<div className="space-y-3">
|
||
{group.questions.map((question, qIndex) => (
|
||
<div key={question.id} className="bg-gray-50 p-2.5 rounded">
|
||
<p className="font-medium text-gray-800 mb-3">
|
||
{qIndex + 1}. {question.questionText}
|
||
{question.isRequired && (
|
||
<span className="text-red-500 ml-1">*</span>
|
||
)}
|
||
</p>
|
||
|
||
{/* Soru tipine göre input alanları */}
|
||
{question.questionType === QuestionTypeEnum.Rating && (
|
||
<div className="space-y-2">
|
||
<div className="flex justify-around items-center">
|
||
{Array.from(
|
||
{
|
||
length:
|
||
(question.maxRating || 5) -
|
||
(question.minRating || 1) +
|
||
1,
|
||
},
|
||
(_, i) => {
|
||
const value = (question.minRating || 1) + i
|
||
return (
|
||
<label
|
||
key={value}
|
||
className="flex flex-col items-center cursor-pointer"
|
||
>
|
||
<input
|
||
type="radio"
|
||
name={`question-${question.id}`}
|
||
value={value}
|
||
checked={
|
||
evaluationResponses[question.id] === value
|
||
}
|
||
onChange={(e) =>
|
||
handleResponseChange(
|
||
question.id,
|
||
Number(e.target.value),
|
||
)
|
||
}
|
||
className="mb-1"
|
||
/>
|
||
<span className="text-xs text-gray-600">
|
||
{question.ratingLabels?.[i] || value}
|
||
</span>
|
||
</label>
|
||
)
|
||
},
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{question.questionType === QuestionTypeEnum.Text && (
|
||
<textarea
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
rows={3}
|
||
placeholder="Yorumunuzu yazın..."
|
||
value={evaluationResponses[question.id] || ''}
|
||
onChange={(e) =>
|
||
handleResponseChange(question.id, e.target.value)
|
||
}
|
||
/>
|
||
)}
|
||
|
||
{question.questionType === QuestionTypeEnum.YesNo && (
|
||
<div className="flex gap-4">
|
||
<label className="flex items-center cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
name={`question-${question.id}`}
|
||
value="yes"
|
||
checked={evaluationResponses[question.id] === 'yes'}
|
||
onChange={(e) =>
|
||
handleResponseChange(question.id, e.target.value)
|
||
}
|
||
className="mr-2"
|
||
/>
|
||
Evet
|
||
</label>
|
||
<label className="flex items-center cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
name={`question-${question.id}`}
|
||
value="no"
|
||
checked={evaluationResponses[question.id] === 'no'}
|
||
onChange={(e) =>
|
||
handleResponseChange(question.id, e.target.value)
|
||
}
|
||
className="mr-2"
|
||
/>
|
||
Hayır
|
||
</label>
|
||
</div>
|
||
)}
|
||
|
||
{question.questionType === QuestionTypeEnum.MultipleChoice && (
|
||
<div className="space-y-2">
|
||
{question.options?.map((option) => (
|
||
<label
|
||
key={option.id}
|
||
className="flex items-center cursor-pointer"
|
||
>
|
||
<input
|
||
type="radio"
|
||
name={`question-${question.id}`}
|
||
value={option.value}
|
||
checked={
|
||
evaluationResponses[question.id] === option.value
|
||
}
|
||
onChange={(e) =>
|
||
handleResponseChange(
|
||
question.id,
|
||
Number(e.target.value),
|
||
)
|
||
}
|
||
className="mr-2"
|
||
/>
|
||
{option.optionText}
|
||
</label>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="flex justify-end gap-2 p-2.5 border-t">
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="px-2.5 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
|
||
>
|
||
İptal
|
||
</button>
|
||
{evaluationTarget && evaluationEvaluator && (
|
||
<button
|
||
onClick={handleSaveEvaluation}
|
||
className="flex items-center gap-2 px-2.5 py-1 text-sm bg-green-600 text-white rounded-md hover:bg-green-700"
|
||
>
|
||
<FaSave className="w-4 h-4" />
|
||
Değerlendirmeyi Kaydet
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Result Detail Modal */}
|
||
{showResultDetailModal && selectedResult && (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-2">
|
||
<div className="bg-white rounded-lg w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
||
<div className="flex justify-between items-center p-2.5 border-b">
|
||
<h2 className="text-base font-semibold">Değerlendirme Detayı</h2>
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="p-1.5 text-gray-400 hover:text-gray-600"
|
||
>
|
||
<FaTimes className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
<div className="p-3 space-y-2.5">
|
||
{/* Genel Bilgiler */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||
<div className="bg-blue-50 p-4 rounded-lg">
|
||
<h3 className="font-medium text-blue-800 mb-2 text-sm">
|
||
Değerlendirme Bilgileri
|
||
</h3>
|
||
<div className="space-y-1.5">
|
||
<div>
|
||
<span className="text-sm text-blue-700 font-medium">Kampanya: </span>
|
||
<span className="text-sm text-blue-800">{selectedResult.campaignName}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm text-blue-700 font-medium">Değerlendirilen: </span>
|
||
<span className="text-sm text-blue-800">
|
||
{selectedResult.evaluatedEmployeeName}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm text-blue-700 font-medium">Değerlendiren: </span>
|
||
<span className="text-sm text-blue-800">{selectedResult.evaluatorName}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm text-blue-700 font-medium">
|
||
Değerlendiren Tipi:{' '}
|
||
</span>
|
||
<span className="text-sm text-blue-800">
|
||
{getAssessorTypeText(selectedResult.evaluatorType)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-green-50 p-4 rounded-lg">
|
||
<h3 className="font-medium text-green-800 mb-2 text-sm">Sonuç Bilgileri</h3>
|
||
<div className="space-y-1.5">
|
||
<div>
|
||
<span className="text-sm text-green-700 font-medium">Durum: </span>
|
||
<span
|
||
className={`text-sm px-2 py-0.5 rounded-full ${getParticipantStatusColor(
|
||
selectedResult.status,
|
||
)}`}
|
||
>
|
||
{getParticipantStatusText(selectedResult.status)}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm text-green-700 font-medium">Toplam Puan: </span>
|
||
<span className="text-sm text-green-800 font-bold">
|
||
{selectedResult.overallScore}/100
|
||
</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm text-green-700 font-medium">Yüzde: </span>
|
||
<span className="text-sm text-green-800 font-bold">
|
||
%{selectedResult.scorePercentage}
|
||
</span>
|
||
</div>
|
||
{selectedResult.completedDate && (
|
||
<div>
|
||
<span className="text-sm text-green-700 font-medium">
|
||
Tamamlanma Tarihi:{' '}
|
||
</span>
|
||
<span className="text-sm text-green-800">
|
||
{new Date(selectedResult.completedDate).toLocaleDateString('tr-TR')}
|
||
</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Detaylı Sonuçlar */}
|
||
<div className="border rounded-lg p-3">
|
||
<h3 className="font-medium text-gray-900 mb-2 text-sm">Detaylı Sonuçlar</h3>
|
||
|
||
{(() => {
|
||
// Seçili sonucu mockEvaluation360Results'tan bul
|
||
const evaluationResult = mockEvaluation360Results.find(
|
||
(r) => r.id === selectedResult.resultId,
|
||
)
|
||
if (!evaluationResult) {
|
||
return (
|
||
<div className="text-center py-8 text-gray-500">
|
||
<FaChartBar className="w-12 h-12 mx-auto mb-3 text-gray-300" />
|
||
<p>Sonuç verisi bulunamadı</p>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-2.5">
|
||
{/* Soru Cevapları */}
|
||
{(() => {
|
||
const campaign = mockEvaluation360.find(
|
||
(c) => c.id === selectedResult.campaignId,
|
||
)
|
||
const template = campaign
|
||
? mockEvaluation360Templates.find((t) => t.id === campaign.templateId)
|
||
: null
|
||
|
||
if (template) {
|
||
return (
|
||
<div>
|
||
<h4 className="font-medium text-gray-800 mb-2 text-sm">
|
||
📝 Soru Cevapları
|
||
</h4>
|
||
<div className="space-y-3">
|
||
{template.questionGroups.map((group) => (
|
||
<div key={group.id} className="bg-gray-50 p-2.5 rounded-lg">
|
||
<div className="mb-3">
|
||
<h5 className="font-medium text-gray-800">
|
||
{group.groupName}
|
||
</h5>
|
||
<p className="text-gray-600">{group.description}</p>
|
||
</div>
|
||
|
||
<div className="space-y-2.5">
|
||
{group.questions.map((question, qIndex) => {
|
||
const response = evaluationResult.participants
|
||
.flatMap((p) => p.responses)
|
||
.find((r) => r.questionId === question.id)
|
||
|
||
if (!response) return null
|
||
|
||
return (
|
||
<div
|
||
key={question.id}
|
||
className="bg-white p-1.5 rounded border-l-4 border-blue-200"
|
||
>
|
||
<div className="mb-2">
|
||
<span className="text-sm font-medium text-gray-800">
|
||
S{qIndex + 1}: {question.questionText}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center gap-3">
|
||
<span className="text-sm text-gray-600">
|
||
Cevap:
|
||
</span>
|
||
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-sm font-medium">
|
||
{(() => {
|
||
if (
|
||
question.questionType ===
|
||
QuestionTypeEnum.Rating
|
||
) {
|
||
const ratingIndex =
|
||
Number(response.responseValue) -
|
||
(question.minRating || 1)
|
||
const label =
|
||
question.ratingLabels?.[ratingIndex]
|
||
return label
|
||
? `${response.responseValue} - ${label}`
|
||
: response.responseValue
|
||
} else if (
|
||
question.questionType ===
|
||
QuestionTypeEnum.YesNo
|
||
) {
|
||
return response.responseValue === 'yes'
|
||
? 'Evet'
|
||
: 'Hayır'
|
||
} else if (
|
||
question.questionType ===
|
||
QuestionTypeEnum.MultipleChoice
|
||
) {
|
||
const option = question.options?.find(
|
||
(o) =>
|
||
o.value ===
|
||
Number(response.responseValue),
|
||
)
|
||
return option
|
||
? option.optionText
|
||
: response.responseValue
|
||
} else {
|
||
return response.responseValue
|
||
}
|
||
})()}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-xs text-gray-500">Puan:</span>
|
||
<span className="px-2 py-1 bg-green-100 text-green-800 rounded text-sm font-bold">
|
||
{response.score}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{response.responseText && (
|
||
<div className="mt-2 p-2 bg-gray-100 rounded">
|
||
<span className="text-xs text-gray-600">
|
||
Detay:{' '}
|
||
</span>
|
||
<span className="text-sm text-gray-700">
|
||
{response.responseText}
|
||
</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
return null
|
||
})()}
|
||
|
||
{/* Grup Bazlı Puanlar */}
|
||
<div>
|
||
<h4 className="font-medium text-gray-800 mb-2 text-sm">
|
||
📊 Grup Bazlı Puanlar
|
||
</h4>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||
{evaluationResult.groupScores.map((groupScore) => (
|
||
<div key={groupScore.groupId} className="bg-gray-50 p-2.5 rounded-lg">
|
||
<div className="flex justify-between items-center mb-2">
|
||
<h5 className="font-medium text-gray-700">
|
||
{groupScore.groupName}
|
||
</h5>
|
||
<span className="text-lg font-bold text-blue-600">
|
||
{groupScore.score}/{groupScore.maxScore}
|
||
</span>
|
||
</div>
|
||
<div className="w-full bg-gray-200 rounded-full h-2 mb-2">
|
||
<div
|
||
className="bg-blue-600 h-2 rounded-full"
|
||
style={{ width: `${groupScore.percentage}%` }}
|
||
></div>
|
||
</div>
|
||
<div className="flex justify-between text-sm text-gray-600">
|
||
<span>%{Math.round(groupScore.percentage)}</span>
|
||
<span>{groupScore.responseCount} cevap</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Değerlendirici Tipi Bazlı Puanlar */}
|
||
<div>
|
||
<h4 className="font-medium text-gray-800 mb-2 text-sm">
|
||
👥 Değerlendirici Tipi Bazlı Puanlar
|
||
</h4>
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||
{evaluationResult.assessorTypeScores.map((assessorScore, index) => (
|
||
<div key={index} className="bg-blue-50 p-2.5 rounded-lg text-center">
|
||
<div className="text-sm text-blue-700 font-medium mb-1">
|
||
{getAssessorTypeText(assessorScore.assessorType)}
|
||
</div>
|
||
<div className="text-2xl font-bold text-blue-800 mb-1">
|
||
%{Math.round(assessorScore.percentage)}
|
||
</div>
|
||
<div className="text-xs text-blue-600">
|
||
{assessorScore.averageScore}/{assessorScore.maxScore} puan
|
||
</div>
|
||
<div className="text-xs text-blue-500">
|
||
{assessorScore.assessorCount} değerlendirici
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Güçlü Yönler */}
|
||
<div>
|
||
<h4 className="font-medium text-gray-800 mb-2 text-sm">💪 Güçlü Yönler</h4>
|
||
<div className="bg-green-50 p-2.5 rounded-lg">
|
||
{evaluationResult.strengths.length > 0 ? (
|
||
<ul className="list-disc list-inside space-y-1">
|
||
{evaluationResult.strengths.map((strength, index) => (
|
||
<li key={index} className="text-green-800">
|
||
{strength}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
) : (
|
||
<p className="text-green-700 italic">
|
||
Güçlü yönler henüz analiz edilmedi
|
||
</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Gelişim Alanları */}
|
||
<div>
|
||
<h4 className="font-medium text-gray-800 mb-2 text-sm">
|
||
📈 Gelişim Alanları
|
||
</h4>
|
||
<div className="bg-amber-50 p-2.5 rounded-lg">
|
||
{evaluationResult.developmentAreas.length > 0 ? (
|
||
<ul className="list-disc list-inside space-y-1">
|
||
{evaluationResult.developmentAreas.map((area, index) => (
|
||
<li key={index} className="text-amber-800">
|
||
{area}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
) : (
|
||
<p className="text-amber-700 italic">
|
||
Gelişim alanları henüz analiz edilmedi
|
||
</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Aksiyon Planı */}
|
||
<div>
|
||
<h4 className="font-medium text-gray-800 mb-2 text-sm">🎯 Aksiyon Planı</h4>
|
||
<div className="bg-purple-50 p-2.5 rounded-lg">
|
||
{evaluationResult.actionPlan.length > 0 ? (
|
||
<ul className="list-decimal list-inside space-y-1">
|
||
{evaluationResult.actionPlan.map((action, index) => (
|
||
<li key={index} className="text-purple-800">
|
||
{action}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
) : (
|
||
<p className="text-purple-700 italic">
|
||
Aksiyon planı henüz oluşturulmadı
|
||
</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Yorumlar */}
|
||
{(evaluationResult.managerComments || evaluationResult.hrComments) && (
|
||
<div>
|
||
<h4 className="font-medium text-gray-800 mb-2 text-sm">💬 Yorumlar</h4>
|
||
<div className="space-y-2">
|
||
{evaluationResult.managerComments && (
|
||
<div className="bg-blue-50 p-2.5 rounded-lg">
|
||
<h5 className="font-medium text-blue-800 mb-2">Yönetici Yorumu</h5>
|
||
<p className="text-blue-700">{evaluationResult.managerComments}</p>
|
||
</div>
|
||
)}
|
||
{evaluationResult.hrComments && (
|
||
<div className="bg-indigo-50 p-2.5 rounded-lg">
|
||
<h5 className="font-medium text-indigo-800 mb-2">İK Yorumu</h5>
|
||
<p className="text-indigo-700">{evaluationResult.hrComments}</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
})()}
|
||
</div>
|
||
</div>
|
||
<div className="flex justify-end gap-2 p-2.5 border-t">
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="px-2.5 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
|
||
>
|
||
Kapat
|
||
</button>
|
||
<button
|
||
onClick={() => {
|
||
handleCloseModal()
|
||
handleEditEvaluationDetail(selectedResult)
|
||
}}
|
||
className="flex items-center gap-2 px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||
>
|
||
<FaEdit className="w-4 h-4" />
|
||
Düzenle
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Result Edit Modal */}
|
||
{showResultEditModal && selectedResult && (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-2">
|
||
<div className="bg-white rounded-lg w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
||
<div className="flex justify-between items-center p-2.5 border-b">
|
||
<h2 className="text-base font-semibold">Değerlendirme Düzenle</h2>
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="p-1.5 text-gray-400 hover:text-gray-600"
|
||
>
|
||
<FaTimes className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
<div className="p-3 space-y-2.5">
|
||
<div className="bg-amber-50 p-2.5 rounded-lg">
|
||
<h3 className="font-medium text-amber-800 mb-2 text-sm">Değerlendirme Bilgileri</h3>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<span className="text-sm text-amber-700 font-medium">Kampanya: </span>
|
||
<span className="text-sm text-amber-800">{selectedResult.campaignName}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm text-amber-700 font-medium">Değerlendirilen: </span>
|
||
<span className="text-sm text-amber-800">
|
||
{selectedResult.evaluatedEmployeeName}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm text-amber-700 font-medium">Değerlendiren: </span>
|
||
<span className="text-sm text-amber-800">{selectedResult.evaluatorName}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-sm text-amber-700 font-medium">Değerlendiren Tipi: </span>
|
||
<span className="text-sm text-amber-800">
|
||
{getAssessorTypeText(selectedResult.evaluatorType)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Soru Grupları ve Cevaplar */}
|
||
{(() => {
|
||
const evaluationResult = mockEvaluation360Results.find(
|
||
(r) => r.id === selectedResult.resultId,
|
||
)
|
||
const campaign = mockEvaluation360.find((c) => c.id === selectedResult.campaignId)
|
||
const template = campaign
|
||
? mockEvaluation360Templates.find((t) => t.id === campaign.templateId)
|
||
: null
|
||
|
||
if (!evaluationResult || !template) {
|
||
return (
|
||
<div className="text-center py-8 text-gray-500">
|
||
<p>Template verisi bulunamadı</p>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-3">
|
||
<h3 className="text-sm font-medium text-gray-900">Soru Grupları ve Cevaplar</h3>
|
||
|
||
{template.questionGroups.map((group) => (
|
||
<div key={group.id} className="border rounded-lg p-2">
|
||
<div className="mb-3">
|
||
<h4 className="font-medium text-gray-900 text-sm">{group.groupName}</h4>
|
||
<p className="text-gray-600">{group.description}</p>
|
||
<div className="flex items-center gap-2 mt-2">
|
||
<span className="text-xs text-gray-500">Ağırlık: %{group.weight}</span>
|
||
<span className="text-xs text-gray-500">•</span>
|
||
<span className="text-xs text-gray-500">
|
||
{group.questions.length} soru
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-2.5">
|
||
{group.questions.map((question, qIndex) => {
|
||
const currentResponse = evaluationResult.participants
|
||
.flatMap((p) => p.responses)
|
||
.find((r) => r.questionId === question.id)
|
||
|
||
const editValue =
|
||
editResponses[question.id] !== undefined
|
||
? editResponses[question.id]
|
||
: currentResponse?.responseValue
|
||
|
||
return (
|
||
<div key={question.id} className="bg-gray-50 p-2.5 rounded-lg">
|
||
<p className="font-medium text-gray-800 mb-3">
|
||
{qIndex + 1}. {question.questionText}
|
||
{question.isRequired && (
|
||
<span className="text-red-500 ml-1">*</span>
|
||
)}
|
||
</p>
|
||
|
||
{currentResponse && (
|
||
<div className="mb-2 p-1.5 bg-blue-50 rounded">
|
||
<span className="text-xs text-blue-700 font-medium">
|
||
Mevcut Cevap:{' '}
|
||
</span>
|
||
<span className="text-xs text-blue-800">
|
||
{typeof currentResponse.responseValue === 'string'
|
||
? currentResponse.responseValue
|
||
: `${currentResponse.responseValue} puan`}
|
||
</span>
|
||
<span className="text-xs text-blue-600 ml-2">
|
||
(Puan: {currentResponse.score})
|
||
</span>
|
||
</div>
|
||
)}
|
||
|
||
{/* Soru tipine göre düzenleme alanları */}
|
||
{question.questionType === QuestionTypeEnum.Rating && (
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-700 font-medium">
|
||
Yeni Puan:
|
||
</label>
|
||
<div className="flex gap-2.5">
|
||
{Array.from(
|
||
{
|
||
length:
|
||
(question.maxRating || 5) -
|
||
(question.minRating || 1) +
|
||
1,
|
||
},
|
||
(_, i) => {
|
||
const value = (question.minRating || 1) + i
|
||
return (
|
||
<label
|
||
key={value}
|
||
className="flex flex-col items-center cursor-pointer"
|
||
>
|
||
<input
|
||
type="radio"
|
||
name={`edit-question-${question.id}`}
|
||
value={value}
|
||
checked={editValue === value}
|
||
onChange={(e) =>
|
||
handleEditResponseChange(
|
||
question.id,
|
||
Number(e.target.value),
|
||
)
|
||
}
|
||
className="mb-1"
|
||
/>
|
||
<span className="text-xs text-gray-600">
|
||
{question.ratingLabels?.[i] || value}
|
||
</span>
|
||
</label>
|
||
)
|
||
},
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{question.questionType === QuestionTypeEnum.Text && (
|
||
<div>
|
||
<label className="block text-sm text-gray-700 font-medium mb-2">
|
||
Yeni Cevap:
|
||
</label>
|
||
<textarea
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
rows={3}
|
||
placeholder="Yeni cevabı yazın..."
|
||
value={editValue || ''}
|
||
onChange={(e) =>
|
||
handleEditResponseChange(question.id, e.target.value)
|
||
}
|
||
/>
|
||
</div>
|
||
)}
|
||
|
||
{question.questionType === QuestionTypeEnum.YesNo && (
|
||
<div>
|
||
<label className="block text-sm text-gray-700 font-medium mb-2">
|
||
Yeni Cevap:
|
||
</label>
|
||
<div className="flex gap-4">
|
||
<label className="flex items-center cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
name={`edit-question-${question.id}`}
|
||
value="yes"
|
||
checked={editValue === 'yes'}
|
||
onChange={(e) =>
|
||
handleEditResponseChange(question.id, e.target.value)
|
||
}
|
||
className="mr-2"
|
||
/>
|
||
Evet
|
||
</label>
|
||
<label className="flex items-center cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
name={`edit-question-${question.id}`}
|
||
value="no"
|
||
checked={editValue === 'no'}
|
||
onChange={(e) =>
|
||
handleEditResponseChange(question.id, e.target.value)
|
||
}
|
||
className="mr-2"
|
||
/>
|
||
Hayır
|
||
</label>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{question.questionType === QuestionTypeEnum.MultipleChoice && (
|
||
<div>
|
||
<label className="block text-sm text-gray-700 font-medium mb-2">
|
||
Yeni Seçim:
|
||
</label>
|
||
<div className="space-y-2">
|
||
{question.options?.map((option) => (
|
||
<label
|
||
key={option.id}
|
||
className="flex items-center cursor-pointer"
|
||
>
|
||
<input
|
||
type="radio"
|
||
name={`edit-question-${question.id}`}
|
||
value={option.value}
|
||
checked={editValue === option.value}
|
||
onChange={(e) =>
|
||
handleEditResponseChange(
|
||
question.id,
|
||
Number(e.target.value),
|
||
)
|
||
}
|
||
className="mr-2"
|
||
/>
|
||
{option.optionText}
|
||
</label>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)
|
||
})()}
|
||
|
||
{/* Yorumlar ve Durum */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Yönetici Yorumları
|
||
</label>
|
||
<textarea
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
rows={4}
|
||
placeholder="Yönetici yorumlarını buraya yazın..."
|
||
value={editManagerComments}
|
||
onChange={(e) => setEditManagerComments(e.target.value)}
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
İK Yorumları
|
||
</label>
|
||
<textarea
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
rows={4}
|
||
placeholder="İK yorumlarını buraya yazın..."
|
||
value={editHrComments}
|
||
onChange={(e) => setEditHrComments(e.target.value)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Sonuç Durumu</label>
|
||
<select
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
||
value={editResultStatus}
|
||
onChange={(e) => setEditResultStatus(e.target.value)}
|
||
>
|
||
<option value="PENDING">Beklemede</option>
|
||
<option value="IN_REVIEW">İncelemede</option>
|
||
<option value="APPROVED">Onaylandı</option>
|
||
<option value="PUBLISHED">Yayınlandı</option>
|
||
</select>
|
||
</div>
|
||
|
||
{/* Mevcut Puan Bilgileri */}
|
||
<div className="border rounded-lg p-3">
|
||
<h4 className="font-medium text-gray-900 mb-2 text-sm">Mevcut Puan Bilgileri</h4>
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div className="text-center p-4 bg-blue-50 rounded-lg">
|
||
<div className="text-2xl font-bold text-blue-600">
|
||
{selectedResult.overallScore}
|
||
</div>
|
||
<div className="text-sm text-blue-700">Toplam Puan</div>
|
||
</div>
|
||
<div className="text-center p-4 bg-green-50 rounded-lg">
|
||
<div className="text-2xl font-bold text-green-600">
|
||
%{selectedResult.scorePercentage}
|
||
</div>
|
||
<div className="text-sm text-green-700">Başarı Yüzdesi</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex justify-end gap-2 p-2.5 border-t">
|
||
<button
|
||
onClick={handleCloseModal}
|
||
className="px-2.5 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
onClick={() => {
|
||
console.log('Güncellenen cevaplar:', editResponses)
|
||
console.log('Yönetici yorumları:', editManagerComments)
|
||
console.log('İK yorumları:', editHrComments)
|
||
console.log('Durum:', editResultStatus)
|
||
alert('Değerlendirme güncellendi!')
|
||
handleCloseModal()
|
||
}}
|
||
className="flex items-center gap-2 px-2.5 py-1 text-sm bg-green-600 text-white rounded-md hover:bg-green-700"
|
||
>
|
||
<FaSave className="w-4 h-4" />
|
||
Kaydet
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</Container>
|
||
)
|
||
}
|
||
|
||
export default Degree360Evaluation
|