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' import SurveyModal from './widgets/SurveyModal' import AnnouncementModal from './widgets/AnnouncementModal' import EventModal from './widgets/EventModal' import { Container } from '@/components/shared' import { usePermission } from '@/utils/hooks/usePermission' import { AnnouncementDto, EventDto, IntranetDashboardDto, SurveyAnswerDto, SurveyDto, } from '@/proxy/intranet/models' import { intranetService } from '@/services/intranet.service' import { useLocalization } from '@/utils/hooks/useLocalization' import useLocale from '@/utils/hooks/useLocale' import { currentLocalDate } from '@/utils/dateUtils' import { useStoreActions, useStoreState } from '@/store/store' import Button from '@/components/ui/Button' import { LuX } from 'react-icons/lu' import type { DashboardWidgetColumn, DashboardWidgetDefinition } from './dashboardWidget' dayjs.extend(relativeTime) dayjs.extend(isBetween) dayjs.extend(localizedFormat) const dashboardColumns: DashboardWidgetColumn[] = ['left', 'center', 'right'] type DashboardWidgetModule = { dashboardWidget?: DashboardWidgetDefinition } const dashboardWidgets = Object.values( import.meta.glob(['./widgets/*.tsx', './SocialWall/index.tsx'], { eager: true, }), ) .map((module) => module.dashboardWidget) .filter((widget): widget is DashboardWidgetDefinition => Boolean(widget)) .sort((left, right) => left.order - right.order) const layoutPresets = [ { id: 'default', columns: [3, 6, 3], labelKey: '::App.Platform.Intranet.Dashboard.Layout.Default', }, { id: 'wide-center', columns: [2, 8, 2], labelKey: '::App.Platform.Intranet.Dashboard.Layout.CenterFocus', }, { id: 'balanced', columns: [4, 4, 4], labelKey: '::App.Platform.Intranet.Dashboard.Layout.Balanced', }, { id: 'wide-left', columns: [4, 6, 2], labelKey: '::App.Platform.Intranet.Dashboard.Layout.WideLeft', }, { id: 'wide-right', columns: [2, 6, 4], labelKey: '::App.Platform.Intranet.Dashboard.Layout.WideRight', }, ] as const const columnSpanClasses: Record = { 2: 'lg:col-span-2', 3: 'lg:col-span-3', 4: 'lg:col-span-4', 6: 'lg:col-span-6', 8: 'lg:col-span-8', } const IntranetDashboard: React.FC = () => { const { checkPermission } = usePermission() const [selectedAnnouncement, setSelectedAnnouncement] = useState(null) const [selectedEvent, setSelectedEvent] = useState(null) const [selectedSurvey, setSelectedSurvey] = useState(null) const [showSurveyModal, setShowSurveyModal] = useState(false) const [isDesignMode, setIsDesignMode] = useState(false) const { dashboardLayout: selectedLayout, hiddenWidgetIds, widgetOrder } = useStoreState( (state) => state.admin.dashboard, ) const { setDashboardLayout, setHiddenWidgetIds, setWidgetOrder, reset: resetDashboard, } = useStoreActions((actions) => actions.admin.dashboard) const [intranetDashboard, setIntranetDashboard] = useState() const { translate } = useLocalization() const currentLocale = useLocale() const fetchIntranetDashboard = async () => { try { const dashboard = await intranetService.getDashboard() if (dashboard.data) { setIntranetDashboard(dashboard.data) } } catch { // hata apiService tarafından ele alınıyor } } useEffect(() => { fetchIntranetDashboard() }, []) const [dragState, setDragState] = useState<{ draggedId: string | null targetColumn: DashboardWidgetColumn | null targetIndex: number | null }>({ draggedId: null, targetColumn: null, targetIndex: null, }) const handleTakeSurvey = (survey: SurveyDto) => { setSelectedSurvey(survey) setShowSurveyModal(true) } const handleSubmitSurvey = async (answers: SurveyAnswerDto[]) => { if (!selectedSurvey) return try { await intranetService.createSurveyResponse( selectedSurvey.id, answers.map((a) => ({ questionId: a.questionId, questionType: a.questionType, value: a.value !== undefined && a.value !== null ? String(a.value) : '', })), ) await fetchIntranetDashboard() } catch (e) { console.error('Survey submit error', e) } setShowSurveyModal(false) setSelectedSurvey(null) } const handleEventLikeChange = (updatedEvent: EventDto) => { setSelectedEvent((current) => (current ? { ...current, ...updatedEvent } : updatedEvent)) setIntranetDashboard((current) => current ? { ...current, events: current.events.map((event) => event.id === updatedEvent.id ? { ...event, ...updatedEvent } : event, ), } : current, ) } const handleAnnouncementLikeChange = (updatedAnnouncement: AnnouncementDto) => { setSelectedAnnouncement((current) => current ? { ...current, ...updatedAnnouncement } : updatedAnnouncement, ) setIntranetDashboard((current) => current ? { ...current, announcements: current.announcements.map((announcement) => announcement.id === updatedAnnouncement.id ? { ...announcement, ...updatedAnnouncement } : announcement, ), } : current, ) } const grantedPolicies = useStoreState((state) => state.abpConfig?.config?.auth.grantedPolicies) const initializeDefaultOrder = () => { const defaultOrder: Record = { left: [], center: [], right: [], } dashboardWidgets.forEach((widget) => { if (checkPermission(widget.permission)) defaultOrder[widget.column].push(widget.id) }) setWidgetOrder(defaultOrder) } useEffect(() => { if (!grantedPolicies) return const hasSavedOrder = dashboardColumns.some((column) => widgetOrder[column].length > 0) if (!hasSavedOrder) { initializeDefaultOrder() return } // Kayıtlı sıralamayı tekilleştir ve henüz yerleştirilmemiş widget'ları ekle const order: Record = { left: [...new Set(widgetOrder.left || [])], center: [...new Set(widgetOrder.center || [])], right: [...new Set(widgetOrder.right || [])], } const allAssigned = new Set([...order.left, ...order.center, ...order.right]) dashboardWidgets.forEach((widget) => { if (!allAssigned.has(widget.id) && checkPermission(widget.permission)) { order[widget.column].push(widget.id) } }) setWidgetOrder(order) }, [grantedPolicies]) const setWidgetVisibility = (widgetId: string, visible: boolean) => { const next = visible ? hiddenWidgetIds.filter((id) => id !== widgetId) : [...new Set([...hiddenWidgetIds, widgetId])] setHiddenWidgetIds(next) } const activeLayout = layoutPresets.find((layout) => layout.id === selectedLayout) || layoutPresets[0] const hiddenWidgets = dashboardWidgets.filter( (widget) => hiddenWidgetIds.includes(widget.id) && checkPermission(widget.permission), ) const handleDragStart = (e: React.DragEvent, widgetId: string) => { setDragState({ draggedId: widgetId, targetColumn: null, targetIndex: null }) e.dataTransfer.effectAllowed = 'move' e.dataTransfer.setData('widgetId', widgetId) } const handleDragEnterWidget = ( e: React.DragEvent, column: DashboardWidgetColumn, index: number, ) => { const rect = (e.currentTarget as HTMLElement).getBoundingClientRect() if (e.clientY - rect.top < rect.height * 0.3) { setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: index })) } else { setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: null })) } } const handleDragEnterColumn = (column: DashboardWidgetColumn) => { setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: null })) } const handleDragLeaveColumn = () => { setDragState((prev) => ({ ...prev, targetColumn: null, targetIndex: null })) } const handleDrop = ( e: React.DragEvent, targetColumn: DashboardWidgetColumn, targetIndex?: number, ) => { e.preventDefault() e.stopPropagation() const widgetId = e.dataTransfer.getData('widgetId') if (!widgetId) return const newOrder = { ...widgetOrder } dashboardColumns.forEach((col) => { newOrder[col] = newOrder[col].filter((id) => id !== widgetId) }) if (targetIndex !== undefined) { newOrder[targetColumn].splice(targetIndex, 0, widgetId) } else { newOrder[targetColumn].push(widgetId) } setWidgetOrder(newOrder) setDragState({ draggedId: null, targetColumn: null, targetIndex: null }) } const handleDragEnd = () => { setDragState({ draggedId: null, targetColumn: null, targetIndex: null }) } const handleColumnDragOver = ( e: React.DragEvent, column: DashboardWidgetColumn, ) => { if (!isDesignMode) return e.preventDefault() const now = Date.now() const lastUpdate = Number(e.currentTarget.dataset.lastColumnUpdate || 0) if (now - lastUpdate > 150) { e.currentTarget.dataset.lastColumnUpdate = now.toString() handleDragEnterColumn(column) } } const handleColumnDrop = (e: React.DragEvent, column: DashboardWidgetColumn) => { if (!isDesignMode) return handleDrop(e, column, widgetOrder[column].length) } const renderWidgets = (column: DashboardWidgetColumn) => { const columnWidgets = widgetOrder[column] || [] const uniqueWidgets = [...new Set(columnWidgets)] return uniqueWidgets .map((widgetId, index) => { const metadata = dashboardWidgets.find((w) => w.id === widgetId) if ( !metadata || !checkPermission(metadata.permission) || hiddenWidgetIds.includes(widgetId) ) return null const isDragging = dragState.draggedId === widgetId const isDropTarget = dragState.targetColumn === column && dragState.targetIndex === index return (
{isDesignMode && isDropTarget && !isDragging && (
{translate('::App.Platform.Intranet.Dashboard.DropHere')}
)}
{ if (isDesignMode) { handleDragStart(e, widgetId) } }} onDragOver={(e) => { if (!isDesignMode) return e.preventDefault() e.stopPropagation() 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={() => { if (isDesignMode) { setDragState((prev) => ({ ...prev, targetColumn: prev.targetColumn, targetIndex: null, })) } }} onDrop={(e) => { if (!isDesignMode) return e.stopPropagation() const rect = (e.currentTarget as HTMLElement).getBoundingClientRect() if (e.clientY - rect.top < rect.height * 0.3) { handleDrop(e, column, index) } else { 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: isDesignMode ? 'none' : 'pan-y', transition: 'border-color 0.3s ease-out, opacity 0.3s ease-out, box-shadow 0.3s ease-out', willChange: isDragging ? 'opacity' : 'auto', }} > {isDesignMode && !isDragging && ( )} {isDesignMode && isDragging && (
{translate('::App.Platform.Intranet.Dashboard.Moving')}
)}
{metadata.render({ dashboard: intranetDashboard, openAnnouncement: setSelectedAnnouncement, openEvent: setSelectedEvent, takeSurvey: handleTakeSurvey, })}
) }) .filter(Boolean) } return (
{isDesignMode && ( )}
{isDesignMode && (
{translate('::App.Platform.Intranet.Dashboard.Layout')} {layoutPresets.map((layout) => ( ))}
)}

{translate('::AI.Welcome')},{' '} {currentLocalDate(new Date(), currentLocale || 'tr')}

{isDesignMode && hiddenWidgets.length > 0 && (
{translate('::App.Platform.Intranet.Dashboard.HiddenWidgets')}: {hiddenWidgets.map((widget) => ( ))}
)}
{dashboardColumns.map((column, index) => { const isEmptyDropTarget = isDesignMode && dragState.targetColumn === column && widgetOrder[column].length === 0 return (
handleColumnDragOver(e, column)} onDragLeave={handleDragLeaveColumn} onDrop={(e) => handleColumnDrop(e, column)} > {renderWidgets(column)} {isEmptyDropTarget && (
{translate('::App.Platform.Intranet.Dashboard.DropWidgetHere')}
)}
) })}
{showSurveyModal && selectedSurvey && ( setShowSurveyModal(false)} onSubmit={handleSubmitSurvey} /> )} {selectedEvent && ( setSelectedEvent(null)} onLikeChange={handleEventLikeChange} /> )} {selectedAnnouncement && ( setSelectedAnnouncement(null)} onLikeChange={handleAnnouncementLikeChange} /> )}
) } export default IntranetDashboard