2025-09-15 19:46:52 +00:00
|
|
|
|
import React, { useState } from 'react'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
import {
|
|
|
|
|
|
FaUsers,
|
|
|
|
|
|
FaEye,
|
|
|
|
|
|
FaEdit,
|
|
|
|
|
|
FaPlus,
|
|
|
|
|
|
FaChartBar,
|
|
|
|
|
|
FaTimes,
|
|
|
|
|
|
FaSave,
|
|
|
|
|
|
FaPlay,
|
|
|
|
|
|
FaPoll,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
} from 'react-icons/fa'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
import {
|
|
|
|
|
|
HrEvaluation360,
|
|
|
|
|
|
CampaignStatusEnum,
|
|
|
|
|
|
AssessorTypeEnum,
|
|
|
|
|
|
ParticipantStatusEnum,
|
|
|
|
|
|
QuestionTypeEnum,
|
|
|
|
|
|
ResultStatusEnum,
|
|
|
|
|
|
HrEvaluation360Result,
|
|
|
|
|
|
HrGroupScore,
|
|
|
|
|
|
HrAssessorTypeScore,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
} 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'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
import {
|
|
|
|
|
|
getAssessorTypeDescription,
|
|
|
|
|
|
getAssessorTypeText,
|
|
|
|
|
|
getParticipantStatusColor,
|
|
|
|
|
|
getParticipantStatusText,
|
|
|
|
|
|
getCampaignStatusColor,
|
|
|
|
|
|
getCampaignStatusText,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
} from '../../../utils/erp'
|
|
|
|
|
|
import { Container } from '@/components/shared'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const Degree360Evaluation: React.FC = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Results tab filters
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const [selectedResultsStatus, setSelectedResultsStatus] = useState<string>('all')
|
|
|
|
|
|
const [selectedResultsEmployee, setSelectedResultsEmployee] = useState<string>('all')
|
|
|
|
|
|
const [selectedResultsDepartment, setSelectedResultsDepartment] = useState<string>('all')
|
|
|
|
|
|
const [selectedResultsCampaign, setSelectedResultsCampaign] = useState<string>('all')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Modal states
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const [selectedResult, setSelectedResult] = useState<{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Evaluation states
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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>('')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const [currentCampaignForEvaluation, setCurrentCampaignForEvaluation] =
|
2025-09-15 19:46:52 +00:00
|
|
|
|
useState<HrEvaluation360 | null>(null)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Evaluation responses state
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const [evaluationResponses, setEvaluationResponses] = useState<Record<string, string | number>>(
|
|
|
|
|
|
{},
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Edit modal states
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const [editResponses, setEditResponses] = useState<Record<string, string | number>>({})
|
|
|
|
|
|
const [editManagerComments, setEditManagerComments] = useState<string>('')
|
|
|
|
|
|
const [editHrComments, setEditHrComments] = useState<string>('')
|
|
|
|
|
|
const [editResultStatus, setEditResultStatus] = useState<string>('PENDING')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Form states
|
|
|
|
|
|
const [campaignFormData, setCampaignFormData] = useState({
|
2025-09-15 19:46:52 +00:00
|
|
|
|
name: '',
|
|
|
|
|
|
description: '',
|
|
|
|
|
|
templateId: '',
|
|
|
|
|
|
departmentId: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
targetEmployees: [] as string[],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
startDate: '',
|
|
|
|
|
|
endDate: '',
|
|
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Event handlers
|
|
|
|
|
|
const handleCloseModal = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
setCampaignFormData({
|
2025-09-15 19:46:52 +00:00
|
|
|
|
name: '',
|
|
|
|
|
|
description: '',
|
|
|
|
|
|
templateId: '',
|
|
|
|
|
|
departmentId: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
targetEmployees: [],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
startDate: '',
|
|
|
|
|
|
endDate: '',
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Response handling functions
|
|
|
|
|
|
const handleResponseChange = (questionId: string, value: string | number) => {
|
|
|
|
|
|
setEvaluationResponses((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
[questionId]: value,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}))
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Edit response handling function
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const handleEditResponseChange = (questionId: string, value: string | number) => {
|
2025-09-15 09:31:47 +00:00
|
|
|
|
setEditResponses((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
[questionId]: value,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}))
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Evaluation kaydetme fonksiyonu
|
|
|
|
|
|
const handleSaveEvaluation = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (!currentCampaignForEvaluation || !evaluationTarget || !evaluationEvaluator) {
|
|
|
|
|
|
alert('Tüm alanları doldurun!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const template = mockEvaluation360Templates.find(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(t) => t.id === currentCampaignForEvaluation.templateId,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
if (!template) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Template bulunamadı!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Responses oluştur
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return {
|
|
|
|
|
|
id: `response-${Date.now()}-${Math.random()}`,
|
|
|
|
|
|
participantId: `participant-${Date.now()}`,
|
|
|
|
|
|
questionId,
|
|
|
|
|
|
question,
|
|
|
|
|
|
responseValue,
|
|
|
|
|
|
responseText: typeof responseValue === 'string' ? responseValue : undefined,
|
|
|
|
|
|
score,
|
|
|
|
|
|
submittedDate: new Date(),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Puanları hesapla
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const totalScore = responses.reduce((sum, response) => sum + response.score, 0)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const maxScore = template.questionGroups
|
|
|
|
|
|
.flatMap((g) => g.questions)
|
2025-09-15 19:46:52 +00:00
|
|
|
|
.reduce((sum, q) => sum + (q.maxRating || 5), 0)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const scorePercentage = maxScore > 0 ? (totalScore / maxScore) * 100 : 0
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Grup skorlarını hesapla
|
|
|
|
|
|
const groupScores: HrGroupScore[] = template.questionGroups.map((group) => {
|
|
|
|
|
|
const groupResponses = responses.filter((r) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
groupId: group.id,
|
|
|
|
|
|
groupName: group.groupName,
|
|
|
|
|
|
score: groupScore,
|
|
|
|
|
|
maxScore: groupMaxScore,
|
|
|
|
|
|
percentage: groupMaxScore > 0 ? (groupScore / groupMaxScore) * 100 : 0,
|
|
|
|
|
|
responseCount: groupResponses.length,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Assessor type skorlarını hesapla
|
|
|
|
|
|
const assessorTypeScores: HrAssessorTypeScore[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
assessorType: selectedAssessorType!,
|
|
|
|
|
|
assessorCount: 1,
|
|
|
|
|
|
averageScore: totalScore,
|
|
|
|
|
|
maxScore: maxScore,
|
|
|
|
|
|
percentage: scorePercentage,
|
|
|
|
|
|
},
|
2025-09-15 19:46:52 +00:00
|
|
|
|
]
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// 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,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
evaluatedEmployee: mockEmployees.find((e) => e.id === evaluationTarget),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
notes: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
overallScore: totalScore,
|
|
|
|
|
|
maxPossibleScore: maxScore,
|
|
|
|
|
|
scorePercentage,
|
|
|
|
|
|
groupScores,
|
|
|
|
|
|
assessorTypeScores,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
status: ResultStatusEnum.Pending,
|
|
|
|
|
|
generatedDate: new Date(),
|
|
|
|
|
|
approvedBy: undefined,
|
|
|
|
|
|
approvedDate: undefined,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Console'a yazdır
|
2025-09-15 19:46:52 +00:00
|
|
|
|
console.log('Evaluation360Result:', evaluationResult)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Değerlendirme başarıyla kaydedildi! Sonuç konsola yazdırıldı.')
|
|
|
|
|
|
handleCloseModal()
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleNewCampaign = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedCampaign(null)
|
|
|
|
|
|
setShowCampaignModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleEditCampaign = (campaign: HrEvaluation360 | null) => {
|
|
|
|
|
|
if (campaign) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedCampaign(campaign)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
setCampaignFormData({
|
|
|
|
|
|
name: campaign.name,
|
|
|
|
|
|
description: campaign.description,
|
|
|
|
|
|
templateId: campaign.templateId,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
departmentId: campaign.departmentId || '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
targetEmployees: campaign.targetEmployees,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
startDate: campaign.startDate.toISOString().split('T')[0],
|
|
|
|
|
|
endDate: campaign.endDate.toISOString().split('T')[0],
|
|
|
|
|
|
})
|
|
|
|
|
|
setShowCampaignModal(true)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleViewCampaign = (campaign: HrEvaluation360) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedCampaign(campaign)
|
|
|
|
|
|
setShowViewModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleSaveCampaign = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
console.log('Değerlendirme kaydediliyor:', campaignFormData)
|
|
|
|
|
|
handleCloseModal()
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleDepartmentChange = (departmentId: string) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedDepartment(departmentId)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
setCampaignFormData({
|
|
|
|
|
|
...campaignFormData,
|
|
|
|
|
|
departmentId,
|
|
|
|
|
|
targetEmployees: [],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Değerlendirme yapma handler'ı
|
|
|
|
|
|
const handleStartEvaluation = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setCurrentCampaignForEvaluation(null)
|
|
|
|
|
|
setEvaluationTarget('')
|
|
|
|
|
|
setEvaluationEvaluator('')
|
|
|
|
|
|
setShowEvaluationModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Otomatik değerlendirici belirleme fonksiyonu (360° metodolojisi)
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const getEvaluatorsByType = (targetEmployeeId: string, assessorType: AssessorTypeEnum) => {
|
|
|
|
|
|
const targetEmployee = mockEmployees.find((emp) => emp.id === targetEmployeeId)
|
|
|
|
|
|
if (!targetEmployee) return []
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
switch (assessorType) {
|
|
|
|
|
|
case AssessorTypeEnum.Self: {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: targetEmployee.id,
|
|
|
|
|
|
name: targetEmployee.fullName,
|
|
|
|
|
|
title: targetEmployee.jobPosition?.name,
|
|
|
|
|
|
department:
|
2025-10-22 08:20:11 +00:00
|
|
|
|
mockDepartments.find((d) => d.id === targetEmployee.departmentId)?.name || '',
|
2025-09-15 19:46:52 +00:00
|
|
|
|
type: 'employee',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
2025-09-15 19:46:52 +00:00
|
|
|
|
]
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case AssessorTypeEnum.Manager: {
|
|
|
|
|
|
const managers = mockEmployees.filter(
|
|
|
|
|
|
(emp) =>
|
2025-10-22 08:20:11 +00:00
|
|
|
|
emp.departmentId === targetEmployee.departmentId &&
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(typeof emp.jobPosition?.level === 'number' &&
|
|
|
|
|
|
typeof targetEmployee.jobPosition?.level === 'number'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
? emp.jobPosition?.level > targetEmployee.jobPosition?.level
|
2025-09-15 19:46:52 +00:00
|
|
|
|
: emp.jobPosition?.name.includes('Müdür') ||
|
|
|
|
|
|
emp.jobPosition?.name.includes('Manager')),
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return managers.map((emp) => ({
|
|
|
|
|
|
id: emp.id,
|
|
|
|
|
|
name: emp.fullName,
|
|
|
|
|
|
title: emp.jobPosition?.name,
|
2025-10-22 08:20:11 +00:00
|
|
|
|
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
2025-09-15 19:46:52 +00:00
|
|
|
|
type: 'employee',
|
|
|
|
|
|
}))
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case AssessorTypeEnum.Peer: {
|
|
|
|
|
|
const peers = mockEmployees.filter(
|
|
|
|
|
|
(emp) =>
|
|
|
|
|
|
emp.id !== targetEmployeeId &&
|
2025-10-22 08:20:11 +00:00
|
|
|
|
emp.departmentId === targetEmployee.departmentId &&
|
2025-09-15 19:46:52 +00:00
|
|
|
|
emp.jobPosition?.level === targetEmployee.jobPosition?.level,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return peers.map((emp) => ({
|
|
|
|
|
|
id: emp.id,
|
|
|
|
|
|
name: emp.fullName,
|
|
|
|
|
|
title: emp.jobPosition?.name,
|
2025-10-22 08:20:11 +00:00
|
|
|
|
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
2025-09-15 19:46:52 +00:00
|
|
|
|
type: 'employee',
|
|
|
|
|
|
}))
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case AssessorTypeEnum.Subordinate: {
|
|
|
|
|
|
const subordinates = mockEmployees.filter(
|
|
|
|
|
|
(emp) =>
|
2025-10-22 08:20:11 +00:00
|
|
|
|
emp.departmentId === targetEmployee.departmentId &&
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(typeof emp.jobPosition?.level === 'number' &&
|
|
|
|
|
|
typeof targetEmployee.jobPosition?.level === 'number'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
? emp.jobPosition?.level < targetEmployee.jobPosition?.level
|
2025-09-15 19:46:52 +00:00
|
|
|
|
: !emp.jobPosition?.name.includes('Müdür') &&
|
|
|
|
|
|
!emp.jobPosition?.name.includes('Manager')),
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return subordinates.map((emp) => ({
|
|
|
|
|
|
id: emp.id,
|
|
|
|
|
|
name: emp.fullName,
|
|
|
|
|
|
title: emp.jobPosition?.name,
|
2025-10-22 08:20:11 +00:00
|
|
|
|
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
2025-09-15 19:46:52 +00:00
|
|
|
|
type: 'employee',
|
|
|
|
|
|
}))
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case AssessorTypeEnum.Customer: {
|
|
|
|
|
|
return mockBusinessParties.map((customer) => ({
|
|
|
|
|
|
id: customer.id,
|
|
|
|
|
|
name: customer.name,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
title: 'Müşteri Temsilcisi',
|
|
|
|
|
|
department: 'Müşteri',
|
|
|
|
|
|
type: 'customer',
|
|
|
|
|
|
}))
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case AssessorTypeEnum.External: {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
id: 'external',
|
|
|
|
|
|
name: 'Dış Değerlendirici',
|
|
|
|
|
|
title: 'Harici',
|
|
|
|
|
|
department: 'Dış Paydaş',
|
|
|
|
|
|
type: 'external',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
2025-09-15 19:46:52 +00:00
|
|
|
|
]
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case AssessorTypeEnum.HRUpperManagement: {
|
|
|
|
|
|
const hrEmployees = mockEmployees.filter(
|
|
|
|
|
|
(emp) =>
|
2025-10-22 08:20:11 +00:00
|
|
|
|
emp.id !== targetEmployeeId && (emp.departmentId === 'hr' || emp.departmentId === '3'), // HR departmanı
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return hrEmployees.map((emp) => ({
|
|
|
|
|
|
id: emp.id,
|
|
|
|
|
|
name: emp.fullName,
|
|
|
|
|
|
title: emp.jobPosition?.name,
|
2025-10-22 08:20:11 +00:00
|
|
|
|
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
2025-09-15 19:46:52 +00:00
|
|
|
|
type: 'employee',
|
|
|
|
|
|
}))
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case AssessorTypeEnum.OtherDepartment: {
|
|
|
|
|
|
const otherDepartmentEmployees = mockEmployees.filter(
|
2025-10-22 08:20:11 +00:00
|
|
|
|
(emp) => emp.id !== targetEmployeeId && emp.departmentId !== targetEmployee.departmentId,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return otherDepartmentEmployees.map((emp) => ({
|
|
|
|
|
|
id: emp.id,
|
|
|
|
|
|
name: emp.fullName,
|
|
|
|
|
|
title: emp.jobPosition?.name,
|
2025-10-22 08:20:11 +00:00
|
|
|
|
department: mockDepartments.find((d) => d.id === emp.departmentId)?.name || '',
|
2025-09-15 19:46:52 +00:00
|
|
|
|
type: 'employee',
|
|
|
|
|
|
}))
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return []
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Değerlendiren bilgisini getiren fonksiyon
|
|
|
|
|
|
const getEvaluatorInfo = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (evaluationEvaluator === 'external') {
|
|
|
|
|
|
return externalEvaluatorName
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedAssessorType === AssessorTypeEnum.Customer) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const customer = mockBusinessParties.find((c) => c.id === selectedCustomerId)
|
|
|
|
|
|
return customer ? customer.name : 'Müşteri Bulunamadı'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const employee = mockEmployees.find((e) => e.id === evaluationEvaluator)
|
|
|
|
|
|
return employee ? employee.fullName : 'Çalışan Bulunamadı'
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Template'deki izin verilen assessor tiplerini getir
|
|
|
|
|
|
const getAllowedAssessorTypes = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (!currentCampaignForEvaluation) return []
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const template = mockEvaluation360Templates.find(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(t) => t.id === currentCampaignForEvaluation.templateId,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return template?.assessorTypes || []
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Helper function to get participants for a campaign from results
|
|
|
|
|
|
const getCampaignParticipants = (campaignId: string) => {
|
|
|
|
|
|
return mockEvaluation360Results
|
|
|
|
|
|
.filter((result) => result.campaignId === campaignId)
|
2025-09-15 19:46:52 +00:00
|
|
|
|
.flatMap((result) => result.participants)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Filtered data
|
|
|
|
|
|
const filteredCampaigns = mockEvaluation360.filter((campaign) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (selectedStatus !== 'all' && campaign.status !== selectedStatus) return false
|
|
|
|
|
|
if (selectedDepartment !== 'all' && campaign.departmentId !== selectedDepartment) return false
|
|
|
|
|
|
if (selectedEmployee !== 'all') {
|
|
|
|
|
|
const participants = getCampaignParticipants(campaign.id)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const hasEmployee = participants.some(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(p) => p.evaluatedEmployeeId === selectedEmployee || p.evaluatorId === selectedEmployee,
|
|
|
|
|
|
)
|
|
|
|
|
|
if (!hasEmployee) return false
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const filteredEmployeesByDepartment = mockEmployees.filter(
|
2025-10-22 08:20:11 +00:00
|
|
|
|
(emp) => selectedDepartment === 'all' || emp.departmentId === selectedDepartment,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Statistics
|
|
|
|
|
|
const totalActiveCampaigns = mockEvaluation360.filter(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(c) => c.status === CampaignStatusEnum.Active,
|
|
|
|
|
|
).length
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const allParticipants = mockEvaluation360Results.flatMap((result) => result.participants)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const totalCompletedEvaluations = allParticipants.filter(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(p) => p.status === ParticipantStatusEnum.Completed,
|
|
|
|
|
|
).length
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const totalPendingEvaluations = allParticipants.filter(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(p) => p.status === ParticipantStatusEnum.Invited || p.status === ParticipantStatusEnum.Started,
|
|
|
|
|
|
).length
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const totalParticipants = allParticipants.length
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Results helper functions
|
|
|
|
|
|
// Participant evaluation helper functions
|
|
|
|
|
|
const handleViewEvaluationDetail = (result: {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
resultId: string
|
|
|
|
|
|
evaluatedEmployeeName: string
|
|
|
|
|
|
evaluatorName: string
|
|
|
|
|
|
campaignName: string
|
|
|
|
|
|
overallScore: number
|
|
|
|
|
|
scorePercentage: number
|
|
|
|
|
|
evaluatedEmployeeId: string
|
|
|
|
|
|
evaluatorId: string
|
|
|
|
|
|
campaignId: string
|
|
|
|
|
|
evaluatorType: AssessorTypeEnum
|
|
|
|
|
|
status: ParticipantStatusEnum
|
|
|
|
|
|
completedDate?: Date
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}) => {
|
|
|
|
|
|
// Değerlendirme detaylarını görüntüle
|
2025-09-15 19:46:52 +00:00
|
|
|
|
console.log('Viewing evaluation detail:', result)
|
|
|
|
|
|
setSelectedResult(result)
|
|
|
|
|
|
setShowResultDetailModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleEditEvaluationDetail = (result: {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
resultId: string
|
|
|
|
|
|
evaluatedEmployeeName: string
|
|
|
|
|
|
evaluatorName: string
|
|
|
|
|
|
campaignName: string
|
|
|
|
|
|
overallScore: number
|
|
|
|
|
|
scorePercentage: number
|
|
|
|
|
|
evaluatedEmployeeId: string
|
|
|
|
|
|
evaluatorId: string
|
|
|
|
|
|
campaignId: string
|
|
|
|
|
|
evaluatorType: AssessorTypeEnum
|
|
|
|
|
|
status: ParticipantStatusEnum
|
|
|
|
|
|
completedDate?: Date
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}) => {
|
|
|
|
|
|
// Değerlendirme detaylarını düzenle
|
2025-09-15 19:46:52 +00:00
|
|
|
|
console.log('Editing evaluation detail:', result)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Mevcut evaluation result'ını bul
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const evaluationResult = mockEvaluation360Results.find((r) => r.id === result.resultId)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
if (evaluationResult) {
|
|
|
|
|
|
// Mevcut cevapları yükle
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const currentResponses: Record<string, string | number> = {}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
evaluationResult.participants.forEach((participant) => {
|
|
|
|
|
|
participant.responses.forEach((response) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
currentResponses[response.questionId] = response.responseValue
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
setEditResponses(currentResponses)
|
|
|
|
|
|
setEditManagerComments(evaluationResult.managerComments || '')
|
|
|
|
|
|
setEditHrComments(evaluationResult.hrComments || '')
|
|
|
|
|
|
setEditResultStatus(evaluationResult.status)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedResult(result)
|
|
|
|
|
|
setShowResultEditModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const getAssessorTypeLabel = (assessorType: AssessorTypeEnum): string => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return getAssessorTypeText(assessorType)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const getAllEvaluationParticipants = () => {
|
|
|
|
|
|
// Tüm sonuçlardan participant verilerini al
|
|
|
|
|
|
const allParticipants = mockEvaluation360Results.flatMap((result) =>
|
|
|
|
|
|
result.participants.map((participant) => ({
|
|
|
|
|
|
...participant,
|
|
|
|
|
|
resultId: result.id,
|
|
|
|
|
|
evaluatedEmployeeName:
|
2025-09-15 19:46:52 +00:00
|
|
|
|
mockEmployees.find((e) => e.id === participant.evaluatedEmployeeId)?.fullName ||
|
|
|
|
|
|
'Bilinmiyor',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
evaluatorName:
|
2025-09-15 19:46:52 +00:00
|
|
|
|
mockEmployees.find((e) => e.id === participant.evaluatorId)?.fullName || 'Bilinmiyor',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
campaignName:
|
2025-09-15 19:46:52 +00:00
|
|
|
|
mockEvaluation360.find((c) => c.id === participant.campaignId)?.name || 'Bilinmiyor',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
overallScore: result.overallScore,
|
|
|
|
|
|
scorePercentage: result.scorePercentage,
|
|
|
|
|
|
result: result,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})),
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Filtreleme işlemi
|
|
|
|
|
|
return allParticipants.filter((participant) => {
|
|
|
|
|
|
// Durum filtresi
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (selectedResultsStatus !== 'all' && participant.status !== selectedResultsStatus) {
|
|
|
|
|
|
return false
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Kampanya filtresi
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (selectedResultsCampaign !== 'all' && participant.campaignId !== selectedResultsCampaign) {
|
|
|
|
|
|
return false
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Değerlendirilecek kişi filtresi
|
|
|
|
|
|
if (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
selectedResultsEmployee !== 'all' &&
|
2025-09-15 09:31:47 +00:00
|
|
|
|
participant.evaluatedEmployeeId !== selectedResultsEmployee
|
|
|
|
|
|
) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return false
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Departman filtresi
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (selectedResultsDepartment !== 'all') {
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const evaluatedEmployee = mockEmployees.find(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(e) => e.id === participant.evaluatedEmployeeId,
|
|
|
|
|
|
)
|
2025-10-22 08:20:11 +00:00
|
|
|
|
if (!evaluatedEmployee || evaluatedEmployee.departmentId !== selectedResultsDepartment) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return false
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Columns for campaigns table
|
|
|
|
|
|
const campaignColumns: Column<HrEvaluation360>[] = [
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'name',
|
|
|
|
|
|
header: 'Değerlendirme Adı',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
sortable: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'departmentId',
|
|
|
|
|
|
header: 'Departman',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (item: HrEvaluation360) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const department = mockDepartments.find((d) => d.id === item.departmentId)
|
|
|
|
|
|
return department?.name || 'Tüm Departmanlar'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'templateId',
|
|
|
|
|
|
header: 'Şablon',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (item: HrEvaluation360) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const template = mockEvaluation360Templates.find((t) => t.id === item.templateId)
|
|
|
|
|
|
return template?.name || 'Bilinmiyor'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'status',
|
|
|
|
|
|
header: 'Durum',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (item: HrEvaluation360) => (
|
|
|
|
|
|
<span
|
|
|
|
|
|
className={`px-2 py-1 text-xs font-medium rounded-full ${getCampaignStatusColor(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
item.status,
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
{getCampaignStatusText(item.status)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'startDate',
|
|
|
|
|
|
header: 'Başlangıç',
|
|
|
|
|
|
render: (item: HrEvaluation360) => item.startDate.toLocaleDateString('tr-TR'),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'endDate',
|
|
|
|
|
|
header: 'Bitiş',
|
|
|
|
|
|
render: (item: HrEvaluation360) => item.endDate.toLocaleDateString('tr-TR'),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'participants',
|
|
|
|
|
|
header: 'Katılımcı',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (item: HrEvaluation360) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const participants = getCampaignParticipants(item.id)
|
|
|
|
|
|
return `${participants.length} kişi`
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'id',
|
|
|
|
|
|
header: 'İşlemler',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
2025-09-15 19:46:52 +00:00
|
|
|
|
]
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
return (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<Container>
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
{/* Header */}
|
|
|
|
|
|
<div className="flex justify-between items-center">
|
|
|
|
|
|
<div>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</div>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{/* 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" />
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{/* 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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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>
|
2025-09-17 09:46:58 +00:00
|
|
|
|
{Object.values(CampaignStatusEnum).map((status) => (
|
|
|
|
|
|
<option key={status} value={status}>
|
|
|
|
|
|
{getCampaignStatusText(status)}
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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}>
|
2025-09-17 09:46:58 +00:00
|
|
|
|
{employee.fullName}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
|
|
|
|
|
|
{/* Campaigns Table */}
|
|
|
|
|
|
<DataTable data={filteredCampaigns} columns={campaignColumns} />
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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>
|
2025-09-17 09:46:58 +00:00
|
|
|
|
{Object.values(ParticipantStatusEnum).map((status) => (
|
|
|
|
|
|
<option key={status} value={status}>
|
|
|
|
|
|
{getParticipantStatusText(status)}
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</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,
|
|
|
|
|
|
)}`}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{selectedCampaign ? 'Değerlendirme Düzenle' : 'Yeni Değerlendirme'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Departman</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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) => (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label key={employee.id} className="flex items-center mb-2">
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<input
|
|
|
|
|
|
type="checkbox"
|
2025-09-15 19:46:52 +00:00
|
|
|
|
checked={campaignFormData.targetEmployees.includes(employee.id)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
if (e.target.checked) {
|
|
|
|
|
|
setCampaignFormData({
|
|
|
|
|
|
...campaignFormData,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
targetEmployees: [...campaignFormData.targetEmployees, employee.id],
|
|
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
setCampaignFormData({
|
|
|
|
|
|
...campaignFormData,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
targetEmployees: campaignFormData.targetEmployees.filter(
|
|
|
|
|
|
(id) => id !== employee.id,
|
|
|
|
|
|
),
|
|
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
className="mr-2"
|
|
|
|
|
|
/>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{employee.firstName} {employee.lastName} -{' '}
|
|
|
|
|
|
{employee.jobPosition?.name || 'Pozisyon Belirtilmemiş'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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" />
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{selectedCampaign ? 'Güncelle' : 'Kaydet'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h2 className="text-base font-semibold">{selectedCampaign.name}</h2>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm font-medium text-gray-600">Adı:</span>
|
|
|
|
|
|
<p className="text-sm text-gray-900">{selectedCampaign.name}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm font-medium text-gray-600">Açıklama:</span>
|
|
|
|
|
|
<p className="text-sm text-gray-900">{selectedCampaign.description}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm font-medium text-gray-600">Durum:</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span
|
|
|
|
|
|
className={`ml-2 px-3 py-1 text-xs font-medium rounded-full ${getCampaignStatusColor(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
selectedCampaign.status,
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
{getCampaignStatusText(selectedCampaign.status)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm font-medium text-gray-600">Tarih Aralığı:</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-sm text-gray-900">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{selectedCampaign.startDate.toLocaleDateString('tr-TR')} -{' '}
|
|
|
|
|
|
{selectedCampaign.endDate.toLocaleDateString('tr-TR')}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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">
|
|
|
|
|
|
{(() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const participants = getCampaignParticipants(selectedCampaign.id)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const completed = participants.filter(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(p) => p.status === ParticipantStatusEnum.Completed,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const started = participants.filter(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(p) => p.status === ParticipantStatusEnum.Started,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const invited = participants.filter(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(p) => p.status === ParticipantStatusEnum.Invited,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<div className="flex justify-between">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-gray-600">Toplam:</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="text-sm font-medium text-gray-900">
|
|
|
|
|
|
{participants.length}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex justify-between">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-gray-600">Tamamlanan:</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="text-sm font-medium text-green-600">
|
|
|
|
|
|
{completed.length}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex justify-between">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-gray-600">Devam Eden:</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="text-sm font-medium text-blue-600">
|
|
|
|
|
|
{started.length}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex justify-between">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-gray-600">Bekleyen:</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="text-sm font-medium text-yellow-600">
|
|
|
|
|
|
{invited.length}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
})()}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Katılımcı Detayları</h3>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{getCampaignParticipants(selectedCampaign.id).map((participant) => {
|
|
|
|
|
|
const targetEmployee = mockEmployees.find(
|
|
|
|
|
|
(emp) => emp.id === participant.evaluatedEmployeeId,
|
|
|
|
|
|
)
|
|
|
|
|
|
const assessorEmployee = mockEmployees.find(
|
|
|
|
|
|
(emp) => emp.id === participant.evaluatorId,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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
|
2025-09-15 09:31:47 +00:00
|
|
|
|
? `Başladı: ${participant.startedDate.toLocaleDateString(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
'tr-TR',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}`
|
2025-09-15 19:46:52 +00:00
|
|
|
|
: `Davet: ${participant.invitedDate.toLocaleDateString('tr-TR')}`}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
)
|
|
|
|
|
|
})}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleCloseModal()
|
|
|
|
|
|
handleEditCampaign(selectedCampaign)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
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
|
2025-09-15 19:46:52 +00:00
|
|
|
|
? 'Değerlendirme Yap'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
: 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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Değerlendirme Seçin</h3>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
{mockEvaluation360
|
2025-09-15 19:46:52 +00:00
|
|
|
|
.filter((campaign) => campaign.status === CampaignStatusEnum.Active)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
.map((campaign) => (
|
|
|
|
|
|
<div
|
|
|
|
|
|
key={campaign.id}
|
|
|
|
|
|
className="border rounded-lg p-2.5 hover:bg-gray-50 cursor-pointer"
|
2025-09-15 19:46:52 +00:00
|
|
|
|
onClick={() => setCurrentCampaignForEvaluation(campaign)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
>
|
|
|
|
|
|
<div className="flex justify-between items-center">
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-900">{campaign.name}</h4>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">{campaign.description}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="flex items-center gap-2 mt-2">
|
|
|
|
|
|
<span
|
|
|
|
|
|
className={`inline-block px-2 py-1 text-xs rounded-full ${getCampaignStatusColor(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
campaign.status,
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
{getCampaignStatusText(campaign.status)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span className="text-xs text-gray-500">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{campaign.startDate.toLocaleDateString()} -{' '}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
{campaign.endDate.toLocaleDateString()}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<FaPlay className="w-4 h-4 text-gray-400" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{mockEvaluation360.filter(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(campaign) => campaign.status === CampaignStatusEnum.Active,
|
2025-09-15 09:31:47 +00:00
|
|
|
|
).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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<strong>Seçili Değerlendirme:</strong> {currentCampaignForEvaluation.name}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
|
{mockEmployees
|
|
|
|
|
|
.filter((emp) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
currentCampaignForEvaluation.targetEmployees.includes(emp.id),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
.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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-900">{employee.fullName}</h4>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">{employee.jobPosition?.name}</p>
|
|
|
|
|
|
<p className="text-gray-600">
|
2025-10-22 08:20:11 +00:00
|
|
|
|
{mockDepartments.find((d) => d.id === employee.departmentId)?.name}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<FaUsers className="w-4 h-4 text-gray-400" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{mockEmployees.filter((emp) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
currentCampaignForEvaluation.targetEmployees.includes(emp.id),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
).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
|
2025-09-15 19:46:52 +00:00
|
|
|
|
onClick={() => setEvaluationTarget('')}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<strong>Değerlendirilecek:</strong>{' '}
|
|
|
|
|
|
{mockEmployees.find((e) => e.id === evaluationTarget)?.fullName}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedAssessorType(assessorType)
|
|
|
|
|
|
setEvaluatorSearchTerm('')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="flex justify-between items-center">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h4 className="font-medium text-gray-900">
|
|
|
|
|
|
{getAssessorTypeText(assessorType)}
|
|
|
|
|
|
</h4>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">
|
2025-09-15 09:31:47 +00:00
|
|
|
|
{getAssessorTypeDescription(assessorType)}
|
|
|
|
|
|
</p>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{getEvaluatorsByType(evaluationTarget, assessorType).length > 0 && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="inline-block mt-1 px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-800">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{getEvaluatorsByType(evaluationTarget, assessorType).length} seçenek
|
|
|
|
|
|
mevcut
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedAssessorType(null)
|
|
|
|
|
|
setEvaluationEvaluator('')
|
|
|
|
|
|
setSelectedCustomerId('')
|
|
|
|
|
|
setExternalEvaluatorName('')
|
|
|
|
|
|
setExternalEvaluatorEmail('')
|
|
|
|
|
|
setEvaluatorSearchTerm('')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<strong>Değerlendirilecek:</strong>{' '}
|
|
|
|
|
|
{mockEmployees.find((e) => e.id === evaluationTarget)?.fullName}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<br />
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<strong>Değerlendirici Tipi:</strong>{' '}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
{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}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
onChange={(e) => setExternalEvaluatorName(e.target.value)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
onChange={(e) => setExternalEvaluatorEmail(e.target.value)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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
|
2025-09-15 19:46:52 +00:00
|
|
|
|
onClick={() => setEvaluationEvaluator('external')}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
onChange={(e) => setEvaluatorSearchTerm(e.target.value)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
|
|
|
|
|
placeholder="Müşteri adında ara..."
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{getEvaluatorsByType(evaluationTarget, selectedAssessorType)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
.filter((evaluator) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
evaluator.name.toLowerCase().includes(evaluatorSearchTerm.toLowerCase()),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
.map((evaluator) => (
|
|
|
|
|
|
<div
|
|
|
|
|
|
key={evaluator.id}
|
|
|
|
|
|
className="border rounded-lg p-2.5 hover:bg-gray-50 cursor-pointer"
|
|
|
|
|
|
onClick={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedCustomerId(evaluator.id)
|
|
|
|
|
|
setEvaluationEvaluator(evaluator.id)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="flex justify-between items-center">
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-900">{evaluator.name}</h4>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">{evaluator.title}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
onChange={(e) => setEvaluatorSearchTerm(e.target.value)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
|
|
|
|
|
placeholder="Çalışan adında ara..."
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{getEvaluatorsByType(evaluationTarget, selectedAssessorType)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
.filter((evaluator) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
evaluator.name.toLowerCase().includes(evaluatorSearchTerm.toLowerCase()),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
.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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-900">{evaluator.name}</h4>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">{evaluator.title}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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>
|
|
|
|
|
|
))}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{getEvaluatorsByType(evaluationTarget, selectedAssessorType).length === 0 && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedAssessorType(null)
|
|
|
|
|
|
setEvaluationEvaluator('')
|
|
|
|
|
|
setSelectedCustomerId('')
|
|
|
|
|
|
setExternalEvaluatorName('')
|
|
|
|
|
|
setExternalEvaluatorEmail('')
|
|
|
|
|
|
setEvaluatorSearchTerm('')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<strong>Değerlendirilen:</strong>{' '}
|
|
|
|
|
|
{mockEmployees.find((e) => e.id === evaluationTarget)?.fullName}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<br />
|
|
|
|
|
|
<strong>Değerlendiren:</strong> {getEvaluatorInfo()}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Burada şablonun sorularını göstereceğiz */}
|
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
|
{currentCampaignForEvaluation &&
|
|
|
|
|
|
mockEvaluation360Templates
|
2025-09-15 19:46:52 +00:00
|
|
|
|
.find((t) => t.id === currentCampaignForEvaluation.templateId)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
?.questionGroups.map((group) => (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
|
{group.questions.map((question, qIndex) => (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<div key={question.id} className="bg-gray-50 p-2.5 rounded">
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="font-medium text-gray-800 mb-3">
|
|
|
|
|
|
{qIndex + 1}. {question.questionText}
|
|
|
|
|
|
{question.isRequired && (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-red-500 ml-1">*</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Soru tipine göre input alanları */}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.questionType === QuestionTypeEnum.Rating && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<div className="flex justify-around items-center">
|
|
|
|
|
|
{Array.from(
|
|
|
|
|
|
{
|
|
|
|
|
|
length:
|
|
|
|
|
|
(question.maxRating || 5) -
|
|
|
|
|
|
(question.minRating || 1) +
|
|
|
|
|
|
1,
|
|
|
|
|
|
},
|
|
|
|
|
|
(_, i) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const value = (question.minRating || 1) + i
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return (
|
|
|
|
|
|
<label
|
|
|
|
|
|
key={value}
|
|
|
|
|
|
className="flex flex-col items-center cursor-pointer"
|
|
|
|
|
|
>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="radio"
|
|
|
|
|
|
name={`question-${question.id}`}
|
|
|
|
|
|
value={value}
|
|
|
|
|
|
checked={
|
2025-09-15 19:46:52 +00:00
|
|
|
|
evaluationResponses[question.id] === value
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
handleResponseChange(
|
|
|
|
|
|
question.id,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
Number(e.target.value),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
className="mb-1"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span className="text-xs text-gray-600">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.ratingLabels?.[i] || value}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
|
|
|
|
|
},
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.questionType === QuestionTypeEnum.Text && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<textarea
|
|
|
|
|
|
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm"
|
|
|
|
|
|
rows={3}
|
|
|
|
|
|
placeholder="Yorumunuzu yazın..."
|
2025-09-15 19:46:52 +00:00
|
|
|
|
value={evaluationResponses[question.id] || ''}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
onChange={(e) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleResponseChange(question.id, e.target.value)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.questionType === QuestionTypeEnum.YesNo && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="flex gap-4">
|
|
|
|
|
|
<label className="flex items-center cursor-pointer">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="radio"
|
|
|
|
|
|
name={`question-${question.id}`}
|
|
|
|
|
|
value="yes"
|
2025-09-15 19:46:52 +00:00
|
|
|
|
checked={evaluationResponses[question.id] === 'yes'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
onChange={(e) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleResponseChange(question.id, e.target.value)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
className="mr-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
Evet
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label className="flex items-center cursor-pointer">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="radio"
|
|
|
|
|
|
name={`question-${question.id}`}
|
|
|
|
|
|
value="no"
|
2025-09-15 19:46:52 +00:00
|
|
|
|
checked={evaluationResponses[question.id] === 'no'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
onChange={(e) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleResponseChange(question.id, e.target.value)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
className="mr-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
Hayır
|
|
|
|
|
|
</label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.questionType === QuestionTypeEnum.MultipleChoice && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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={
|
2025-09-15 19:46:52 +00:00
|
|
|
|
evaluationResponses[question.id] === option.value
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
handleResponseChange(
|
|
|
|
|
|
question.id,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
Number(e.target.value),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-blue-700 font-medium">Kampanya: </span>
|
|
|
|
|
|
<span className="text-sm text-blue-800">{selectedResult.campaignName}</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-blue-700 font-medium">Değerlendirilen: </span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="text-sm text-blue-800">
|
|
|
|
|
|
{selectedResult.evaluatedEmployeeName}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-blue-700 font-medium">Değerlendiren: </span>
|
|
|
|
|
|
<span className="text-sm text-blue-800">{selectedResult.evaluatorName}</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<span className="text-sm text-blue-700 font-medium">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
Değerlendiren Tipi:{' '}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
<span className="text-sm text-blue-800">
|
|
|
|
|
|
{getAssessorTypeText(selectedResult.evaluatorType)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="bg-green-50 p-4 rounded-lg">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h3 className="font-medium text-green-800 mb-2 text-sm">Sonuç Bilgileri</h3>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="space-y-1.5">
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-green-700 font-medium">Durum: </span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span
|
|
|
|
|
|
className={`text-sm px-2 py-0.5 rounded-full ${getParticipantStatusColor(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
selectedResult.status,
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
{getParticipantStatusText(selectedResult.status)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-green-700 font-medium">Toplam Puan: </span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="text-sm text-green-800 font-bold">
|
|
|
|
|
|
{selectedResult.overallScore}/100
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-green-700 font-medium">Yüzde: </span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
Tamamlanma Tarihi:{' '}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
<span className="text-sm text-green-800">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{new Date(selectedResult.completedDate).toLocaleDateString('tr-TR')}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Detaylı Sonuçlar */}
|
|
|
|
|
|
<div className="border rounded-lg p-3">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h3 className="font-medium text-gray-900 mb-2 text-sm">Detaylı Sonuçlar</h3>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
{(() => {
|
|
|
|
|
|
// Seçili sonucu mockEvaluation360Results'tan bul
|
|
|
|
|
|
const evaluationResult = mockEvaluation360Results.find(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(r) => r.id === selectedResult.resultId,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="space-y-2.5">
|
|
|
|
|
|
{/* Soru Cevapları */}
|
|
|
|
|
|
{(() => {
|
|
|
|
|
|
const campaign = mockEvaluation360.find(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(c) => c.id === selectedResult.campaignId,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const template = campaign
|
2025-09-15 19:46:52 +00:00
|
|
|
|
? mockEvaluation360Templates.find((t) => t.id === campaign.templateId)
|
|
|
|
|
|
: null
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
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) => (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<div key={group.id} className="bg-gray-50 p-2.5 rounded-lg">
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="mb-3">
|
|
|
|
|
|
<h5 className="font-medium text-gray-800">
|
|
|
|
|
|
{group.groupName}
|
|
|
|
|
|
</h5>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">{group.description}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2.5">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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
|
|
|
|
|
|
}
|
|
|
|
|
|
})()}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
|
|
|
|
|
|
{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>
|
|
|
|
|
|
)
|
|
|
|
|
|
})}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return null
|
2025-09-15 09:31:47 +00:00
|
|
|
|
})()}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 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) => (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<div key={groupScore.groupId} className="bg-gray-50 p-2.5 rounded-lg">
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span>%{Math.round(groupScore.percentage)}</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{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)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<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>
|
|
|
|
|
|
))}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Güçlü Yönler */}
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-800 mb-2 text-sm">💪 Güçlü Yönler</h4>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="bg-green-50 p-2.5 rounded-lg">
|
|
|
|
|
|
{evaluationResult.strengths.length > 0 ? (
|
|
|
|
|
|
<ul className="list-disc list-inside space-y-1">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{evaluationResult.strengths.map((strength, index) => (
|
|
|
|
|
|
<li key={index} className="text-green-800">
|
|
|
|
|
|
{strength}
|
|
|
|
|
|
</li>
|
|
|
|
|
|
))}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{evaluationResult.developmentAreas.map((area, index) => (
|
|
|
|
|
|
<li key={index} className="text-amber-800">
|
|
|
|
|
|
{area}
|
|
|
|
|
|
</li>
|
|
|
|
|
|
))}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</ul>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<p className="text-amber-700 italic">
|
|
|
|
|
|
Gelişim alanları henüz analiz edilmedi
|
|
|
|
|
|
</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Aksiyon Planı */}
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-800 mb-2 text-sm">🎯 Aksiyon Planı</h4>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="bg-purple-50 p-2.5 rounded-lg">
|
|
|
|
|
|
{evaluationResult.actionPlan.length > 0 ? (
|
|
|
|
|
|
<ul className="list-decimal list-inside space-y-1">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{evaluationResult.actionPlan.map((action, index) => (
|
|
|
|
|
|
<li key={index} className="text-purple-800">
|
|
|
|
|
|
{action}
|
|
|
|
|
|
</li>
|
|
|
|
|
|
))}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</ul>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<p className="text-purple-700 italic">
|
|
|
|
|
|
Aksiyon planı henüz oluşturulmadı
|
|
|
|
|
|
</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Yorumlar */}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{(evaluationResult.managerComments || evaluationResult.hrComments) && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-800 mb-2 text-sm">💬 Yorumlar</h4>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
{evaluationResult.managerComments && (
|
|
|
|
|
|
<div className="bg-blue-50 p-2.5 rounded-lg">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h5 className="font-medium text-blue-800 mb-2">Yönetici Yorumu</h5>
|
|
|
|
|
|
<p className="text-blue-700">{evaluationResult.managerComments}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{evaluationResult.hrComments && (
|
|
|
|
|
|
<div className="bg-indigo-50 p-2.5 rounded-lg">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h5 className="font-medium text-indigo-800 mb-2">İK Yorumu</h5>
|
|
|
|
|
|
<p className="text-indigo-700">{evaluationResult.hrComments}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
})()}
|
|
|
|
|
|
</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={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleCloseModal()
|
|
|
|
|
|
handleEditEvaluationDetail(selectedResult)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h3 className="font-medium text-amber-800 mb-2 text-sm">Değerlendirme Bilgileri</h3>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-amber-700 font-medium">Kampanya: </span>
|
|
|
|
|
|
<span className="text-sm text-amber-800">{selectedResult.campaignName}</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-amber-700 font-medium">Değerlendirilen: </span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="text-sm text-amber-800">
|
|
|
|
|
|
{selectedResult.evaluatedEmployeeName}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-amber-700 font-medium">Değerlendiren: </span>
|
|
|
|
|
|
<span className="text-sm text-amber-800">{selectedResult.evaluatorName}</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-sm text-amber-700 font-medium">Değerlendiren Tipi: </span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span className="text-sm text-amber-800">
|
|
|
|
|
|
{getAssessorTypeText(selectedResult.evaluatorType)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Soru Grupları ve Cevaplar */}
|
|
|
|
|
|
{(() => {
|
|
|
|
|
|
const evaluationResult = mockEvaluation360Results.find(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(r) => r.id === selectedResult.resultId,
|
|
|
|
|
|
)
|
|
|
|
|
|
const campaign = mockEvaluation360.find((c) => c.id === selectedResult.campaignId)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const template = campaign
|
2025-09-15 19:46:52 +00:00
|
|
|
|
? mockEvaluation360Templates.find((t) => t.id === campaign.templateId)
|
|
|
|
|
|
: null
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
if (!evaluationResult || !template) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="text-center py-8 text-gray-500">
|
|
|
|
|
|
<p>Template verisi bulunamadı</p>
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="space-y-3">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h3 className="text-sm font-medium text-gray-900">Soru Grupları ve Cevaplar</h3>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
{template.questionGroups.map((group) => (
|
|
|
|
|
|
<div key={group.id} className="border rounded-lg p-2">
|
|
|
|
|
|
<div className="mb-3">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-900 text-sm">{group.groupName}</h4>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">{group.description}</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="flex items-center gap-2 mt-2">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="text-xs text-gray-500">Ağırlık: %{group.weight}</span>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const currentResponse = evaluationResult.participants
|
|
|
|
|
|
.flatMap((p) => p.responses)
|
|
|
|
|
|
.find((r) => r.questionId === question.id)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const editValue =
|
|
|
|
|
|
editResponses[question.id] !== undefined
|
|
|
|
|
|
? editResponses[question.id]
|
2025-09-15 19:46:52 +00:00
|
|
|
|
: currentResponse?.responseValue
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
return (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<div key={question.id} className="bg-gray-50 p-2.5 rounded-lg">
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
Mevcut Cevap:{' '}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
<span className="text-xs text-blue-800">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{typeof currentResponse.responseValue === 'string'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
? 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ı */}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.questionType === QuestionTypeEnum.Rating && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const value = (question.minRating || 1) + i
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
Number(e.target.value),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
className="mb-1"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span className="text-xs text-gray-600">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.ratingLabels?.[i] || value}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
|
|
|
|
|
},
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.questionType === QuestionTypeEnum.Text && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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..."
|
2025-09-15 19:46:52 +00:00
|
|
|
|
value={editValue || ''}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
onChange={(e) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleEditResponseChange(question.id, e.target.value)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.questionType === QuestionTypeEnum.YesNo && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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"
|
2025-09-15 19:46:52 +00:00
|
|
|
|
checked={editValue === 'yes'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
onChange={(e) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleEditResponseChange(question.id, e.target.value)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
className="mr-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
Evet
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label className="flex items-center cursor-pointer">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="radio"
|
|
|
|
|
|
name={`edit-question-${question.id}`}
|
|
|
|
|
|
value="no"
|
2025-09-15 19:46:52 +00:00
|
|
|
|
checked={editValue === 'no'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
onChange={(e) =>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleEditResponseChange(question.id, e.target.value)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
className="mr-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
Hayır
|
|
|
|
|
|
</label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{question.questionType === QuestionTypeEnum.MultipleChoice && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
Number(e.target.value),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
className="mr-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
{option.optionText}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
})}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
})()}
|
|
|
|
|
|
|
|
|
|
|
|
{/* 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>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Sonuç Durumu</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="font-medium text-gray-900 mb-2 text-sm">Mevcut Puan Bilgileri</h4>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<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={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
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()
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
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>
|
|
|
|
|
|
)}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</Container>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
export default Degree360Evaluation
|