erp-platform/ui/src/views/intranet/Event.tsx

465 lines
20 KiB
TypeScript
Raw Normal View History

2025-10-18 22:37:20 +00:00
import React, { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import {
HiHeart,
HiChatBubbleLeft,
HiMapPin,
HiUsers,
HiCalendar,
HiXMark,
HiChevronLeft,
HiChevronRight,
2025-10-18 22:37:20 +00:00
} 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'
2025-10-18 22:37:20 +00:00
dayjs.locale('tr')
dayjs.extend(relativeTime)
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')
2025-10-18 22:37:20 +00:00
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)
2025-10-18 22:37:20 +00:00
const handleLikeEvent = (eventId: string) => {
setEvents((prev) => prev.map((e) => (e.id === eventId ? { ...e, likes: e.likes + 1 } : e)))
2025-10-18 22:37:20 +00:00
if (selectedEvent?.id === eventId) {
setSelectedEvent((prev) => (prev ? { ...prev, likes: prev.likes + 1 } : null))
2025-10-18 22:37:20 +00:00
}
}
const handleAddComment = (eventId: string) => {
if (!newComment.trim()) return
const comment: EventComment = {
id: `c${Date.now()}`,
author: mockEmployees[0],
2025-10-18 22:37:20 +00:00
content: newComment,
createdAt: new Date(),
likes: 0,
2025-10-18 22:37:20 +00:00
}
setEvents((prev) =>
prev.map((e) => (e.id === eventId ? { ...e, comments: [...e.comments, comment] } : e)),
)
2025-10-18 22:37:20 +00:00
if (selectedEvent?.id === eventId) {
setSelectedEvent((prev) => (prev ? { ...prev, comments: [...prev.comments, comment] } : null))
2025-10-18 22:37:20 +00:00
}
setNewComment('')
}
const getTypeColor = (type: string) => {
const colors: Record<string, string> = {
social: 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-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',
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',
2025-10-18 22:37:20 +00:00
}
return colors[type] || colors.social
}
const getTypeLabel = (type: string) => {
const labels: Record<string, string> = {
social: '🎉 Sosyal',
training: '📚 Eğitim',
company: '🏢 Kurumsal',
sport: '⚽ Spor',
culture: '🎨 Kültür',
2025-10-18 22:37:20 +00:00
}
return labels[type] || type
}
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 p-6">
<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>
2025-10-18 22:37:20 +00:00
<p className="text-gray-600 dark:text-gray-400 mt-1">
Şirket etkinlikleri, fotoğraflar ve anılar
</p>
</div>
{/* Filter Tabs */}
<div className="flex gap-3 overflow-x-auto pb-2">
{[
{ value: 'all' as const, label: '🌟 Tümü' },
{ value: 'social' as const, label: '🎉 Sosyal' },
{ 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' },
2025-10-18 22:37:20 +00:00
].map((tab) => (
<button
key={tab.value}
onClick={() => setSelectedFilter(tab.value)}
className={`px-4 py-2 rounded-lg border-2 transition-all whitespace-nowrap ${
selectedFilter === tab.value
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300'
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300'
}`}
>
{tab.label}
</button>
))}
</div>
{/* Events Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredEvents.map((event, idx) => (
<motion.div
key={event.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: idx * 0.1 }}
className="bg-white dark:bg-gray-800 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:shadow-xl transition-all cursor-pointer"
onClick={() => setSelectedEvent(event)}
>
{/* Cover Photo */}
<div className="relative h-48 overflow-hidden">
<img
src={event.photos[0]}
alt={event.title}
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)}`}
>
2025-10-18 22:37:20 +00:00
{getTypeLabel(event.type)}
</span>
</div>
{event.photos.length > 1 && (
<div className="absolute bottom-3 right-3 bg-black/70 text-white px-2 py-1 rounded text-xs">
+{event.photos.length - 1} fotoğraf
</div>
)}
</div>
{/* Content */}
<div className="p-4 space-y-3">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white line-clamp-2">
{event.title}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
{event.description}
</p>
<div className="flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400">
<div className="flex items-center gap-1">
<HiCalendar className="w-4 h-4" />
{dayjs(event.date).format('DD MMMM YYYY')}
</div>
<div className="flex items-center gap-1">
<HiMapPin className="w-4 h-4" />
{event.location}
</div>
</div>
<div className="flex items-center justify-between pt-3 border-t border-gray-200 dark:border-gray-700">
<div className="flex items-center gap-4 text-sm">
<button
onClick={(e) => {
e.stopPropagation()
handleLikeEvent(event.id)
}}
className="flex items-center gap-1 text-gray-600 dark:text-gray-400 hover:text-red-500 transition-colors"
>
<HiHeart className="w-5 h-5" />
{event.likes}
</button>
<div className="flex items-center gap-1 text-gray-600 dark:text-gray-400">
<HiChatBubbleLeft className="w-5 h-5" />
{event.comments.length}
</div>
</div>
<div className="flex items-center gap-1 text-sm text-gray-600 dark:text-gray-400">
<HiUsers className="w-4 h-4" />
{event.participants} kişi
</div>
</div>
</div>
</motion.div>
))}
</div>
{filteredEvents.length === 0 && (
<div className="text-center py-12 text-gray-500 dark:text-gray-400">
<p className="text-lg">Bu kategoride henüz etkinlik yok</p>
</div>
)}
</div>
2025-10-18 22:37:20 +00:00
{/* Event Detail Modal */}
<AnimatePresence>
{selectedEvent && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/50 z-40"
onClick={() => setSelectedEvent(null)}
/>
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 overflow-y-auto">
2025-10-18 22:37:20 +00:00
<motion.div
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-4xl w-full max-h-[90vh] overflow-y-auto my-8"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="sticky top-0 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 p-6 z-10">
<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)}`}
>
{getTypeLabel(selectedEvent.type)}
</span>
<span className="text-sm text-gray-600 dark:text-gray-400">
{dayjs(selectedEvent.date).format('DD MMMM YYYY')}
</span>
2025-10-18 22:37:20 +00:00
</div>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
{selectedEvent.title}
</h2>
<p className="text-gray-600 dark:text-gray-400 mt-2">
{selectedEvent.description}
</p>
2025-10-18 22:37:20 +00:00
</div>
<button
onClick={() => setSelectedEvent(null)}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
>
<HiXMark className="w-6 h-6 text-gray-500" />
</button>
</div>
2025-10-18 22:37:20 +00:00
<div className="flex items-center gap-6 mt-4 text-sm text-gray-600 dark:text-gray-400">
<div className="flex items-center gap-2">
<HiMapPin className="w-5 h-5" />
{selectedEvent.location}
</div>
<div className="flex items-center gap-2">
<HiUsers className="w-5 h-5" />
{selectedEvent.participants} katılımcı
</div>
<div className="flex items-center gap-2">
<img
src={selectedEvent.organizer.avatar}
alt={selectedEvent.organizer.fullName}
className="w-6 h-6 rounded-full"
/>
<span>Düzenleyen: {selectedEvent.organizer.fullName}</span>
</div>
</div>
</div>
{/* Photo Gallery */}
<div className="p-6">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
📸 Fotoğraflar ({selectedEvent.photos.length})
</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
{selectedEvent.photos.map((photo, idx) => (
<motion.div
key={idx}
whileHover={{ scale: 1.05 }}
className="relative aspect-square rounded-lg overflow-hidden cursor-pointer"
onClick={() => {
setSelectedPhotoIndex(idx)
setShowPhotoModal(true)
}}
>
2025-10-18 22:37:20 +00:00
<img
src={photo}
alt={`${selectedEvent.title} - ${idx + 1}`}
className="w-full h-full object-cover"
2025-10-18 22:37:20 +00:00
/>
</motion.div>
))}
2025-10-18 22:37:20 +00:00
</div>
</div>
2025-10-18 22:37:20 +00:00
{/* Comments Section */}
<div className="p-6 border-t border-gray-200 dark:border-gray-700">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
💬 Yorumlar ({selectedEvent.comments.length})
2025-10-18 22:37:20 +00:00
</h3>
<div className="flex items-center gap-4 text-sm">
<button
onClick={() => handleLikeEvent(selectedEvent.id)}
className="flex items-center gap-2 px-3 py-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 transition-colors"
>
<HiHeart className="w-5 h-5 text-red-500" />
{selectedEvent.likes} beğeni
</button>
2025-10-18 22:37:20 +00:00
</div>
</div>
{/* Comments List */}
<div className="space-y-4 mb-4">
{selectedEvent.comments.map((comment) => (
<div
key={comment.id}
className="flex gap-3 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg"
>
<img
src={comment.author.avatar}
alt={comment.author.fullName}
className="w-10 h-10 rounded-full"
/>
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<span className="font-semibold text-gray-900 dark:text-white">
{comment.author.fullName}
</span>
<span className="text-xs text-gray-500 dark:text-gray-400">
{dayjs(comment.createdAt).fromNow()}
</span>
</div>
<p className="text-gray-700 dark:text-gray-300 text-sm">
{comment.content}
</p>
<div className="flex items-center gap-2 mt-2">
<button className="text-xs text-gray-500 hover:text-red-500 transition-colors">
{comment.likes} beğeni
</button>
2025-10-18 22:37:20 +00:00
</div>
</div>
</div>
))}
</div>
2025-10-18 22:37:20 +00:00
{/* Add Comment */}
<div className="flex gap-3">
<img
src="https://ui-avatars.com/api/?name=Sedat+Ozturk&background=3b82f6&color=fff"
alt="You"
className="w-10 h-10 rounded-full"
/>
<div className="flex-1 flex gap-2">
<input
type="text"
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
onKeyPress={(e) => {
if (e.key === 'Enter') {
handleAddComment(selectedEvent.id)
}
}}
placeholder="Yorumunuzu yazın..."
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
2025-10-18 22:37:20 +00:00
/>
<button
onClick={() => handleAddComment(selectedEvent.id)}
disabled={!newComment.trim()}
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg transition-colors"
>
Gönder
</button>
2025-10-18 22:37:20 +00:00
</div>
</div>
</div>
</motion.div>
</div>
</>
)}
</AnimatePresence>
2025-10-18 22:37:20 +00:00
{/* Photo Viewer Modal */}
<AnimatePresence>
{showPhotoModal && selectedEvent && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/90 z-50"
onClick={() => setShowPhotoModal(false)}
/>
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
2025-10-18 22:37:20 +00:00
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
className="relative max-w-5xl w-full"
onClick={(e) => e.stopPropagation()}
>
{/* Close Button */}
<button
onClick={() => setShowPhotoModal(false)}
className="absolute top-4 right-4 p-2 bg-black/50 hover:bg-black/70 rounded-full text-white z-10"
2025-10-18 22:37:20 +00:00
>
<HiXMark className="w-6 h-6" />
</button>
2025-10-18 22:37:20 +00:00
{/* Navigation */}
{selectedEvent.photos.length > 1 && (
<>
<button
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,
)
}
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" />
</button>
</>
)}
2025-10-18 22:37:20 +00:00
{/* Image */}
<img
src={selectedEvent.photos[selectedPhotoIndex]}
alt={`${selectedEvent.title} - ${selectedPhotoIndex + 1}`}
className="w-full h-auto max-h-[80vh] object-contain rounded-lg"
/>
2025-10-18 22:37:20 +00:00
{/* Counter */}
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 px-4 py-2 bg-black/70 text-white rounded-full text-sm">
{selectedPhotoIndex + 1} / {selectedEvent.photos.length}
</div>
</motion.div>
</div>
</>
)}
</AnimatePresence>
2025-10-18 22:37:20 +00:00
</div>
)
}
export default EventsModule