erp-platform/ui/src/views/intranet/Dashboard.tsx
2026-01-27 12:58:35 +03:00

750 lines
29 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react'
import { AnimatePresence } from 'framer-motion'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import isBetween from 'dayjs/plugin/isBetween'
import localizedFormat from 'dayjs/plugin/localizedFormat'
// Widgets
import TodayBirthdays from './widgets/TodayBirthdays'
import UpcomingEvents from './widgets/UpcomingEvents'
import RecentDocuments from './widgets/RecentDocuments'
import PriorityTasks from './widgets/PriorityTasks'
import MealWeeklyMenu from './widgets/MealWeeklyMenu'
import LeaveManagement from './widgets/LeaveManagement'
import OvertimeManagement from './widgets/OvertimeManagement'
import ExpenseManagement from './widgets/ExpenseManagement'
import UpcomingTrainings from './widgets/UpcomingTrainings'
import ActiveReservations from './widgets/ActiveReservations'
import ActiveSurveys from './widgets/ActiveSurveys'
import Visitors from './widgets/Visitors'
// Modals
import SurveyModal from './modals/SurveyModal'
import LeaveRequestModal from './modals/LeaveRequestModal'
import OvertimeRequestModal from './modals/OvertimeRequestModal'
import ExpenseRequestModal from './modals/ExpenseRequestModal'
import ReservationRequestModal from './modals/ReservationRequestModal'
import AnnouncementDetailModal from './modals/AnnouncementDetailModal'
// Social Wall
import SocialWall from './SocialWall'
import { Container } from '@/components/shared'
import { usePermission } from '@/utils/hooks/usePermission'
import {
AnnouncementDto,
IntranetDashboardDto,
SurveyAnswerDto,
SurveyDto,
} from '@/proxy/intranet/models'
import { intranetService } from '@/services/intranet.service'
import Announcements from './widgets/Announcements'
import ShuttleRoute from './widgets/ShuttleRoute'
import { useLocalization } from '@/utils/hooks/useLocalization'
import useLocale from '@/utils/hooks/useLocale'
import { currentLocalDate } from '@/utils/dateUtils'
import { useStoreState } from '@/store/store'
dayjs.extend(relativeTime)
dayjs.extend(isBetween)
dayjs.extend(localizedFormat)
const WIDGET_ORDER_KEY = 'dashboard-widget-order'
const IntranetDashboard: React.FC = () => {
const { checkPermission } = usePermission()
const [selectedAnnouncement, setSelectedAnnouncement] = useState<AnnouncementDto | null>(null)
const [selectedSurvey, setSelectedSurvey] = useState<SurveyDto | null>(null)
const [showSurveyModal, setShowSurveyModal] = useState(false)
const [showLeaveModal, setShowLeaveModal] = useState(false)
const [showOvertimeModal, setShowOvertimeModal] = useState(false)
const [showExpenseModal, setShowExpenseModal] = useState(false)
const [showReservationModal, setShowReservationModal] = useState(false)
const [isDesignMode, setIsDesignMode] = useState(false)
const [widgetOrder, setWidgetOrder] = useState<Record<string, string[]>>({
left: [],
center: [],
right: [],
})
const [intranetDashboard, setIntranetDashboard] = useState<IntranetDashboardDto>()
const { translate } = useLocalization()
const currentLocale = useLocale()
const fetchIntranetDashboard = async () => {
const dashboard = await intranetService.getDashboard()
if (dashboard.data) {
setIntranetDashboard(dashboard.data)
}
}
useEffect(() => {
fetchIntranetDashboard()
}, [])
// Drag state'leri birleştirildi
const [dragState, setDragState] = useState<{
draggedId: string | null
targetColumn: string | null
targetIndex: number | null
}>({
draggedId: null,
targetColumn: null,
targetIndex: null,
})
const handleTakeSurvey = (survey: SurveyDto) => {
setSelectedSurvey(survey)
setShowSurveyModal(true)
}
const handleSubmitSurvey = (answers: SurveyAnswerDto[]) => {
setShowSurveyModal(false)
setSelectedSurvey(null)
}
const handleSubmitLeave = () => {
setShowLeaveModal(false)
}
const handleSubmitOvertime = () => {
setShowOvertimeModal(false)
}
const handleSubmitExpense = () => {
setShowExpenseModal(false)
}
const handleSubmitReservation = () => {
setShowReservationModal(false)
}
// Widget metadata (component'lar yerine sadece meta bilgiler)
const widgetMetadata = [
{ id: 'upcoming-events', permission: 'App.Intranet.Events.Event.Widget', column: 'left' },
{ id: 'today-birthdays', permission: 'App.Hr.Employee.Widget', column: 'left' },
{ id: 'documents', permission: 'App.Files.Widget', column: 'left' },
{ id: 'upcoming-trainings', permission: 'App.Hr.Training.Widget', column: 'left' },
{ id: 'active-reservations', permission: 'App.Intranet.Reservation.Widget', column: 'left' },
{ id: 'active-surveys', permission: 'App.Hr.Survey.Widget', column: 'left' },
{ id: 'visitors', permission: 'App.Intranet.Visitor.Widget', column: 'left' },
{ id: 'expense-management', permission: 'App.Hr.Expense.Widget', column: 'left' },
{ id: 'social-wall', permission: 'App.Intranet.SocialPost.Widget', column: 'center' },
{
id: 'announcements',
permission: 'App.Intranet.Announcement.Widget',
column: 'right',
},
{ id: 'priority-tasks', permission: 'App.Project.ProjectTask.Widget', column: 'right' },
{ id: 'meal-weekly-menu', permission: 'App.Intranet.Meal.Widget', column: 'right' },
{ id: 'shuttle-route', permission: 'App.Intranet.ShuttleRoute.Widget', column: 'right' },
{ id: 'leave-management', permission: 'App.Hr.Leave.Widget', column: 'right' },
{ id: 'overtime-management', permission: 'App.Hr.Overtime.Widget', column: 'right' },
]
// Widget sıralamasını yükle
useEffect(() => {
const savedOrder = localStorage.getItem(WIDGET_ORDER_KEY)
if (savedOrder) {
try {
const parsed = JSON.parse(savedOrder) as Record<string, unknown[]>
// Duplicate key'leri temizle
const cleanedOrder: Record<string, string[]> = {
left: [...new Set((parsed.left || []) as string[])],
center: [...new Set((parsed.center || []) as string[])],
right: [...new Set((parsed.right || []) as string[])],
}
setWidgetOrder(cleanedOrder)
} catch (error) {
console.error('Widget order parse error:', error)
initializeDefaultOrder()
}
} else {
initializeDefaultOrder()
}
}, [])
// If permissions arrive after mount, initialize default order when needed
const grantedPolicies = useStoreState((state) => state.abpConfig?.config?.auth.grantedPolicies)
useEffect(() => {
if (
grantedPolicies &&
(!widgetOrder.left.length && !widgetOrder.center.length && !widgetOrder.right.length)
) {
initializeDefaultOrder()
}
}, [grantedPolicies])
const initializeDefaultOrder = () => {
const defaultOrder = {
left: widgetMetadata
.filter((w) => w.column === 'left' && checkPermission(w.permission))
.map((w) => w.id),
center: widgetMetadata
.filter((w) => w.column === 'center' && checkPermission(w.permission))
.map((w) => w.id),
right: widgetMetadata
.filter((w) => w.column === 'right' && checkPermission(w.permission))
.map((w) => w.id),
}
setWidgetOrder(defaultOrder)
}
// Widget sıralamasını kaydet
const saveWidgetOrder = (newOrder: Record<string, string[]>) => {
setWidgetOrder(newOrder)
localStorage.setItem(WIDGET_ORDER_KEY, JSON.stringify(newOrder))
}
const handleDragStart = (e: React.DragEvent, widgetId: string, column: string) => {
setDragState({ draggedId: widgetId, targetColumn: null, targetIndex: null })
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.setData('widgetId', widgetId)
e.dataTransfer.setData('sourceColumn', column)
}
const handleDragEnterWidget = (e: React.DragEvent, column: string, index: number) => {
// Sadece widget'ın üst kısmına yakınsa indicator göster
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
const mouseY = e.clientY
const widgetTop = rect.top
const widgetHeight = rect.height
const threshold = widgetHeight * 0.3 // Üst %30'luk alan
if (mouseY - widgetTop < threshold) {
// Üst kısma yakın - indicator göster
setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: index }))
} else {
// Widget'ın ortasında veya altında - indicator gösterme
setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: null }))
}
}
const handleDragEnterColumn = (column: string) => {
setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: null }))
}
const handleDragLeaveColumn = () => {
setDragState((prev) => ({ ...prev, targetColumn: null, targetIndex: null }))
}
const handleDrop = (e: React.DragEvent, targetColumn: string, targetIndex?: number) => {
e.preventDefault()
e.stopPropagation()
const widgetId = e.dataTransfer.getData('widgetId')
const sourceColumn = e.dataTransfer.getData('sourceColumn')
if (!widgetId || !sourceColumn) return
const newOrder = { ...widgetOrder }
// ÖNCE tüm kolonlardan bu widget'ı kaldır (duplicate önleme)
Object.keys(newOrder).forEach((col) => {
newOrder[col] = newOrder[col].filter((id) => id !== widgetId)
})
// SONRA hedef kolona ekle
if (targetIndex !== undefined) {
newOrder[targetColumn].splice(targetIndex, 0, widgetId)
} else {
newOrder[targetColumn].push(widgetId)
}
// Duplicate'leri temizle
Object.keys(newOrder).forEach((col) => {
newOrder[col] = [...new Set(newOrder[col])]
})
saveWidgetOrder(newOrder)
setDragState({ draggedId: null, targetColumn: null, targetIndex: null })
}
const handleDragEnd = () => {
setDragState({ draggedId: null, targetColumn: null, targetIndex: null })
}
// Widget component'ını render et
const renderWidgetComponent = (widgetId: string) => {
switch (widgetId) {
case 'upcoming-events':
return <UpcomingEvents events={intranetDashboard?.events || []} />
case 'today-birthdays':
return <TodayBirthdays employees={intranetDashboard?.birthdays || []} />
case 'visitors':
return <Visitors visitors={intranetDashboard?.visitors || []} />
case 'active-reservations':
return (
<ActiveReservations
reservations={intranetDashboard?.reservations || []}
onNewReservation={() => setShowReservationModal(true)}
/>
)
case 'upcoming-trainings':
return <UpcomingTrainings trainings={intranetDashboard?.trainings || []} />
case 'expense-management':
return (
<ExpenseManagement
expenses={
intranetDashboard?.expenses || {
totalRequested: 0,
totalApproved: 0,
last5Expenses: [],
}
}
onNewExpense={() => setShowExpenseModal(true)}
/>
)
case 'documents':
return <RecentDocuments documents={intranetDashboard?.documents || []} />
case 'announcements':
return (
<Announcements
announcements={intranetDashboard?.announcements || []}
onAnnouncementClick={setSelectedAnnouncement}
/>
)
case 'shuttle-route':
return <ShuttleRoute shuttleRoutes={intranetDashboard?.shuttleRoutes || []} />
case 'meal-weekly-menu':
return <MealWeeklyMenu meals={intranetDashboard?.meals || []} />
case 'leave-management':
return (
<LeaveManagement
leaves={intranetDashboard?.leaves || []}
onNewLeave={() => setShowLeaveModal(true)}
/>
)
case 'overtime-management':
return (
<OvertimeManagement
overtimes={intranetDashboard?.overtimes || []}
onNewOvertime={() => setShowOvertimeModal(true)}
/>
)
case 'active-surveys':
return (
<ActiveSurveys
surveys={intranetDashboard?.surveys || []}
onTakeSurvey={handleTakeSurvey}
/>
)
case 'social-wall':
return <SocialWall posts={intranetDashboard?.socialPosts || []} />
case 'priority-tasks':
return <PriorityTasks tasks={intranetDashboard?.tasks || []} />
default:
return null
}
}
// Widget'ları render et
const renderWidgets = (column: 'left' | 'center' | 'right') => {
const columnWidgets = widgetOrder[column] || []
// Duplicate'leri filtrele
const uniqueWidgets = [...new Set(columnWidgets)]
return uniqueWidgets
.map((widgetId, index) => {
const metadata = widgetMetadata.find((w) => w.id === widgetId)
if (!metadata || !checkPermission(metadata.permission)) return null
const isDragging = dragState.draggedId === widgetId
const isDropTarget = dragState.targetColumn === column && dragState.targetIndex === index
return (
<div key={`${column}-${widgetId}-${index}`} className="relative group">
{/* Drop indicator - SADECE widget'ların arasına (üst %30'luk alana) gelince göster */}
{isDesignMode && isDropTarget && !isDragging && (
<div className="absolute -top-5 left-0 right-0 z-20 animate-in fade-in slide-in-from-top-2 duration-300">
{/* Çizgi */}
<div className="h-2 bg-gradient-to-r from-transparent via-blue-500 to-transparent rounded-full shadow-lg" />
{/* Badge */}
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-gradient-to-r from-blue-500 to-blue-600 text-white text-xs px-4 py-2 rounded-full whitespace-nowrap shadow-xl font-semibold flex items-center gap-2 border-2 border-white dark:border-gray-800">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
<span>Buraya Bırak</span>
</div>
</div>
)}
<div
draggable={isDesignMode}
onDragStart={(e) => {
if (isDesignMode) {
handleDragStart(e, widgetId, column)
// Drag ghost image'i gizle
const ghost = document.createElement('div')
ghost.style.opacity = '0'
e.dataTransfer.setDragImage(ghost, 0, 0)
}
}}
onDragOver={(e) => {
if (!isDesignMode) return
e.preventDefault()
e.stopPropagation()
// Throttle: Sadece düzenli aralıklarla güncelle
const now = Date.now()
if (
!e.currentTarget.dataset.lastUpdate ||
now - parseInt(e.currentTarget.dataset.lastUpdate) > 150
) {
e.currentTarget.dataset.lastUpdate = now.toString()
handleDragEnterWidget(e, column, index)
}
}}
onDragLeave={(e) => {
// Widget'tan çıkınca indicator'ı kaldır
if (isDesignMode) {
setDragState((prev) => ({
...prev,
targetColumn: prev.targetColumn,
targetIndex: null,
}))
}
}}
onDrop={(e) => {
if (!isDesignMode) return
e.stopPropagation()
// Drop pozisyonunu hesapla
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
const mouseY = e.clientY
const widgetTop = rect.top
const widgetHeight = rect.height
const threshold = widgetHeight * 0.3
if (mouseY - widgetTop < threshold) {
// Üst kısma bırak - mevcut index'e ekle
handleDrop(e, column, index)
} else {
// Alt kısma bırak - sonraki index'e ekle
handleDrop(e, column, index + 1)
}
}}
onDragEnd={handleDragEnd}
className={`
relative
${
isDesignMode
? `border-2 border-dashed rounded-lg cursor-move
${
isDragging
? 'border-blue-400 opacity-70 bg-blue-50/30 dark:bg-blue-900/10'
: 'border-gray-300 dark:border-gray-600 hover:border-blue-400 dark:hover:border-blue-500 hover:shadow-md'
}
transition-all duration-300 ease-out`
: 'border-0 transition-none'
}
`}
style={{
touchAction: 'none',
transition:
'border-color 0.3s ease-out, opacity 0.3s ease-out, box-shadow 0.3s ease-out',
willChange: isDragging ? 'opacity' : 'auto',
}}
>
{/* Dragging overlay - daha minimal */}
{isDesignMode && isDragging && (
<div className="absolute inset-0 bg-white/60 dark:bg-gray-900/40 rounded-lg z-10 flex items-center justify-center backdrop-blur-[1px] transition-opacity duration-300">
<div className="bg-gradient-to-r from-blue-500 to-blue-600 text-white px-4 py-2 rounded-lg font-medium shadow-lg flex items-center gap-2">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"
/>
</svg>
<span>Taşınıyor</span>
</div>
</div>
)}
<div
className={`${isDesignMode ? 'p-1.5' : ''} transition-all duration-500 ease-out`}
>
{renderWidgetComponent(widgetId)}
</div>
</div>
</div>
)
})
.filter(Boolean)
}
return (
<Container>
<div className="mx-auto space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
{/* Design Mode Toggle */}
<div className="flex items-center gap-2">
<label
htmlFor="design-mode-toggle"
className="text-sm font-medium text-gray-700 dark:text-gray-300 cursor-pointer"
>
🎨 {translate('::App.Platform.Intranet.Dashboard.DesignMode')}
</label>
<button
id="design-mode-toggle"
onClick={() => setIsDesignMode(!isDesignMode)}
className={`
relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
${isDesignMode ? 'bg-blue-600' : 'bg-gray-300 dark:bg-gray-600'}
`}
role="switch"
aria-checked={isDesignMode}
>
<span
className={`
inline-block h-4 w-4 transform rounded-full bg-white transition-transform
${isDesignMode ? 'translate-x-6' : 'translate-x-1'}
`}
/>
</button>
</div>
{/* Reset Button - Sadece design mode aktifken görünsün */}
{isDesignMode && (
<button
onClick={() => {
localStorage.removeItem(WIDGET_ORDER_KEY)
initializeDefaultOrder()
}}
className="text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors flex items-center gap-1"
title="Widget düzenini varsayılana döndür"
>
🔄 {translate('::App.Platform.Intranet.Dashboard.Reset')}
</button>
)}
</div>
<div>
<p className="text-gray-600 dark:text-gray-400 mt-1">
<span className="font-medium">{translate('::AI.Welcome')},</span>{' '}
{currentLocalDate(new Date(), currentLocale || 'tr')}
</p>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-12">
<div
className={`lg:col-span-3 space-y-6 min-h-[100px] rounded-xl p-1
${
isDesignMode && dragState.targetColumn === 'left' && dragState.targetIndex === null
? 'bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-blue-300 dark:ring-blue-600 shadow-lg'
: 'bg-transparent'
}
transition-all duration-700 ease-out
`}
onDragOver={(e) => {
if (!isDesignMode) return
e.preventDefault()
// Throttle: Sadece her 150ms'de bir güncelle
const now = Date.now()
const target = e.currentTarget as HTMLElement
if (
!target.dataset.lastColumnUpdate ||
now - parseInt(target.dataset.lastColumnUpdate) > 150
) {
target.dataset.lastColumnUpdate = now.toString()
handleDragEnterColumn('left')
}
}}
onDragLeave={() => {
if (isDesignMode) handleDragLeaveColumn()
}}
onDrop={(e) => {
if (!isDesignMode) return
e.stopPropagation()
const columnWidgets = widgetOrder['left'] || []
handleDrop(e, 'left', columnWidgets.length)
}}
>
{renderWidgets('left')}
{isDesignMode &&
dragState.targetColumn === 'left' &&
widgetOrder['left']?.length === 0 && (
<div className="flex items-center justify-center h-40 border-2 border-dashed border-blue-300 dark:border-blue-600 rounded-xl bg-blue-50/50 dark:bg-blue-900/10 transition-all duration-500 ease-out">
<p className="text-blue-600 dark:text-blue-400 font-medium flex items-center gap-2">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
/>
</svg>
<span>Widget'ı buraya bırakın</span>
</p>
</div>
)}
</div>
<div
className={`lg:col-span-6 space-y-6 min-h-[100px] rounded-xl p-1
${
isDesignMode &&
dragState.targetColumn === 'center' &&
dragState.targetIndex === null
? 'bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-blue-300 dark:ring-blue-600 shadow-lg'
: 'bg-transparent'
}
transition-all duration-700 ease-out
`}
onDragOver={(e) => {
if (!isDesignMode) return
e.preventDefault()
const now = Date.now()
const target = e.currentTarget as HTMLElement
if (
!target.dataset.lastColumnUpdate ||
now - parseInt(target.dataset.lastColumnUpdate) > 150
) {
target.dataset.lastColumnUpdate = now.toString()
handleDragEnterColumn('center')
}
}}
onDragLeave={() => {
if (isDesignMode) handleDragLeaveColumn()
}}
onDrop={(e) => {
if (!isDesignMode) return
e.stopPropagation()
const columnWidgets = widgetOrder['center'] || []
handleDrop(e, 'center', columnWidgets.length)
}}
>
{renderWidgets('center')}
{isDesignMode &&
dragState.targetColumn === 'center' &&
widgetOrder['center']?.length === 0 && (
<div className="flex items-center justify-center rounded-xl bg-blue-50/50 dark:bg-blue-900/10 transition-all duration-500 ease-out">
<p className="text-blue-600 dark:text-blue-400 font-medium flex items-center gap-2">
<span>Widget'ı buraya bırakın</span>
</p>
</div>
)}
</div>
<div
className={`lg:col-span-3 space-y-6 min-h-[100px] rounded-xl p-1
${
isDesignMode && dragState.targetColumn === 'right' && dragState.targetIndex === null
? 'bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-blue-300 dark:ring-blue-600 shadow-lg'
: 'bg-transparent'
}
transition-all duration-700 ease-out
`}
onDragOver={(e) => {
if (!isDesignMode) return
e.preventDefault()
const now = Date.now()
const target = e.currentTarget as HTMLElement
if (
!target.dataset.lastColumnUpdate ||
now - parseInt(target.dataset.lastColumnUpdate) > 150
) {
target.dataset.lastColumnUpdate = now.toString()
handleDragEnterColumn('right')
}
}}
onDragLeave={() => {
if (isDesignMode) handleDragLeaveColumn()
}}
onDrop={(e) => {
if (!isDesignMode) return
e.stopPropagation()
const columnWidgets = widgetOrder['right'] || []
handleDrop(e, 'right', columnWidgets.length)
}}
>
{renderWidgets('right')}
{isDesignMode &&
dragState.targetColumn === 'right' &&
widgetOrder['right']?.length === 0 && (
<div className="flex items-center justify-center h-40 border-2 border-dashed border-blue-300 dark:border-blue-600 rounded-xl bg-blue-50/50 dark:bg-blue-900/10 transition-all duration-500 ease-out">
<p className="text-blue-600 dark:text-blue-400 font-medium flex items-center gap-2">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
/>
</svg>
<span>Widget'ı buraya bırakın</span>
</p>
</div>
)}
</div>
</div>
</div>
<AnimatePresence>
{showSurveyModal && selectedSurvey && (
<SurveyModal
survey={selectedSurvey}
onClose={() => setShowSurveyModal(false)}
onSubmit={handleSubmitSurvey}
/>
)}
</AnimatePresence>
<AnimatePresence>
{showLeaveModal && (
<LeaveRequestModal
onClose={() => setShowLeaveModal(false)}
onSubmit={handleSubmitLeave}
/>
)}
</AnimatePresence>
<AnimatePresence>
{showOvertimeModal && (
<OvertimeRequestModal
onClose={() => setShowOvertimeModal(false)}
onSubmit={handleSubmitOvertime}
/>
)}
</AnimatePresence>
<AnimatePresence>
{showExpenseModal && (
<ExpenseRequestModal
onClose={() => setShowExpenseModal(false)}
onSubmit={handleSubmitExpense}
/>
)}
</AnimatePresence>
<AnimatePresence>
{showReservationModal && (
<ReservationRequestModal
onClose={() => setShowReservationModal(false)}
onSubmit={handleSubmitReservation}
/>
)}
</AnimatePresence>
<AnimatePresence>
{selectedAnnouncement && (
<AnnouncementDetailModal
announcement={selectedAnnouncement}
onClose={() => setSelectedAnnouncement(null)}
/>
)}
</AnimatePresence>
</Container>
)
}
export default IntranetDashboard