Hr Survey güncellemesi
This commit is contained in:
parent
68482e0a8d
commit
f02136d358
5 changed files with 523 additions and 108 deletions
|
|
@ -11,6 +11,10 @@ import {
|
||||||
MealMenu,
|
MealMenu,
|
||||||
ShuttleRoute,
|
ShuttleRoute,
|
||||||
Survey,
|
Survey,
|
||||||
|
SurveyQuestion,
|
||||||
|
SurveyQuestionOption,
|
||||||
|
SurveyResponse,
|
||||||
|
SurveyAnswer,
|
||||||
SocialPost,
|
SocialPost,
|
||||||
} from '@/types/intranet'
|
} from '@/types/intranet'
|
||||||
|
|
||||||
|
|
@ -70,7 +74,58 @@ export const mockSurveys: Survey[] = [
|
||||||
creatorId: mockEmployees[0],
|
creatorId: mockEmployees[0],
|
||||||
creationTime: new Date('2024-10-01'),
|
creationTime: new Date('2024-10-01'),
|
||||||
deadline: new Date('2024-10-31'),
|
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,
|
responses: 45,
|
||||||
targetAudience: ['Tüm Çalışanlar'],
|
targetAudience: ['Tüm Çalışanlar'],
|
||||||
status: 'active',
|
status: 'active',
|
||||||
|
|
@ -83,7 +138,53 @@ export const mockSurveys: Survey[] = [
|
||||||
creatorId: mockEmployees[2],
|
creatorId: mockEmployees[2],
|
||||||
creationTime: new Date('2024-10-10'),
|
creationTime: new Date('2024-10-10'),
|
||||||
deadline: new Date('2024-11-15'),
|
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,
|
responses: 28,
|
||||||
targetAudience: ['Yazılım Geliştirme', 'Ürün Yönetimi'],
|
targetAudience: ['Yazılım Geliştirme', 'Ürün Yönetimi'],
|
||||||
status: 'active',
|
status: 'active',
|
||||||
|
|
@ -96,7 +197,43 @@ export const mockSurveys: Survey[] = [
|
||||||
creatorId: mockEmployees[4],
|
creatorId: mockEmployees[4],
|
||||||
creationTime: new Date('2024-09-15'),
|
creationTime: new Date('2024-09-15'),
|
||||||
deadline: new Date('2024-09-30'),
|
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,
|
responses: 62,
|
||||||
targetAudience: ['Tüm Çalışanlar'],
|
targetAudience: ['Tüm Çalışanlar'],
|
||||||
status: 'closed',
|
status: 'closed',
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,29 @@ export interface ShuttleRoute {
|
||||||
type: 'morning' | 'evening'
|
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
|
// Anket
|
||||||
export interface Survey {
|
export interface Survey {
|
||||||
id: string
|
id: string
|
||||||
|
|
@ -169,13 +192,29 @@ export interface Survey {
|
||||||
creatorId: HrEmployee
|
creatorId: HrEmployee
|
||||||
creationTime: Date
|
creationTime: Date
|
||||||
deadline: Date
|
deadline: Date
|
||||||
totalQuestions: number
|
questions: SurveyQuestion[]
|
||||||
responses: number
|
responses: number
|
||||||
targetAudience: string[]
|
targetAudience: string[]
|
||||||
status: 'draft' | 'active' | 'closed'
|
status: 'draft' | 'active' | 'closed'
|
||||||
isAnonymous: boolean
|
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
|
// Ziyaretçi
|
||||||
export interface Visitor {
|
export interface Visitor {
|
||||||
id: string
|
id: string
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import AnnouncementDetailModal from './modals/AnnouncementDetailModal'
|
||||||
|
|
||||||
// Social Wall
|
// Social Wall
|
||||||
import SocialWall from './SocialWall'
|
import SocialWall from './SocialWall'
|
||||||
import { Announcement, Survey } from '@/types/intranet'
|
import { Announcement, Survey, SurveyAnswer } from '@/types/intranet'
|
||||||
import { Container } from '@/components/shared'
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
dayjs.locale('tr')
|
dayjs.locale('tr')
|
||||||
|
|
@ -52,7 +52,9 @@ const IntranetDashboard: React.FC = () => {
|
||||||
setShowSurveyModal(true)
|
setShowSurveyModal(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmitSurvey = () => {
|
const handleSubmitSurvey = (answers: SurveyAnswer[]) => {
|
||||||
|
console.log('Survey submitted with answers:', answers)
|
||||||
|
// Burada survey cevapları API'ye gönderilecek
|
||||||
setShowSurveyModal(false)
|
setShowSurveyModal(false)
|
||||||
setSelectedSurvey(null)
|
setSelectedSurvey(null)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,214 @@
|
||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { FaTimes } from 'react-icons/fa'
|
import { FaTimes } from 'react-icons/fa'
|
||||||
import { Survey } from '@/types/intranet'
|
import { Survey, SurveyQuestion, SurveyAnswer } from '@/types/intranet'
|
||||||
|
|
||||||
interface SurveyModalProps {
|
interface SurveyModalProps {
|
||||||
survey: Survey
|
survey: Survey
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onSubmit: () => void
|
onSubmit: (answers: SurveyAnswer[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit }) => {
|
const SurveyModal: React.FC<SurveyModalProps> = ({ 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 (
|
||||||
|
<div key={question.id} className="space-y-2">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-2 flex-wrap">
|
||||||
|
{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 (
|
||||||
|
<label
|
||||||
|
key={value}
|
||||||
|
className={`flex flex-col items-center gap-1 px-3 py-2 border rounded-lg cursor-pointer transition-colors ${
|
||||||
|
answers[question.id] === value
|
||||||
|
? 'bg-blue-100 border-blue-500 dark:bg-blue-900/30 dark:border-blue-400'
|
||||||
|
: 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name={`question-${question.id}`}
|
||||||
|
value={value}
|
||||||
|
checked={answers[question.id] === value}
|
||||||
|
onChange={(e) => handleAnswerChange(question.id, parseInt(e.target.value))}
|
||||||
|
className="sr-only"
|
||||||
|
/>
|
||||||
|
<span className="font-medium text-gray-900 dark:text-white">{value}</span>
|
||||||
|
<span className="text-xs text-gray-500 dark:text-gray-400 text-center">{label}</span>
|
||||||
|
</label>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
case 'multiple-choice':
|
||||||
|
return (
|
||||||
|
<div key={question.id} className="space-y-2">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||||
|
</label>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{question.options?.map(option => (
|
||||||
|
<label
|
||||||
|
key={option.id}
|
||||||
|
className={`flex items-center gap-3 p-3 border rounded-lg cursor-pointer transition-colors ${
|
||||||
|
answers[question.id] === option.id
|
||||||
|
? 'bg-blue-50 border-blue-500 dark:bg-blue-900/20 dark:border-blue-400'
|
||||||
|
: 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name={`question-${question.id}`}
|
||||||
|
value={option.id}
|
||||||
|
checked={answers[question.id] === option.id}
|
||||||
|
onChange={(e) => handleAnswerChange(question.id, e.target.value)}
|
||||||
|
className="w-4 h-4 text-blue-600"
|
||||||
|
/>
|
||||||
|
<span className="text-sm text-gray-900 dark:text-white">{option.text}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
case 'yes-no':
|
||||||
|
return (
|
||||||
|
<div key={question.id} className="space-y-2">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{['yes', 'no'].map(value => (
|
||||||
|
<label
|
||||||
|
key={value}
|
||||||
|
className={`flex items-center gap-2 px-4 py-2 border rounded-lg cursor-pointer transition-colors ${
|
||||||
|
answers[question.id] === value
|
||||||
|
? 'bg-blue-50 border-blue-500 dark:bg-blue-900/20 dark:border-blue-400'
|
||||||
|
: 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name={`question-${question.id}`}
|
||||||
|
value={value}
|
||||||
|
checked={answers[question.id] === value}
|
||||||
|
onChange={(e) => handleAnswerChange(question.id, e.target.value)}
|
||||||
|
className="w-4 h-4 text-blue-600"
|
||||||
|
/>
|
||||||
|
<span className="text-sm text-gray-900 dark:text-white">
|
||||||
|
{value === 'yes' ? 'Evet' : 'Hayır'}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
case 'text':
|
||||||
|
return (
|
||||||
|
<div key={question.id} className="space-y-2">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={answers[question.id] || ''}
|
||||||
|
onChange={(e) => 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 && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
case 'textarea':
|
||||||
|
return (
|
||||||
|
<div key={question.id} className="space-y-2">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
rows={4}
|
||||||
|
value={answers[question.id] || ''}
|
||||||
|
onChange={(e) => 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="Yorumlarınızı buraya yazabilirsiniz..."
|
||||||
|
/>
|
||||||
|
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -44,78 +243,30 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form
|
<form onSubmit={handleSubmit} className="p-6 space-y-6">
|
||||||
onSubmit={(e) => {
|
<div className="space-y-6">
|
||||||
e.preventDefault()
|
{survey.questions
|
||||||
onSubmit()
|
.sort((a, b) => a.order - b.order)
|
||||||
}}
|
.map((question, index) => renderQuestion(question, index))}
|
||||||
className="p-6 space-y-6"
|
|
||||||
>
|
|
||||||
{/* Örnek Anket Soruları */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
||||||
1. Genel memnuniyet düzeyiniz nedir? *
|
|
||||||
</label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{[1, 2, 3, 4, 5].map((rating) => (
|
|
||||||
<label
|
|
||||||
key={rating}
|
|
||||||
className="flex items-center gap-2 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700"
|
|
||||||
>
|
|
||||||
<input type="radio" name="rating" value={rating} required />
|
|
||||||
<span className="text-sm text-gray-900 dark:text-white">{rating}</span>
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
||||||
2. Hangi departmanda çalışıyorsunuz? *
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
required
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
<option value="">Seçiniz</option>
|
|
||||||
<option value="it">Bilgi Teknolojileri</option>
|
|
||||||
<option value="hr">İnsan Kaynakları</option>
|
|
||||||
<option value="finance">Finans</option>
|
|
||||||
<option value="sales">Satış</option>
|
|
||||||
<option value="marketing">Pazarlama</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
||||||
3. Görüş ve önerileriniz
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
rows={4}
|
|
||||||
placeholder="Yorumlarınızı buraya yazabilirsiniz..."
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{!survey.isAnonymous && (
|
|
||||||
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3">
|
|
||||||
<p className="text-sm text-blue-700 dark:text-blue-300">
|
|
||||||
ℹ️ Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız
|
|
||||||
kaydedilecektir.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{survey.isAnonymous && (
|
|
||||||
<div className="bg-green-50 dark:bg-green-900/20 rounded-lg p-3">
|
|
||||||
<p className="text-sm text-green-700 dark:text-green-300">
|
|
||||||
✅ Bu anket anonimdir. Kimlik bilgileriniz kaydedilmeyecektir.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!survey.isAnonymous && (
|
||||||
|
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3">
|
||||||
|
<p className="text-sm text-blue-700 dark:text-blue-300">
|
||||||
|
ℹ️ Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız
|
||||||
|
kaydedilecektir.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{survey.isAnonymous && (
|
||||||
|
<div className="bg-green-50 dark:bg-green-900/20 rounded-lg p-3">
|
||||||
|
<p className="text-sm text-green-700 dark:text-green-300">
|
||||||
|
✅ Bu anket anonimdir. Kimlik bilgileriniz kaydedilmeyecektir.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex gap-3 pt-4 border-t border-gray-200 dark:border-gray-700">
|
<div className="flex gap-3 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FaClipboardCheck } from 'react-icons/fa'
|
import { FaClipboardCheck, FaQuestionCircle, FaUsers, FaClock, FaArrowRight } from 'react-icons/fa'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { mockSurveys } from '../../../mocks/mockIntranet'
|
import { mockSurveys } from '../../../mocks/mockIntranet'
|
||||||
import { Survey } from '@/types/intranet'
|
import { Survey } from '@/types/intranet'
|
||||||
|
|
@ -9,47 +9,133 @@ interface ActiveSurveysProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActiveSurveys: React.FC<ActiveSurveysProps> = ({ onTakeSurvey }) => {
|
const ActiveSurveys: React.FC<ActiveSurveysProps> = ({ onTakeSurvey }) => {
|
||||||
|
const activeSurveys = mockSurveys.filter((s) => s.status === 'active').slice(0, 3)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
<div className="bg-gradient-to-br from-white to-gray-50 dark:from-gray-800 dark:to-gray-850 rounded-xl shadow-lg border border-gray-200/50 dark:border-gray-700/50 overflow-hidden">
|
||||||
|
{/* Header with gradient */}
|
||||||
|
|
||||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
<h2 className="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||||
<FaClipboardCheck className="w-5 h-5" />
|
<FaClipboardCheck className="w-5 h-5" />
|
||||||
Aktif Anketler
|
Aktif Anketler
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 space-y-3">
|
|
||||||
{mockSurveys
|
<div className="p-6 space-y-4">
|
||||||
.filter((s) => s.status === 'active')
|
{activeSurveys.map((survey, index) => {
|
||||||
.slice(0, 3)
|
const daysLeft = dayjs(survey.deadline).diff(dayjs(), 'day')
|
||||||
.map((survey) => (
|
const urgency = daysLeft <= 3 ? 'urgent' : daysLeft <= 7 ? 'warning' : 'normal'
|
||||||
|
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
key={survey.id}
|
key={survey.id}
|
||||||
onClick={() => onTakeSurvey(survey)}
|
onClick={() => onTakeSurvey(survey)}
|
||||||
className="p-3 rounded-lg bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800 hover:bg-purple-100 dark:hover:bg-purple-900/30 cursor-pointer transition-colors"
|
className="group relative p-5 rounded-xl bg-white dark:bg-gray-750 border border-gray-200 dark:border-gray-600 hover:border-purple-300 dark:hover:border-purple-500 cursor-pointer transition-all duration-300 hover:shadow-lg hover:-translate-y-1"
|
||||||
>
|
>
|
||||||
<h4 className="text-sm font-medium text-gray-900 dark:text-white mb-1">
|
{/* Background gradient on hover */}
|
||||||
{survey.title}
|
<div className="absolute inset-0 bg-gradient-to-r from-purple-50 to-pink-50 dark:from-purple-900/10 dark:to-pink-900/10 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
</h4>
|
|
||||||
<div className="flex items-center justify-between text-xs">
|
<div className="relative">
|
||||||
<span className="text-gray-600 dark:text-gray-400">
|
{/* Survey Title */}
|
||||||
{survey.totalQuestions} soru
|
<div className="flex items-start justify-between mb-3">
|
||||||
</span>
|
<h4 className="text-base font-semibold text-gray-900 dark:text-white group-hover:text-purple-700 dark:group-hover:text-purple-300 transition-colors">
|
||||||
<span className="text-purple-600 dark:text-purple-400 font-medium">
|
{survey.title}
|
||||||
{survey.responses} yanıt
|
</h4>
|
||||||
</span>
|
<div
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
urgency === 'urgent'
|
||||||
|
? 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'
|
||||||
|
: urgency === 'warning'
|
||||||
|
? 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300'
|
||||||
|
: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{daysLeft > 0 ? `${daysLeft} gün` : 'Son gün'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Survey Stats */}
|
||||||
|
<div className="grid grid-cols-3 gap-4 mb-4">
|
||||||
|
<div className="flex items-center gap-2 text-sm">
|
||||||
|
<div className="p-1.5 bg-blue-100 dark:bg-blue-900/30 rounded-lg">
|
||||||
|
<FaQuestionCircle className="w-3 h-3 text-blue-600 dark:text-blue-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-gray-500 dark:text-gray-400">Sorular</p>
|
||||||
|
<p className="font-semibold text-gray-900 dark:text-white">
|
||||||
|
{survey.questions.length}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2 text-sm">
|
||||||
|
<div className="p-1.5 bg-green-100 dark:bg-green-900/30 rounded-lg">
|
||||||
|
<FaUsers className="w-3 h-3 text-green-600 dark:text-green-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-gray-500 dark:text-gray-400">Yanıtlar</p>
|
||||||
|
<p className="font-semibold text-gray-900 dark:text-white">
|
||||||
|
{survey.responses}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2 text-sm">
|
||||||
|
<div className="p-1.5 bg-purple-100 dark:bg-purple-900/30 rounded-lg">
|
||||||
|
<FaClock className="w-3 h-3 text-purple-600 dark:text-purple-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-gray-500 dark:text-gray-400">Süre</p>
|
||||||
|
<p className="font-semibold text-gray-900 dark:text-white">~5dk</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Progress Bar */}
|
||||||
|
<div className="mb-4">
|
||||||
|
<div className="flex justify-between text-xs mb-1">
|
||||||
|
<span className="text-gray-600 dark:text-gray-400">Tamamlanma oranı</span>
|
||||||
|
<span className="text-gray-800 dark:text-gray-200 font-medium">
|
||||||
|
{Math.round((survey.responses / 100) * 100)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2">
|
||||||
|
<div
|
||||||
|
className="bg-gradient-to-r from-purple-500 to-pink-500 h-2 rounded-full transition-all duration-500"
|
||||||
|
style={{ width: `${Math.min((survey.responses / 100) * 100, 100)}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Deadline */}
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
<FaClock className="inline w-3 h-3 mr-1" />
|
||||||
|
Son tarih: {dayjs(survey.deadline).format('DD MMMM YYYY')}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Action Button */}
|
||||||
|
<button className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white text-sm font-medium rounded-lg transition-all duration-300 transform group-hover:scale-[1.02] shadow-sm hover:shadow-md">
|
||||||
|
Anketi Doldur
|
||||||
|
<FaArrowRight className="w-3 h-3 transition-transform group-hover:translate-x-1" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
|
||||||
Son tarih: {dayjs(survey.deadline).format('DD MMM')}
|
|
||||||
</p>
|
|
||||||
<button className="mt-2 w-full px-3 py-1.5 bg-purple-600 hover:bg-purple-700 text-white text-xs rounded-md transition-colors">
|
|
||||||
Anketi Doldur
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
)
|
||||||
{mockSurveys.filter((s) => s.status === 'active').length === 0 && (
|
})}
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 text-center py-4">
|
|
||||||
Aktif anket yok
|
{activeSurveys.length === 0 && (
|
||||||
</p>
|
<div className="text-center py-12">
|
||||||
|
<div className="inline-flex items-center justify-center w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-full mb-4">
|
||||||
|
<FaClipboardCheck className="w-8 h-8 text-gray-400" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">
|
||||||
|
Aktif anket bulunmuyor
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Yeni anketler eklendiğinde burada görünecektir.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue