akfatimeline 1.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 (42) hide show
  1. package/README.md +924 -0
  2. package/babel.config.json +4 -0
  3. package/package.json +50 -0
  4. package/public/favicon.ico +0 -0
  5. package/public/index.html +43 -0
  6. package/public/logo192.png +0 -0
  7. package/public/logo512.png +0 -0
  8. package/public/manifest.json +25 -0
  9. package/public/robots.txt +3 -0
  10. package/src/App copy.js +185 -0
  11. package/src/App.css +38 -0
  12. package/src/App.js +201 -0
  13. package/src/App.test.js +8 -0
  14. package/src/components/Timeline/DragAndDropHandler.js +35 -0
  15. package/src/components/Timeline/EventTooltip.js +206 -0
  16. package/src/components/Timeline/Indicator.js +30 -0
  17. package/src/components/Timeline/MasterHeader.js +55 -0
  18. package/src/components/Timeline/Resources.js +53 -0
  19. package/src/components/Timeline/ResourcesHeader.js +14 -0
  20. package/src/components/Timeline/Timeline.css +534 -0
  21. package/src/components/Timeline/Timeline.js +277 -0
  22. package/src/components/Timeline/TimelineCell.js +8 -0
  23. package/src/components/Timeline/TimelineContent copy.js +421 -0
  24. package/src/components/Timeline/TimelineContent.js +422 -0
  25. package/src/components/Timeline/TimelineEvents.js +114 -0
  26. package/src/components/Timeline/TimelineHeader.js +43 -0
  27. package/src/components/Timeline/TimelineMonthContainer.js +29 -0
  28. package/src/components/Timeline/TimelineResources.js +16 -0
  29. package/src/dist/Timeline.js +277 -0
  30. package/src/hooks/useDragAndDrop.js +80 -0
  31. package/src/hooks/useEventDragDrop.js +120 -0
  32. package/src/hooks/useExtendEvent.js +28 -0
  33. package/src/index.css +13 -0
  34. package/src/index.js +17 -0
  35. package/src/logo.svg +1 -0
  36. package/src/reportWebVitals.js +13 -0
  37. package/src/setupTests.js +5 -0
  38. package/src/utils/HorizontalVirtualScroll.js +0 -0
  39. package/src/utils/dateUtils.js +36 -0
  40. package/src/utils/filterTimelineData.js +21 -0
  41. package/src/utils/timelineUtils.js +40 -0
  42. package/webpack.config.js +31 -0
@@ -0,0 +1,277 @@
1
+ import React, { useState, useEffect } 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 { generateTimelineData } from "../../utils/timelineUtils";
10
+
11
+ const Timeline = ({
12
+ resources,
13
+ programDate = null,
14
+ events = [],
15
+ resourceSettings = {
16
+ showIdAsName: false,
17
+ isGrouped: true,
18
+ isCollapsible: true,
19
+ },
20
+ indicatorOn = false,
21
+ dropInfo,
22
+ setDropInfo,
23
+
24
+ masterHeaderView = true,
25
+ resourceHeaderContent = "Akfa Timeline",
26
+ eventsDragOn = true,
27
+ eventsExtendOn = true,
28
+ createNewEventOn = true,
29
+ onDragInfo,
30
+ onExtendInfo,
31
+ onCreateEventInfo,
32
+ // İsteğe bağlı event tıklama callback'leri
33
+ onEventClick,
34
+ onEventRightClick,
35
+
36
+ // Yatay scroll özelliği aç/kapa
37
+ horizontalScrollOn = false, // Varsayılan false
38
+ }) => {
39
+ // ---------------------------------------------------------
40
+ // 1) timelineData oluştur (dates, monthHeaders vs.)
41
+ // ---------------------------------------------------------
42
+ const timelineData = generateTimelineData(2020, 2030); // 10 yıllık veri
43
+ const { dates, monthHeaders } = timelineData;
44
+
45
+ // ---------------------------------------------------------
46
+ // 2) local state
47
+ // ---------------------------------------------------------
48
+ const [collapsedGroups, setCollapsedGroups] = useState({});
49
+ const [selectedDate, setSelectedDate] = useState(() => {
50
+ const date = programDate ? new Date(programDate) : new Date();
51
+ date.setDate(date.getDate() - 3);
52
+ return date;
53
+ });
54
+ const [localEvents, setLocalEvents] = useState(events);
55
+
56
+ const [selectedEvent, setSelectedEvent] = useState(null);
57
+ const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
58
+
59
+ // dayRange = ekranda göstermeyi istediğimiz gün/hücre sayısı (ör. 30 gün)
60
+ const [dayRange, setDayRange] = useState(30);
61
+
62
+ const [isDarkMode, setIsDarkMode] = useState(false);
63
+
64
+ // ---------------------------------------------------------
65
+ // 3) Sabit hücre genişliği (örneğin 56.95 px)
66
+ // Container genişliği = dayRange * cellWidth
67
+ // ---------------------------------------------------------
68
+ const cellWidth = 56.95; // her gün/hücre ~57 piksel
69
+ const containerWidth = dayRange * cellWidth;
70
+ // örneğin dayRange=30 => containerWidth=30*56.95=1708.5 px
71
+
72
+ // ---------------------------------------------------------
73
+ // 4) Event Tooltip logic
74
+ // ---------------------------------------------------------
75
+ const handleEventClick = (event, e) => {
76
+ // Harici onEventClick callback'i varsa, önce onu tetikleyelim
77
+ if (onEventClick) {
78
+ onEventClick(event, e);
79
+ }
80
+ // Ardından tooltip göstermek istiyorsak:
81
+ const eventElement = e.currentTarget;
82
+ if (eventElement) {
83
+ const rect = eventElement.getBoundingClientRect();
84
+ setTooltipPosition({
85
+ top: rect.top + window.scrollY,
86
+ left: rect.left + rect.width / 2 + window.scrollX,
87
+ });
88
+ setSelectedEvent(event);
89
+ }
90
+ };
91
+
92
+ const handleCloseTooltip = () => {
93
+ setSelectedEvent(null);
94
+ };
95
+
96
+ // ---------------------------------------------------------
97
+ // 5) Tarih filtreleme => filteredDates
98
+ // ---------------------------------------------------------
99
+ const startIndex = dates.findIndex((d) => d.fullDate >= selectedDate);
100
+ const endIndex = startIndex + dayRange;
101
+ const filteredDates =
102
+ startIndex !== -1 ? dates.slice(startIndex, Math.min(endIndex, dates.length)) : [];
103
+
104
+ const today = programDate ? new Date(programDate) : new Date();
105
+ const todayIndex = filteredDates.findIndex(
106
+ (d) => new Date(d.fullDate).toDateString() === today.toDateString()
107
+ );
108
+ const totalDays = filteredDates.length;
109
+
110
+ // ---------------------------------------------------------
111
+ // 6) Grupları aç/kapa
112
+ // ---------------------------------------------------------
113
+ const toggleGroupCollapse = (groupName) => {
114
+ setCollapsedGroups((prev) => ({
115
+ ...prev,
116
+ [groupName]: !prev[groupName],
117
+ }));
118
+ };
119
+
120
+ // ---------------------------------------------------------
121
+ // 7) Navigation fonksiyonları
122
+ // ---------------------------------------------------------
123
+ const handleToday = () => {
124
+ const date = programDate ? new Date(programDate) : new Date();
125
+ date.setDate(date.getDate() - 3);
126
+ setSelectedDate(date);
127
+ };
128
+
129
+ const handleAdvance = () =>
130
+ setSelectedDate((prev) => new Date(prev.getTime() + 5 * 24 * 60 * 60 * 1000));
131
+
132
+ const handleRetreat = () =>
133
+ setSelectedDate((prev) => new Date(prev.getTime() - 5 * 24 * 60 * 60 * 1000));
134
+
135
+ const handleMonthRetreat = () =>
136
+ setSelectedDate((prev) => {
137
+ const newDate = new Date(prev);
138
+ newDate.setMonth(newDate.getMonth() - 1);
139
+ return newDate;
140
+ });
141
+
142
+ const handleMonthAdvance = () =>
143
+ setSelectedDate((prev) => {
144
+ const newDate = new Date(prev);
145
+ newDate.setMonth(newDate.getMonth() + 1);
146
+ return newDate;
147
+ });
148
+
149
+ // ---------------------------------------------------------
150
+ // 8) Dark Mode
151
+ // ---------------------------------------------------------
152
+ const toggleDarkMode = () => {
153
+ setIsDarkMode((prevMode) => !prevMode);
154
+ };
155
+
156
+ useEffect(() => {
157
+ document.body.classList.toggle("dark-mode", isDarkMode);
158
+ }, [isDarkMode]);
159
+
160
+ // ---------------------------------------------------------
161
+ // 9) Ay başlıklarını filtrele
162
+ // ---------------------------------------------------------
163
+ const filteredMonthHeaders = monthHeaders
164
+ .map((header) => {
165
+ const adjustedStartIndex = Math.max(header.startIndex, startIndex);
166
+ const adjustedEndIndex = Math.min(header.endIndex, endIndex - 1);
167
+ return {
168
+ ...header,
169
+ startIndex: adjustedStartIndex,
170
+ endIndex: adjustedEndIndex,
171
+ };
172
+ })
173
+ .filter((header) => header.startIndex <= header.endIndex);
174
+
175
+ // ---------------------------------------------------------
176
+ // 10) Return
177
+ // ---------------------------------------------------------
178
+ return (
179
+ <div className={`timeline-container ${isDarkMode ? "dark-mode" : ""}`}>
180
+ {/* Üst kısım: MasterHeader */}
181
+ {masterHeaderView && (
182
+ <div className="timeline-master-header">
183
+ <MasterHeader
184
+ onToday={handleToday}
185
+ onAdvance={handleAdvance}
186
+ onRetreat={handleRetreat}
187
+ onMonthAdvance={handleMonthAdvance}
188
+ onMonthRetreat={handleMonthRetreat}
189
+ dayRange={dayRange}
190
+ setDayRange={setDayRange} // dayRange'ı burada user değiştirebilir
191
+ isDarkMode={isDarkMode}
192
+ toggleDarkMode={toggleDarkMode}
193
+ />
194
+ </div>
195
+ )}
196
+ {/* Body: Sol kısım => Resources, Sağ kısım => timeline */}
197
+ <div className="timeline-body">
198
+ <div className="timeline-resources-container">
199
+ <ResourcesHeader content={resourceHeaderContent} />
200
+ <Resources
201
+ groupedResources={resources}
202
+ toggleGroupCollapse={toggleGroupCollapse}
203
+ collapsedGroups={collapsedGroups}
204
+ resourceSettings={resourceSettings}
205
+ />
206
+ </div>
207
+
208
+ {/*
209
+ Dış kap => .timeline-scrollable-container
210
+ horizontalScrollOn => overflow-x auto/hidden
211
+ */}
212
+ <div
213
+ className="timeline-scrollable-container"
214
+ style={{
215
+ overflowX: horizontalScrollOn ? "auto" : "hidden",
216
+ }}
217
+ >
218
+ {/*
219
+ İç kap => .timeline-header-content-wrapper
220
+ Genişlik => dayRange * cellWidth px (eğer horizontalScrollOn=true)
221
+ Yoksa 100% (scroll devre dışı)
222
+ */}
223
+ <div
224
+ className="timeline-header-content-wrapper"
225
+ style={{
226
+ width: horizontalScrollOn ? `${containerWidth}px` : "100%",
227
+ }}
228
+ >
229
+ <TimelineHeader
230
+ dates={filteredDates}
231
+ monthHeaders={filteredMonthHeaders}
232
+ />
233
+
234
+ <TimelineContent
235
+ // Props
236
+ groupedResources={resources}
237
+ dates={filteredDates}
238
+ collapsedGroups={collapsedGroups}
239
+ events={localEvents}
240
+ setEvents={setLocalEvents}
241
+ onEventClick={handleEventClick}
242
+ todayIndex={todayIndex}
243
+ totalDays={totalDays}
244
+ indicatorOn={indicatorOn}
245
+ resourceSettings={resourceSettings}
246
+ toggleGroupCollapse={toggleGroupCollapse}
247
+ setDropInfo={setDropInfo}
248
+
249
+ // Yeni prop'lar
250
+ eventsDragOn={eventsDragOn}
251
+ eventsExtendOn={eventsExtendOn}
252
+ createNewEventOn={createNewEventOn}
253
+ onDragInfo={onDragInfo}
254
+ onExtendInfo={onExtendInfo}
255
+ onCreateEventInfo={onCreateEventInfo}
256
+ onEventRightClick={onEventRightClick}
257
+ />
258
+
259
+ {/* Tooltip */}
260
+ {selectedEvent && (
261
+ <EventTooltip
262
+ event={selectedEvent}
263
+ position={tooltipPosition}
264
+ onClose={handleCloseTooltip}
265
+ onDelete={(eventId) =>
266
+ setLocalEvents((prev) => prev.filter((e) => e.id !== eventId))
267
+ }
268
+ />
269
+ )}
270
+ </div>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ );
275
+ };
276
+
277
+ export default Timeline;
@@ -0,0 +1,8 @@
1
+ // src/components/Timeline/TimelineCell.js
2
+ import React from "react";
3
+
4
+ const TimelineCell = () => {
5
+ return <div style={{ width: "100%", height: "100%" }}></div>;
6
+ };
7
+
8
+ export default TimelineCell;
@@ -0,0 +1,421 @@
1
+ import React, { useState, useRef, useEffect } from "react";
2
+ import { parseDate } from "../../utils/dateUtils";
3
+ import useDragAndDrop from "../../hooks/useDragAndDrop";
4
+ import useEventDragDrop from "../../hooks/useEventDragDrop";
5
+ import EventTooltip from "./EventTooltip";
6
+ import Indicator from "./Indicator";
7
+
8
+ const TimelineContent = ({ groupedResources, dates, collapsedGroups, events, setEvents, onEventClick, todayIndex, indicatorOn, resourceSettings,
9
+
10
+ }) => {
11
+
12
+ const containerRef = useRef(null);
13
+ const { isDragging, dragStart, dragEnd } = useDragAndDrop(events, setEvents);
14
+ const totalDays = dates.length; // Toplam gün sayısı hesaplandı
15
+ const { handleDragStart, handleDragOver, handleDrop, handleDragEnd } = useEventDragDrop(events, setEvents);
16
+
17
+ console.log("events content:", events);
18
+ console.log("Event Resource ID:", events.resourceId);
19
+ console.log("Current Resource ID:", groupedResources);
20
+
21
+ // Tooltip için state
22
+ const [selectedEvent, setSelectedEvent] = useState(null);
23
+ const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
24
+ // Yeni Event oluşturma durumu ve geçici event
25
+ const [isCreating, setIsCreating] = useState(false);
26
+ const [tempEvent, setTempEvent] = useState(null);
27
+
28
+
29
+ const handleEventClick = (event, e) => {
30
+ e.stopPropagation();
31
+ const eventElement = e.currentTarget;
32
+ if (eventElement) {
33
+ const rect = eventElement.getBoundingClientRect();
34
+ setTooltipPosition({
35
+ top: rect.top + window.scrollY,
36
+ left: rect.left + rect.width / 2 + window.scrollX,
37
+ });
38
+ setSelectedEvent(event); // Seçilen event'i state'e kaydediyoruz
39
+ }
40
+ };
41
+
42
+
43
+ // Mouse hareketiyle geçici event'in bitiş tarihini güncelle
44
+ const handleMouseMove = (e) => {
45
+ if (isCreating && tempEvent) {
46
+ const cell = document.elementFromPoint(e.clientX, e.clientY);
47
+
48
+ // Hücre genişliğini dinamik olarak hesapla
49
+ const cellWidth = cell?.offsetWidth || 30;
50
+
51
+ const startX = tempEvent.startX || e.clientX; // İlk tıklamanın X pozisyonu
52
+ const deltaX = e.clientX - startX; // Hareket edilen mesafe
53
+ const daysToAdd = Math.max(1, Math.floor(deltaX / cellWidth)); // Gün hesaplama (en az 1 gün)
54
+
55
+ // EndDate'i güncelle
56
+ const newEndDate = new Date(tempEvent.startDate.getTime());
57
+ newEndDate.setDate(newEndDate.getDate() + daysToAdd);
58
+
59
+ // TempEvent'i güncelle ve yeni başlığı ayarla
60
+ setTempEvent({
61
+ ...tempEvent,
62
+ endDate: newEndDate,
63
+ startX: startX,
64
+ title: `${daysToAdd} Gece`, // Gün sayısını ve "Gece" kelimesini başlığa ekle
65
+ });
66
+
67
+ console.log("Mouse X Delta:", deltaX);
68
+ console.log("Days to Add:", daysToAdd);
69
+ console.log("New End Date:", newEndDate);
70
+ console.log("Cell Width:", cellWidth);
71
+ }
72
+ };
73
+
74
+
75
+
76
+
77
+
78
+ // Mouse bırakıldığında geçici event'i kaydet
79
+ const handleMouseUp = () => {
80
+ if (isCreating && tempEvent) {
81
+ setEvents([...events, tempEvent]); // Geçici event kalıcı hale gelir
82
+ setTempEvent(null); // Geçici eventi sıfırla
83
+ setIsCreating(false); // Event oluşturmayı kapat
84
+ }
85
+ };
86
+
87
+ // Yeni event oluşturma fonksiyonu
88
+
89
+ const handleCellClick = (resourceId, date) => {
90
+ const startDate = parseDate(date.fullDate);
91
+ const newEvent = {
92
+ id: Date.now(),
93
+ title: "1 Gece",
94
+ startDate,
95
+ endDate: new Date(startDate.getTime() + 24 * 60 * 60 * 1000),
96
+ resourceId,
97
+ color: "#ff5722",
98
+ };
99
+ setTempEvent(newEvent); // Geçici event state'e ekle
100
+ setIsCreating(true); // Yeni bir event oluşturuldu
101
+ };
102
+
103
+ // Event silme fonksiyonu
104
+ const handleDeleteEvent = (eventId) => {
105
+ setEvents(events.filter((event) => event.id !== eventId));
106
+ };
107
+
108
+
109
+ useEffect(() => {
110
+ if (isCreating) {
111
+ window.addEventListener("mousemove", handleMouseMove);
112
+ window.addEventListener("mouseup", handleMouseUp);
113
+
114
+ return () => {
115
+ window.removeEventListener("mousemove", handleMouseMove);
116
+ window.removeEventListener("mouseup", handleMouseUp);
117
+ };
118
+ }
119
+ }, );
120
+
121
+
122
+ // Sürükleme sırasında seçilen hücreleri vurgulamak için fonksiyon
123
+ const isCellSelected = (resourceId, date) => {
124
+ if (!dragStart || !dragEnd) return false;
125
+ if (resourceId !== dragStart.resourceId) return false;
126
+
127
+ const startIndex = dates.findIndex(
128
+ (d) => parseDate(d.fullDate).getTime() === parseDate(dragStart.date).getTime()
129
+ );
130
+ const endIndex = dates.findIndex(
131
+ (d) => parseDate(d.fullDate).getTime() === parseDate(dragEnd.date).getTime()
132
+ );
133
+ const currentIndex = dates.findIndex(
134
+ (d) => parseDate(d.fullDate).getTime() === parseDate(date.fullDate).getTime()
135
+ );
136
+
137
+ if (startIndex === -1 || endIndex === -1 || currentIndex === -1) return false;
138
+
139
+ return (
140
+ currentIndex >= Math.min(startIndex, endIndex) &&
141
+ currentIndex <= Math.max(startIndex, endIndex)
142
+ );
143
+ };
144
+
145
+ const calculatePosition = (event, dates) => {
146
+ const startDate = parseDate(event.startDate);
147
+ const endDate = parseDate(event.endDate);
148
+
149
+ const startIndex = dates.findIndex(
150
+ (d) => parseDate(d.fullDate).toDateString() === startDate.toDateString()
151
+ );
152
+ const endIndex = dates.findIndex(
153
+ (d) => parseDate(d.fullDate).toDateString() === endDate.toDateString()
154
+ );
155
+
156
+ const totalDays = dates.length;
157
+
158
+ console.log("Total Days:", totalDays);
159
+ // Eğer başlangıç ve bitiş timeline'ın tamamen dışında ise, görünmez olarak işaretle
160
+ if (startIndex < 0 && endIndex < 0) {
161
+ // Eğer hem başlangıç hem de bitiş timeline'ın sol tarafında ise, tamamen görünmez
162
+ console.log("Event completely out of bounds on the left:", event.title);
163
+ return {
164
+ isVisible: false,
165
+ left: 0,
166
+ width: 0,
167
+ isPartialStart: false,
168
+ isPartialEnd: false,
169
+ };
170
+ }
171
+
172
+ if (startIndex >= totalDays && endIndex >= totalDays) {
173
+ // Eğer hem başlangıç hem de bitiş timeline'ın sağ tarafında ise, tamamen görünmez
174
+ console.log("Event completely out of bounds on the right:", event.title);
175
+ return {
176
+ isVisible: false,
177
+ left: 0,
178
+ width: 0,
179
+ isPartialStart: false,
180
+ isPartialEnd: false,
181
+ };
182
+ }
183
+
184
+
185
+ // Etkin Başlangıç ve Bitiş İndeksleri
186
+ const effectiveStartIndex = Math.max(startIndex, 0); // Başlangıç en az 0 olmalı
187
+ const effectiveEndIndex = Math.min(endIndex, totalDays - 1); // Bitiş en fazla toplam gün sayısı - 1 olmalı
188
+
189
+ // Kesik başlangıç veya bitiş kontrolü
190
+ const isPartialStart = startIndex < 0; // Başlangıç timeline dışında mı?
191
+ const isPartialEnd = endIndex >= totalDays; // Bitiş timeline dışında mı?
192
+
193
+ // Sol tarafın ekran dışında kalması durumu
194
+ if (isPartialStart) {
195
+ console.log("Event partially visible on the left:", event.title);
196
+ }
197
+
198
+ // Sağ tarafın ekran dışında kalması durumu
199
+ if (isPartialEnd) {
200
+ console.log("Event partially visible on the right:", event.title);
201
+ }
202
+
203
+ // Başlangıç ve bitiş pozisyonlarını ayrı ayrı hesapla
204
+ const leftPercentage = ((effectiveStartIndex + (isPartialStart ? 0 : 0.5)) / totalDays) * 100;
205
+ const rightPercentage = ((effectiveEndIndex + (isPartialEnd ? 1 : 0.5)) / totalDays) * 100;
206
+
207
+ // Genişlik hesaplama
208
+ const widthPercentage = rightPercentage - leftPercentage;
209
+
210
+ // Sonuçları döndür
211
+ return {
212
+ isVisible: true,
213
+ left: `${leftPercentage}%`,
214
+ width: `${widthPercentage}%`,
215
+ isPartialStart,
216
+ isPartialEnd,
217
+ };
218
+ };
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+ useEffect(() => {
228
+ if (isCreating) {
229
+ window.addEventListener("mousemove", handleMouseMove);
230
+ window.addEventListener("mouseup", handleMouseUp);
231
+
232
+ return () => {
233
+ window.removeEventListener("mousemove", handleMouseMove);
234
+ window.removeEventListener("mouseup", handleMouseUp);
235
+ };
236
+ }
237
+ }, );
238
+
239
+
240
+
241
+
242
+ const handleCloseTooltip = () => {
243
+ setSelectedEvent(null);
244
+ };
245
+
246
+ const handleDelete = (eventId) => {
247
+ handleDeleteEvent(eventId);
248
+ handleCloseTooltip();
249
+ };
250
+
251
+ return (
252
+ <div
253
+ ref={containerRef}
254
+ style={{
255
+ display: "flex",
256
+ flexDirection: "column",
257
+ width: "100%",
258
+ userSelect: isDragging ? "none" : "auto",
259
+ position: "relative",
260
+ }}
261
+ >
262
+
263
+ {indicatorOn && <Indicator todayIndex={todayIndex} totalDays={totalDays} />}
264
+
265
+
266
+ {groupedResources.map((group, groupIndex) => (
267
+ <div key={groupIndex} style={{ marginBottom: "0px" }}>
268
+ {/* Grup Başlığı */}
269
+ {resourceSettings.isGrouped && (
270
+ <div style={{ display: "flex", marginTop: "-0.08rem" }}>
271
+ {dates.map((dateObj, colIndex) => (
272
+ <div
273
+ key={`group-header-${groupIndex}-${colIndex}`}
274
+ style={{
275
+ flex: 1,
276
+ height: "2.58rem",
277
+ backgroundColor: "#4b5563",
278
+ border: "1px solid #ccc",
279
+ boxSizing: "border-box",
280
+ display: "flex",
281
+ alignItems: "center",
282
+ justifyContent: "center",
283
+ }}
284
+ ></div>
285
+ ))}
286
+ </div>
287
+ )}
288
+
289
+ {/* Kaynaklar */}
290
+ {!collapsedGroups[group.groupName] &&
291
+ group.resources.map((resource, rowIndex) => {
292
+ const resourceEvents = events.filter(
293
+ (event) => event.resourceId === resource.id
294
+ );
295
+
296
+ return (
297
+ <div
298
+ key={resource.id}
299
+ style={{
300
+ display: "flex",
301
+ boxSizing: "border-box",
302
+ position: "relative",
303
+ height: "40px",
304
+ border: "1px solid #ccc",
305
+ }}
306
+ >
307
+ {/* Event'leri Render Et */}
308
+ {resourceEvents.map((event) => {
309
+ const { isVisible, left, width, isPartialStart, isPartialEnd } =
310
+ calculatePosition(event, dates);
311
+
312
+ if (!isVisible) return null;
313
+
314
+ return (
315
+ <div
316
+ key={event.id}
317
+ draggable
318
+ onDragStart={(e) => handleDragStart(e, event.id)} // Sürükleme başlat
319
+ onDragEnd={() => handleDragEnd()} // Sürükleme bitir
320
+ onClick={(e) => handleEventClick(event, e)} // Tıklama olayını burada tetikliyoruz
321
+ style={{
322
+ position: "absolute",
323
+ top: "5px",
324
+ left: left,
325
+ width: width,
326
+ height: "80%",
327
+ backgroundColor: event.color || "#ff7f50",
328
+ color: "#fff",
329
+ fontSize: "14px",
330
+ padding: "5px",
331
+ borderTopLeftRadius: isPartialStart ? "0px" : "20px",
332
+ borderBottomLeftRadius: isPartialStart ? "0px" : "20px",
333
+ borderTopRightRadius: isPartialEnd ? "0px" : "20px",
334
+ borderBottomRightRadius: isPartialEnd ? "0px" : "20px",
335
+ textAlign: "left",
336
+ display: "flex",
337
+ alignItems: "center",
338
+ justifyContent: "center",
339
+ boxSizing: "border-box",
340
+ zIndex: 10,
341
+ overflow: "hidden",
342
+ textOverflow: "ellipsis",
343
+ whiteSpace: "nowrap",
344
+ border: "1px solid #fff",
345
+ cursor: "grab", // Sürükleme ikonu
346
+ }}
347
+ >
348
+ {event.title}
349
+ </div>
350
+ );
351
+ })}
352
+
353
+ {tempEvent && tempEvent.resourceId === resource.id && (
354
+ <div
355
+ style={{
356
+ position: "absolute",
357
+ ...calculatePosition(tempEvent, dates),
358
+ top: "5px",
359
+ height: "80%",
360
+ backgroundColor: tempEvent.color,
361
+ opacity: 0.7,
362
+ borderRadius: "20px",
363
+ zIndex: 9,
364
+ display: "flex",
365
+ alignItems: "center",
366
+ justifyContent: "center",
367
+ color: "#fff",
368
+ fontSize: "14px",
369
+ }}
370
+ >
371
+ {tempEvent.title}
372
+ </div>
373
+ )}
374
+
375
+ {/* Tarih Hücreleri */}
376
+ {dates.map((dateObj, colIndex) => (
377
+ <div
378
+ key={`cell-${groupIndex}-${rowIndex}-${colIndex}`}
379
+ data-date={JSON.stringify(dateObj)}
380
+ data-resource-id={resource.id}
381
+ onMouseDown={() => handleCellClick(resource.id, dateObj)}
382
+ onDragOver={(e) => handleDragOver(e)} // Hücre üzerine sürükleme
383
+ onDrop={(e) =>
384
+ handleDrop(e, resource.id, parseDate(dateObj.fullDate)) // `dateObj.fullDate`'yi doğru şekilde geçiyoruz
385
+ } // Bırakma olayı
386
+ style={{
387
+ flex: 1,
388
+ height: "100%",
389
+ position: "relative",
390
+ borderLeft: colIndex === 0 ? "1px solid #ccc" : "none",
391
+ borderRight: "1px solid #ccc",
392
+ backgroundColor: isCellSelected(resource.id, dateObj)
393
+ ? "rgba(59, 130, 246, 0.3)"
394
+ : "transparent",
395
+ cursor: "pointer",
396
+ }}
397
+ ></div>
398
+ ))}
399
+ </div>
400
+ );
401
+ })}
402
+ </div>
403
+ ))}
404
+
405
+
406
+
407
+
408
+
409
+ {selectedEvent && (
410
+ <EventTooltip
411
+ event={selectedEvent}
412
+ position={tooltipPosition}
413
+ onClose={handleCloseTooltip}
414
+ onDelete={() => handleDelete(selectedEvent.id)}
415
+ />
416
+ )}
417
+ </div>
418
+ );
419
+ };
420
+
421
+ export default TimelineContent;