İntranet modülündeki Dialoglar düzeltildi.

This commit is contained in:
Sedat Öztürk 2025-10-19 10:39:10 +03:00
parent ddaaa56ea0
commit e8093784e0
10 changed files with 1371 additions and 1087 deletions

View file

@ -8,12 +8,13 @@ import {
HiCalendar,
HiXMark,
HiChevronLeft,
HiChevronRight
HiChevronRight,
} from 'react-icons/hi2'
import dayjs from 'dayjs'
import 'dayjs/locale/tr'
import relativeTime from 'dayjs/plugin/relativeTime'
import { mockEvents, CalendarEvent, EventComment } from '../../../mocks/mockIntranetData'
import { mockEmployees } from '@/mocks/mockEmployees'
dayjs.locale('tr')
dayjs.extend(relativeTime)
@ -22,20 +23,21 @@ const EventsModule: React.FC = () => {
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null)
const [selectedPhotoIndex, setSelectedPhotoIndex] = useState(0)
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 [events, setEvents] = useState<CalendarEvent[]>(mockEvents)
const filteredEvents = selectedFilter === 'all'
? events.filter(e => e.isPublished)
: events.filter(e => e.isPublished && e.type === selectedFilter)
const filteredEvents =
selectedFilter === 'all'
? events.filter((e) => e.isPublished)
: events.filter((e) => e.isPublished && e.type === selectedFilter)
const handleLikeEvent = (eventId: string) => {
setEvents(prev => prev.map(e =>
e.id === eventId ? { ...e, likes: e.likes + 1 } : e
))
setEvents((prev) => prev.map((e) => (e.id === eventId ? { ...e, likes: e.likes + 1 } : e)))
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 = {
id: `c${Date.now()}`,
author: {
id: 'current-user',
fullName: 'Sedat Öztürk',
avatar: 'https://ui-avatars.com/api/?name=Sedat+Ozturk&background=3b82f6&color=fff'
},
author: mockEmployees[0],
content: newComment,
createdAt: new Date(),
likes: 0
likes: 0,
}
setEvents(prev => prev.map(e =>
e.id === eventId ? { ...e, comments: [...e.comments, comment] } : e
))
setEvents((prev) =>
prev.map((e) => (e.id === eventId ? { ...e, comments: [...e.comments, comment] } : e)),
)
if (selectedEvent?.id === eventId) {
setSelectedEvent(prev => prev ? { ...prev, comments: [...prev.comments, comment] } : null)
setSelectedEvent((prev) => (prev ? { ...prev, comments: [...prev.comments, comment] } : null))
}
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',
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',
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
}
@ -82,7 +80,7 @@ const EventsModule: React.FC = () => {
training: '📚 Eğitim',
company: '🏢 Kurumsal',
sport: '⚽ Spor',
culture: '🎨 Kültür'
culture: '🎨 Kültür',
}
return labels[type] || type
}
@ -92,9 +90,7 @@ const EventsModule: React.FC = () => {
<div className="max-w-7xl mx-auto space-y-6">
{/* Header */}
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
🎊 Etkinlikler
</h1>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">🎊 Etkinlikler</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Şirket etkinlikleri, fotoğraflar ve anılar
</p>
@ -108,7 +104,7 @@ const EventsModule: React.FC = () => {
{ value: 'training' as const, label: '📚 Eğitim' },
{ value: 'company' as const, label: '🏢 Kurumsal' },
{ value: 'sport' as const, label: '⚽ Spor' },
{ value: 'culture' as const, label: '🎨 Kültür' }
{ value: 'culture' as const, label: '🎨 Kültür' },
].map((tab) => (
<button
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"
/>
<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)}
</span>
</div>
@ -207,6 +205,7 @@ const EventsModule: React.FC = () => {
<p className="text-lg">Bu kategoride henüz etkinlik yok</p>
</div>
)}
</div>
{/* Event Detail Modal */}
<AnimatePresence>
@ -232,7 +231,9 @@ const EventsModule: React.FC = () => {
<div className="flex items-start justify-between">
<div className="flex-1">
<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)}
</span>
<span className="text-sm text-gray-600 dark:text-gray-400">
@ -418,17 +419,21 @@ const EventsModule: React.FC = () => {
{selectedEvent.photos.length > 1 && (
<>
<button
onClick={() => setSelectedPhotoIndex((prev) =>
prev === 0 ? selectedEvent.photos.length - 1 : prev - 1
)}
onClick={() =>
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"
>
<HiChevronLeft className="w-6 h-6" />
</button>
<button
onClick={() => setSelectedPhotoIndex((prev) =>
prev === selectedEvent.photos.length - 1 ? 0 : prev + 1
)}
onClick={() =>
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"
>
<HiChevronRight className="w-6 h-6" />
@ -453,7 +458,6 @@ const EventsModule: React.FC = () => {
)}
</AnimatePresence>
</div>
</div>
)
}

View file

@ -12,7 +12,7 @@ import {
HiArrowTrendingDown as ArrowTrendingDownIcon,
HiXMark,
HiEye,
HiPaperClip
HiPaperClip,
} from 'react-icons/hi2'
import dayjs from 'dayjs'
import 'dayjs/locale/tr'
@ -25,7 +25,8 @@ import {
mockAnniversaries,
mockQuickLinks,
mockTasks,
Announcement
mockDocuments,
Announcement,
} from '../../../mocks/mockIntranetData'
dayjs.locale('tr')
@ -37,36 +38,49 @@ const IntranetDashboard: React.FC = () => {
const [selectedAnnouncement, setSelectedAnnouncement] = useState<Announcement | null>(null)
// Bugünün etkinlikleri
const todayEvents = mockEvents.filter(event =>
event.isPublished && dayjs(event.date).isSame(dayjs(), 'day')
const todayEvents = mockEvents.filter(
(event) => event.isPublished && dayjs(event.date).isSame(dayjs(), 'day'),
)
// Yaklaşan etkinlikler (7 gün içinde)
const upcomingEvents = mockEvents.filter(event =>
const upcomingEvents = mockEvents.filter(
(event) =>
event.isPublished &&
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
const weekBirthdays = mockBirthdays.filter(b =>
dayjs(b.date).isBetween(dayjs().startOf('week'), dayjs().endOf('week'))
)
// Bugün doğanlar (sadece ay ve günü karşılaştır)
const todayBirthdays = mockBirthdays.filter((b) => {
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
const monthAnniversaries = mockAnniversaries.filter(a =>
dayjs(a.hireDate).month() === dayjs().month()
const monthAnniversaries = mockAnniversaries.filter(
(a) => dayjs(a.hireDate).month() === dayjs().month(),
)
// Öncelikli görevler
const priorityTasks = mockTasks.filter(t =>
t.priority === 'high' || t.priority === 'urgent'
).slice(0, 3)
const priorityTasks = mockTasks
.filter((t) => t.priority === 'high' || t.priority === 'urgent')
.slice(0, 3)
// Sabitlenmiş duyurular
const pinnedAnnouncements = mockAnnouncements
.filter(a => a.isPinned)
.slice(0, 3)
const pinnedAnnouncements = mockAnnouncements.filter((a) => a.isPinned).slice(0, 3)
const getCategoryColor = (category: 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',
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',
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
}
@ -84,7 +98,7 @@ const IntranetDashboard: React.FC = () => {
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',
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]
}
@ -142,7 +156,9 @@ const IntranetDashboard: React.FC = () => {
<h3 className="text-base font-semibold text-gray-900 dark:text-white">
{announcement.title}
</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 === 'hr' && 'İK'}
{announcement.category === 'it' && 'IT'}
@ -214,7 +230,9 @@ const IntranetDashboard: React.FC = () => {
<h3 className="text-sm font-medium text-gray-900 dark:text-white">
{task.title}
</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 === 'high' && 'Yüksek'}
{task.priority === 'medium' && 'Orta'}
@ -278,6 +296,46 @@ const IntranetDashboard: React.FC = () => {
</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 */}
<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">
@ -287,7 +345,8 @@ const IntranetDashboard: React.FC = () => {
</h2>
</div>
<div className="p-4 space-y-3">
{upcomingEvents.slice(0, 3).map((event) => (
{upcomingEvents.length > 0 ? (
upcomingEvents.slice(0, 3).map((event) => (
<div
key={event.id}
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}
</p>
</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>
@ -364,6 +475,7 @@ const IntranetDashboard: React.FC = () => {
)}
</div>
</div>
</div>
{/* Announcement Detail Modal */}
<AnimatePresence>
@ -381,7 +493,7 @@ const IntranetDashboard: React.FC = () => {
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
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()}
>
{/* Header */}
@ -389,7 +501,9 @@ const IntranetDashboard: React.FC = () => {
<div className="flex items-start justify-between">
<div className="flex-1">
<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 === 'hr' && '👥 İnsan Kaynakları'}
{selectedAnnouncement.category === 'it' && '💻 Bilgi Teknolojileri'}
@ -424,7 +538,9 @@ const IntranetDashboard: React.FC = () => {
{selectedAnnouncement.author.fullName}
</p>
<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 className="flex items-center gap-1">
<HiEye className="w-4 h-4" />
@ -454,7 +570,8 @@ const IntranetDashboard: React.FC = () => {
</div>
{/* 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">
<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" />
@ -488,7 +605,8 @@ const IntranetDashboard: React.FC = () => {
)}
{/* 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">
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-3">
Hedef Departmanlar
@ -532,7 +650,6 @@ const IntranetDashboard: React.FC = () => {
)}
</AnimatePresence>
</div>
</div>
)
}

View file

@ -13,6 +13,9 @@ import {
HiBuildingOffice2,
HiClipboardDocumentCheck,
HiUserPlus,
HiBars3,
HiXMark,
HiChevronLeft,
} from 'react-icons/hi2'
import {
mockTasks,
@ -127,6 +130,8 @@ interface IntranetSidebarProps {
const IntranetSidebar: React.FC<IntranetSidebarProps> = ({ activePath, onNavigate }) => {
const [expandedMenus, setExpandedMenus] = useState<string[]>(['hr'])
const [isCollapsed, setIsCollapsed] = useState(false)
const [isMobileOpen, setIsMobileOpen] = useState(false)
// Dinamik badge sayılarını hesapla
const badgeCounts = useMemo(() => {
@ -180,6 +185,7 @@ const IntranetSidebar: React.FC<IntranetSidebarProps> = ({ activePath, onNavigat
toggleMenu(item.id)
} else if (item.path) {
onNavigate(item.path)
setIsMobileOpen(false)
}
}}
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'
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800'
} ${level > 0 ? 'ml-6' : ''}`}
title={isCollapsed ? item.label : undefined}
>
<div className="flex items-center gap-3">
<item.icon className="w-5 h-5" />
<span className="font-medium text-sm">{item.label}</span>
<div className="flex items-center gap-3 min-w-0">
<item.icon className="w-5 h-5 flex-shrink-0" />
{!isCollapsed && <span className="font-medium text-sm truncate">{item.label}</span>}
</div>
<div className="flex items-center gap-2">
{!isCollapsed && (
<div className="flex items-center gap-2 flex-shrink-0">
{item.badge && item.badge > 0 && (
<span className="px-2 py-0.5 bg-red-500 text-white text-xs rounded-full">
{item.badge}
</span>
)}
{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" />
</motion.div>
)}
</div>
)}
</button>
{hasChildren && (
<AnimatePresence>
{isExpanded && (
{isExpanded && !isCollapsed && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
@ -228,13 +240,74 @@ const IntranetSidebar: React.FC<IntranetSidebarProps> = ({ activePath, onNavigat
}
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">
<h2 className="text-xl font-bold text-gray-900 dark:text-white">İntranet Portal</h2>
<>
{/* Mobile Toggle Button */}
<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>
<nav className="p-4 space-y-1">{menuItems.map((item) => renderMenuItem(item))}</nav>
</div>
</motion.div>
</>
)
}

View file

@ -1,13 +1,6 @@
import React, { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import {
HiKey,
HiCalendar,
HiTruck,
HiCog,
HiPlus,
HiXMark
} from 'react-icons/hi2'
import { HiKey, HiCalendar, HiTruck, HiCog, HiPlus, HiXMark } from 'react-icons/hi2'
import dayjs from 'dayjs'
import { mockReservations, Reservation } from '../../../mocks/mockIntranetData'
@ -15,9 +8,10 @@ const ReservationsModule: React.FC = () => {
const [selectedType, setSelectedType] = useState<'all' | 'room' | 'vehicle' | 'equipment'>('all')
const [showNewReservation, setShowNewReservation] = useState(false)
const filteredReservations = selectedType === 'all'
const filteredReservations =
selectedType === 'all'
? mockReservations
: mockReservations.filter(r => r.type === selectedType)
: mockReservations.filter((r) => r.type === selectedType)
const getTypeIcon = (type: string) => {
switch (type) {
@ -36,7 +30,7 @@ const ReservationsModule: React.FC = () => {
const labels: Record<string, string> = {
room: 'Toplantı Salonu',
vehicle: 'Araç',
equipment: 'Ekipman'
equipment: 'Ekipman',
}
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',
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',
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
}
@ -56,7 +50,7 @@ const ReservationsModule: React.FC = () => {
pending: 'Bekliyor',
approved: 'Onaylandı',
rejected: 'Reddedildi',
completed: 'Tamamlandı'
completed: 'Tamamlandı',
}
return labels[status] || status
}
@ -67,9 +61,7 @@ const ReservationsModule: React.FC = () => {
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
🔑 Rezervasyonlar
</h1>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">🔑 Rezervasyonlar</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Oda, araç ve ekipman rezervasyonları
</p>
@ -89,7 +81,7 @@ const ReservationsModule: React.FC = () => {
{ value: 'all' as const, label: 'Tümü', icon: HiCalendar },
{ value: 'room' as const, label: 'Toplantı Salonu', icon: HiKey },
{ 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) => (
<button
key={type.value}
@ -131,7 +123,9 @@ const ReservationsModule: React.FC = () => {
{getTypeLabel(reservation.type)}
</p>
</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)}
</span>
</div>
@ -197,6 +191,7 @@ const ReservationsModule: React.FC = () => {
</div>
)}
</div>
</div>
{/* New Reservation Modal */}
<AnimatePresence>
@ -312,7 +307,6 @@ const ReservationsModule: React.FC = () => {
)}
</AnimatePresence>
</div>
</div>
)
}

View file

@ -162,12 +162,6 @@ const CreatePost: React.FC<CreatePostProps> = ({ onCreatePost }) => {
value={content}
onChange={(e) => setContent(e.target.value)}
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?"
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',

View file

@ -9,9 +9,8 @@ const SurveysModule: React.FC = () => {
const [showSurveyModal, setShowSurveyModal] = useState(false)
const [selectedSurvey, setSelectedSurvey] = useState<Survey | null>(null)
const filteredSurveys = selectedStatus === 'all'
? mockSurveys
: mockSurveys.filter(s => s.status === selectedStatus)
const filteredSurveys =
selectedStatus === 'all' ? mockSurveys : mockSurveys.filter((s) => s.status === selectedStatus)
const handleTakeSurvey = (survey: Survey) => {
setSelectedSurvey(survey)
@ -29,7 +28,7 @@ const SurveysModule: React.FC = () => {
const colors: Record<string, string> = {
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',
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
}
@ -53,7 +52,7 @@ const SurveysModule: React.FC = () => {
{ value: 'all' as const, label: 'Tümü' },
{ value: 'active' as const, label: 'Aktif' },
{ value: 'draft' as const, label: 'Taslak' },
{ value: 'closed' as const, label: 'Kapalı' }
{ value: 'closed' as const, label: 'Kapalı' },
].map((tab) => (
<button
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">
{survey.title}
</h3>
<span 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
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>
</div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{survey.description}
</p>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">{survey.description}</p>
<div className="grid grid-cols-2 gap-4 mb-4">
<div>
<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>
<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>
<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>
</div>
)}
</div>
{/* Survey Modal */}
<AnimatePresence>
{showSurveyModal && selectedSurvey && (
@ -206,7 +213,10 @@ const SurveysModule: React.FC = () => {
</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">
<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>
@ -245,7 +255,8 @@ const SurveysModule: React.FC = () => {
{!selectedSurvey.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.
Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız
kaydedilecektir.
</p>
</div>
)}
@ -281,7 +292,6 @@ const SurveysModule: React.FC = () => {
)}
</AnimatePresence>
</div>
</div>
)
}

View file

@ -9,7 +9,7 @@ import {
useSensor,
useSensors,
closestCorners,
useDroppable
useDroppable,
} from '@dnd-kit/core'
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
import { useSortable } from '@dnd-kit/sortable'
@ -20,7 +20,7 @@ import {
HiClock,
HiChatBubbleLeftRight,
HiPaperClip,
HiTrash
HiTrash,
} from 'react-icons/hi2'
import dayjs from 'dayjs'
import 'dayjs/locale/tr'
@ -40,11 +40,7 @@ interface DroppableColumnProps {
const DroppableColumn: React.FC<DroppableColumnProps> = ({ id, children }) => {
const { setNodeRef } = useDroppable({ id })
return (
<div ref={setNodeRef} >
{children}
</div>
)
return <div ref={setNodeRef}>{children}</div>
}
// Sortable Task Card Component
@ -61,21 +57,16 @@ const SortableTaskCard: React.FC<SortableTaskCardProps> = ({
onTaskClick,
getPriorityColor,
getPriorityLabel,
isOverdue
isOverdue,
}) => {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging
} = useSortable({ id: task.id })
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: task.id,
})
const style = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1
opacity: isDragging ? 0.5 : 1,
}
const overdue = isOverdue(task.dueDate) && task.status !== 'done'
@ -87,9 +78,7 @@ const SortableTaskCard: React.FC<SortableTaskCardProps> = ({
{...attributes}
{...listeners}
className={`bg-white dark:bg-gray-800 rounded-lg p-3 sm:p-4 border-2 cursor-move hover:shadow-lg transition-all ${
overdue
? 'border-red-300 dark:border-red-700'
: 'border-gray-200 dark:border-gray-700'
overdue ? 'border-red-300 dark:border-red-700' : 'border-gray-200 dark:border-gray-700'
} ${isDragging ? 'shadow-2xl ring-4 ring-blue-500/50' : ''}`}
onClick={(e) => {
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">
<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)}
</span>
{overdue && (
<span className="text-xs text-red-600 dark:text-red-400 font-medium">
Gecikmiş
</span>
<span className="text-xs text-red-600 dark:text-red-400 font-medium"> Gecikmiş</span>
)}
</div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">
{task.title}
</h4>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">{task.title}</h4>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3 line-clamp-2">
{task.description}
@ -180,6 +167,7 @@ const TasksModule: React.FC = () => {
const [tasks, setTasks] = useState<Task[]>(mockTasks)
const [selectedTask, setSelectedTask] = useState<Task | null>(null)
const [activeId, setActiveId] = useState<string | null>(null)
const [dragOverColumn, setDragOverColumn] = useState<TaskStatus | null>(null)
const [showNewTaskModal, setShowNewTaskModal] = useState(false)
const [newTaskColumn, setNewTaskColumn] = useState<TaskStatus>('todo')
@ -188,23 +176,45 @@ const TasksModule: React.FC = () => {
activationConstraint: {
distance: 8,
},
})
}),
)
const columns: { id: TaskStatus; title: string; icon: string; color: string }[] = [
{ id: 'todo', title: 'Yapılacak', icon: '📋', color: 'gray' },
{ id: 'in-progress', title: 'Devam Ediyor', icon: '⚙️', color: 'blue' },
{ 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) => {
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 { active, over } = event
setActiveId(null)
setDragOverColumn(null)
if (!over) return
@ -215,37 +225,33 @@ const TasksModule: React.FC = () => {
// If it's a task id, find that task's column
let newStatus: TaskStatus
const overColumn = columns.find(col => col.id === over.id)
const overColumn = columns.find((col) => col.id === over.id)
if (overColumn) {
newStatus = overColumn.id
} else {
// 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
newStatus = overTask.status
}
// Update task status
setTasks(prevTasks =>
prevTasks.map(task =>
task.id === taskId ? { ...task, status: newStatus } : task
)
setTasks((prevTasks) =>
prevTasks.map((task) => (task.id === taskId ? { ...task, status: newStatus } : task)),
)
if (selectedTask?.id === taskId) {
setSelectedTask(prev => prev ? { ...prev, status: newStatus } : null)
setSelectedTask((prev) => (prev ? { ...prev, status: newStatus } : null))
}
}
const handleStatusChange = (taskId: string, newStatus: TaskStatus) => {
setTasks(prevTasks =>
prevTasks.map(task =>
task.id === taskId ? { ...task, status: newStatus } : task
)
setTasks((prevTasks) =>
prevTasks.map((task) => (task.id === taskId ? { ...task, status: newStatus } : task)),
)
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
createdAt: new Date(),
labels: [],
comments: 0
comments: 0,
}
setTasks(prev => [...prev, newTask])
setTasks((prev) => [...prev, newTask])
setShowNewTaskModal(false)
}
const handleDeleteTask = (taskId: string) => {
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)
}
}
@ -288,9 +294,11 @@ const TasksModule: React.FC = () => {
const getPriorityColor = (priority: string) => {
const colors = {
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',
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]
}
@ -300,7 +308,7 @@ const TasksModule: React.FC = () => {
low: 'Düşük',
medium: 'Orta',
high: 'Yüksek',
urgent: '🔥 Acil'
urgent: '🔥 Acil',
}
return labels[priority as keyof typeof labels]
}
@ -314,6 +322,7 @@ const TasksModule: React.FC = () => {
sensors={sensors}
collisionDetection={closestCorners}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
>
<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 */}
<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">
{columns.map(column => {
{columns.map((column) => {
const columnTasks = getTasksByStatus(column.id)
return (
<DroppableColumn key={column.id} id={column.id}>
<SortableContext
id={column.id}
items={columnTasks.map(t => t.id)}
items={columnTasks.map((t) => t.id)}
strategy={verticalListSortingStrategy}
>
<div
@ -362,7 +370,13 @@ const TasksModule: React.FC = () => {
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<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}
</h3>
</div>
@ -371,7 +385,7 @@ const TasksModule: React.FC = () => {
{/* Tasks */}
<div className="space-y-3 flex-1">
{columnTasks.map(task => (
{columnTasks.map((task) => (
<SortableTaskCard
key={task.id}
task={task}
@ -403,19 +417,18 @@ const TasksModule: React.FC = () => {
{activeId ? (
<div className="opacity-50">
{(() => {
const task = tasks.find(t => t.id === activeId)
const task = tasks.find((t) => t.id === activeId)
if (!task) return null
return (
<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">
{task.title}
</h4>
<h4 className="font-semibold text-gray-900 dark:text-white">{task.title}</h4>
</div>
)
})()}
</div>
) : null}
</DragOverlay>
</div>
{/* New Task Modal */}
<AnimatePresence>
@ -488,7 +501,9 @@ const TasksModule: React.FC = () => {
<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">
📋 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>
</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="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)}
</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">
@ -565,7 +582,9 @@ const TasksModule: React.FC = () => {
</p>
<select
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"
>
<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">
<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>
<button
onClick={() => handleDeleteTask(selectedTask.id)}
@ -647,7 +667,6 @@ const TasksModule: React.FC = () => {
)}
</AnimatePresence>
</div>
</div>
</DndContext>
)
}

View file

@ -7,31 +7,37 @@ import {
HiMapPin,
HiXMark,
HiCheckBadge,
HiCalendar
HiCalendar,
} from 'react-icons/hi2'
import dayjs from 'dayjs'
import { mockTrainings, mockCertificates, Training } from '../../../mocks/mockIntranetData'
const TrainingModule: React.FC = () => {
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 [showEnrollSuccess, setShowEnrollSuccess] = useState(false)
const filteredTrainings = selectedTab === 'all'
? trainings
: trainings.filter(t => t.status === selectedTab)
const filteredTrainings =
selectedTab === 'all' ? trainings : trainings.filter((t) => t.status === selectedTab)
const handleEnroll = (trainingId: string) => {
setTrainings(prev => prev.map(t =>
setTrainings((prev) =>
prev.map((t) =>
t.id === trainingId && t.enrolled < t.maxParticipants
? { ...t, enrolled: t.enrolled + 1 }
: t
))
: t,
),
)
// Seçili eğitimi de güncelle
if (selectedTraining?.id === trainingId && selectedTraining.enrolled < selectedTraining.maxParticipants) {
setSelectedTraining(prev => prev ? { ...prev, enrolled: prev.enrolled + 1 } : null)
if (
selectedTraining?.id === trainingId &&
selectedTraining.enrolled < selectedTraining.maxParticipants
) {
setSelectedTraining((prev) => (prev ? { ...prev, enrolled: prev.enrolled + 1 } : null))
}
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',
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',
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
}
@ -52,8 +58,11 @@ const TrainingModule: React.FC = () => {
const getStatusBadge = (status: 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' },
ongoing: { 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' }
ongoing: {
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
}
@ -77,9 +86,21 @@ const TrainingModule: React.FC = () => {
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
{[
{ 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: 'Tamamlanan', value: mockTrainings.filter(t => t.status === 'completed').length, tab: 'completed' 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: 'Tamamlanan',
value: mockTrainings.filter((t) => t.status === 'completed').length,
tab: 'completed' as const,
},
].map((stat, idx) => (
<motion.button
key={idx}
@ -122,11 +143,19 @@ const TrainingModule: React.FC = () => {
<div className="p-6">
<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}
</span>
<span 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
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>
</div>
@ -153,11 +182,14 @@ const TrainingModule: React.FC = () => {
</div>
<div className="flex items-center gap-2">
<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>
{training.enrolled < training.maxParticipants && training.status === 'upcoming' && (
{training.enrolled < training.maxParticipants &&
training.status === 'upcoming' && (
<button
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"
@ -217,7 +249,7 @@ const TrainingModule: React.FC = () => {
))}
</div>
</div>
</div>
{/* Training Detail Modal */}
<AnimatePresence>
{selectedTraining && (
@ -269,32 +301,43 @@ const TrainingModule: React.FC = () => {
<div className="grid grid-cols-2 gap-4">
<div>
<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>
<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}
</span>
</div>
<div>
<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">
{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>
</div>
<div>
<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>
<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>
{selectedTraining.location && (
<div>
<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>
@ -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="bg-blue-600 h-3 rounded-full transition-all"
style={{ width: `${(selectedTraining.enrolled / selectedTraining.maxParticipants) * 100}%` }}
style={{
width: `${(selectedTraining.enrolled / selectedTraining.maxParticipants) * 100}%`,
}}
/>
</div>
<span className="text-sm font-medium text-gray-900 dark:text-white">
@ -314,7 +359,8 @@ const TrainingModule: React.FC = () => {
</div>
</div>
{selectedTraining.enrolled < selectedTraining.maxParticipants && selectedTraining.status === 'upcoming' && (
{selectedTraining.enrolled < selectedTraining.maxParticipants &&
selectedTraining.status === 'upcoming' && (
<button
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"
@ -347,7 +393,6 @@ const TrainingModule: React.FC = () => {
)}
</AnimatePresence>
</div>
</div>
)
}

View file

@ -5,36 +5,33 @@ import dayjs from 'dayjs'
import { mockVisitors, Visitor } from '../../../mocks/mockIntranetData'
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 [visitors, setVisitors] = useState<Visitor[]>(mockVisitors)
const filteredVisitors = selectedStatus === 'all'
? visitors
: visitors.filter(v => v.status === selectedStatus)
const filteredVisitors =
selectedStatus === 'all' ? visitors : visitors.filter((v) => v.status === selectedStatus)
const handleCheckIn = (visitorId: string) => {
setVisitors(prev => prev.map(v =>
v.id === visitorId
? { ...v, status: 'checked-in', checkIn: new Date() }
: v
))
setVisitors((prev) =>
prev.map((v) =>
v.id === visitorId ? { ...v, status: 'checked-in', checkIn: new Date() } : v,
),
)
}
const handleCheckOut = (visitorId: string) => {
setVisitors(prev => prev.map(v =>
v.id === visitorId
? { ...v, status: 'checked-out', checkOut: new Date() }
: v
))
setVisitors((prev) =>
prev.map((v) =>
v.id === visitorId ? { ...v, status: 'checked-out', checkOut: new Date() } : v,
),
)
}
const handleCancel = (visitorId: string) => {
setVisitors(prev => prev.map(v =>
v.id === visitorId
? { ...v, status: 'cancelled' }
: v
))
setVisitors((prev) => prev.map((v) => (v.id === visitorId ? { ...v, status: 'cancelled' } : v)))
}
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',
'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',
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
}
@ -52,7 +49,7 @@ const VisitorsModule: React.FC = () => {
scheduled: '📅 Planlandı',
'checked-in': '✅ Giriş Yaptı',
'checked-out': '🚪 Çıkış Yaptı',
cancelled: '❌ İptal Edildi'
cancelled: '❌ İptal Edildi',
}
return labels[status] || status
}
@ -83,9 +80,21 @@ const VisitorsModule: React.FC = () => {
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{[
{ 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: ıkış Yaptı', value: mockVisitors.filter(v => v.status === 'checked-out').length, status: 'checked-out' 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: ıkış Yaptı',
value: mockVisitors.filter((v) => v.status === 'checked-out').length,
status: 'checked-out' as const,
},
].map((stat, idx) => (
<motion.button
key={idx}
@ -132,14 +141,18 @@ const VisitorsModule: React.FC = () => {
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">{visitor.company}</p>
</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)}
</span>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-3">
<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">
<HiClock className="w-4 h-4" />
{dayjs(visitor.visitDate).format('DD MMM, HH:mm')}
@ -202,7 +215,7 @@ const VisitorsModule: React.FC = () => {
{visitor.host.fullName}
</p>
<p className="text-xs text-gray-600 dark:text-gray-400">
{visitor.host.department}
{visitor.host.department?.name}
</p>
</div>
</div>
@ -245,6 +258,7 @@ const VisitorsModule: React.FC = () => {
</motion.div>
))}
</div>
</div>
{/* New Visitor Modal */}
<AnimatePresence>
@ -373,7 +387,6 @@ const VisitorsModule: React.FC = () => {
)}
</AnimatePresence>
</div>
</div>
)
}

View file

@ -606,25 +606,40 @@ export const mockDocuments: Document[] = [
export const mockBirthdays: Birthday[] = [
{
employee: mockEmployees[0],
date: new Date('2024-10-20'),
age: 34,
date: new Date('1990-10-19'), // Bugün
age: 35,
},
{
employee: mockEmployees[1],
date: new Date('2024-10-25'),
age: 32,
date: new Date('1992-10-21'), // Bu hafta içinde
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[] = [
{
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,
},
{
employee: mockEmployees[0],
hireDate: new Date('2020-03-15'),
hireDate: new Date('2021-10-20'), // Bu ay (Ekim)
years: 4,
},
]