İntranet modülündeki Dialoglar düzeltildi.
This commit is contained in:
parent
ddaaa56ea0
commit
e8093784e0
10 changed files with 1371 additions and 1087 deletions
|
|
@ -8,12 +8,13 @@ import {
|
||||||
HiCalendar,
|
HiCalendar,
|
||||||
HiXMark,
|
HiXMark,
|
||||||
HiChevronLeft,
|
HiChevronLeft,
|
||||||
HiChevronRight
|
HiChevronRight,
|
||||||
} from 'react-icons/hi2'
|
} from 'react-icons/hi2'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import 'dayjs/locale/tr'
|
import 'dayjs/locale/tr'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { mockEvents, CalendarEvent, EventComment } from '../../../mocks/mockIntranetData'
|
import { mockEvents, CalendarEvent, EventComment } from '../../../mocks/mockIntranetData'
|
||||||
|
import { mockEmployees } from '@/mocks/mockEmployees'
|
||||||
|
|
||||||
dayjs.locale('tr')
|
dayjs.locale('tr')
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
@ -22,20 +23,21 @@ const EventsModule: React.FC = () => {
|
||||||
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null)
|
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null)
|
||||||
const [selectedPhotoIndex, setSelectedPhotoIndex] = useState(0)
|
const [selectedPhotoIndex, setSelectedPhotoIndex] = useState(0)
|
||||||
const [showPhotoModal, setShowPhotoModal] = useState(false)
|
const [showPhotoModal, setShowPhotoModal] = useState(false)
|
||||||
const [selectedFilter, setSelectedFilter] = useState<'all' | 'social' | 'training' | 'company' | 'sport' | 'culture'>('all')
|
const [selectedFilter, setSelectedFilter] = useState<
|
||||||
|
'all' | 'social' | 'training' | 'company' | 'sport' | 'culture'
|
||||||
|
>('all')
|
||||||
const [newComment, setNewComment] = useState('')
|
const [newComment, setNewComment] = useState('')
|
||||||
const [events, setEvents] = useState<CalendarEvent[]>(mockEvents)
|
const [events, setEvents] = useState<CalendarEvent[]>(mockEvents)
|
||||||
|
|
||||||
const filteredEvents = selectedFilter === 'all'
|
const filteredEvents =
|
||||||
? events.filter(e => e.isPublished)
|
selectedFilter === 'all'
|
||||||
: events.filter(e => e.isPublished && e.type === selectedFilter)
|
? events.filter((e) => e.isPublished)
|
||||||
|
: events.filter((e) => e.isPublished && e.type === selectedFilter)
|
||||||
|
|
||||||
const handleLikeEvent = (eventId: string) => {
|
const handleLikeEvent = (eventId: string) => {
|
||||||
setEvents(prev => prev.map(e =>
|
setEvents((prev) => prev.map((e) => (e.id === eventId ? { ...e, likes: e.likes + 1 } : e)))
|
||||||
e.id === eventId ? { ...e, likes: e.likes + 1 } : e
|
|
||||||
))
|
|
||||||
if (selectedEvent?.id === eventId) {
|
if (selectedEvent?.id === eventId) {
|
||||||
setSelectedEvent(prev => prev ? { ...prev, likes: prev.likes + 1 } : null)
|
setSelectedEvent((prev) => (prev ? { ...prev, likes: prev.likes + 1 } : null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,22 +46,18 @@ const EventsModule: React.FC = () => {
|
||||||
|
|
||||||
const comment: EventComment = {
|
const comment: EventComment = {
|
||||||
id: `c${Date.now()}`,
|
id: `c${Date.now()}`,
|
||||||
author: {
|
author: mockEmployees[0],
|
||||||
id: 'current-user',
|
|
||||||
fullName: 'Sedat Öztürk',
|
|
||||||
avatar: 'https://ui-avatars.com/api/?name=Sedat+Ozturk&background=3b82f6&color=fff'
|
|
||||||
},
|
|
||||||
content: newComment,
|
content: newComment,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
likes: 0
|
likes: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
setEvents(prev => prev.map(e =>
|
setEvents((prev) =>
|
||||||
e.id === eventId ? { ...e, comments: [...e.comments, comment] } : e
|
prev.map((e) => (e.id === eventId ? { ...e, comments: [...e.comments, comment] } : e)),
|
||||||
))
|
)
|
||||||
|
|
||||||
if (selectedEvent?.id === eventId) {
|
if (selectedEvent?.id === eventId) {
|
||||||
setSelectedEvent(prev => prev ? { ...prev, comments: [...prev.comments, comment] } : null)
|
setSelectedEvent((prev) => (prev ? { ...prev, comments: [...prev.comments, comment] } : null))
|
||||||
}
|
}
|
||||||
|
|
||||||
setNewComment('')
|
setNewComment('')
|
||||||
|
|
@ -71,7 +69,7 @@ const EventsModule: React.FC = () => {
|
||||||
training: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300',
|
training: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300',
|
||||||
company: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
company: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||||
sport: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
sport: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
||||||
culture: 'bg-pink-100 dark:bg-pink-900/30 text-pink-700 dark:text-pink-300'
|
culture: 'bg-pink-100 dark:bg-pink-900/30 text-pink-700 dark:text-pink-300',
|
||||||
}
|
}
|
||||||
return colors[type] || colors.social
|
return colors[type] || colors.social
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +80,7 @@ const EventsModule: React.FC = () => {
|
||||||
training: '📚 Eğitim',
|
training: '📚 Eğitim',
|
||||||
company: '🏢 Kurumsal',
|
company: '🏢 Kurumsal',
|
||||||
sport: '⚽ Spor',
|
sport: '⚽ Spor',
|
||||||
culture: '🎨 Kültür'
|
culture: '🎨 Kültür',
|
||||||
}
|
}
|
||||||
return labels[type] || type
|
return labels[type] || type
|
||||||
}
|
}
|
||||||
|
|
@ -92,9 +90,7 @@ const EventsModule: React.FC = () => {
|
||||||
<div className="max-w-7xl mx-auto space-y-6">
|
<div className="max-w-7xl mx-auto space-y-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">🎊 Etkinlikler</h1>
|
||||||
🎊 Etkinlikler
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||||
Şirket etkinlikleri, fotoğraflar ve anılar
|
Şirket etkinlikleri, fotoğraflar ve anılar
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -108,7 +104,7 @@ const EventsModule: React.FC = () => {
|
||||||
{ value: 'training' as const, label: '📚 Eğitim' },
|
{ value: 'training' as const, label: '📚 Eğitim' },
|
||||||
{ value: 'company' as const, label: '🏢 Kurumsal' },
|
{ value: 'company' as const, label: '🏢 Kurumsal' },
|
||||||
{ value: 'sport' as const, label: '⚽ Spor' },
|
{ value: 'sport' as const, label: '⚽ Spor' },
|
||||||
{ value: 'culture' as const, label: '🎨 Kültür' }
|
{ value: 'culture' as const, label: '🎨 Kültür' },
|
||||||
].map((tab) => (
|
].map((tab) => (
|
||||||
<button
|
<button
|
||||||
key={tab.value}
|
key={tab.value}
|
||||||
|
|
@ -143,7 +139,9 @@ const EventsModule: React.FC = () => {
|
||||||
className="w-full h-full object-cover hover:scale-110 transition-transform duration-300"
|
className="w-full h-full object-cover hover:scale-110 transition-transform duration-300"
|
||||||
/>
|
/>
|
||||||
<div className="absolute top-3 right-3">
|
<div className="absolute top-3 right-3">
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getTypeColor(event.type)}`}>
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-medium ${getTypeColor(event.type)}`}
|
||||||
|
>
|
||||||
{getTypeLabel(event.type)}
|
{getTypeLabel(event.type)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -207,6 +205,7 @@ const EventsModule: React.FC = () => {
|
||||||
<p className="text-lg">Bu kategoride henüz etkinlik yok</p>
|
<p className="text-lg">Bu kategoride henüz etkinlik yok</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Event Detail Modal */}
|
{/* Event Detail Modal */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|
@ -232,7 +231,9 @@ const EventsModule: React.FC = () => {
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<div className="flex items-center gap-3 mb-2">
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getTypeColor(selectedEvent.type)}`}>
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-medium ${getTypeColor(selectedEvent.type)}`}
|
||||||
|
>
|
||||||
{getTypeLabel(selectedEvent.type)}
|
{getTypeLabel(selectedEvent.type)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm text-gray-600 dark:text-gray-400">
|
<span className="text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
|
@ -418,17 +419,21 @@ const EventsModule: React.FC = () => {
|
||||||
{selectedEvent.photos.length > 1 && (
|
{selectedEvent.photos.length > 1 && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedPhotoIndex((prev) =>
|
onClick={() =>
|
||||||
prev === 0 ? selectedEvent.photos.length - 1 : prev - 1
|
setSelectedPhotoIndex((prev) =>
|
||||||
)}
|
prev === 0 ? selectedEvent.photos.length - 1 : prev - 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
className="absolute left-4 top-1/2 -translate-y-1/2 p-2 bg-black/50 hover:bg-black/70 rounded-full text-white"
|
className="absolute left-4 top-1/2 -translate-y-1/2 p-2 bg-black/50 hover:bg-black/70 rounded-full text-white"
|
||||||
>
|
>
|
||||||
<HiChevronLeft className="w-6 h-6" />
|
<HiChevronLeft className="w-6 h-6" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedPhotoIndex((prev) =>
|
onClick={() =>
|
||||||
prev === selectedEvent.photos.length - 1 ? 0 : prev + 1
|
setSelectedPhotoIndex((prev) =>
|
||||||
)}
|
prev === selectedEvent.photos.length - 1 ? 0 : prev + 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
className="absolute right-4 top-1/2 -translate-y-1/2 p-2 bg-black/50 hover:bg-black/70 rounded-full text-white"
|
className="absolute right-4 top-1/2 -translate-y-1/2 p-2 bg-black/50 hover:bg-black/70 rounded-full text-white"
|
||||||
>
|
>
|
||||||
<HiChevronRight className="w-6 h-6" />
|
<HiChevronRight className="w-6 h-6" />
|
||||||
|
|
@ -453,7 +458,6 @@ const EventsModule: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {
|
||||||
HiArrowTrendingDown as ArrowTrendingDownIcon,
|
HiArrowTrendingDown as ArrowTrendingDownIcon,
|
||||||
HiXMark,
|
HiXMark,
|
||||||
HiEye,
|
HiEye,
|
||||||
HiPaperClip
|
HiPaperClip,
|
||||||
} from 'react-icons/hi2'
|
} from 'react-icons/hi2'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import 'dayjs/locale/tr'
|
import 'dayjs/locale/tr'
|
||||||
|
|
@ -25,7 +25,8 @@ import {
|
||||||
mockAnniversaries,
|
mockAnniversaries,
|
||||||
mockQuickLinks,
|
mockQuickLinks,
|
||||||
mockTasks,
|
mockTasks,
|
||||||
Announcement
|
mockDocuments,
|
||||||
|
Announcement,
|
||||||
} from '../../../mocks/mockIntranetData'
|
} from '../../../mocks/mockIntranetData'
|
||||||
|
|
||||||
dayjs.locale('tr')
|
dayjs.locale('tr')
|
||||||
|
|
@ -37,36 +38,49 @@ const IntranetDashboard: React.FC = () => {
|
||||||
const [selectedAnnouncement, setSelectedAnnouncement] = useState<Announcement | null>(null)
|
const [selectedAnnouncement, setSelectedAnnouncement] = useState<Announcement | null>(null)
|
||||||
|
|
||||||
// Bugünün etkinlikleri
|
// Bugünün etkinlikleri
|
||||||
const todayEvents = mockEvents.filter(event =>
|
const todayEvents = mockEvents.filter(
|
||||||
event.isPublished && dayjs(event.date).isSame(dayjs(), 'day')
|
(event) => event.isPublished && dayjs(event.date).isSame(dayjs(), 'day'),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Yaklaşan etkinlikler (7 gün içinde)
|
// Yaklaşan etkinlikler (7 gün içinde)
|
||||||
const upcomingEvents = mockEvents.filter(event =>
|
const upcomingEvents = mockEvents.filter(
|
||||||
|
(event) =>
|
||||||
event.isPublished &&
|
event.isPublished &&
|
||||||
dayjs(event.date).isAfter(dayjs()) &&
|
dayjs(event.date).isAfter(dayjs()) &&
|
||||||
dayjs(event.date).isBefore(dayjs().add(7, 'day'))
|
dayjs(event.date).isBefore(dayjs().add(7, 'day')),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bu haftaki doğum günleri
|
// Bugün doğanlar (sadece ay ve günü karşılaştır)
|
||||||
const weekBirthdays = mockBirthdays.filter(b =>
|
const todayBirthdays = mockBirthdays.filter((b) => {
|
||||||
dayjs(b.date).isBetween(dayjs().startOf('week'), dayjs().endOf('week'))
|
const birthDate = dayjs(b.date)
|
||||||
)
|
const today = dayjs()
|
||||||
|
return birthDate.month() === today.month() && birthDate.date() === today.date()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Bu haftaki doğum günleri (sadece ay ve günü karşılaştır)
|
||||||
|
const weekBirthdays = mockBirthdays.filter((b) => {
|
||||||
|
const birthDate = dayjs(b.date)
|
||||||
|
const today = dayjs()
|
||||||
|
const weekStart = today.startOf('week')
|
||||||
|
const weekEnd = today.endOf('week')
|
||||||
|
|
||||||
|
// Bu yılki doğum gününü oluştur
|
||||||
|
const thisBirthday = today.year(today.year()).month(birthDate.month()).date(birthDate.date())
|
||||||
|
return thisBirthday.isBetween(weekStart, weekEnd, null, '[]')
|
||||||
|
})
|
||||||
|
|
||||||
// Bu ayki iş yıldönümleri
|
// Bu ayki iş yıldönümleri
|
||||||
const monthAnniversaries = mockAnniversaries.filter(a =>
|
const monthAnniversaries = mockAnniversaries.filter(
|
||||||
dayjs(a.hireDate).month() === dayjs().month()
|
(a) => dayjs(a.hireDate).month() === dayjs().month(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Öncelikli görevler
|
// Öncelikli görevler
|
||||||
const priorityTasks = mockTasks.filter(t =>
|
const priorityTasks = mockTasks
|
||||||
t.priority === 'high' || t.priority === 'urgent'
|
.filter((t) => t.priority === 'high' || t.priority === 'urgent')
|
||||||
).slice(0, 3)
|
.slice(0, 3)
|
||||||
|
|
||||||
// Sabitlenmiş duyurular
|
// Sabitlenmiş duyurular
|
||||||
const pinnedAnnouncements = mockAnnouncements
|
const pinnedAnnouncements = mockAnnouncements.filter((a) => a.isPinned).slice(0, 3)
|
||||||
.filter(a => a.isPinned)
|
|
||||||
.slice(0, 3)
|
|
||||||
|
|
||||||
const getCategoryColor = (category: string) => {
|
const getCategoryColor = (category: string) => {
|
||||||
const colors: Record<string, string> = {
|
const colors: Record<string, string> = {
|
||||||
|
|
@ -74,7 +88,7 @@ const IntranetDashboard: React.FC = () => {
|
||||||
hr: 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300',
|
hr: 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300',
|
||||||
it: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
it: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
||||||
event: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
event: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||||
urgent: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300'
|
urgent: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300',
|
||||||
}
|
}
|
||||||
return colors[category] || colors.general
|
return colors[category] || colors.general
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +98,7 @@ const IntranetDashboard: React.FC = () => {
|
||||||
low: 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300',
|
low: 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300',
|
||||||
medium: 'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-300',
|
medium: 'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-300',
|
||||||
high: 'bg-orange-100 dark:bg-orange-900/30 text-orange-600 dark:text-orange-300',
|
high: 'bg-orange-100 dark:bg-orange-900/30 text-orange-600 dark:text-orange-300',
|
||||||
urgent: 'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-300'
|
urgent: 'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-300',
|
||||||
}
|
}
|
||||||
return colors[priority]
|
return colors[priority]
|
||||||
}
|
}
|
||||||
|
|
@ -142,7 +156,9 @@ const IntranetDashboard: React.FC = () => {
|
||||||
<h3 className="text-base font-semibold text-gray-900 dark:text-white">
|
<h3 className="text-base font-semibold text-gray-900 dark:text-white">
|
||||||
{announcement.title}
|
{announcement.title}
|
||||||
</h3>
|
</h3>
|
||||||
<span className={`px-2 py-1 text-xs rounded-full ${getCategoryColor(announcement.category)}`}>
|
<span
|
||||||
|
className={`px-2 py-1 text-xs rounded-full ${getCategoryColor(announcement.category)}`}
|
||||||
|
>
|
||||||
{announcement.category === 'general' && 'Genel'}
|
{announcement.category === 'general' && 'Genel'}
|
||||||
{announcement.category === 'hr' && 'İK'}
|
{announcement.category === 'hr' && 'İK'}
|
||||||
{announcement.category === 'it' && 'IT'}
|
{announcement.category === 'it' && 'IT'}
|
||||||
|
|
@ -214,7 +230,9 @@ const IntranetDashboard: React.FC = () => {
|
||||||
<h3 className="text-sm font-medium text-gray-900 dark:text-white">
|
<h3 className="text-sm font-medium text-gray-900 dark:text-white">
|
||||||
{task.title}
|
{task.title}
|
||||||
</h3>
|
</h3>
|
||||||
<span className={`px-2 py-0.5 text-xs rounded ${getPriorityColor(task.priority)}`}>
|
<span
|
||||||
|
className={`px-2 py-0.5 text-xs rounded ${getPriorityColor(task.priority)}`}
|
||||||
|
>
|
||||||
{task.priority === 'urgent' && '🔥 Acil'}
|
{task.priority === 'urgent' && '🔥 Acil'}
|
||||||
{task.priority === 'high' && 'Yüksek'}
|
{task.priority === 'high' && 'Yüksek'}
|
||||||
{task.priority === 'medium' && 'Orta'}
|
{task.priority === 'medium' && 'Orta'}
|
||||||
|
|
@ -278,6 +296,46 @@ const IntranetDashboard: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Bugün Doğanlar */}
|
||||||
|
<div className="bg-gradient-to-br from-pink-50 to-purple-50 dark:from-pink-900/20 dark:to-purple-900/20 rounded-lg shadow-sm border border-pink-200 dark:border-pink-800">
|
||||||
|
<div className="p-6">
|
||||||
|
<h2 className="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2 mb-4">
|
||||||
|
🎂 Bugün Doğanlar
|
||||||
|
</h2>
|
||||||
|
{todayBirthdays.length > 0 ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{todayBirthdays.map((birthday, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="flex items-center gap-3 p-3 bg-white/50 dark:bg-gray-800/50 rounded-lg"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={birthday.employee.avatar}
|
||||||
|
alt={birthday.employee.fullName}
|
||||||
|
className="w-12 h-12 rounded-full border-2 border-pink-300 dark:border-pink-700"
|
||||||
|
/>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||||
|
{birthday.employee.fullName}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-600 dark:text-gray-400">
|
||||||
|
{birthday.age} yaşında 🎉
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 dark:text-gray-500 mt-1">
|
||||||
|
{birthday.employee.department?.name || 'Genel'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-400 text-center py-4">
|
||||||
|
Bugün doğan yok
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Yaklaşan Etkinlikler */}
|
{/* Yaklaşan Etkinlikler */}
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
||||||
<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">
|
||||||
|
|
@ -287,7 +345,8 @@ const IntranetDashboard: React.FC = () => {
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 space-y-3">
|
<div className="p-4 space-y-3">
|
||||||
{upcomingEvents.slice(0, 3).map((event) => (
|
{upcomingEvents.length > 0 ? (
|
||||||
|
upcomingEvents.slice(0, 3).map((event) => (
|
||||||
<div
|
<div
|
||||||
key={event.id}
|
key={event.id}
|
||||||
className="p-3 rounded-lg border-l-4 bg-gray-50 dark:bg-gray-700/50 border-l-green-500"
|
className="p-3 rounded-lg border-l-4 bg-gray-50 dark:bg-gray-700/50 border-l-green-500"
|
||||||
|
|
@ -299,6 +358,58 @@ const IntranetDashboard: React.FC = () => {
|
||||||
{dayjs(event.date).format('DD MMMM YYYY')} - {event.location}
|
{dayjs(event.date).format('DD MMMM YYYY')} - {event.location}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-400 text-center py-4">
|
||||||
|
Yaklaşan etkinlik yok
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Dokümanlar */}
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
||||||
|
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||||
|
<DocumentTextIcon className="w-5 h-5" />
|
||||||
|
Son Dokümanlar
|
||||||
|
</h2>
|
||||||
|
<button className="text-sm text-blue-600 hover:text-blue-700 dark:text-blue-400">
|
||||||
|
Tümü
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||||
|
{mockDocuments.slice(0, 5).map((doc) => (
|
||||||
|
<div
|
||||||
|
key={doc.id}
|
||||||
|
className="p-4 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="p-2 bg-blue-100 dark:bg-blue-900/30 rounded-lg">
|
||||||
|
<DocumentTextIcon className="w-5 h-5 text-blue-600 dark:text-blue-400" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h4 className="text-sm font-medium text-gray-900 dark:text-white truncate">
|
||||||
|
{doc.name}
|
||||||
|
</h4>
|
||||||
|
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
||||||
|
{doc.category === 'policy' && '📋 Politika'}
|
||||||
|
{doc.category === 'procedure' && '📝 Prosedür'}
|
||||||
|
{doc.category === 'form' && '📄 Form'}
|
||||||
|
{doc.category === 'template' && '<27> Şablon'}
|
||||||
|
{doc.category === 'report' && '📊 Rapor'}
|
||||||
|
{doc.category === 'other' && '📄 Diğer'}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-2 mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
<span>{dayjs(doc.uploadDate).fromNow()}</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>{doc.size}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -364,6 +475,7 @@ const IntranetDashboard: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Announcement Detail Modal */}
|
{/* Announcement Detail Modal */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|
@ -381,7 +493,7 @@ const IntranetDashboard: React.FC = () => {
|
||||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-3xl w-full my-8"
|
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-3xl w-full"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
|
|
@ -389,7 +501,9 @@ const IntranetDashboard: React.FC = () => {
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-3 mb-3">
|
<div className="flex items-center gap-3 mb-3">
|
||||||
<span className={`px-3 py-1 text-xs font-medium rounded-full ${getCategoryColor(selectedAnnouncement.category)}`}>
|
<span
|
||||||
|
className={`px-3 py-1 text-xs font-medium rounded-full ${getCategoryColor(selectedAnnouncement.category)}`}
|
||||||
|
>
|
||||||
{selectedAnnouncement.category === 'general' && '📢 Genel'}
|
{selectedAnnouncement.category === 'general' && '📢 Genel'}
|
||||||
{selectedAnnouncement.category === 'hr' && '👥 İnsan Kaynakları'}
|
{selectedAnnouncement.category === 'hr' && '👥 İnsan Kaynakları'}
|
||||||
{selectedAnnouncement.category === 'it' && '💻 Bilgi Teknolojileri'}
|
{selectedAnnouncement.category === 'it' && '💻 Bilgi Teknolojileri'}
|
||||||
|
|
@ -424,7 +538,9 @@ const IntranetDashboard: React.FC = () => {
|
||||||
{selectedAnnouncement.author.fullName}
|
{selectedAnnouncement.author.fullName}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400">
|
<div className="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400">
|
||||||
<span>{dayjs(selectedAnnouncement.publishDate).format('DD MMMM YYYY, HH:mm')}</span>
|
<span>
|
||||||
|
{dayjs(selectedAnnouncement.publishDate).format('DD MMMM YYYY, HH:mm')}
|
||||||
|
</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<HiEye className="w-4 h-4" />
|
<HiEye className="w-4 h-4" />
|
||||||
|
|
@ -454,7 +570,8 @@ const IntranetDashboard: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Attachments */}
|
{/* Attachments */}
|
||||||
{selectedAnnouncement.attachments && selectedAnnouncement.attachments.length > 0 && (
|
{selectedAnnouncement.attachments &&
|
||||||
|
selectedAnnouncement.attachments.length > 0 && (
|
||||||
<div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
|
<div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-3 flex items-center gap-2">
|
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-3 flex items-center gap-2">
|
||||||
<HiPaperClip className="w-5 h-5" />
|
<HiPaperClip className="w-5 h-5" />
|
||||||
|
|
@ -488,7 +605,8 @@ const IntranetDashboard: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Departments */}
|
{/* Departments */}
|
||||||
{selectedAnnouncement.departments && selectedAnnouncement.departments.length > 0 && (
|
{selectedAnnouncement.departments &&
|
||||||
|
selectedAnnouncement.departments.length > 0 && (
|
||||||
<div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
|
<div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-3">
|
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-3">
|
||||||
Hedef Departmanlar
|
Hedef Departmanlar
|
||||||
|
|
@ -532,7 +650,6 @@ const IntranetDashboard: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ import {
|
||||||
HiBuildingOffice2,
|
HiBuildingOffice2,
|
||||||
HiClipboardDocumentCheck,
|
HiClipboardDocumentCheck,
|
||||||
HiUserPlus,
|
HiUserPlus,
|
||||||
|
HiBars3,
|
||||||
|
HiXMark,
|
||||||
|
HiChevronLeft,
|
||||||
} from 'react-icons/hi2'
|
} from 'react-icons/hi2'
|
||||||
import {
|
import {
|
||||||
mockTasks,
|
mockTasks,
|
||||||
|
|
@ -127,6 +130,8 @@ interface IntranetSidebarProps {
|
||||||
|
|
||||||
const IntranetSidebar: React.FC<IntranetSidebarProps> = ({ activePath, onNavigate }) => {
|
const IntranetSidebar: React.FC<IntranetSidebarProps> = ({ activePath, onNavigate }) => {
|
||||||
const [expandedMenus, setExpandedMenus] = useState<string[]>(['hr'])
|
const [expandedMenus, setExpandedMenus] = useState<string[]>(['hr'])
|
||||||
|
const [isCollapsed, setIsCollapsed] = useState(false)
|
||||||
|
const [isMobileOpen, setIsMobileOpen] = useState(false)
|
||||||
|
|
||||||
// Dinamik badge sayılarını hesapla
|
// Dinamik badge sayılarını hesapla
|
||||||
const badgeCounts = useMemo(() => {
|
const badgeCounts = useMemo(() => {
|
||||||
|
|
@ -180,6 +185,7 @@ const IntranetSidebar: React.FC<IntranetSidebarProps> = ({ activePath, onNavigat
|
||||||
toggleMenu(item.id)
|
toggleMenu(item.id)
|
||||||
} else if (item.path) {
|
} else if (item.path) {
|
||||||
onNavigate(item.path)
|
onNavigate(item.path)
|
||||||
|
setIsMobileOpen(false)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className={`w-full flex items-center justify-between px-3 py-2.5 rounded-lg transition-colors ${
|
className={`w-full flex items-center justify-between px-3 py-2.5 rounded-lg transition-colors ${
|
||||||
|
|
@ -187,28 +193,34 @@ const IntranetSidebar: React.FC<IntranetSidebarProps> = ({ activePath, onNavigat
|
||||||
? 'bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300'
|
? 'bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300'
|
||||||
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800'
|
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800'
|
||||||
} ${level > 0 ? 'ml-6' : ''}`}
|
} ${level > 0 ? 'ml-6' : ''}`}
|
||||||
|
title={isCollapsed ? item.label : undefined}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3 min-w-0">
|
||||||
<item.icon className="w-5 h-5" />
|
<item.icon className="w-5 h-5 flex-shrink-0" />
|
||||||
<span className="font-medium text-sm">{item.label}</span>
|
{!isCollapsed && <span className="font-medium text-sm truncate">{item.label}</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
{!isCollapsed && (
|
||||||
|
<div className="flex items-center gap-2 flex-shrink-0">
|
||||||
{item.badge && item.badge > 0 && (
|
{item.badge && item.badge > 0 && (
|
||||||
<span className="px-2 py-0.5 bg-red-500 text-white text-xs rounded-full">
|
<span className="px-2 py-0.5 bg-red-500 text-white text-xs rounded-full">
|
||||||
{item.badge}
|
{item.badge}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{hasChildren && (
|
{hasChildren && (
|
||||||
<motion.div animate={{ rotate: isExpanded ? 90 : 0 }} transition={{ duration: 0.2 }}>
|
<motion.div
|
||||||
|
animate={{ rotate: isExpanded ? 90 : 0 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
>
|
||||||
<HiChevronRight className="w-4 h-4" />
|
<HiChevronRight className="w-4 h-4" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{hasChildren && (
|
{hasChildren && (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{isExpanded && (
|
{isExpanded && !isCollapsed && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ height: 0, opacity: 0 }}
|
initial={{ height: 0, opacity: 0 }}
|
||||||
animate={{ height: 'auto', opacity: 1 }}
|
animate={{ height: 'auto', opacity: 1 }}
|
||||||
|
|
@ -228,13 +240,74 @@ const IntranetSidebar: React.FC<IntranetSidebarProps> = ({ activePath, onNavigat
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 h-screen sticky top-0 overflow-y-auto">
|
<>
|
||||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
{/* Mobile Toggle Button */}
|
||||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">İntranet Portal</h2>
|
<button
|
||||||
|
onClick={() => setIsMobileOpen(!isMobileOpen)}
|
||||||
|
className="lg:hidden fixed top-4 left-4 z-50 p-2 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700"
|
||||||
|
>
|
||||||
|
{isMobileOpen ? (
|
||||||
|
<HiXMark className="w-6 h-6 text-gray-700 dark:text-gray-300" />
|
||||||
|
) : (
|
||||||
|
<HiBars3 className="w-6 h-6 text-gray-700 dark:text-gray-300" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Mobile Overlay */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{isMobileOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
onClick={() => setIsMobileOpen(false)}
|
||||||
|
className="lg:hidden fixed inset-0 bg-black/50 z-40"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<motion.div
|
||||||
|
initial={false}
|
||||||
|
animate={{
|
||||||
|
width: isCollapsed ? '80px' : '256px',
|
||||||
|
x: isMobileOpen ? 0 : undefined,
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className={`
|
||||||
|
bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700
|
||||||
|
h-screen sticky top-0 overflow-y-auto
|
||||||
|
${isMobileOpen ? 'fixed left-0 top-0 z-40 lg:relative' : 'hidden lg:block'}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||||
|
{!isCollapsed ? (
|
||||||
|
<>
|
||||||
|
<h2 className="text-xl font-bold text-gray-900 dark:text-white truncate">
|
||||||
|
İntranet Portal
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsCollapsed(true)}
|
||||||
|
className="hidden lg:block p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
|
||||||
|
title="Daralt"
|
||||||
|
>
|
||||||
|
<HiChevronLeft className="w-5 h-5 text-gray-600 dark:text-gray-400" />
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => setIsCollapsed(false)}
|
||||||
|
className="w-full flex justify-center p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
|
||||||
|
title="Genişlet"
|
||||||
|
>
|
||||||
|
<HiBars3 className="w-6 h-6 text-gray-600 dark:text-gray-400" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="p-4 space-y-1">{menuItems.map((item) => renderMenuItem(item))}</nav>
|
<nav className="p-4 space-y-1">{menuItems.map((item) => renderMenuItem(item))}</nav>
|
||||||
</div>
|
</motion.div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,6 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import {
|
import { HiKey, HiCalendar, HiTruck, HiCog, HiPlus, HiXMark } from 'react-icons/hi2'
|
||||||
HiKey,
|
|
||||||
HiCalendar,
|
|
||||||
HiTruck,
|
|
||||||
HiCog,
|
|
||||||
HiPlus,
|
|
||||||
HiXMark
|
|
||||||
} from 'react-icons/hi2'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { mockReservations, Reservation } from '../../../mocks/mockIntranetData'
|
import { mockReservations, Reservation } from '../../../mocks/mockIntranetData'
|
||||||
|
|
||||||
|
|
@ -15,9 +8,10 @@ const ReservationsModule: React.FC = () => {
|
||||||
const [selectedType, setSelectedType] = useState<'all' | 'room' | 'vehicle' | 'equipment'>('all')
|
const [selectedType, setSelectedType] = useState<'all' | 'room' | 'vehicle' | 'equipment'>('all')
|
||||||
const [showNewReservation, setShowNewReservation] = useState(false)
|
const [showNewReservation, setShowNewReservation] = useState(false)
|
||||||
|
|
||||||
const filteredReservations = selectedType === 'all'
|
const filteredReservations =
|
||||||
|
selectedType === 'all'
|
||||||
? mockReservations
|
? mockReservations
|
||||||
: mockReservations.filter(r => r.type === selectedType)
|
: mockReservations.filter((r) => r.type === selectedType)
|
||||||
|
|
||||||
const getTypeIcon = (type: string) => {
|
const getTypeIcon = (type: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
@ -36,7 +30,7 @@ const ReservationsModule: React.FC = () => {
|
||||||
const labels: Record<string, string> = {
|
const labels: Record<string, string> = {
|
||||||
room: 'Toplantı Salonu',
|
room: 'Toplantı Salonu',
|
||||||
vehicle: 'Araç',
|
vehicle: 'Araç',
|
||||||
equipment: 'Ekipman'
|
equipment: 'Ekipman',
|
||||||
}
|
}
|
||||||
return labels[type] || type
|
return labels[type] || type
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +40,7 @@ const ReservationsModule: React.FC = () => {
|
||||||
pending: 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300',
|
pending: 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300',
|
||||||
approved: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
approved: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||||
rejected: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300',
|
rejected: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300',
|
||||||
completed: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
|
completed: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300',
|
||||||
}
|
}
|
||||||
return colors[status] || colors.pending
|
return colors[status] || colors.pending
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +50,7 @@ const ReservationsModule: React.FC = () => {
|
||||||
pending: 'Bekliyor',
|
pending: 'Bekliyor',
|
||||||
approved: 'Onaylandı',
|
approved: 'Onaylandı',
|
||||||
rejected: 'Reddedildi',
|
rejected: 'Reddedildi',
|
||||||
completed: 'Tamamlandı'
|
completed: 'Tamamlandı',
|
||||||
}
|
}
|
||||||
return labels[status] || status
|
return labels[status] || status
|
||||||
}
|
}
|
||||||
|
|
@ -67,9 +61,7 @@ const ReservationsModule: React.FC = () => {
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">🔑 Rezervasyonlar</h1>
|
||||||
🔑 Rezervasyonlar
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||||
Oda, araç ve ekipman rezervasyonları
|
Oda, araç ve ekipman rezervasyonları
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -89,7 +81,7 @@ const ReservationsModule: React.FC = () => {
|
||||||
{ value: 'all' as const, label: 'Tümü', icon: HiCalendar },
|
{ value: 'all' as const, label: 'Tümü', icon: HiCalendar },
|
||||||
{ value: 'room' as const, label: 'Toplantı Salonu', icon: HiKey },
|
{ value: 'room' as const, label: 'Toplantı Salonu', icon: HiKey },
|
||||||
{ value: 'vehicle' as const, label: 'Araç', icon: HiTruck },
|
{ value: 'vehicle' as const, label: 'Araç', icon: HiTruck },
|
||||||
{ value: 'equipment' as const, label: 'Ekipman', icon: HiCog }
|
{ value: 'equipment' as const, label: 'Ekipman', icon: HiCog },
|
||||||
].map((type) => (
|
].map((type) => (
|
||||||
<button
|
<button
|
||||||
key={type.value}
|
key={type.value}
|
||||||
|
|
@ -131,7 +123,9 @@ const ReservationsModule: React.FC = () => {
|
||||||
{getTypeLabel(reservation.type)}
|
{getTypeLabel(reservation.type)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusColor(reservation.status)}`}>
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusColor(reservation.status)}`}
|
||||||
|
>
|
||||||
{getStatusLabel(reservation.status)}
|
{getStatusLabel(reservation.status)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -197,6 +191,7 @@ const ReservationsModule: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* New Reservation Modal */}
|
{/* New Reservation Modal */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|
@ -312,7 +307,6 @@ const ReservationsModule: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -162,12 +162,6 @@ const CreatePost: React.FC<CreatePostProps> = ({ onCreatePost }) => {
|
||||||
value={content}
|
value={content}
|
||||||
onChange={(e) => setContent(e.target.value)}
|
onChange={(e) => setContent(e.target.value)}
|
||||||
onFocus={() => setIsExpanded(true)}
|
onFocus={() => setIsExpanded(true)}
|
||||||
onBlur={() => {
|
|
||||||
// Eğer içerik, medya veya konum yoksa küçült
|
|
||||||
if (!content.trim() && mediaItems.length === 0 && !location && !mediaType) {
|
|
||||||
setIsExpanded(false)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
placeholder="Ne düşünüyorsunuz?"
|
placeholder="Ne düşünüyorsunuz?"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none transition-all',
|
'w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none transition-all',
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,8 @@ const SurveysModule: React.FC = () => {
|
||||||
const [showSurveyModal, setShowSurveyModal] = useState(false)
|
const [showSurveyModal, setShowSurveyModal] = useState(false)
|
||||||
const [selectedSurvey, setSelectedSurvey] = useState<Survey | null>(null)
|
const [selectedSurvey, setSelectedSurvey] = useState<Survey | null>(null)
|
||||||
|
|
||||||
const filteredSurveys = selectedStatus === 'all'
|
const filteredSurveys =
|
||||||
? mockSurveys
|
selectedStatus === 'all' ? mockSurveys : mockSurveys.filter((s) => s.status === selectedStatus)
|
||||||
: mockSurveys.filter(s => s.status === selectedStatus)
|
|
||||||
|
|
||||||
const handleTakeSurvey = (survey: Survey) => {
|
const handleTakeSurvey = (survey: Survey) => {
|
||||||
setSelectedSurvey(survey)
|
setSelectedSurvey(survey)
|
||||||
|
|
@ -29,7 +28,7 @@ const SurveysModule: React.FC = () => {
|
||||||
const colors: Record<string, string> = {
|
const colors: Record<string, string> = {
|
||||||
draft: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300',
|
draft: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300',
|
||||||
active: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
active: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||||
closed: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300'
|
closed: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300',
|
||||||
}
|
}
|
||||||
return colors[status] || colors.draft
|
return colors[status] || colors.draft
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +52,7 @@ const SurveysModule: React.FC = () => {
|
||||||
{ value: 'all' as const, label: 'Tümü' },
|
{ value: 'all' as const, label: 'Tümü' },
|
||||||
{ value: 'active' as const, label: 'Aktif' },
|
{ value: 'active' as const, label: 'Aktif' },
|
||||||
{ value: 'draft' as const, label: 'Taslak' },
|
{ value: 'draft' as const, label: 'Taslak' },
|
||||||
{ value: 'closed' as const, label: 'Kapalı' }
|
{ value: 'closed' as const, label: 'Kapalı' },
|
||||||
].map((tab) => (
|
].map((tab) => (
|
||||||
<button
|
<button
|
||||||
key={tab.value}
|
key={tab.value}
|
||||||
|
|
@ -83,23 +82,31 @@ const SurveysModule: React.FC = () => {
|
||||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white pr-4">
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-white pr-4">
|
||||||
{survey.title}
|
{survey.title}
|
||||||
</h3>
|
</h3>
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium whitespace-nowrap ${getStatusColor(survey.status)}`}>
|
<span
|
||||||
{survey.status === 'active' ? 'Aktif' : survey.status === 'draft' ? 'Taslak' : 'Kapalı'}
|
className={`px-3 py-1 rounded-full text-xs font-medium whitespace-nowrap ${getStatusColor(survey.status)}`}
|
||||||
|
>
|
||||||
|
{survey.status === 'active'
|
||||||
|
? 'Aktif'
|
||||||
|
: survey.status === 'draft'
|
||||||
|
? 'Taslak'
|
||||||
|
: 'Kapalı'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">{survey.description}</p>
|
||||||
{survey.description}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">Soru Sayısı</p>
|
<p className="text-xs text-gray-500 dark:text-gray-400">Soru Sayısı</p>
|
||||||
<p className="text-sm font-medium text-gray-900 dark:text-white">{survey.totalQuestions} soru</p>
|
<p className="text-sm font-medium text-gray-900 dark:text-white">
|
||||||
|
{survey.totalQuestions} soru
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">Yanıt</p>
|
<p className="text-xs text-gray-500 dark:text-gray-400">Yanıt</p>
|
||||||
<p className="text-sm font-medium text-gray-900 dark:text-white">{survey.responses} kişi</p>
|
<p className="text-sm font-medium text-gray-900 dark:text-white">
|
||||||
|
{survey.responses} kişi
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">Son Tarih</p>
|
<p className="text-xs text-gray-500 dark:text-gray-400">Son Tarih</p>
|
||||||
|
|
@ -154,7 +161,7 @@ const SurveysModule: React.FC = () => {
|
||||||
<p>Anket bulunamadı</p>
|
<p>Anket bulunamadı</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
{/* Survey Modal */}
|
{/* Survey Modal */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{showSurveyModal && selectedSurvey && (
|
{showSurveyModal && selectedSurvey && (
|
||||||
|
|
@ -206,7 +213,10 @@ const SurveysModule: React.FC = () => {
|
||||||
</label>
|
</label>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{[1, 2, 3, 4, 5].map((rating) => (
|
{[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">
|
<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 />
|
<input type="radio" name="rating" value={rating} required />
|
||||||
<span className="text-sm text-gray-900 dark:text-white">{rating}</span>
|
<span className="text-sm text-gray-900 dark:text-white">{rating}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -245,7 +255,8 @@ const SurveysModule: React.FC = () => {
|
||||||
{!selectedSurvey.isAnonymous && (
|
{!selectedSurvey.isAnonymous && (
|
||||||
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3">
|
<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">
|
<p className="text-sm text-blue-700 dark:text-blue-300">
|
||||||
ℹ️ Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız kaydedilecektir.
|
ℹ️ Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız
|
||||||
|
kaydedilecektir.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -281,7 +292,6 @@ const SurveysModule: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
useSensor,
|
useSensor,
|
||||||
useSensors,
|
useSensors,
|
||||||
closestCorners,
|
closestCorners,
|
||||||
useDroppable
|
useDroppable,
|
||||||
} from '@dnd-kit/core'
|
} from '@dnd-kit/core'
|
||||||
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
|
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
|
||||||
import { useSortable } from '@dnd-kit/sortable'
|
import { useSortable } from '@dnd-kit/sortable'
|
||||||
|
|
@ -20,7 +20,7 @@ import {
|
||||||
HiClock,
|
HiClock,
|
||||||
HiChatBubbleLeftRight,
|
HiChatBubbleLeftRight,
|
||||||
HiPaperClip,
|
HiPaperClip,
|
||||||
HiTrash
|
HiTrash,
|
||||||
} from 'react-icons/hi2'
|
} from 'react-icons/hi2'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import 'dayjs/locale/tr'
|
import 'dayjs/locale/tr'
|
||||||
|
|
@ -40,11 +40,7 @@ interface DroppableColumnProps {
|
||||||
const DroppableColumn: React.FC<DroppableColumnProps> = ({ id, children }) => {
|
const DroppableColumn: React.FC<DroppableColumnProps> = ({ id, children }) => {
|
||||||
const { setNodeRef } = useDroppable({ id })
|
const { setNodeRef } = useDroppable({ id })
|
||||||
|
|
||||||
return (
|
return <div ref={setNodeRef}>{children}</div>
|
||||||
<div ref={setNodeRef} >
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sortable Task Card Component
|
// Sortable Task Card Component
|
||||||
|
|
@ -61,21 +57,16 @@ const SortableTaskCard: React.FC<SortableTaskCardProps> = ({
|
||||||
onTaskClick,
|
onTaskClick,
|
||||||
getPriorityColor,
|
getPriorityColor,
|
||||||
getPriorityLabel,
|
getPriorityLabel,
|
||||||
isOverdue
|
isOverdue,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||||
attributes,
|
id: task.id,
|
||||||
listeners,
|
})
|
||||||
setNodeRef,
|
|
||||||
transform,
|
|
||||||
transition,
|
|
||||||
isDragging
|
|
||||||
} = useSortable({ id: task.id })
|
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
transform: CSS.Transform.toString(transform),
|
transform: CSS.Transform.toString(transform),
|
||||||
transition,
|
transition,
|
||||||
opacity: isDragging ? 0.5 : 1
|
opacity: isDragging ? 0.5 : 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
const overdue = isOverdue(task.dueDate) && task.status !== 'done'
|
const overdue = isOverdue(task.dueDate) && task.status !== 'done'
|
||||||
|
|
@ -87,9 +78,7 @@ const SortableTaskCard: React.FC<SortableTaskCardProps> = ({
|
||||||
{...attributes}
|
{...attributes}
|
||||||
{...listeners}
|
{...listeners}
|
||||||
className={`bg-white dark:bg-gray-800 rounded-lg p-3 sm:p-4 border-2 cursor-move hover:shadow-lg transition-all ${
|
className={`bg-white dark:bg-gray-800 rounded-lg p-3 sm:p-4 border-2 cursor-move hover:shadow-lg transition-all ${
|
||||||
overdue
|
overdue ? 'border-red-300 dark:border-red-700' : 'border-gray-200 dark:border-gray-700'
|
||||||
? 'border-red-300 dark:border-red-700'
|
|
||||||
: 'border-gray-200 dark:border-gray-700'
|
|
||||||
} ${isDragging ? 'shadow-2xl ring-4 ring-blue-500/50' : ''}`}
|
} ${isDragging ? 'shadow-2xl ring-4 ring-blue-500/50' : ''}`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (!(e.target as HTMLElement).closest('[data-no-click]')) {
|
if (!(e.target as HTMLElement).closest('[data-no-click]')) {
|
||||||
|
|
@ -98,19 +87,17 @@ const SortableTaskCard: React.FC<SortableTaskCardProps> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between mb-2 sm:mb-3">
|
<div className="flex items-start justify-between mb-2 sm:mb-3">
|
||||||
<span className={`px-2 py-1 text-xs font-medium rounded border ${getPriorityColor(task.priority)}`}>
|
<span
|
||||||
|
className={`px-2 py-1 text-xs font-medium rounded border ${getPriorityColor(task.priority)}`}
|
||||||
|
>
|
||||||
{getPriorityLabel(task.priority)}
|
{getPriorityLabel(task.priority)}
|
||||||
</span>
|
</span>
|
||||||
{overdue && (
|
{overdue && (
|
||||||
<span className="text-xs text-red-600 dark:text-red-400 font-medium">
|
<span className="text-xs text-red-600 dark:text-red-400 font-medium">⚠️ Gecikmiş</span>
|
||||||
⚠️ Gecikmiş
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">
|
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">{task.title}</h4>
|
||||||
{task.title}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3 line-clamp-2">
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3 line-clamp-2">
|
||||||
{task.description}
|
{task.description}
|
||||||
|
|
@ -180,6 +167,7 @@ const TasksModule: React.FC = () => {
|
||||||
const [tasks, setTasks] = useState<Task[]>(mockTasks)
|
const [tasks, setTasks] = useState<Task[]>(mockTasks)
|
||||||
const [selectedTask, setSelectedTask] = useState<Task | null>(null)
|
const [selectedTask, setSelectedTask] = useState<Task | null>(null)
|
||||||
const [activeId, setActiveId] = useState<string | null>(null)
|
const [activeId, setActiveId] = useState<string | null>(null)
|
||||||
|
const [dragOverColumn, setDragOverColumn] = useState<TaskStatus | null>(null)
|
||||||
const [showNewTaskModal, setShowNewTaskModal] = useState(false)
|
const [showNewTaskModal, setShowNewTaskModal] = useState(false)
|
||||||
const [newTaskColumn, setNewTaskColumn] = useState<TaskStatus>('todo')
|
const [newTaskColumn, setNewTaskColumn] = useState<TaskStatus>('todo')
|
||||||
|
|
||||||
|
|
@ -188,23 +176,45 @@ const TasksModule: React.FC = () => {
|
||||||
activationConstraint: {
|
activationConstraint: {
|
||||||
distance: 8,
|
distance: 8,
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const columns: { id: TaskStatus; title: string; icon: string; color: string }[] = [
|
const columns: { id: TaskStatus; title: string; icon: string; color: string }[] = [
|
||||||
{ id: 'todo', title: 'Yapılacak', icon: '📋', color: 'gray' },
|
{ id: 'todo', title: 'Yapılacak', icon: '📋', color: 'gray' },
|
||||||
{ id: 'in-progress', title: 'Devam Ediyor', icon: '⚙️', color: 'blue' },
|
{ id: 'in-progress', title: 'Devam Ediyor', icon: '⚙️', color: 'blue' },
|
||||||
{ id: 'review', title: 'İncelemede', icon: '👀', color: 'yellow' },
|
{ id: 'review', title: 'İncelemede', icon: '👀', color: 'yellow' },
|
||||||
{ id: 'done', title: 'Tamamlandı', icon: '✅', color: 'green' }
|
{ id: 'done', title: 'Tamamlandı', icon: '✅', color: 'green' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const handleDragStart = (event: DragStartEvent) => {
|
const handleDragStart = (event: DragStartEvent) => {
|
||||||
setActiveId(event.active.id as string)
|
setActiveId(event.active.id as string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDragOver = (event: any) => {
|
||||||
|
const { over } = event
|
||||||
|
|
||||||
|
if (!over) {
|
||||||
|
setDragOverColumn(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if over a column or a task
|
||||||
|
const overColumn = columns.find((col) => col.id === over.id)
|
||||||
|
if (overColumn) {
|
||||||
|
setDragOverColumn(overColumn.id)
|
||||||
|
} else {
|
||||||
|
// over.id is a task, find its column
|
||||||
|
const overTask = tasks.find((t) => t.id === over.id)
|
||||||
|
if (overTask) {
|
||||||
|
setDragOverColumn(overTask.status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleDragEnd = (event: DragEndEvent) => {
|
const handleDragEnd = (event: DragEndEvent) => {
|
||||||
const { active, over } = event
|
const { active, over } = event
|
||||||
setActiveId(null)
|
setActiveId(null)
|
||||||
|
setDragOverColumn(null)
|
||||||
|
|
||||||
if (!over) return
|
if (!over) return
|
||||||
|
|
||||||
|
|
@ -215,37 +225,33 @@ const TasksModule: React.FC = () => {
|
||||||
// If it's a task id, find that task's column
|
// If it's a task id, find that task's column
|
||||||
let newStatus: TaskStatus
|
let newStatus: TaskStatus
|
||||||
|
|
||||||
const overColumn = columns.find(col => col.id === over.id)
|
const overColumn = columns.find((col) => col.id === over.id)
|
||||||
if (overColumn) {
|
if (overColumn) {
|
||||||
newStatus = overColumn.id
|
newStatus = overColumn.id
|
||||||
} else {
|
} else {
|
||||||
// over.id is a task, find its column
|
// over.id is a task, find its column
|
||||||
const overTask = tasks.find(t => t.id === over.id)
|
const overTask = tasks.find((t) => t.id === over.id)
|
||||||
if (!overTask) return
|
if (!overTask) return
|
||||||
newStatus = overTask.status
|
newStatus = overTask.status
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update task status
|
// Update task status
|
||||||
setTasks(prevTasks =>
|
setTasks((prevTasks) =>
|
||||||
prevTasks.map(task =>
|
prevTasks.map((task) => (task.id === taskId ? { ...task, status: newStatus } : task)),
|
||||||
task.id === taskId ? { ...task, status: newStatus } : task
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (selectedTask?.id === taskId) {
|
if (selectedTask?.id === taskId) {
|
||||||
setSelectedTask(prev => prev ? { ...prev, status: newStatus } : null)
|
setSelectedTask((prev) => (prev ? { ...prev, status: newStatus } : null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleStatusChange = (taskId: string, newStatus: TaskStatus) => {
|
const handleStatusChange = (taskId: string, newStatus: TaskStatus) => {
|
||||||
setTasks(prevTasks =>
|
setTasks((prevTasks) =>
|
||||||
prevTasks.map(task =>
|
prevTasks.map((task) => (task.id === taskId ? { ...task, status: newStatus } : task)),
|
||||||
task.id === taskId ? { ...task, status: newStatus } : task
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (selectedTask?.id === taskId) {
|
if (selectedTask?.id === taskId) {
|
||||||
setSelectedTask(prev => prev ? { ...prev, status: newStatus } : null)
|
setSelectedTask((prev) => (prev ? { ...prev, status: newStatus } : null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,16 +273,16 @@ const TasksModule: React.FC = () => {
|
||||||
dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days from now
|
dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days from now
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
labels: [],
|
labels: [],
|
||||||
comments: 0
|
comments: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
setTasks(prev => [...prev, newTask])
|
setTasks((prev) => [...prev, newTask])
|
||||||
setShowNewTaskModal(false)
|
setShowNewTaskModal(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteTask = (taskId: string) => {
|
const handleDeleteTask = (taskId: string) => {
|
||||||
if (window.confirm('Bu görevi silmek istediğinizden emin misiniz?')) {
|
if (window.confirm('Bu görevi silmek istediğinizden emin misiniz?')) {
|
||||||
setTasks(prevTasks => prevTasks.filter(task => task.id !== taskId))
|
setTasks((prevTasks) => prevTasks.filter((task) => task.id !== taskId))
|
||||||
setSelectedTask(null)
|
setSelectedTask(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -288,9 +294,11 @@ const TasksModule: React.FC = () => {
|
||||||
const getPriorityColor = (priority: string) => {
|
const getPriorityColor = (priority: string) => {
|
||||||
const colors = {
|
const colors = {
|
||||||
low: 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border-gray-200 dark:border-gray-600',
|
low: 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border-gray-200 dark:border-gray-600',
|
||||||
medium: 'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-300 border-blue-200 dark:border-blue-700',
|
medium:
|
||||||
|
'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-300 border-blue-200 dark:border-blue-700',
|
||||||
high: 'bg-orange-100 dark:bg-orange-900/30 text-orange-600 dark:text-orange-300 border-orange-200 dark:border-orange-700',
|
high: 'bg-orange-100 dark:bg-orange-900/30 text-orange-600 dark:text-orange-300 border-orange-200 dark:border-orange-700',
|
||||||
urgent: 'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-300 border-red-200 dark:border-red-700'
|
urgent:
|
||||||
|
'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-300 border-red-200 dark:border-red-700',
|
||||||
}
|
}
|
||||||
return colors[priority as keyof typeof colors]
|
return colors[priority as keyof typeof colors]
|
||||||
}
|
}
|
||||||
|
|
@ -300,7 +308,7 @@ const TasksModule: React.FC = () => {
|
||||||
low: 'Düşük',
|
low: 'Düşük',
|
||||||
medium: 'Orta',
|
medium: 'Orta',
|
||||||
high: 'Yüksek',
|
high: 'Yüksek',
|
||||||
urgent: '🔥 Acil'
|
urgent: '🔥 Acil',
|
||||||
}
|
}
|
||||||
return labels[priority as keyof typeof labels]
|
return labels[priority as keyof typeof labels]
|
||||||
}
|
}
|
||||||
|
|
@ -314,6 +322,7 @@ const TasksModule: React.FC = () => {
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
collisionDetection={closestCorners}
|
collisionDetection={closestCorners}
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
>
|
>
|
||||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 p-3 sm:p-4 md:p-6">
|
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 p-3 sm:p-4 md:p-6">
|
||||||
|
|
@ -343,15 +352,14 @@ const TasksModule: React.FC = () => {
|
||||||
|
|
||||||
{/* Kanban Board */}
|
{/* Kanban Board */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4 overflow-x-auto pb-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4 overflow-x-auto pb-4">
|
||||||
|
|
||||||
<div className="kanban-container sm:contents">
|
<div className="kanban-container sm:contents">
|
||||||
{columns.map(column => {
|
{columns.map((column) => {
|
||||||
const columnTasks = getTasksByStatus(column.id)
|
const columnTasks = getTasksByStatus(column.id)
|
||||||
return (
|
return (
|
||||||
<DroppableColumn key={column.id} id={column.id}>
|
<DroppableColumn key={column.id} id={column.id}>
|
||||||
<SortableContext
|
<SortableContext
|
||||||
id={column.id}
|
id={column.id}
|
||||||
items={columnTasks.map(t => t.id)}
|
items={columnTasks.map((t) => t.id)}
|
||||||
strategy={verticalListSortingStrategy}
|
strategy={verticalListSortingStrategy}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -362,7 +370,13 @@ const TasksModule: React.FC = () => {
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-lg sm:text-xl">{column.icon}</span>
|
<span className="text-lg sm:text-xl">{column.icon}</span>
|
||||||
<h3 className="font-semibold text-gray-900 dark:text-white text-sm sm:text-base">
|
<h3
|
||||||
|
className={`font-semibold text-sm sm:text-base transition-colors duration-200 ${
|
||||||
|
dragOverColumn === column.id
|
||||||
|
? 'text-blue-600 dark:text-blue-400'
|
||||||
|
: 'text-gray-900 dark:text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{column.title}
|
{column.title}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -371,7 +385,7 @@ const TasksModule: React.FC = () => {
|
||||||
|
|
||||||
{/* Tasks */}
|
{/* Tasks */}
|
||||||
<div className="space-y-3 flex-1">
|
<div className="space-y-3 flex-1">
|
||||||
{columnTasks.map(task => (
|
{columnTasks.map((task) => (
|
||||||
<SortableTaskCard
|
<SortableTaskCard
|
||||||
key={task.id}
|
key={task.id}
|
||||||
task={task}
|
task={task}
|
||||||
|
|
@ -403,19 +417,18 @@ const TasksModule: React.FC = () => {
|
||||||
{activeId ? (
|
{activeId ? (
|
||||||
<div className="opacity-50">
|
<div className="opacity-50">
|
||||||
{(() => {
|
{(() => {
|
||||||
const task = tasks.find(t => t.id === activeId)
|
const task = tasks.find((t) => t.id === activeId)
|
||||||
if (!task) return null
|
if (!task) return null
|
||||||
return (
|
return (
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg p-4 border-2 border-blue-500 shadow-2xl">
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-4 border-2 border-blue-500 shadow-2xl">
|
||||||
<h4 className="font-semibold text-gray-900 dark:text-white">
|
<h4 className="font-semibold text-gray-900 dark:text-white">{task.title}</h4>
|
||||||
{task.title}
|
|
||||||
</h4>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* New Task Modal */}
|
{/* New Task Modal */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|
@ -488,7 +501,9 @@ const TasksModule: React.FC = () => {
|
||||||
|
|
||||||
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3">
|
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3">
|
||||||
<p className="text-xs sm:text-sm text-blue-700 dark:text-blue-300">
|
<p className="text-xs sm:text-sm text-blue-700 dark:text-blue-300">
|
||||||
📋 Görev <strong>{columns.find(c => c.id === newTaskColumn)?.title}</strong> kolonuna eklenecek
|
📋 Görev{' '}
|
||||||
|
<strong>{columns.find((c) => c.id === newTaskColumn)?.title}</strong>{' '}
|
||||||
|
kolonuna eklenecek
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -534,7 +549,9 @@ const TasksModule: React.FC = () => {
|
||||||
>
|
>
|
||||||
<div className="p-4 sm:p-6 border-b border-gray-200 dark:border-gray-700 flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
<div className="p-4 sm:p-6 border-b border-gray-200 dark:border-gray-700 flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
||||||
<div className="flex flex-wrap items-center gap-2 sm:gap-3">
|
<div className="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||||
<span className={`px-2 sm:px-3 py-1 sm:py-1.5 text-xs sm:text-sm font-medium rounded border ${getPriorityColor(selectedTask.priority)}`}>
|
<span
|
||||||
|
className={`px-2 sm:px-3 py-1 sm:py-1.5 text-xs sm:text-sm font-medium rounded border ${getPriorityColor(selectedTask.priority)}`}
|
||||||
|
>
|
||||||
{getPriorityLabel(selectedTask.priority)}
|
{getPriorityLabel(selectedTask.priority)}
|
||||||
</span>
|
</span>
|
||||||
<span className="px-2 sm:px-3 py-1 sm:py-1.5 bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 text-xs sm:text-sm font-medium rounded">
|
<span className="px-2 sm:px-3 py-1 sm:py-1.5 bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 text-xs sm:text-sm font-medium rounded">
|
||||||
|
|
@ -565,7 +582,9 @@ const TasksModule: React.FC = () => {
|
||||||
</p>
|
</p>
|
||||||
<select
|
<select
|
||||||
value={selectedTask.status}
|
value={selectedTask.status}
|
||||||
onChange={(e) => handleStatusChange(selectedTask.id, e.target.value as TaskStatus)}
|
onChange={(e) =>
|
||||||
|
handleStatusChange(selectedTask.id, e.target.value as TaskStatus)
|
||||||
|
}
|
||||||
className="w-full px-3 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 cursor-pointer"
|
className="w-full px-3 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 cursor-pointer"
|
||||||
>
|
>
|
||||||
<option value="todo">📋 Yapılacak</option>
|
<option value="todo">📋 Yapılacak</option>
|
||||||
|
|
@ -630,7 +649,8 @@ const TasksModule: React.FC = () => {
|
||||||
|
|
||||||
<div className="pt-6 border-t border-gray-200 dark:border-gray-700 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
|
<div className="pt-6 border-t border-gray-200 dark:border-gray-700 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||||
Oluşturan: {selectedTask.assignedBy.fullName} • {dayjs(selectedTask.createdAt).format('DD MMMM YYYY')}
|
Oluşturan: {selectedTask.assignedBy.fullName} •{' '}
|
||||||
|
{dayjs(selectedTask.createdAt).format('DD MMMM YYYY')}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDeleteTask(selectedTask.id)}
|
onClick={() => handleDeleteTask(selectedTask.id)}
|
||||||
|
|
@ -647,7 +667,6 @@ const TasksModule: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</DndContext>
|
</DndContext>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,31 +7,37 @@ import {
|
||||||
HiMapPin,
|
HiMapPin,
|
||||||
HiXMark,
|
HiXMark,
|
||||||
HiCheckBadge,
|
HiCheckBadge,
|
||||||
HiCalendar
|
HiCalendar,
|
||||||
} from 'react-icons/hi2'
|
} from 'react-icons/hi2'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { mockTrainings, mockCertificates, Training } from '../../../mocks/mockIntranetData'
|
import { mockTrainings, mockCertificates, Training } from '../../../mocks/mockIntranetData'
|
||||||
|
|
||||||
const TrainingModule: React.FC = () => {
|
const TrainingModule: React.FC = () => {
|
||||||
const [selectedTraining, setSelectedTraining] = useState<Training | null>(null)
|
const [selectedTraining, setSelectedTraining] = useState<Training | null>(null)
|
||||||
const [selectedTab, setSelectedTab] = useState<'all' | 'upcoming' | 'ongoing' | 'completed'>('all')
|
const [selectedTab, setSelectedTab] = useState<'all' | 'upcoming' | 'ongoing' | 'completed'>(
|
||||||
|
'all',
|
||||||
|
)
|
||||||
const [trainings, setTrainings] = useState<Training[]>(mockTrainings)
|
const [trainings, setTrainings] = useState<Training[]>(mockTrainings)
|
||||||
const [showEnrollSuccess, setShowEnrollSuccess] = useState(false)
|
const [showEnrollSuccess, setShowEnrollSuccess] = useState(false)
|
||||||
|
|
||||||
const filteredTrainings = selectedTab === 'all'
|
const filteredTrainings =
|
||||||
? trainings
|
selectedTab === 'all' ? trainings : trainings.filter((t) => t.status === selectedTab)
|
||||||
: trainings.filter(t => t.status === selectedTab)
|
|
||||||
|
|
||||||
const handleEnroll = (trainingId: string) => {
|
const handleEnroll = (trainingId: string) => {
|
||||||
setTrainings(prev => prev.map(t =>
|
setTrainings((prev) =>
|
||||||
|
prev.map((t) =>
|
||||||
t.id === trainingId && t.enrolled < t.maxParticipants
|
t.id === trainingId && t.enrolled < t.maxParticipants
|
||||||
? { ...t, enrolled: t.enrolled + 1 }
|
? { ...t, enrolled: t.enrolled + 1 }
|
||||||
: t
|
: t,
|
||||||
))
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Seçili eğitimi de güncelle
|
// Seçili eğitimi de güncelle
|
||||||
if (selectedTraining?.id === trainingId && selectedTraining.enrolled < selectedTraining.maxParticipants) {
|
if (
|
||||||
setSelectedTraining(prev => prev ? { ...prev, enrolled: prev.enrolled + 1 } : null)
|
selectedTraining?.id === trainingId &&
|
||||||
|
selectedTraining.enrolled < selectedTraining.maxParticipants
|
||||||
|
) {
|
||||||
|
setSelectedTraining((prev) => (prev ? { ...prev, enrolled: prev.enrolled + 1 } : null))
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowEnrollSuccess(true)
|
setShowEnrollSuccess(true)
|
||||||
|
|
@ -44,7 +50,7 @@ const TrainingModule: React.FC = () => {
|
||||||
'soft-skills': 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300',
|
'soft-skills': 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300',
|
||||||
management: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
management: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||||
compliance: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
compliance: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
||||||
other: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
|
other: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300',
|
||||||
}
|
}
|
||||||
return colors[category] || colors.other
|
return colors[category] || colors.other
|
||||||
}
|
}
|
||||||
|
|
@ -52,8 +58,11 @@ const TrainingModule: React.FC = () => {
|
||||||
const getStatusBadge = (status: string) => {
|
const getStatusBadge = (status: string) => {
|
||||||
const badges: Record<string, { bg: string; text: string }> = {
|
const badges: Record<string, { bg: string; text: string }> = {
|
||||||
upcoming: { bg: 'bg-blue-100 dark:bg-blue-900/30', text: 'text-blue-700 dark:text-blue-300' },
|
upcoming: { bg: 'bg-blue-100 dark:bg-blue-900/30', text: 'text-blue-700 dark:text-blue-300' },
|
||||||
ongoing: { bg: 'bg-green-100 dark:bg-green-900/30', text: 'text-green-700 dark:text-green-300' },
|
ongoing: {
|
||||||
completed: { bg: 'bg-gray-100 dark:bg-gray-700', text: 'text-gray-700 dark:text-gray-300' }
|
bg: 'bg-green-100 dark:bg-green-900/30',
|
||||||
|
text: 'text-green-700 dark:text-green-300',
|
||||||
|
},
|
||||||
|
completed: { bg: 'bg-gray-100 dark:bg-gray-700', text: 'text-gray-700 dark:text-gray-300' },
|
||||||
}
|
}
|
||||||
return badges[status] || badges.upcoming
|
return badges[status] || badges.upcoming
|
||||||
}
|
}
|
||||||
|
|
@ -77,9 +86,21 @@ const TrainingModule: React.FC = () => {
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||||
{[
|
{[
|
||||||
{ label: 'Tümü', value: mockTrainings.length, tab: 'all' as const },
|
{ label: 'Tümü', value: mockTrainings.length, tab: 'all' as const },
|
||||||
{ label: 'Yaklaşan', value: mockTrainings.filter(t => t.status === 'upcoming').length, tab: 'upcoming' as const },
|
{
|
||||||
{ label: 'Devam Eden', value: mockTrainings.filter(t => t.status === 'ongoing').length, tab: 'ongoing' as const },
|
label: 'Yaklaşan',
|
||||||
{ label: 'Tamamlanan', value: mockTrainings.filter(t => t.status === 'completed').length, tab: 'completed' as const }
|
value: mockTrainings.filter((t) => t.status === 'upcoming').length,
|
||||||
|
tab: 'upcoming' as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Devam Eden',
|
||||||
|
value: mockTrainings.filter((t) => t.status === 'ongoing').length,
|
||||||
|
tab: 'ongoing' as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tamamlanan',
|
||||||
|
value: mockTrainings.filter((t) => t.status === 'completed').length,
|
||||||
|
tab: 'completed' as const,
|
||||||
|
},
|
||||||
].map((stat, idx) => (
|
].map((stat, idx) => (
|
||||||
<motion.button
|
<motion.button
|
||||||
key={idx}
|
key={idx}
|
||||||
|
|
@ -122,11 +143,19 @@ const TrainingModule: React.FC = () => {
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<div className="flex items-start justify-between mb-3">
|
<div className="flex items-start justify-between mb-3">
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getCategoryColor(training.category)}`}>
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-medium ${getCategoryColor(training.category)}`}
|
||||||
|
>
|
||||||
{training.category}
|
{training.category}
|
||||||
</span>
|
</span>
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${badge.bg} ${badge.text}`}>
|
<span
|
||||||
{training.status === 'upcoming' ? 'Yaklaşan' : training.status === 'ongoing' ? 'Devam Ediyor' : 'Tamamlandı'}
|
className={`px-3 py-1 rounded-full text-xs font-medium ${badge.bg} ${badge.text}`}
|
||||||
|
>
|
||||||
|
{training.status === 'upcoming'
|
||||||
|
? 'Yaklaşan'
|
||||||
|
: training.status === 'ongoing'
|
||||||
|
? 'Devam Ediyor'
|
||||||
|
: 'Tamamlandı'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -153,11 +182,14 @@ const TrainingModule: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<HiUsers className="w-4 h-4" />
|
<HiUsers className="w-4 h-4" />
|
||||||
<span>{training.enrolled} / {training.maxParticipants} katılımcı</span>
|
<span>
|
||||||
|
{training.enrolled} / {training.maxParticipants} katılımcı
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{training.enrolled < training.maxParticipants && training.status === 'upcoming' && (
|
{training.enrolled < training.maxParticipants &&
|
||||||
|
training.status === 'upcoming' && (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleEnroll(training.id)}
|
onClick={() => handleEnroll(training.id)}
|
||||||
className="mt-4 w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
|
className="mt-4 w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
|
||||||
|
|
@ -217,7 +249,7 @@ const TrainingModule: React.FC = () => {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{/* Training Detail Modal */}
|
{/* Training Detail Modal */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{selectedTraining && (
|
{selectedTraining && (
|
||||||
|
|
@ -269,32 +301,43 @@ const TrainingModule: React.FC = () => {
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Eğitmen</p>
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Eğitmen</p>
|
||||||
<p className="font-medium text-gray-900 dark:text-white">{selectedTraining.instructor}</p>
|
<p className="font-medium text-gray-900 dark:text-white">
|
||||||
|
{selectedTraining.instructor}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Kategori</p>
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Kategori</p>
|
||||||
<span className={`inline-block px-3 py-1 rounded-full text-sm ${getCategoryColor(selectedTraining.category)}`}>
|
<span
|
||||||
|
className={`inline-block px-3 py-1 rounded-full text-sm ${getCategoryColor(selectedTraining.category)}`}
|
||||||
|
>
|
||||||
{selectedTraining.category}
|
{selectedTraining.category}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Tarih</p>
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Tarih</p>
|
||||||
<p className="font-medium text-gray-900 dark:text-white">
|
<p className="font-medium text-gray-900 dark:text-white">
|
||||||
{dayjs(selectedTraining.startDate).format('DD MMM')} - {dayjs(selectedTraining.endDate).format('DD MMM YYYY')}
|
{dayjs(selectedTraining.startDate).format('DD MMM')} -{' '}
|
||||||
|
{dayjs(selectedTraining.endDate).format('DD MMM YYYY')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Süre</p>
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Süre</p>
|
||||||
<p className="font-medium text-gray-900 dark:text-white">{selectedTraining.duration} saat</p>
|
<p className="font-medium text-gray-900 dark:text-white">
|
||||||
|
{selectedTraining.duration} saat
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Tip</p>
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Tip</p>
|
||||||
<p className="font-medium text-gray-900 dark:text-white capitalize">{selectedTraining.type}</p>
|
<p className="font-medium text-gray-900 dark:text-white capitalize">
|
||||||
|
{selectedTraining.type}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{selectedTraining.location && (
|
{selectedTraining.location && (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Konum</p>
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">Konum</p>
|
||||||
<p className="font-medium text-gray-900 dark:text-white">{selectedTraining.location}</p>
|
<p className="font-medium text-gray-900 dark:text-white">
|
||||||
|
{selectedTraining.location}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -305,7 +348,9 @@ const TrainingModule: React.FC = () => {
|
||||||
<div className="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-3">
|
<div className="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-3">
|
||||||
<div
|
<div
|
||||||
className="bg-blue-600 h-3 rounded-full transition-all"
|
className="bg-blue-600 h-3 rounded-full transition-all"
|
||||||
style={{ width: `${(selectedTraining.enrolled / selectedTraining.maxParticipants) * 100}%` }}
|
style={{
|
||||||
|
width: `${(selectedTraining.enrolled / selectedTraining.maxParticipants) * 100}%`,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-medium text-gray-900 dark:text-white">
|
<span className="text-sm font-medium text-gray-900 dark:text-white">
|
||||||
|
|
@ -314,7 +359,8 @@ const TrainingModule: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedTraining.enrolled < selectedTraining.maxParticipants && selectedTraining.status === 'upcoming' && (
|
{selectedTraining.enrolled < selectedTraining.maxParticipants &&
|
||||||
|
selectedTraining.status === 'upcoming' && (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleEnroll(selectedTraining.id)}
|
onClick={() => handleEnroll(selectedTraining.id)}
|
||||||
className="w-full px-4 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
|
className="w-full px-4 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
|
||||||
|
|
@ -347,7 +393,6 @@ const TrainingModule: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,36 +5,33 @@ import dayjs from 'dayjs'
|
||||||
import { mockVisitors, Visitor } from '../../../mocks/mockIntranetData'
|
import { mockVisitors, Visitor } from '../../../mocks/mockIntranetData'
|
||||||
|
|
||||||
const VisitorsModule: React.FC = () => {
|
const VisitorsModule: React.FC = () => {
|
||||||
const [selectedStatus, setSelectedStatus] = useState<'all' | 'scheduled' | 'checked-in' | 'checked-out' | 'cancelled'>('all')
|
const [selectedStatus, setSelectedStatus] = useState<
|
||||||
|
'all' | 'scheduled' | 'checked-in' | 'checked-out' | 'cancelled'
|
||||||
|
>('all')
|
||||||
const [showNewVisitor, setShowNewVisitor] = useState(false)
|
const [showNewVisitor, setShowNewVisitor] = useState(false)
|
||||||
const [visitors, setVisitors] = useState<Visitor[]>(mockVisitors)
|
const [visitors, setVisitors] = useState<Visitor[]>(mockVisitors)
|
||||||
|
|
||||||
const filteredVisitors = selectedStatus === 'all'
|
const filteredVisitors =
|
||||||
? visitors
|
selectedStatus === 'all' ? visitors : visitors.filter((v) => v.status === selectedStatus)
|
||||||
: visitors.filter(v => v.status === selectedStatus)
|
|
||||||
|
|
||||||
const handleCheckIn = (visitorId: string) => {
|
const handleCheckIn = (visitorId: string) => {
|
||||||
setVisitors(prev => prev.map(v =>
|
setVisitors((prev) =>
|
||||||
v.id === visitorId
|
prev.map((v) =>
|
||||||
? { ...v, status: 'checked-in', checkIn: new Date() }
|
v.id === visitorId ? { ...v, status: 'checked-in', checkIn: new Date() } : v,
|
||||||
: v
|
),
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCheckOut = (visitorId: string) => {
|
const handleCheckOut = (visitorId: string) => {
|
||||||
setVisitors(prev => prev.map(v =>
|
setVisitors((prev) =>
|
||||||
v.id === visitorId
|
prev.map((v) =>
|
||||||
? { ...v, status: 'checked-out', checkOut: new Date() }
|
v.id === visitorId ? { ...v, status: 'checked-out', checkOut: new Date() } : v,
|
||||||
: v
|
),
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCancel = (visitorId: string) => {
|
const handleCancel = (visitorId: string) => {
|
||||||
setVisitors(prev => prev.map(v =>
|
setVisitors((prev) => prev.map((v) => (v.id === visitorId ? { ...v, status: 'cancelled' } : v)))
|
||||||
v.id === visitorId
|
|
||||||
? { ...v, status: 'cancelled' }
|
|
||||||
: v
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
|
|
@ -42,7 +39,7 @@ const VisitorsModule: React.FC = () => {
|
||||||
scheduled: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300',
|
scheduled: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300',
|
||||||
'checked-in': 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
'checked-in': 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||||
'checked-out': 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300',
|
'checked-out': 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300',
|
||||||
cancelled: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300'
|
cancelled: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300',
|
||||||
}
|
}
|
||||||
return colors[status] || colors.scheduled
|
return colors[status] || colors.scheduled
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +49,7 @@ const VisitorsModule: React.FC = () => {
|
||||||
scheduled: '📅 Planlandı',
|
scheduled: '📅 Planlandı',
|
||||||
'checked-in': '✅ Giriş Yaptı',
|
'checked-in': '✅ Giriş Yaptı',
|
||||||
'checked-out': '🚪 Çıkış Yaptı',
|
'checked-out': '🚪 Çıkış Yaptı',
|
||||||
cancelled: '❌ İptal Edildi'
|
cancelled: '❌ İptal Edildi',
|
||||||
}
|
}
|
||||||
return labels[status] || status
|
return labels[status] || status
|
||||||
}
|
}
|
||||||
|
|
@ -83,9 +80,21 @@ const VisitorsModule: React.FC = () => {
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
{[
|
{[
|
||||||
{ label: 'Tümü', value: mockVisitors.length, status: 'all' as const },
|
{ label: 'Tümü', value: mockVisitors.length, status: 'all' as const },
|
||||||
{ label: 'Planlandı', value: mockVisitors.filter(v => v.status === 'scheduled').length, status: 'scheduled' as const },
|
{
|
||||||
{ label: 'İçeride', value: mockVisitors.filter(v => v.status === 'checked-in').length, status: 'checked-in' as const },
|
label: 'Planlandı',
|
||||||
{ label: 'Çıkış Yaptı', value: mockVisitors.filter(v => v.status === 'checked-out').length, status: 'checked-out' as const }
|
value: mockVisitors.filter((v) => v.status === 'scheduled').length,
|
||||||
|
status: 'scheduled' as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'İçeride',
|
||||||
|
value: mockVisitors.filter((v) => v.status === 'checked-in').length,
|
||||||
|
status: 'checked-in' as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Çıkış Yaptı',
|
||||||
|
value: mockVisitors.filter((v) => v.status === 'checked-out').length,
|
||||||
|
status: 'checked-out' as const,
|
||||||
|
},
|
||||||
].map((stat, idx) => (
|
].map((stat, idx) => (
|
||||||
<motion.button
|
<motion.button
|
||||||
key={idx}
|
key={idx}
|
||||||
|
|
@ -132,14 +141,18 @@ const VisitorsModule: React.FC = () => {
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">{visitor.company}</p>
|
<p className="text-sm text-gray-600 dark:text-gray-400">{visitor.company}</p>
|
||||||
</div>
|
</div>
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusColor(visitor.status)}`}>
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusColor(visitor.status)}`}
|
||||||
|
>
|
||||||
{getStatusLabel(visitor.status)}
|
{getStatusLabel(visitor.status)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-3">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-3">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400 mb-1">Ziyaret Tarihi</p>
|
<p className="text-xs text-gray-500 dark:text-gray-400 mb-1">
|
||||||
|
Ziyaret Tarihi
|
||||||
|
</p>
|
||||||
<div className="flex items-center gap-1 text-sm text-gray-900 dark:text-white">
|
<div className="flex items-center gap-1 text-sm text-gray-900 dark:text-white">
|
||||||
<HiClock className="w-4 h-4" />
|
<HiClock className="w-4 h-4" />
|
||||||
{dayjs(visitor.visitDate).format('DD MMM, HH:mm')}
|
{dayjs(visitor.visitDate).format('DD MMM, HH:mm')}
|
||||||
|
|
@ -202,7 +215,7 @@ const VisitorsModule: React.FC = () => {
|
||||||
{visitor.host.fullName}
|
{visitor.host.fullName}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-600 dark:text-gray-400">
|
<p className="text-xs text-gray-600 dark:text-gray-400">
|
||||||
{visitor.host.department}
|
{visitor.host.department?.name}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -245,6 +258,7 @@ const VisitorsModule: React.FC = () => {
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* New Visitor Modal */}
|
{/* New Visitor Modal */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|
@ -373,7 +387,6 @@ const VisitorsModule: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -606,25 +606,40 @@ export const mockDocuments: Document[] = [
|
||||||
export const mockBirthdays: Birthday[] = [
|
export const mockBirthdays: Birthday[] = [
|
||||||
{
|
{
|
||||||
employee: mockEmployees[0],
|
employee: mockEmployees[0],
|
||||||
date: new Date('2024-10-20'),
|
date: new Date('1990-10-19'), // Bugün
|
||||||
age: 34,
|
age: 35,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
employee: mockEmployees[1],
|
employee: mockEmployees[1],
|
||||||
date: new Date('2024-10-25'),
|
date: new Date('1992-10-21'), // Bu hafta içinde
|
||||||
age: 32,
|
age: 33,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
employee: mockEmployees[2],
|
||||||
|
date: new Date('1988-10-23'), // Bu hafta içinde
|
||||||
|
age: 37,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
employee: mockEmployees[3],
|
||||||
|
date: new Date('1995-10-28'), // Bu ay içinde
|
||||||
|
age: 30,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const mockAnniversaries: WorkAnniversary[] = [
|
export const mockAnniversaries: WorkAnniversary[] = [
|
||||||
{
|
{
|
||||||
employee: mockEmployees[2],
|
employee: mockEmployees[2],
|
||||||
hireDate: new Date('2019-09-10'),
|
hireDate: new Date('2019-10-10'), // Bu ay (Ekim)
|
||||||
|
years: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
employee: mockEmployees[4],
|
||||||
|
hireDate: new Date('2020-10-15'), // Bu ay (Ekim)
|
||||||
years: 5,
|
years: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
employee: mockEmployees[0],
|
employee: mockEmployees[0],
|
||||||
hireDate: new Date('2020-03-15'),
|
hireDate: new Date('2021-10-20'), // Bu ay (Ekim)
|
||||||
years: 4,
|
years: 4,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue