akfatimeline 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/Timeline.js +16 -48
  2. package/{src/components/Timeline/DailyView.js → dist/components/Timeline/DailyView.jsx} +1 -0
  3. package/{src/components/Timeline/EventTooltip.js → dist/components/Timeline/EventTooltip.jsx} +207 -206
  4. package/{src/components/Timeline/Indicator.js → dist/components/Timeline/Indicator.jsx} +27 -26
  5. package/{src/components/Timeline/MasterHeader.js → dist/components/Timeline/MasterHeader.jsx} +105 -104
  6. package/{src/components/Timeline/Resources.js → dist/components/Timeline/Resources.jsx} +54 -53
  7. package/{src/components/Timeline/ResourcesHeader.js → dist/components/Timeline/ResourcesHeader.jsx} +15 -14
  8. package/{src/components/Timeline/Timeline.js → dist/components/Timeline/Timeline.jsx} +572 -607
  9. package/{src/components/Timeline/TimelineContent.js → dist/components/Timeline/TimelineContent.jsx} +837 -838
  10. package/{src/components/Timeline/TimelineHeader.js → dist/components/Timeline/TimelineHeader.jsx} +55 -54
  11. package/dist/components/Timeline/TimelineMonthContainer.js +2 -2
  12. package/package.json +25 -25
  13. package/src/components/Timeline/AutocompleteSelect.jsx +150 -0
  14. package/src/components/Timeline/ContextMenu.jsx +149 -0
  15. package/src/components/Timeline/DailyView.jsx +256 -0
  16. package/src/components/Timeline/EventBadge.jsx +26 -0
  17. package/src/components/Timeline/EventDetailModal.jsx +138 -0
  18. package/src/components/Timeline/EventIcon.jsx +47 -0
  19. package/src/components/Timeline/EventTooltip.jsx +207 -0
  20. package/src/components/Timeline/Indicator.jsx +27 -0
  21. package/src/components/Timeline/LoadingSpinner.jsx +48 -0
  22. package/src/components/Timeline/MasterHeader.jsx +105 -0
  23. package/src/components/Timeline/Resources.jsx +54 -0
  24. package/src/components/Timeline/ResourcesHeader.jsx +15 -0
  25. package/src/components/Timeline/Timeline.jsx +572 -0
  26. package/src/components/Timeline/TimelineContent.jsx +837 -0
  27. package/src/components/Timeline/TimelineHeader.jsx +55 -0
  28. package/src/components/Timeline/TimelineMonthContainer.js +2 -2
  29. package/src/library.js +8 -8
  30. /package/{src/components/Timeline/AutocompleteSelect.js → dist/components/Timeline/AutocompleteSelect.jsx} +0 -0
  31. /package/{src/components/Timeline/ContextMenu.js → dist/components/Timeline/ContextMenu.jsx} +0 -0
  32. /package/{src/components/Timeline/EventBadge.js → dist/components/Timeline/EventBadge.jsx} +0 -0
  33. /package/{src/components/Timeline/EventDetailModal.js → dist/components/Timeline/EventDetailModal.jsx} +0 -0
  34. /package/{src/components/Timeline/EventIcon.js → dist/components/Timeline/EventIcon.jsx} +0 -0
  35. /package/{src/components/Timeline/LoadingSpinner.js → dist/components/Timeline/LoadingSpinner.jsx} +0 -0
@@ -1,607 +1,572 @@
1
- import React, { useState, useEffect, useRef, useCallback } from "react";
2
- import MasterHeader from "./MasterHeader";
3
- import ResourcesHeader from "./ResourcesHeader";
4
- import Resources from "./Resources";
5
- import TimelineHeader from "./TimelineHeader";
6
- import TimelineContent from "./TimelineContent";
7
- import "./Timeline.css";
8
- import EventTooltip from "./EventTooltip";
9
- import EventDetailModal from "./EventDetailModal";
10
- import DailyView from "./DailyView";
11
- import { generateTimelineData } from "../../utils/timelineUtils";
12
- import { useTouchGestures } from "../../hooks/useTouchGestures";
13
- import useKeyboardShortcuts from "../../hooks/useKeyboardShortcuts";
14
- import useEventManagement from "../../hooks/useEventManagement";
15
-
16
- const Timeline = ({
17
- resources,
18
- programDate = null,
19
- events = [],
20
- resourceSettings = {
21
- showIdAsName: false,
22
- isGrouped: true,
23
- isCollapsible: true,
24
- },
25
- indicatorOn = false,
26
- dropInfo,
27
- setDropInfo,
28
-
29
- masterHeaderView = true,
30
- resourceHeaderContent = "Akfa Timeline", // String veya React component olabilir
31
- eventsDragOn = true,
32
- eventsExtendOn = true,
33
- createNewEventOn = true,
34
- onDragInfo,
35
- onExtendInfo,
36
- onCreateEventInfo,
37
- // İsteğe bağlı event tıklama callback'leri
38
- onEventClick,
39
- onEventRightClick,
40
-
41
- // Yatay scroll özelliği aç/kapa
42
- horizontalScrollOn = false, // Varsayılan false
43
-
44
- dayRange = 30,
45
- setDayRange,
46
- themeType = "light", // Tema bilgisi varsayılan olarak light
47
-
48
- // Zoom özelliği
49
- zoomLevel = 1.0, // Zoom seviyesi (0.5 = %50, 1.0 = %100, 2.0 = %200)
50
- setZoomLevel, // Zoom seviyesini değiştiren fonksiyon
51
- zoomOn = true, // Zoom özelliğini aç/kapa
52
- minZoomLevel = 0.5, // Minimum zoom seviyesi
53
- maxZoomLevel = 3.0, // Maksimum zoom seviyesi
54
- zoomStep = 0.25, // Her zoom adımında değişecek miktar
55
-
56
- eventTooltipOn = true,
57
- tooltipComponent: TooltipComponent, // Özelleştirilebilir Tooltip bileşeni
58
- tempEventStyle = {},
59
- eventStyleResolver = () => ({}),
60
- indicatorDate = new Date(),
61
- onToday,
62
- onAdvance,
63
- onRetreat,
64
- onMonthAdvance,
65
- onMonthRetreat,
66
-
67
- // Event Alignment Mode
68
- eventAlignmentMode = "center", // "center" | "full" - center: gün ortasından başlar, full: gün başından başlar
69
-
70
- // Past Date Protection
71
- preventPastEvents = false, // Geçmiş tarihlere rezervasyon oluşturmayı engelle
72
- minDate = null, // Minimum tarih (eğer belirtilmezse indicatorDate kullanılır)
73
-
74
- // Weekend Highlighting
75
- highlightWeekends = false, // Hafta sonlarını farklı renkte göster
76
-
77
- // Cell Tooltip
78
- cellTooltipOn = false, // Cell tooltip'lerini aktif et
79
- cellTooltipResolver = null, // (resource, date) => tooltip content döndüren fonksiyon
80
-
81
- // Cell Context Menu
82
- cellContextMenuOn = false, // Cell context menu'yu aç/kapa
83
- cellContextMenuItems = [], // Context menu öğeleri: [{ id, label, icon, onClick, disabled, separator, danger, shortcut, tooltip, hidden }]
84
- onCellContextMenu = null, // Context menu açıldığında çağrılacak callback: (resource, date, event) => {}
85
-
86
- // Daily View
87
- dailyViewOn = true, // Daily view özelliğini aç/kapa
88
-
89
- // Event Icons & Badges
90
- eventIconsOn = false, // Event ikonlarını göster/gizle
91
- eventIconResolver = null, // (event) => icon type döndüren fonksiyon
92
- eventBadgesOn = false, // Event badge'lerini göster/gizle
93
- eventBadgeResolver = null, // (event) => { text, type, position } döndüren fonksiyon
94
-
95
- // Loading State
96
- isLoading = false, // Timeline yükleniyor mu?
97
- loadingType = 'spinner', // 'spinner', 'dots', 'pulse'
98
-
99
- // Event Management
100
- eventManagementOn = false, // Event yönetimi özelliklerini aktif et
101
- onEventDelete = null, // Event silme callback
102
- onEventUpdate = null, // Event güncelleme callback
103
- onEventCopy = null, // Event kopyalama callback
104
- onEventPaste = null, // Event yapıştırma callback
105
-
106
- // Keyboard Shortcuts
107
- keyboardShortcutsOn = false, // Keyboard shortcuts'ları aktif et
108
- keyboardShortcutsConfig = {
109
- onNavigateLeft: null,
110
- onNavigateRight: null,
111
- onNavigateUp: null,
112
- onNavigateDown: null,
113
- onDelete: null,
114
- onUndo: null,
115
- onRedo: null,
116
- onCopy: null,
117
- onPaste: null,
118
- },
119
- keyboardShortcutsKeyMap = {}, // Özelleştirilebilir tuş haritası
120
- }) => {
121
- // ---------------------------------------------------------
122
- // 1) timelineData oluştur (dates, monthHeaders vs.)
123
- // ---------------------------------------------------------
124
- const timelineData = generateTimelineData(2020, 2030); // 10 yıllık veri
125
- const { dates, monthHeaders } = timelineData;
126
- const [selectedDate, setSelectedDate] = useState(() => {
127
- const date = programDate ? new Date(programDate) : new Date();
128
- date.setDate(date.getDate() - 3); // Program tarihinden 3 gün öncesini al
129
- return date;
130
- });
131
-
132
- // ---------------------------------------------------------
133
- // 2) local state
134
- // ---------------------------------------------------------
135
- const [collapsedGroups, setCollapsedGroups] = useState({});
136
-
137
- // Event Management
138
- const eventManagement = useEventManagement(
139
- events,
140
- (newEvents) => {
141
- setLocalEvents(newEvents);
142
- if (onEventUpdate) {
143
- onEventUpdate(newEvents);
144
- }
145
- }
146
- );
147
-
148
- const [localEvents, setLocalEvents] = useState(events);
149
-
150
- // Update local events when events prop changes
151
- useEffect(() => {
152
- setLocalEvents(events);
153
- }, [events]);
154
-
155
- const [selectedEvent, setSelectedEvent] = useState(null);
156
- const [tooltipPosition] = useState({ top: 0, left: 0 });
157
- const [editingEvent, setEditingEvent] = useState(null);
158
- const [isModalOpen, setIsModalOpen] = useState(false);
159
-
160
- // Daily View State
161
- const [dailyView, setDailyView] = useState({
162
- isOpen: false,
163
- resource: null,
164
- date: null,
165
- });
166
-
167
- // dayRange = ekranda göstermeyi istediğimiz gün/hücre sayısı (ör. 30 gün)
168
-
169
- const [isDarkMode, setIsDarkMode] = useState(themeType === "dark");
170
-
171
- useEffect(() => {
172
- if (themeType !== undefined) {
173
- setIsDarkMode(themeType === "dark");
174
- }
175
- }, [themeType]);
176
-
177
- // toggleDarkMode removed - not used
178
- // ---------------------------------------------------------
179
- // 3) Zoom seviyesine göre hücre genişliği
180
- // Container genişliği = dayRange * cellWidth
181
- // ---------------------------------------------------------
182
- const baseCellWidth = 56.95; // Temel hücre genişliği (zoom = 1.0 için)
183
- const cellWidth = baseCellWidth * zoomLevel; // Zoom seviyesine göre hücre genişliği
184
- const containerWidth = dayRange * cellWidth;
185
-
186
- // ---------------------------------------------------------
187
- // 4) Touch Gestures (Mobil desteği)
188
- // ---------------------------------------------------------
189
- const timelineContainerRef = useRef(null);
190
- const touchGestures = useTouchGestures({
191
- onSwipeLeft: () => {
192
- if (onAdvance) onAdvance();
193
- },
194
- onSwipeRight: () => {
195
- if (onRetreat) onRetreat();
196
- },
197
- enabled: true,
198
- });
199
- // örneğin dayRange=30 => containerWidth=30*56.95=1708.5 px
200
-
201
- // ---------------------------------------------------------
202
- // 4) Event Tooltip logic
203
- // ---------------------------------------------------------
204
- // handleEventClick removed - handled in TimelineContent
205
-
206
- const handleCloseTooltip = () => {
207
- setSelectedEvent(null);
208
- };
209
-
210
- // Event Management Handlers
211
- const handleEventDoubleClick = (event) => {
212
- if (eventManagementOn) {
213
- setEditingEvent(event);
214
- setIsModalOpen(true);
215
- }
216
- };
217
-
218
- const handleEventRightClickInternal = (event, e) => {
219
- if (eventManagementOn) {
220
- e.preventDefault();
221
- eventManagement.selectEvent(event.id, false);
222
- }
223
- if (onEventRightClick) {
224
- onEventRightClick(event, e);
225
- }
226
- };
227
-
228
- const handleEventSave = (updatedEvent) => {
229
- if (eventManagementOn) {
230
- const newEvents = localEvents.map((ev) =>
231
- ev.id === updatedEvent.id ? updatedEvent : ev
232
- );
233
- eventManagement.setEvents(newEvents, true);
234
- if (onEventUpdate) {
235
- onEventUpdate(newEvents);
236
- }
237
- }
238
- setIsModalOpen(false);
239
- setEditingEvent(null);
240
- };
241
-
242
- const handleEventDelete = (eventId) => {
243
- if (eventManagementOn) {
244
- const newEvents = localEvents.filter((ev) => ev.id !== eventId);
245
- eventManagement.setEvents(newEvents, true);
246
- if (onEventDelete) {
247
- onEventDelete([eventId]);
248
- }
249
- }
250
- setIsModalOpen(false);
251
- setEditingEvent(null);
252
- };
253
-
254
- // ---------------------------------------------------------
255
- // 5) Tarih filtreleme => filteredDates
256
- // ---------------------------------------------------------
257
- const startIndex = dates.findIndex((d) => d.fullDate >= selectedDate);
258
- const endIndex = startIndex + dayRange;
259
- const filteredDates =
260
- startIndex !== -1 ? dates.slice(startIndex, Math.min(endIndex, dates.length)) : [];
261
-
262
- const today = programDate ? new Date(programDate) : new Date();
263
- today.setDate(today.getDate() - 3);
264
-
265
-
266
-
267
- const todayIndex = filteredDates.findIndex(
268
- (d) => new Date(d.fullDate).toDateString() === new Date(indicatorDate).toDateString()
269
- );
270
-
271
- // ---------------------------------------------------------
272
- // 6) Grupları aç/kapa
273
- // ---------------------------------------------------------
274
- const toggleGroupCollapse = (groupName) => {
275
- setCollapsedGroups((prev) => ({
276
- ...prev,
277
- [groupName]: !prev[groupName],
278
- }));
279
- };
280
-
281
- // ---------------------------------------------------------
282
- // 7) Navigation fonksiyonları
283
- // --------------------------------------------------------- const { dates, monthHeaders } = timelineData;
284
-
285
-
286
-
287
- const handleDateChange = (newDate) => {
288
- setSelectedDate(new Date(newDate));
289
- };
290
-
291
- const handleToday = () => {
292
- const today = programDate ? new Date(programDate) : new Date();
293
- today.setDate(today.getDate() - 3); // Program tarihinden 3 gün öncesini ayarla
294
- setSelectedDate(today);
295
- };
296
-
297
-
298
-
299
- const handleAdvance = () => {
300
- setSelectedDate((prev) => new Date(prev.getTime() + 5 * 24 * 60 * 60 * 1000));
301
- };
302
-
303
- const handleRetreat = () => {
304
- setSelectedDate((prev) => new Date(prev.getTime() - 5 * 24 * 60 * 60 * 1000));
305
- };
306
-
307
- const handleMonthAdvance = () => {
308
- setSelectedDate((prev) => {
309
- const newDate = new Date(prev);
310
- newDate.setMonth(newDate.getMonth() + 1);
311
- return newDate;
312
- });
313
- };
314
-
315
- const handleMonthRetreat = () => {
316
- setSelectedDate((prev) => {
317
- const newDate = new Date(prev);
318
- newDate.setMonth(newDate.getMonth() - 1);
319
- return newDate;
320
- });
321
- };
322
-
323
- // ---------------------------------------------------------
324
- // 8) Dark Mode
325
- // ---------------------------------------------------------
326
-
327
-
328
- // ---------------------------------------------------------
329
- // 8.5) Zoom handlers
330
- // ---------------------------------------------------------
331
- const handleZoomIn = useCallback(() => {
332
- if (setZoomLevel && zoomOn) {
333
- setZoomLevel((prev) => Math.min(maxZoomLevel, prev + zoomStep));
334
- }
335
- }, [setZoomLevel, zoomOn, maxZoomLevel, zoomStep]);
336
-
337
- const handleZoomOut = useCallback(() => {
338
- if (setZoomLevel && zoomOn) {
339
- setZoomLevel((prev) => Math.max(minZoomLevel, prev - zoomStep));
340
- }
341
- }, [setZoomLevel, zoomOn, minZoomLevel, zoomStep]);
342
-
343
- // ---------------------------------------------------------
344
- // 8.6) Keyboard Shortcuts
345
- // ---------------------------------------------------------
346
- useKeyboardShortcuts({
347
- enabled: keyboardShortcutsOn,
348
- onNavigateLeft: keyboardShortcutsConfig.onNavigateLeft || onRetreat,
349
- onNavigateRight: keyboardShortcutsConfig.onNavigateRight || onAdvance,
350
- onNavigateUp: keyboardShortcutsConfig.onNavigateUp || (() => console.log("Navigate Up")),
351
- onNavigateDown: keyboardShortcutsConfig.onNavigateDown || (() => console.log("Navigate Down")),
352
- onDelete: keyboardShortcutsConfig.onDelete || (eventManagementOn ? eventManagement.deleteSelectedEvents : null),
353
- onUndo: keyboardShortcutsConfig.onUndo || (eventManagementOn ? eventManagement.undo : null),
354
- onRedo: keyboardShortcutsConfig.onRedo || (eventManagementOn ? eventManagement.redo : null),
355
- onCopy: keyboardShortcutsConfig.onCopy || (eventManagementOn ? eventManagement.copySelectedEvents : null),
356
- onPaste: keyboardShortcutsConfig.onPaste || null,
357
- onZoomIn: keyboardShortcutsConfig.onZoomIn || (zoomOn ? handleZoomIn : null),
358
- onZoomOut: keyboardShortcutsConfig.onZoomOut || (zoomOn ? handleZoomOut : null),
359
- keyMap: keyboardShortcutsKeyMap,
360
- });
361
-
362
- // ---------------------------------------------------------
363
- // 9) Ay başlıklarını filtrele
364
- // ---------------------------------------------------------
365
- const filteredMonthHeaders = monthHeaders
366
- .map((header) => {
367
- const adjustedStartIndex = Math.max(header.startIndex, startIndex);
368
- const adjustedEndIndex = Math.min(header.endIndex, endIndex - 1);
369
- return {
370
- ...header,
371
- startIndex: adjustedStartIndex,
372
- endIndex: adjustedEndIndex,
373
- };
374
- })
375
- .filter((header) => header.startIndex <= header.endIndex);
376
-
377
- // ---------------------------------------------------------
378
- // 10) Return
379
- // ---------------------------------------------------------
380
- return (
381
- <div
382
- className={`timeline-container ${isDarkMode ? "dark-mode" : ""}`}
383
- ref={timelineContainerRef}
384
- {...touchGestures}
385
- >
386
- {/* Üst kısım: MasterHeader */}
387
- {masterHeaderView && (
388
- <div className="timeline-master-header">
389
- <MasterHeader
390
- selectedDate={selectedDate} // Seçili tarihi gönder
391
- onDateSelect={handleDateChange}
392
- onToday={handleToday}
393
- onAdvance={handleAdvance}
394
- onRetreat={handleRetreat}
395
- onMonthAdvance={handleMonthAdvance}
396
- onMonthRetreat={handleMonthRetreat}
397
- dayRange={dayRange}
398
- setDayRange={setDayRange}
399
- zoomLevel={zoomLevel}
400
- setZoomLevel={setZoomLevel}
401
- zoomOn={zoomOn}
402
- minZoomLevel={minZoomLevel}
403
- maxZoomLevel={maxZoomLevel}
404
- zoomStep={zoomStep}
405
- />
406
- </div>
407
- )}
408
- {/* Body: Sol kısım => Resources, Sağ kısım => timeline */}
409
- <div className="timeline-body">
410
- <div className="timeline-resources-container"
411
- style={{ overflow: "hidden"}}>
412
- <ResourcesHeader content={resourceHeaderContent} />
413
- <Resources
414
- groupedResources={resources}
415
- toggleGroupCollapse={toggleGroupCollapse}
416
- collapsedGroups={collapsedGroups}
417
- resourceSettings={resourceSettings}
418
- />
419
- </div>
420
-
421
-
422
- <div
423
- className="timeline-scrollable-container"
424
- style={{
425
- overflowX: horizontalScrollOn ? "auto" : "hidden",
426
- }}
427
- >
428
-
429
- <div
430
- className="timeline-header-content-wrapper"
431
- style={{
432
- width: horizontalScrollOn ? `${containerWidth}px` : "100%",
433
- }}
434
- >
435
- <TimelineHeader
436
- dates={filteredDates}
437
- monthHeaders={filteredMonthHeaders}
438
- highlightWeekends={highlightWeekends}
439
- />
440
-
441
- <TimelineContent
442
- groupedResources={resources}
443
- dates={filteredDates}
444
- collapsedGroups={collapsedGroups}
445
- events={localEvents}
446
- setEvents={setLocalEvents}
447
- onEventClick={onEventClick}
448
- onEventDoubleClick={eventManagementOn ? handleEventDoubleClick : null}
449
- onEventRightClick={eventManagementOn ? handleEventRightClickInternal : onEventRightClick}
450
- selectedEvents={eventManagementOn ? eventManagement.selectedEvents : []}
451
- onEventSelect={eventManagementOn ? eventManagement.selectEvent : null}
452
- todayIndex={todayIndex}
453
- indicatorOn={indicatorOn}
454
- resourceSettings={resourceSettings}
455
- toggleGroupCollapse={toggleGroupCollapse}
456
- setDropInfo={setDropInfo}
457
- eventsDragOn={eventsDragOn}
458
- eventsExtendOn={eventsExtendOn}
459
- createNewEventOn={createNewEventOn}
460
- onDragInfo={onDragInfo}
461
- onExtendInfo={onExtendInfo}
462
- onCreateEventInfo={onCreateEventInfo}
463
- onEventRightClick={onEventRightClick}
464
- eventTooltipOn={eventTooltipOn} // Tooltip kontrolü
465
- tooltipComponent={TooltipComponent} // Özelleştirilebilir Tooltip bileşeni
466
- tempEventStyle = {tempEventStyle}
467
- eventStyleResolver={eventStyleResolver}
468
- eventAlignmentMode={eventAlignmentMode}
469
- preventPastEvents={preventPastEvents}
470
- minDate={minDate || indicatorDate}
471
- highlightWeekends={highlightWeekends}
472
- cellTooltipOn={cellTooltipOn}
473
- cellTooltipResolver={cellTooltipResolver}
474
- cellContextMenuOn={cellContextMenuOn}
475
- cellContextMenuItems={cellContextMenuItems.map(item => {
476
- // Daily timeline item'ını özel handle et
477
- if (item.id === 'daily-timeline' && dailyViewOn) {
478
- return {
479
- ...item,
480
- onClick: (resource, date) => {
481
- setDailyView({
482
- isOpen: true,
483
- resource,
484
- date,
485
- });
486
- if (item.onClick) {
487
- item.onClick(resource, date);
488
- }
489
- },
490
- };
491
- }
492
- return item;
493
- })}
494
- onCellContextMenu={onCellContextMenu}
495
- eventIconsOn={eventIconsOn}
496
- eventIconResolver={eventIconResolver}
497
- eventBadgesOn={eventBadgesOn}
498
- eventBadgeResolver={eventBadgeResolver}
499
- isLoading={isLoading}
500
- loadingType={loadingType}
501
-
502
- />
503
-
504
- {/* Tooltip */}
505
- {selectedEvent && (
506
- <EventTooltip
507
- event={selectedEvent}
508
- position={tooltipPosition}
509
- onClose={handleCloseTooltip}
510
- onDelete={(eventId) =>
511
- setLocalEvents((prev) => prev.filter((e) => e.id !== eventId))
512
- }
513
- />
514
- )}
515
- </div>
516
- </div>
517
- </div>
518
-
519
- {/* Event Detail Modal */}
520
- {eventManagementOn && (
521
- <EventDetailModal
522
- event={editingEvent}
523
- isOpen={isModalOpen}
524
- onClose={() => {
525
- setIsModalOpen(false);
526
- setEditingEvent(null);
527
- }}
528
- onSave={handleEventSave}
529
- onDelete={handleEventDelete}
530
- resources={resources.flatMap((group) => group.resources || [])}
531
- />
532
- )}
533
-
534
- {/* Daily View */}
535
- {dailyViewOn && (
536
- <DailyView
537
- isOpen={dailyView.isOpen}
538
- onClose={() => setDailyView({ isOpen: false, resource: null, date: null })}
539
- resource={dailyView.resource}
540
- date={dailyView.date}
541
- events={localEvents}
542
- onEventCreate={(newEvent) => {
543
- const updatedEvents = [...localEvents, newEvent];
544
- setLocalEvents(updatedEvents);
545
- if (onEventUpdate) {
546
- onEventUpdate(updatedEvents);
547
- }
548
- }}
549
- onEventUpdate={(updatedEvent) => {
550
- const updatedEvents = localEvents.map(e =>
551
- e.id === updatedEvent.id ? updatedEvent : e
552
- );
553
- setLocalEvents(updatedEvents);
554
- if (onEventUpdate) {
555
- onEventUpdate(updatedEvents);
556
- }
557
- }}
558
- onEventDelete={(eventId) => {
559
- const updatedEvents = localEvents.filter(e => e.id !== eventId);
560
- setLocalEvents(updatedEvents);
561
- if (onEventUpdate) {
562
- onEventUpdate(updatedEvents);
563
- }
564
- }}
565
- themeType={themeType}
566
- />
567
- )}
568
-
569
- {/* Daily View */}
570
- {dailyViewOn && (
571
- <DailyView
572
- isOpen={dailyView.isOpen}
573
- onClose={() => setDailyView({ isOpen: false, resource: null, date: null })}
574
- resource={dailyView.resource}
575
- date={dailyView.date}
576
- events={localEvents}
577
- onEventCreate={(newEvent) => {
578
- const updatedEvents = [...localEvents, newEvent];
579
- setLocalEvents(updatedEvents);
580
- if (onEventUpdate) {
581
- onEventUpdate(updatedEvents);
582
- }
583
- }}
584
- onEventUpdate={(updatedEvent) => {
585
- const updatedEvents = localEvents.map(e =>
586
- e.id === updatedEvent.id ? updatedEvent : e
587
- );
588
- setLocalEvents(updatedEvents);
589
- if (onEventUpdate) {
590
- onEventUpdate(updatedEvents);
591
- }
592
- }}
593
- onEventDelete={(eventId) => {
594
- const updatedEvents = localEvents.filter(e => e.id !== eventId);
595
- setLocalEvents(updatedEvents);
596
- if (onEventUpdate) {
597
- onEventUpdate(updatedEvents);
598
- }
599
- }}
600
- themeType={themeType}
601
- />
602
- )}
603
- </div>
604
- );
605
- };
606
-
607
- export default Timeline;
1
+ import React, { useState, useEffect, useRef, useCallback } from "react";
2
+ import MasterHeader from "./MasterHeader.jsx";
3
+ import ResourcesHeader from "./ResourcesHeader.jsx";
4
+ import Resources from "./Resources.jsx";
5
+ import TimelineHeader from "./TimelineHeader.jsx";
6
+ import TimelineContent from "./TimelineContent.jsx";
7
+ import "./Timeline.css";
8
+ import EventTooltip from "./EventTooltip.jsx";
9
+ import EventDetailModal from "./EventDetailModal.jsx";
10
+ import DailyView from "./DailyView.jsx";
11
+ import { generateTimelineData } from "../../utils/timelineUtils";
12
+ import { useTouchGestures } from "../../hooks/useTouchGestures";
13
+ import useKeyboardShortcuts from "../../hooks/useKeyboardShortcuts";
14
+ import useEventManagement from "../../hooks/useEventManagement";
15
+
16
+ const Timeline = ({
17
+ resources,
18
+ programDate = null,
19
+ events = [],
20
+ resourceSettings = {
21
+ showIdAsName: false,
22
+ isGrouped: true,
23
+ isCollapsible: true,
24
+ },
25
+ indicatorOn = false,
26
+ dropInfo,
27
+ setDropInfo,
28
+
29
+ masterHeaderView = true,
30
+ resourceHeaderContent = "Akfa Timeline", // String veya React component olabilir
31
+ eventsDragOn = true,
32
+ eventsExtendOn = true,
33
+ createNewEventOn = true,
34
+ onDragInfo,
35
+ onExtendInfo,
36
+ onCreateEventInfo,
37
+ // İsteğe bağlı event tıklama callback'leri
38
+ onEventClick,
39
+ onEventRightClick,
40
+
41
+ // Yatay scroll özelliği aç/kapa
42
+ horizontalScrollOn = false, // Varsayılan false
43
+
44
+ dayRange = 30,
45
+ setDayRange,
46
+ themeType = "light", // Tema bilgisi varsayılan olarak light
47
+
48
+ // Zoom özelliği
49
+ zoomLevel = 1.0, // Zoom seviyesi (0.5 = %50, 1.0 = %100, 2.0 = %200)
50
+ setZoomLevel, // Zoom seviyesini değiştiren fonksiyon
51
+ zoomOn = true, // Zoom özelliğini aç/kapa
52
+ minZoomLevel = 0.5, // Minimum zoom seviyesi
53
+ maxZoomLevel = 3.0, // Maksimum zoom seviyesi
54
+ zoomStep = 0.25, // Her zoom adımında değişecek miktar
55
+
56
+ eventTooltipOn = true,
57
+ tooltipComponent: TooltipComponent, // Özelleştirilebilir Tooltip bileşeni
58
+ tempEventStyle = {},
59
+ eventStyleResolver = () => ({}),
60
+ indicatorDate = new Date(),
61
+ onToday,
62
+ onAdvance,
63
+ onRetreat,
64
+ onMonthAdvance,
65
+ onMonthRetreat,
66
+
67
+ // Event Alignment Mode
68
+ eventAlignmentMode = "center", // "center" | "full" - center: gün ortasından başlar, full: gün başından başlar
69
+
70
+ // Past Date Protection
71
+ preventPastEvents = false, // Geçmiş tarihlere rezervasyon oluşturmayı engelle
72
+ minDate = null, // Minimum tarih (eğer belirtilmezse indicatorDate kullanılır)
73
+
74
+ // Weekend Highlighting
75
+ highlightWeekends = false, // Hafta sonlarını farklı renkte göster
76
+
77
+ // Cell Tooltip
78
+ cellTooltipOn = false, // Cell tooltip'lerini aktif et
79
+ cellTooltipResolver = null, // (resource, date) => tooltip content döndüren fonksiyon
80
+
81
+ // Cell Context Menu
82
+ cellContextMenuOn = false, // Cell context menu'yu aç/kapa
83
+ cellContextMenuItems = [], // Context menu öğeleri: [{ id, label, icon, onClick, disabled, separator, danger, shortcut, tooltip, hidden }]
84
+ onCellContextMenu = null, // Context menu açıldığında çağrılacak callback: (resource, date, event) => {}
85
+
86
+ // Daily View
87
+ dailyViewOn = true, // Daily view özelliğini aç/kapa
88
+
89
+ // Event Icons & Badges
90
+ eventIconsOn = false, // Event ikonlarını göster/gizle
91
+ eventIconResolver = null, // (event) => icon type döndüren fonksiyon
92
+ eventBadgesOn = false, // Event badge'lerini göster/gizle
93
+ eventBadgeResolver = null, // (event) => { text, type, position } döndüren fonksiyon
94
+
95
+ // Loading State
96
+ isLoading = false, // Timeline yükleniyor mu?
97
+ loadingType = 'spinner', // 'spinner', 'dots', 'pulse'
98
+
99
+ // Event Management
100
+ eventManagementOn = false, // Event yönetimi özelliklerini aktif et
101
+ onEventDelete = null, // Event silme callback
102
+ onEventUpdate = null, // Event güncelleme callback
103
+ onEventCopy = null, // Event kopyalama callback
104
+ onEventPaste = null, // Event yapıştırma callback
105
+
106
+ // Keyboard Shortcuts
107
+ keyboardShortcutsOn = false, // Keyboard shortcuts'ları aktif et
108
+ keyboardShortcutsConfig = {
109
+ onNavigateLeft: null,
110
+ onNavigateRight: null,
111
+ onNavigateUp: null,
112
+ onNavigateDown: null,
113
+ onDelete: null,
114
+ onUndo: null,
115
+ onRedo: null,
116
+ onCopy: null,
117
+ onPaste: null,
118
+ },
119
+ keyboardShortcutsKeyMap = {}, // Özelleştirilebilir tuş haritası
120
+ }) => {
121
+ // ---------------------------------------------------------
122
+ // 1) timelineData oluştur (dates, monthHeaders vs.)
123
+ // ---------------------------------------------------------
124
+ const timelineData = generateTimelineData(2020, 2030); // 10 yıllık veri
125
+ const { dates, monthHeaders } = timelineData;
126
+ const [selectedDate, setSelectedDate] = useState(() => {
127
+ const date = programDate ? new Date(programDate) : new Date();
128
+ date.setDate(date.getDate() - 3); // Program tarihinden 3 gün öncesini al
129
+ return date;
130
+ });
131
+
132
+ // ---------------------------------------------------------
133
+ // 2) local state
134
+ // ---------------------------------------------------------
135
+ const [collapsedGroups, setCollapsedGroups] = useState({});
136
+
137
+ // Event Management
138
+ const eventManagement = useEventManagement(
139
+ events,
140
+ (newEvents) => {
141
+ setLocalEvents(newEvents);
142
+ if (onEventUpdate) {
143
+ onEventUpdate(newEvents);
144
+ }
145
+ }
146
+ );
147
+
148
+ const [localEvents, setLocalEvents] = useState(events);
149
+
150
+ // Update local events when events prop changes
151
+ useEffect(() => {
152
+ setLocalEvents(events);
153
+ }, [events]);
154
+
155
+ const [selectedEvent, setSelectedEvent] = useState(null);
156
+ const [tooltipPosition] = useState({ top: 0, left: 0 });
157
+ const [editingEvent, setEditingEvent] = useState(null);
158
+ const [isModalOpen, setIsModalOpen] = useState(false);
159
+
160
+ // Daily View State
161
+ const [dailyView, setDailyView] = useState({
162
+ isOpen: false,
163
+ resource: null,
164
+ date: null,
165
+ });
166
+
167
+ // dayRange = ekranda göstermeyi istediğimiz gün/hücre sayısı (ör. 30 gün)
168
+
169
+ const [isDarkMode, setIsDarkMode] = useState(themeType === "dark");
170
+
171
+ useEffect(() => {
172
+ if (themeType !== undefined) {
173
+ setIsDarkMode(themeType === "dark");
174
+ }
175
+ }, [themeType]);
176
+
177
+ // toggleDarkMode removed - not used
178
+ // ---------------------------------------------------------
179
+ // 3) Zoom seviyesine göre hücre genişliği
180
+ // Container genişliği = dayRange * cellWidth
181
+ // ---------------------------------------------------------
182
+ const baseCellWidth = 56.95; // Temel hücre genişliği (zoom = 1.0 için)
183
+ const cellWidth = baseCellWidth * zoomLevel; // Zoom seviyesine göre hücre genişliği
184
+ const containerWidth = dayRange * cellWidth;
185
+
186
+ // ---------------------------------------------------------
187
+ // 4) Touch Gestures (Mobil desteği)
188
+ // ---------------------------------------------------------
189
+ const timelineContainerRef = useRef(null);
190
+ const touchGestures = useTouchGestures({
191
+ onSwipeLeft: () => {
192
+ if (onAdvance) onAdvance();
193
+ },
194
+ onSwipeRight: () => {
195
+ if (onRetreat) onRetreat();
196
+ },
197
+ enabled: true,
198
+ });
199
+ // örneğin dayRange=30 => containerWidth=30*56.95=1708.5 px
200
+
201
+ // ---------------------------------------------------------
202
+ // 4) Event Tooltip logic
203
+ // ---------------------------------------------------------
204
+ // handleEventClick removed - handled in TimelineContent
205
+
206
+ const handleCloseTooltip = () => {
207
+ setSelectedEvent(null);
208
+ };
209
+
210
+ // Event Management Handlers
211
+ const handleEventDoubleClick = (event) => {
212
+ if (eventManagementOn) {
213
+ setEditingEvent(event);
214
+ setIsModalOpen(true);
215
+ }
216
+ };
217
+
218
+ const handleEventRightClickInternal = (event, e) => {
219
+ if (eventManagementOn) {
220
+ e.preventDefault();
221
+ eventManagement.selectEvent(event.id, false);
222
+ }
223
+ if (onEventRightClick) {
224
+ onEventRightClick(event, e);
225
+ }
226
+ };
227
+
228
+ const handleEventSave = (updatedEvent) => {
229
+ if (eventManagementOn) {
230
+ const newEvents = localEvents.map((ev) =>
231
+ ev.id === updatedEvent.id ? updatedEvent : ev
232
+ );
233
+ eventManagement.setEvents(newEvents, true);
234
+ if (onEventUpdate) {
235
+ onEventUpdate(newEvents);
236
+ }
237
+ }
238
+ setIsModalOpen(false);
239
+ setEditingEvent(null);
240
+ };
241
+
242
+ const handleEventDelete = (eventId) => {
243
+ if (eventManagementOn) {
244
+ const newEvents = localEvents.filter((ev) => ev.id !== eventId);
245
+ eventManagement.setEvents(newEvents, true);
246
+ if (onEventDelete) {
247
+ onEventDelete([eventId]);
248
+ }
249
+ }
250
+ setIsModalOpen(false);
251
+ setEditingEvent(null);
252
+ };
253
+
254
+ // ---------------------------------------------------------
255
+ // 5) Tarih filtreleme => filteredDates
256
+ // ---------------------------------------------------------
257
+ const startIndex = dates.findIndex((d) => d.fullDate >= selectedDate);
258
+ const endIndex = startIndex + dayRange;
259
+ const filteredDates =
260
+ startIndex !== -1 ? dates.slice(startIndex, Math.min(endIndex, dates.length)) : [];
261
+
262
+ const today = programDate ? new Date(programDate) : new Date();
263
+ today.setDate(today.getDate() - 3);
264
+
265
+
266
+
267
+ const todayIndex = filteredDates.findIndex(
268
+ (d) => new Date(d.fullDate).toDateString() === new Date(indicatorDate).toDateString()
269
+ );
270
+
271
+ // ---------------------------------------------------------
272
+ // 6) Grupları aç/kapa
273
+ // ---------------------------------------------------------
274
+ const toggleGroupCollapse = (groupName) => {
275
+ setCollapsedGroups((prev) => ({
276
+ ...prev,
277
+ [groupName]: !prev[groupName],
278
+ }));
279
+ };
280
+
281
+ // ---------------------------------------------------------
282
+ // 7) Navigation fonksiyonları
283
+ // --------------------------------------------------------- const { dates, monthHeaders } = timelineData;
284
+
285
+
286
+
287
+ const handleDateChange = (newDate) => {
288
+ setSelectedDate(new Date(newDate));
289
+ };
290
+
291
+ const handleToday = () => {
292
+ const today = programDate ? new Date(programDate) : new Date();
293
+ today.setDate(today.getDate() - 3); // Program tarihinden 3 gün öncesini ayarla
294
+ setSelectedDate(today);
295
+ };
296
+
297
+
298
+ const handleAdvance = () => {
299
+ setSelectedDate((prev) => new Date(prev.getTime() + 5 * 24 * 60 * 60 * 1000));
300
+ };
301
+
302
+ const handleRetreat = () => {
303
+ setSelectedDate((prev) => new Date(prev.getTime() - 5 * 24 * 60 * 60 * 1000));
304
+ };
305
+
306
+ const handleMonthAdvance = () => {
307
+ setSelectedDate((prev) => {
308
+ const newDate = new Date(prev);
309
+ newDate.setMonth(newDate.getMonth() + 1);
310
+ return newDate;
311
+ });
312
+ };
313
+
314
+ const handleMonthRetreat = () => {
315
+ setSelectedDate((prev) => {
316
+ const newDate = new Date(prev);
317
+ newDate.setMonth(newDate.getMonth() - 1);
318
+ return newDate;
319
+ });
320
+ };
321
+
322
+ // ---------------------------------------------------------
323
+ // 8) Dark Mode
324
+ // ---------------------------------------------------------
325
+
326
+
327
+ // ---------------------------------------------------------
328
+ // 8.5) Zoom handlers
329
+ // ---------------------------------------------------------
330
+ const handleZoomIn = useCallback(() => {
331
+ if (setZoomLevel && zoomOn) {
332
+ setZoomLevel((prev) => Math.min(maxZoomLevel, prev + zoomStep));
333
+ }
334
+ }, [setZoomLevel, zoomOn, maxZoomLevel, zoomStep]);
335
+
336
+ const handleZoomOut = useCallback(() => {
337
+ if (setZoomLevel && zoomOn) {
338
+ setZoomLevel((prev) => Math.max(minZoomLevel, prev - zoomStep));
339
+ }
340
+ }, [setZoomLevel, zoomOn, minZoomLevel, zoomStep]);
341
+
342
+ // ---------------------------------------------------------
343
+ // 8.6) Keyboard Shortcuts
344
+ // ---------------------------------------------------------
345
+ useKeyboardShortcuts({
346
+ enabled: keyboardShortcutsOn,
347
+ onNavigateLeft: keyboardShortcutsConfig.onNavigateLeft || onRetreat,
348
+ onNavigateRight: keyboardShortcutsConfig.onNavigateRight || onAdvance,
349
+ onNavigateUp: keyboardShortcutsConfig.onNavigateUp || (() => console.log("Navigate Up")),
350
+ onNavigateDown: keyboardShortcutsConfig.onNavigateDown || (() => console.log("Navigate Down")),
351
+ onDelete: keyboardShortcutsConfig.onDelete || (eventManagementOn ? eventManagement.deleteSelectedEvents : null),
352
+ onUndo: keyboardShortcutsConfig.onUndo || (eventManagementOn ? eventManagement.undo : null),
353
+ onRedo: keyboardShortcutsConfig.onRedo || (eventManagementOn ? eventManagement.redo : null),
354
+ onCopy: keyboardShortcutsConfig.onCopy || (eventManagementOn ? eventManagement.copySelectedEvents : null),
355
+ onPaste: keyboardShortcutsConfig.onPaste || null,
356
+ onZoomIn: keyboardShortcutsConfig.onZoomIn || (zoomOn ? handleZoomIn : null),
357
+ onZoomOut: keyboardShortcutsConfig.onZoomOut || (zoomOn ? handleZoomOut : null),
358
+ keyMap: keyboardShortcutsKeyMap,
359
+ });
360
+
361
+ // ---------------------------------------------------------
362
+ // 9) Ay başlıklarını filtrele
363
+ // ---------------------------------------------------------
364
+ const filteredMonthHeaders = monthHeaders
365
+ .map((header) => {
366
+ const adjustedStartIndex = Math.max(header.startIndex, startIndex);
367
+ const adjustedEndIndex = Math.min(header.endIndex, endIndex - 1);
368
+ return {
369
+ ...header,
370
+ startIndex: adjustedStartIndex,
371
+ endIndex: adjustedEndIndex,
372
+ };
373
+ })
374
+ .filter((header) => header.startIndex <= header.endIndex);
375
+
376
+ // ---------------------------------------------------------
377
+ // 10) Return
378
+ // ---------------------------------------------------------
379
+ return (
380
+ <div
381
+ className={`timeline-container ${isDarkMode ? "dark-mode" : ""}`}
382
+ ref={timelineContainerRef}
383
+ {...touchGestures}
384
+ >
385
+ {/* Üst kısım: MasterHeader */}
386
+ {masterHeaderView && (
387
+ <div className="timeline-master-header">
388
+ <MasterHeader
389
+ selectedDate={selectedDate} // Seçili tarihi gönder
390
+ onDateSelect={handleDateChange}
391
+ onToday={handleToday}
392
+ onAdvance={handleAdvance}
393
+ onRetreat={handleRetreat}
394
+ onMonthAdvance={handleMonthAdvance}
395
+ onMonthRetreat={handleMonthRetreat}
396
+ dayRange={dayRange}
397
+ setDayRange={setDayRange}
398
+ zoomLevel={zoomLevel}
399
+ setZoomLevel={setZoomLevel}
400
+ zoomOn={zoomOn}
401
+ minZoomLevel={minZoomLevel}
402
+ maxZoomLevel={maxZoomLevel}
403
+ zoomStep={zoomStep}
404
+ />
405
+ </div>
406
+ )}
407
+ {/* Body: Sol kısım => Resources, Sağ kısım => timeline */}
408
+ <div className="timeline-body">
409
+ <div className="timeline-resources-container"
410
+ style={{ overflow: "hidden"}}>
411
+ <ResourcesHeader content={resourceHeaderContent} />
412
+ <Resources
413
+ groupedResources={resources}
414
+ toggleGroupCollapse={toggleGroupCollapse}
415
+ collapsedGroups={collapsedGroups}
416
+ resourceSettings={resourceSettings}
417
+ />
418
+ </div>
419
+
420
+
421
+ <div
422
+ className="timeline-scrollable-container"
423
+ style={{
424
+ overflowX: horizontalScrollOn ? "auto" : "hidden",
425
+ }}
426
+ >
427
+
428
+ <div
429
+ className="timeline-header-content-wrapper"
430
+ style={{
431
+ width: horizontalScrollOn ? `${containerWidth}px` : "100%",
432
+ }}
433
+ >
434
+ <TimelineHeader
435
+ dates={filteredDates}
436
+ monthHeaders={filteredMonthHeaders}
437
+ highlightWeekends={highlightWeekends}
438
+ />
439
+
440
+ <TimelineContent
441
+ groupedResources={resources}
442
+ dates={filteredDates}
443
+ collapsedGroups={collapsedGroups}
444
+ events={localEvents}
445
+ setEvents={setLocalEvents}
446
+ onEventClick={onEventClick}
447
+ onEventDoubleClick={eventManagementOn ? handleEventDoubleClick : null}
448
+ onEventRightClick={eventManagementOn ? handleEventRightClickInternal : onEventRightClick}
449
+ selectedEvents={eventManagementOn ? eventManagement.selectedEvents : []}
450
+ onEventSelect={eventManagementOn ? eventManagement.selectEvent : null}
451
+ todayIndex={todayIndex}
452
+ indicatorOn={indicatorOn}
453
+ resourceSettings={resourceSettings}
454
+ toggleGroupCollapse={toggleGroupCollapse}
455
+ setDropInfo={setDropInfo}
456
+ eventsDragOn={eventsDragOn}
457
+ eventsExtendOn={eventsExtendOn}
458
+ createNewEventOn={createNewEventOn}
459
+ onDragInfo={onDragInfo}
460
+ onExtendInfo={onExtendInfo}
461
+ onCreateEventInfo={onCreateEventInfo}
462
+ onEventRightClick={onEventRightClick}
463
+ eventTooltipOn={eventTooltipOn} // Tooltip kontrolü
464
+ tooltipComponent={TooltipComponent} // Özelleştirilebilir Tooltip bileşeni
465
+ tempEventStyle = {tempEventStyle}
466
+ eventStyleResolver={eventStyleResolver}
467
+ eventAlignmentMode={eventAlignmentMode}
468
+ preventPastEvents={preventPastEvents}
469
+ minDate={minDate || indicatorDate}
470
+ highlightWeekends={highlightWeekends}
471
+ cellTooltipOn={cellTooltipOn}
472
+ cellTooltipResolver={cellTooltipResolver}
473
+ cellContextMenuOn={cellContextMenuOn}
474
+ cellContextMenuItems={cellContextMenuItems.map(item => {
475
+ // Daily timeline item'ını özel handle et
476
+ if (item.id === 'daily-timeline' && dailyViewOn) {
477
+ return {
478
+ ...item,
479
+ onClick: (resource, date) => {
480
+ setDailyView({
481
+ isOpen: true,
482
+ resource,
483
+ date,
484
+ });
485
+ if (item.onClick) {
486
+ item.onClick(resource, date);
487
+ }
488
+ },
489
+ };
490
+ }
491
+ return item;
492
+ })}
493
+ onCellContextMenu={onCellContextMenu}
494
+ eventIconsOn={eventIconsOn}
495
+ eventIconResolver={eventIconResolver}
496
+ eventBadgesOn={eventBadgesOn}
497
+ eventBadgeResolver={eventBadgeResolver}
498
+ isLoading={isLoading}
499
+ loadingType={loadingType}
500
+
501
+ />
502
+
503
+ {/* Tooltip */}
504
+ {selectedEvent && (
505
+ <EventTooltip
506
+ event={selectedEvent}
507
+ position={tooltipPosition}
508
+ onClose={handleCloseTooltip}
509
+ onDelete={(eventId) =>
510
+ setLocalEvents((prev) => prev.filter((e) => e.id !== eventId))
511
+ }
512
+ />
513
+ )}
514
+ </div>
515
+ </div>
516
+ </div>
517
+
518
+ {/* Event Detail Modal */}
519
+ {eventManagementOn && (
520
+ <EventDetailModal
521
+ event={editingEvent}
522
+ isOpen={isModalOpen}
523
+ onClose={() => {
524
+ setIsModalOpen(false);
525
+ setEditingEvent(null);
526
+ }}
527
+ onSave={handleEventSave}
528
+ onDelete={handleEventDelete}
529
+ resources={resources.flatMap((group) => group.resources || [])}
530
+ />
531
+ )}
532
+
533
+ {/* Daily View */}
534
+ {dailyViewOn && (
535
+ <DailyView
536
+ isOpen={dailyView.isOpen}
537
+ onClose={() => setDailyView({ isOpen: false, resource: null, date: null })}
538
+ resource={dailyView.resource}
539
+ date={dailyView.date}
540
+ events={localEvents}
541
+ onEventCreate={(newEvent) => {
542
+ const updatedEvents = [...localEvents, newEvent];
543
+ setLocalEvents(updatedEvents);
544
+ if (onEventUpdate) {
545
+ onEventUpdate(updatedEvents);
546
+ }
547
+ }}
548
+ onEventUpdate={(updatedEvent) => {
549
+ const updatedEvents = localEvents.map(e =>
550
+ e.id === updatedEvent.id ? updatedEvent : e
551
+ );
552
+ setLocalEvents(updatedEvents);
553
+ if (onEventUpdate) {
554
+ onEventUpdate(updatedEvents);
555
+ }
556
+ }}
557
+ onEventDelete={(eventId) => {
558
+ const updatedEvents = localEvents.filter(e => e.id !== eventId);
559
+ setLocalEvents(updatedEvents);
560
+ if (onEventUpdate) {
561
+ onEventUpdate(updatedEvents);
562
+ }
563
+ }}
564
+ themeType={themeType}
565
+ />
566
+ )}
567
+ </div>
568
+ );
569
+ };
570
+
571
+ export default Timeline;
572
+