From f02136d3588e9a71ac76d223bd45fb7f8656cfec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Sat, 25 Oct 2025 22:43:36 +0300 Subject: [PATCH] =?UTF-8?q?Hr=20Survey=20g=C3=BCncellemesi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/mocks/mockIntranet.ts | 143 ++++++++- ui/src/types/intranet.ts | 41 ++- ui/src/views/intranet/Dashboard.tsx | 6 +- ui/src/views/intranet/modals/SurveyModal.tsx | 297 +++++++++++++----- .../views/intranet/widgets/ActiveSurveys.tsx | 144 +++++++-- 5 files changed, 523 insertions(+), 108 deletions(-) diff --git a/ui/src/mocks/mockIntranet.ts b/ui/src/mocks/mockIntranet.ts index a0e52b7e..e5ccb04e 100644 --- a/ui/src/mocks/mockIntranet.ts +++ b/ui/src/mocks/mockIntranet.ts @@ -11,6 +11,10 @@ import { MealMenu, ShuttleRoute, Survey, + SurveyQuestion, + SurveyQuestionOption, + SurveyResponse, + SurveyAnswer, SocialPost, } from '@/types/intranet' @@ -70,7 +74,58 @@ export const mockSurveys: Survey[] = [ creatorId: mockEmployees[0], creationTime: new Date('2024-10-01'), deadline: new Date('2024-10-31'), - totalQuestions: 25, + questions: [ + { + id: 'q1', + surveyId: 'survey1', + questionText: 'Genel memnuniyet düzeyiniz nedir?', + type: 'rating', + order: 1, + isRequired: true, + ratingConfig: { + min: 1, + max: 5, + labels: { + 1: 'Çok Kötü', + 2: 'Kötü', + 3: 'Orta', + 4: 'İyi', + 5: 'Çok İyi' + } + } + }, + { + id: 'q2', + surveyId: 'survey1', + questionText: 'Hangi departmanda çalışıyorsunuz?', + type: 'multiple-choice', + order: 2, + isRequired: true, + options: [ + { id: 'opt1', text: 'Bilgi Teknolojileri', order: 1 }, + { id: 'opt2', text: 'İnsan Kaynakları', order: 2 }, + { id: 'opt3', text: 'Finans', order: 3 }, + { id: 'opt4', text: 'Satış', order: 4 }, + { id: 'opt5', text: 'Pazarlama', order: 5 } + ] + }, + { + id: 'q3', + surveyId: 'survey1', + questionText: 'Görüş ve önerileriniz', + type: 'textarea', + order: 3, + isRequired: false + }, + { + id: 'q4', + surveyId: 'survey1', + questionText: 'Çalışma ortamından memnun musunuz?', + type: 'yes-no', + order: 4, + isRequired: true + } + ], responses: 45, targetAudience: ['Tüm Çalışanlar'], status: 'active', @@ -83,7 +138,53 @@ export const mockSurveys: Survey[] = [ creatorId: mockEmployees[2], creationTime: new Date('2024-10-10'), deadline: new Date('2024-11-15'), - totalQuestions: 15, + questions: [ + { + id: 'q5', + surveyId: 'survey2', + questionText: 'Hangi teknoloji konularında eğitim almak istiyorsunuz?', + type: 'multiple-choice', + order: 1, + isRequired: true, + options: [ + { id: 'opt6', text: 'React / Frontend', order: 1 }, + { id: 'opt7', text: 'Node.js / Backend', order: 2 }, + { id: 'opt8', text: 'Database / SQL', order: 3 }, + { id: 'opt9', text: 'DevOps / Cloud', order: 4 }, + { id: 'opt10', text: 'Mobile Development', order: 5 } + ] + }, + { + id: 'q6', + surveyId: 'survey2', + questionText: 'Eğitim formatı tercihiniz nedir?', + type: 'multiple-choice', + order: 2, + isRequired: true, + options: [ + { id: 'opt11', text: 'Online Eğitim', order: 1 }, + { id: 'opt12', text: 'Yüz Yüze Eğitim', order: 2 }, + { id: 'opt13', text: 'Hibrit (Karma)', order: 3 } + ] + }, + { + id: 'q7', + surveyId: 'survey2', + questionText: 'Eğitim için haftalık ne kadar zaman ayırabilirsiniz?', + type: 'rating', + order: 3, + isRequired: true, + ratingConfig: { + min: 1, + max: 10, + labels: { + 1: '1 saat', + 5: '5 saat', + 10: '10+ saat' + } + } + } + ], responses: 28, targetAudience: ['Yazılım Geliştirme', 'Ürün Yönetimi'], status: 'active', @@ -96,7 +197,43 @@ export const mockSurveys: Survey[] = [ creatorId: mockEmployees[4], creationTime: new Date('2024-09-15'), deadline: new Date('2024-09-30'), - totalQuestions: 10, + questions: [ + { + id: 'q8', + surveyId: 'survey3', + questionText: 'Yemek kalitesini nasıl değerlendiriyorsunuz?', + type: 'rating', + order: 1, + isRequired: true, + ratingConfig: { + min: 1, + max: 5, + labels: { + 1: 'Çok Kötü', + 2: 'Kötü', + 3: 'Orta', + 4: 'İyi', + 5: 'Mükemmel' + } + } + }, + { + id: 'q9', + surveyId: 'survey3', + questionText: 'Hangi yemekleri daha sık görmek istiyorsunuz?', + type: 'textarea', + order: 2, + isRequired: false + }, + { + id: 'q10', + surveyId: 'survey3', + questionText: 'Servis hızından memnun musunuz?', + type: 'yes-no', + order: 3, + isRequired: true + } + ], responses: 62, targetAudience: ['Tüm Çalışanlar'], status: 'closed', diff --git a/ui/src/types/intranet.ts b/ui/src/types/intranet.ts index e4b58d4a..c23e7b66 100644 --- a/ui/src/types/intranet.ts +++ b/ui/src/types/intranet.ts @@ -161,6 +161,29 @@ export interface ShuttleRoute { type: 'morning' | 'evening' } +// Anket Sorusu +export interface SurveyQuestion { + id: string + surveyId: string + questionText: string + type: 'rating' | 'multiple-choice' | 'text' | 'textarea' | 'yes-no' + order: number + isRequired: boolean + options?: SurveyQuestionOption[] + ratingConfig?: { + min: number + max: number + labels?: { [key: number]: string } + } +} + +// Anket Sorusu Seçeneği +export interface SurveyQuestionOption { + id: string + text: string + order: number +} + // Anket export interface Survey { id: string @@ -169,13 +192,29 @@ export interface Survey { creatorId: HrEmployee creationTime: Date deadline: Date - totalQuestions: number + questions: SurveyQuestion[] responses: number targetAudience: string[] status: 'draft' | 'active' | 'closed' isAnonymous: boolean } +// Anket Cevabı +export interface SurveyResponse { + id: string + surveyId: string + respondentId?: string // Anonymous ise null + submissionTime: Date + answers: SurveyAnswer[] +} + +// Anket Cevap +export interface SurveyAnswer { + questionId: string + questionType: 'rating' | 'multiple-choice' | 'text' | 'textarea' | 'yes-no' + value: string | number | string[] +} + // Ziyaretçi export interface Visitor { id: string diff --git a/ui/src/views/intranet/Dashboard.tsx b/ui/src/views/intranet/Dashboard.tsx index 7e015e05..c62ab017 100644 --- a/ui/src/views/intranet/Dashboard.tsx +++ b/ui/src/views/intranet/Dashboard.tsx @@ -31,7 +31,7 @@ import AnnouncementDetailModal from './modals/AnnouncementDetailModal' // Social Wall import SocialWall from './SocialWall' -import { Announcement, Survey } from '@/types/intranet' +import { Announcement, Survey, SurveyAnswer } from '@/types/intranet' import { Container } from '@/components/shared' dayjs.locale('tr') @@ -52,7 +52,9 @@ const IntranetDashboard: React.FC = () => { setShowSurveyModal(true) } - const handleSubmitSurvey = () => { + const handleSubmitSurvey = (answers: SurveyAnswer[]) => { + console.log('Survey submitted with answers:', answers) + // Burada survey cevapları API'ye gönderilecek setShowSurveyModal(false) setSelectedSurvey(null) } diff --git a/ui/src/views/intranet/modals/SurveyModal.tsx b/ui/src/views/intranet/modals/SurveyModal.tsx index ffc1d692..28da6d28 100644 --- a/ui/src/views/intranet/modals/SurveyModal.tsx +++ b/ui/src/views/intranet/modals/SurveyModal.tsx @@ -1,15 +1,214 @@ -import React from 'react' +import React, { useState } from 'react' import { motion } from 'framer-motion' import { FaTimes } from 'react-icons/fa' -import { Survey } from '@/types/intranet' +import { Survey, SurveyQuestion, SurveyAnswer } from '@/types/intranet' interface SurveyModalProps { survey: Survey onClose: () => void - onSubmit: () => void + onSubmit: (answers: SurveyAnswer[]) => void } const SurveyModal: React.FC = ({ survey, onClose, onSubmit }) => { + const [answers, setAnswers] = useState<{ [questionId: string]: any }>({}) + const [errors, setErrors] = useState<{ [questionId: string]: string }>({}) + + const handleAnswerChange = (questionId: string, value: any) => { + setAnswers(prev => ({ + ...prev, + [questionId]: value + })) + + // Clear error when user provides an answer + if (errors[questionId]) { + setErrors(prev => ({ + ...prev, + [questionId]: '' + })) + } + } + + const validateAnswers = (): boolean => { + const newErrors: { [questionId: string]: string } = {} + + survey.questions.forEach(question => { + if (question.isRequired && (!answers[question.id] || answers[question.id] === '')) { + newErrors[question.id] = 'Bu alan zorunludur' + } + }) + + setErrors(newErrors) + return Object.keys(newErrors).length === 0 + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + + if (!validateAnswers()) { + return + } + + const surveyAnswers: SurveyAnswer[] = survey.questions.map(question => ({ + questionId: question.id, + questionType: question.type, + value: answers[question.id] || (question.type === 'multiple-choice' ? '' : + question.type === 'rating' ? 0 : '') + })) + + onSubmit(surveyAnswers) + } + + const renderQuestion = (question: SurveyQuestion, index: number) => { + const questionNumber = index + 1 + const hasError = !!errors[question.id] + + switch (question.type) { + case 'rating': + return ( +
+ +
+ {Array.from({ length: question.ratingConfig!.max - question.ratingConfig!.min + 1 }, (_, i) => { + const value = question.ratingConfig!.min + i + const label = question.ratingConfig!.labels?.[value] || value.toString() + return ( + + ) + })} +
+ {hasError &&

{errors[question.id]}

} +
+ ) + + case 'multiple-choice': + return ( +
+ +
+ {question.options?.map(option => ( + + ))} +
+ {hasError &&

{errors[question.id]}

} +
+ ) + + case 'yes-no': + return ( +
+ +
+ {['yes', 'no'].map(value => ( + + ))} +
+ {hasError &&

{errors[question.id]}

} +
+ ) + + case 'text': + return ( +
+ + handleAnswerChange(question.id, e.target.value)} + className={`w-full px-4 py-2 border rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 ${ + hasError ? 'border-red-500' : 'border-gray-300 dark:border-gray-600' + }`} + placeholder="Cevabınızı yazın..." + /> + {hasError &&

{errors[question.id]}

} +
+ ) + + case 'textarea': + return ( +
+ +