akfatimeline 1.0.6 → 1.1.1
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.
- package/CHANGELOG.md +66 -35
- package/dist/Timeline.js +4309 -1677
- package/dist/components/Timeline/AutocompleteSelect.js +150 -0
- package/dist/components/Timeline/ContextMenu.js +149 -0
- package/dist/components/Timeline/DailyView.js +255 -0
- package/dist/components/Timeline/DatePickerComponent.js +13 -0
- package/{public/dist/dist → dist}/components/Timeline/DragAndDropHandler.js +34 -34
- package/dist/components/Timeline/EventBadge.js +26 -0
- package/dist/components/Timeline/EventDetailModal.js +138 -0
- package/dist/components/Timeline/EventIcon.js +47 -0
- package/dist/{dist/components → components}/Timeline/EventTooltip.js +206 -206
- package/dist/components/Timeline/FilterPanel.js +179 -0
- package/dist/{dist/components → components}/Timeline/Indicator.js +26 -26
- package/dist/components/Timeline/LoadingSpinner.js +48 -0
- package/dist/{dist/components → components}/Timeline/MasterHeader.js +104 -68
- package/{public/dist/dist → dist}/components/Timeline/Resources.js +53 -53
- package/dist/{dist/components → components}/Timeline/ResourcesHeader.js +14 -14
- package/dist/components/Timeline/Timeline.css +2491 -0
- package/dist/components/Timeline/Timeline.js +607 -0
- package/dist/{dist/components → components}/Timeline/TimelineCell.js +8 -8
- package/dist/components/Timeline/TimelineContent.js +838 -0
- package/{public/dist/dist → dist}/components/Timeline/TimelineEvents.js +114 -114
- package/dist/components/Timeline/TimelineHeader.js +54 -0
- package/{public/dist/dist → dist}/components/Timeline/TimelineMonthContainer.js +29 -29
- package/{public/dist/dist → dist}/components/Timeline/TimelineResources.js +16 -16
- package/{public/dist/dist → dist}/hooks/useDragAndDrop.js +80 -80
- package/dist/{dist/hooks → hooks}/useEventDragDrop.js +126 -126
- package/dist/hooks/useEventManagement.js +173 -0
- package/dist/hooks/useEventSelection.js +82 -0
- package/{public/dist/dist → dist}/hooks/useExtendEvent.js +28 -28
- package/dist/hooks/useKeyboardShortcuts.js +158 -0
- package/dist/hooks/useTouchGestures.js +90 -0
- package/dist/utils/conflictUtils.js +105 -0
- package/dist/{dist/utils → utils}/dateUtils.js +36 -36
- package/dist/{dist/utils → utils}/filterTimelineData.js +20 -20
- package/dist/utils/filterUtils.js +106 -0
- package/dist/utils/timeUtils.js +179 -0
- package/dist/{dist/utils → utils}/timelineUtils.js +39 -39
- package/dist/utils/viewModeUtils.js +54 -0
- package/package.json +37 -18
- package/src/App.js +300 -19
- package/src/components/Timeline/AutocompleteSelect.js +150 -0
- package/src/components/Timeline/ContextMenu.js +149 -0
- package/src/components/Timeline/DailyView.js +255 -0
- package/src/components/Timeline/DatePickerComponent.js +13 -17
- package/src/components/Timeline/DragAndDropHandler.js +34 -34
- package/src/components/Timeline/EventBadge.js +26 -0
- package/src/components/Timeline/EventDetailModal.js +138 -0
- package/src/components/Timeline/EventIcon.js +47 -0
- package/src/components/Timeline/EventTooltip.js +206 -206
- package/src/components/Timeline/FilterPanel.js +179 -0
- package/src/components/Timeline/Indicator.js +26 -26
- package/src/components/Timeline/LoadingSpinner.js +48 -0
- package/src/components/Timeline/MasterHeader.js +104 -68
- package/src/components/Timeline/Resources.js +53 -53
- package/src/components/Timeline/ResourcesHeader.js +14 -14
- package/src/components/Timeline/Timeline.css +2491 -616
- package/src/components/Timeline/Timeline.js +607 -309
- package/src/components/Timeline/TimelineCell.js +8 -8
- package/src/components/Timeline/TimelineContent.js +838 -446
- package/src/components/Timeline/TimelineEvents.js +114 -114
- package/src/components/Timeline/TimelineHeader.js +54 -43
- package/src/components/Timeline/TimelineMonthContainer.js +29 -29
- package/src/components/Timeline/TimelineResources.js +16 -16
- package/src/demo.css +4 -0
- package/src/hooks/useDragAndDrop.js +80 -80
- package/src/hooks/useEventDragDrop.js +126 -126
- package/src/hooks/useEventManagement.js +173 -0
- package/src/hooks/useEventSelection.js +82 -0
- package/src/hooks/useExtendEvent.js +28 -28
- package/src/hooks/useKeyboardShortcuts.js +158 -0
- package/src/hooks/useTouchGestures.js +90 -0
- package/src/index.js +1 -7
- package/src/library.js +26 -0
- package/src/utils/conflictUtils.js +105 -0
- package/src/utils/dateUtils.js +36 -36
- package/src/utils/filterTimelineData.js +20 -20
- package/src/utils/filterUtils.js +106 -0
- package/src/utils/timeUtils.js +179 -0
- package/src/utils/timelineUtils.js +39 -39
- package/src/utils/viewModeUtils.js +54 -0
- package/.babelrc +0 -6
- package/babel.config.json +0 -4
- package/dist/dist/components/Timeline/DatePickerComponent.js +0 -17
- package/dist/dist/components/Timeline/DragAndDropHandler.js +0 -35
- package/dist/dist/components/Timeline/Resources.js +0 -53
- package/dist/dist/components/Timeline/Timeline.css +0 -616
- package/dist/dist/components/Timeline/Timeline.js +0 -309
- package/dist/dist/components/Timeline/TimelineContent.js +0 -446
- package/dist/dist/components/Timeline/TimelineEvents.js +0 -114
- package/dist/dist/components/Timeline/TimelineHeader.js +0 -43
- package/dist/dist/components/Timeline/TimelineMonthContainer.js +0 -29
- package/dist/dist/components/Timeline/TimelineResources.js +0 -16
- package/dist/dist/hooks/useDragAndDrop.js +0 -80
- package/dist/dist/hooks/useExtendEvent.js +0 -28
- package/public/dist/Timeline.js +0 -3277
- package/public/dist/dist/components/Timeline/DatePickerComponent.js +0 -17
- package/public/dist/dist/components/Timeline/EventTooltip.js +0 -206
- package/public/dist/dist/components/Timeline/Indicator.js +0 -29
- package/public/dist/dist/components/Timeline/MasterHeader.js +0 -68
- package/public/dist/dist/components/Timeline/ResourcesHeader.js +0 -14
- package/public/dist/dist/components/Timeline/Timeline.css +0 -616
- package/public/dist/dist/components/Timeline/Timeline.js +0 -304
- package/public/dist/dist/components/Timeline/TimelineCell.js +0 -8
- package/public/dist/dist/components/Timeline/TimelineContent.js +0 -447
- package/public/dist/dist/components/Timeline/TimelineHeader.js +0 -43
- package/public/dist/dist/hooks/useEventDragDrop.js +0 -126
- package/public/dist/dist/utils/HorizontalVirtualScroll.js +0 -0
- package/public/dist/dist/utils/dateUtils.js +0 -36
- package/public/dist/dist/utils/filterTimelineData.js +0 -21
- package/public/dist/dist/utils/timelineUtils.js +0 -40
- package/public/favicon.ico +0 -0
- package/public/index kutuphane /304/261c/304/261n.html" +0 -43
- package/public/index tasarim icin.html +0 -20
- package/public/index.html +0 -43
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +0 -25
- package/public/robots.txt +0 -3
- package/src/App.css +0 -38
- package/src/App.test.js +0 -8
- package/src/dist/Timeline.js +0 -277
- package/src/index.css +0 -13
- package/src/logo.svg +0 -1
- package/src/reportWebVitals.js +0 -13
- package/src/setupTests.js +0 -5
- package/webpack.config.js +0 -49
- /package/dist/{dist/utils → utils}/HorizontalVirtualScroll.js +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|
2
|
+
import './Timeline.css';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Daily View Component
|
|
6
|
+
* Seçili resource ve tarih için günlük saat bazlı timeline görünümü
|
|
7
|
+
*/
|
|
8
|
+
const DailyView = ({
|
|
9
|
+
isOpen,
|
|
10
|
+
onClose,
|
|
11
|
+
resource,
|
|
12
|
+
date,
|
|
13
|
+
events = [],
|
|
14
|
+
onEventCreate,
|
|
15
|
+
onEventUpdate,
|
|
16
|
+
onEventDelete,
|
|
17
|
+
themeType = 'light',
|
|
18
|
+
}) => {
|
|
19
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
20
|
+
const [tempEvent, setTempEvent] = useState(null);
|
|
21
|
+
const containerRef = useRef(null);
|
|
22
|
+
const timelineRef = useRef(null);
|
|
23
|
+
|
|
24
|
+
// Saatleri oluştur (0-23)
|
|
25
|
+
const hours = Array.from({ length: 24 }, (_, i) => i);
|
|
26
|
+
|
|
27
|
+
// O güne ait event'leri filtrele
|
|
28
|
+
const dayEvents = events.filter(event => {
|
|
29
|
+
if (!event.startDate || !date) return false;
|
|
30
|
+
const eventDate = new Date(event.startDate);
|
|
31
|
+
const selectedDate = new Date(date.fullDate || date);
|
|
32
|
+
return (
|
|
33
|
+
eventDate.getFullYear() === selectedDate.getFullYear() &&
|
|
34
|
+
eventDate.getMonth() === selectedDate.getMonth() &&
|
|
35
|
+
eventDate.getDate() === selectedDate.getDate() &&
|
|
36
|
+
event.resourceId === resource?.id
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Saat bazlı event pozisyonu hesapla
|
|
41
|
+
const getEventPosition = (event) => {
|
|
42
|
+
const startDate = new Date(event.startDate);
|
|
43
|
+
const endDate = new Date(event.endDate);
|
|
44
|
+
const startHour = startDate.getHours() + startDate.getMinutes() / 60;
|
|
45
|
+
const endHour = endDate.getHours() + endDate.getMinutes() / 60;
|
|
46
|
+
const duration = endHour - startHour;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
top: `${(startHour / 24) * 100}%`,
|
|
50
|
+
height: `${(duration / 24) * 100}%`,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Timeline'a tıklandığında
|
|
55
|
+
const handleTimelineClick = (e) => {
|
|
56
|
+
if (!onEventCreate || !timelineRef.current) return;
|
|
57
|
+
|
|
58
|
+
const rect = timelineRef.current.getBoundingClientRect();
|
|
59
|
+
const clickedY = e.clientY - rect.top;
|
|
60
|
+
const timelineHeight = rect.height;
|
|
61
|
+
const totalMinutes = Math.max(0, Math.min((clickedY / timelineHeight) * (24 * 60), 24 * 60));
|
|
62
|
+
|
|
63
|
+
const startHour = Math.floor(totalMinutes / 60);
|
|
64
|
+
const startMinutes = Math.floor(totalMinutes % 60);
|
|
65
|
+
|
|
66
|
+
const startDate = new Date(date.fullDate || date);
|
|
67
|
+
startDate.setHours(startHour, startMinutes, 0, 0);
|
|
68
|
+
|
|
69
|
+
const endDate = new Date(startDate);
|
|
70
|
+
endDate.setTime(startDate.getTime() + 30 * 60 * 1000); // Varsayılan 30 dakika
|
|
71
|
+
|
|
72
|
+
setIsCreating(true);
|
|
73
|
+
setTempEvent({
|
|
74
|
+
startDate,
|
|
75
|
+
endDate,
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Mouse hareketi ile event oluşturma
|
|
80
|
+
const handleMouseMove = useCallback((e) => {
|
|
81
|
+
if (!isCreating || !tempEvent || !timelineRef.current) return;
|
|
82
|
+
|
|
83
|
+
const rect = timelineRef.current.getBoundingClientRect();
|
|
84
|
+
const mouseY = e.clientY - rect.top;
|
|
85
|
+
const timelineHeight = rect.height;
|
|
86
|
+
const totalMinutes = Math.max(0, Math.min((mouseY / timelineHeight) * (24 * 60), 24 * 60));
|
|
87
|
+
|
|
88
|
+
const endHour = Math.floor(totalMinutes / 60);
|
|
89
|
+
const endMinutes = Math.floor(totalMinutes % 60);
|
|
90
|
+
|
|
91
|
+
const newEndDate = new Date(tempEvent.startDate);
|
|
92
|
+
newEndDate.setHours(endHour, endMinutes, 0, 0);
|
|
93
|
+
|
|
94
|
+
// Bitiş saati başlangıçtan önce olamaz
|
|
95
|
+
if (newEndDate <= tempEvent.startDate) {
|
|
96
|
+
newEndDate.setTime(tempEvent.startDate.getTime() + 15 * 60 * 1000); // En az 15 dakika
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setTempEvent(prev => ({
|
|
100
|
+
...prev,
|
|
101
|
+
endDate: newEndDate,
|
|
102
|
+
}));
|
|
103
|
+
}, [isCreating, tempEvent]);
|
|
104
|
+
|
|
105
|
+
// Mouse bırakıldığında event oluştur
|
|
106
|
+
const handleMouseUp = useCallback(() => {
|
|
107
|
+
if (!isCreating || !tempEvent || !onEventCreate) return;
|
|
108
|
+
|
|
109
|
+
// Minimum süre kontrolü (en az 15 dakika)
|
|
110
|
+
const duration = tempEvent.endDate.getTime() - tempEvent.startDate.getTime();
|
|
111
|
+
if (duration < 15 * 60 * 1000) {
|
|
112
|
+
setIsCreating(false);
|
|
113
|
+
setTempEvent(null);
|
|
114
|
+
return; // Çok kısa, event oluşturma
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const newEvent = {
|
|
118
|
+
id: `daily-${Date.now()}`,
|
|
119
|
+
title: 'Yeni Randevu',
|
|
120
|
+
startDate: tempEvent.startDate,
|
|
121
|
+
endDate: tempEvent.endDate,
|
|
122
|
+
resourceId: resource?.id,
|
|
123
|
+
isHourly: true, // Saatlik rezervasyon flag'i
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
onEventCreate(newEvent);
|
|
127
|
+
setIsCreating(false);
|
|
128
|
+
setTempEvent(null);
|
|
129
|
+
}, [isCreating, tempEvent, onEventCreate, resource]);
|
|
130
|
+
|
|
131
|
+
// Global mouse event listener'ları
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (!isCreating) return;
|
|
134
|
+
|
|
135
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
136
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
137
|
+
|
|
138
|
+
return () => {
|
|
139
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
140
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
141
|
+
};
|
|
142
|
+
}, [isCreating, handleMouseMove, handleMouseUp]);
|
|
143
|
+
|
|
144
|
+
// Tarih formatı
|
|
145
|
+
const formatDate = (dateObj) => {
|
|
146
|
+
if (!dateObj) return '';
|
|
147
|
+
const d = new Date(dateObj.fullDate || dateObj);
|
|
148
|
+
return d.toLocaleDateString('tr-TR', {
|
|
149
|
+
weekday: 'long',
|
|
150
|
+
year: 'numeric',
|
|
151
|
+
month: 'long',
|
|
152
|
+
day: 'numeric',
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Saat formatı
|
|
157
|
+
const formatHour = (hour) => {
|
|
158
|
+
return `${hour.toString().padStart(2, '0')}:00`;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if (!isOpen || !resource || !date) return null;
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<div
|
|
165
|
+
className={`daily-view-overlay ${themeType === 'dark' ? 'dark-mode' : ''}`}
|
|
166
|
+
onClick={onClose}
|
|
167
|
+
>
|
|
168
|
+
<div
|
|
169
|
+
ref={containerRef}
|
|
170
|
+
className="daily-view-container"
|
|
171
|
+
onClick={(e) => e.stopPropagation()}
|
|
172
|
+
>
|
|
173
|
+
{/* Header */}
|
|
174
|
+
<div className="daily-view-header">
|
|
175
|
+
<div className="daily-view-header-content">
|
|
176
|
+
<h2 className="daily-view-title">{resource.name || resource.id}</h2>
|
|
177
|
+
<p className="daily-view-date">{formatDate(date)}</p>
|
|
178
|
+
</div>
|
|
179
|
+
<button className="daily-view-close" onClick={onClose}>
|
|
180
|
+
✕
|
|
181
|
+
</button>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{/* Timeline Body */}
|
|
185
|
+
<div className="daily-view-body">
|
|
186
|
+
{/* Saatler (Sol Taraf) */}
|
|
187
|
+
<div className="daily-view-hours">
|
|
188
|
+
{hours.map(hour => (
|
|
189
|
+
<div key={hour} className="daily-view-hour-label">
|
|
190
|
+
{formatHour(hour)}
|
|
191
|
+
</div>
|
|
192
|
+
))}
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
{/* Timeline Content */}
|
|
196
|
+
<div
|
|
197
|
+
ref={timelineRef}
|
|
198
|
+
className="daily-view-timeline"
|
|
199
|
+
onClick={handleTimelineClick}
|
|
200
|
+
>
|
|
201
|
+
{/* Saat hücreleri */}
|
|
202
|
+
{hours.map(hour => (
|
|
203
|
+
<div
|
|
204
|
+
key={hour}
|
|
205
|
+
className="daily-view-hour-cell"
|
|
206
|
+
/>
|
|
207
|
+
))}
|
|
208
|
+
|
|
209
|
+
{/* Event'ler (timeline üzerinde absolute position) */}
|
|
210
|
+
{dayEvents.map(event => {
|
|
211
|
+
const position = getEventPosition(event);
|
|
212
|
+
return (
|
|
213
|
+
<div
|
|
214
|
+
key={event.id}
|
|
215
|
+
className="daily-view-event"
|
|
216
|
+
style={position}
|
|
217
|
+
onClick={(e) => {
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
// Event detayları göster
|
|
220
|
+
}}
|
|
221
|
+
>
|
|
222
|
+
<span className="daily-view-event-title">{event.title}</span>
|
|
223
|
+
<span className="daily-view-event-time">
|
|
224
|
+
{new Date(event.startDate).toLocaleTimeString('tr-TR', {
|
|
225
|
+
hour: '2-digit',
|
|
226
|
+
minute: '2-digit'
|
|
227
|
+
})} - {new Date(event.endDate).toLocaleTimeString('tr-TR', {
|
|
228
|
+
hour: '2-digit',
|
|
229
|
+
minute: '2-digit'
|
|
230
|
+
})}
|
|
231
|
+
</span>
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
})}
|
|
235
|
+
|
|
236
|
+
{/* Geçici event (oluşturuluyor) */}
|
|
237
|
+
{isCreating && tempEvent && (
|
|
238
|
+
<div
|
|
239
|
+
className="daily-view-temp-event"
|
|
240
|
+
style={getEventPosition({
|
|
241
|
+
startDate: tempEvent.startDate,
|
|
242
|
+
endDate: tempEvent.endDate,
|
|
243
|
+
})}
|
|
244
|
+
>
|
|
245
|
+
Yeni Randevu
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export default DailyView;
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import React
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return <input type="text" ref={pickerRef} />;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default DatePickerComponent;
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
const DatePickerComponent = ({ onDateSelect }) => {
|
|
4
|
+
return (
|
|
5
|
+
<input
|
|
6
|
+
type="date"
|
|
7
|
+
onChange={(e) => onDateSelect(new Date(e.target.value))}
|
|
8
|
+
className="master-header-date-picker"
|
|
9
|
+
/>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default DatePickerComponent;
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* "dd/mm/yyyy" formatındaki bir tarih string'ini Date objesine dönüştürür.
|
|
3
|
-
* Eğer dateInput bir string değilse, direkt Date objesini döndürür.
|
|
4
|
-
* @param {string | Object | Date} dateInput - "dd/mm/yyyy" formatında tarih stringi veya {fullDate: Date, display: string} objesi veya Date objesi.
|
|
5
|
-
* @returns {Date} - Date objesi.
|
|
6
|
-
*/
|
|
7
|
-
export const parseDate = (dateInput) => {
|
|
8
|
-
if (dateInput instanceof Date) {
|
|
9
|
-
return dateInput;
|
|
10
|
-
}
|
|
11
|
-
if (typeof dateInput === 'string') {
|
|
12
|
-
const [day, month, year] = dateInput.split("/").map(Number);
|
|
13
|
-
return new Date(year, month - 1, day);
|
|
14
|
-
} else if (typeof dateInput === 'object' && dateInput.fullDate instanceof Date) {
|
|
15
|
-
return new Date(dateInput.fullDate);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
console.error("parseDate received invalid input:", dateInput);
|
|
19
|
-
return new Date(); // veya hata fırlat
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Bir tarihin belirli bir aralık içinde olup olmadığını kontrol eder.
|
|
25
|
-
* @param {string | Object | Date} date - "dd/mm/yyyy" formatında tarih stringi, {fullDate: Date, display: string} objesi veya Date objesi.
|
|
26
|
-
* @param {string | Object | Date} startDate - "dd/mm/yyyy" formatında başlangıç tarihi stringi, {fullDate: Date, display: string} objesi veya Date objesi.
|
|
27
|
-
* @param {string | Object | Date} endDate - "dd/mm/yyyy" formatında bitiş tarihi stringi, {fullDate: Date, display: string} objesi veya Date objesi.
|
|
28
|
-
* @returns {boolean} - Tarih aralık içinde ise true, değilse false.
|
|
29
|
-
*/
|
|
30
|
-
export const isDateInRange = (date, startDate, endDate) => {
|
|
31
|
-
const d = parseDate(date);
|
|
32
|
-
const start = parseDate(startDate);
|
|
33
|
-
const end = parseDate(endDate);
|
|
34
|
-
return d >= start && d <= end;
|
|
1
|
+
/**
|
|
2
|
+
* "dd/mm/yyyy" formatındaki bir tarih string'ini Date objesine dönüştürür.
|
|
3
|
+
* Eğer dateInput bir string değilse, direkt Date objesini döndürür.
|
|
4
|
+
* @param {string | Object | Date} dateInput - "dd/mm/yyyy" formatında tarih stringi veya {fullDate: Date, display: string} objesi veya Date objesi.
|
|
5
|
+
* @returns {Date} - Date objesi.
|
|
6
|
+
*/
|
|
7
|
+
export const parseDate = (dateInput) => {
|
|
8
|
+
if (dateInput instanceof Date) {
|
|
9
|
+
return dateInput;
|
|
10
|
+
}
|
|
11
|
+
if (typeof dateInput === 'string') {
|
|
12
|
+
const [day, month, year] = dateInput.split("/").map(Number);
|
|
13
|
+
return new Date(year, month - 1, day);
|
|
14
|
+
} else if (typeof dateInput === 'object' && dateInput.fullDate instanceof Date) {
|
|
15
|
+
return new Date(dateInput.fullDate);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.error("parseDate received invalid input:", dateInput);
|
|
19
|
+
return new Date(); // veya hata fırlat
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Bir tarihin belirli bir aralık içinde olup olmadığını kontrol eder.
|
|
25
|
+
* @param {string | Object | Date} date - "dd/mm/yyyy" formatında tarih stringi, {fullDate: Date, display: string} objesi veya Date objesi.
|
|
26
|
+
* @param {string | Object | Date} startDate - "dd/mm/yyyy" formatında başlangıç tarihi stringi, {fullDate: Date, display: string} objesi veya Date objesi.
|
|
27
|
+
* @param {string | Object | Date} endDate - "dd/mm/yyyy" formatında bitiş tarihi stringi, {fullDate: Date, display: string} objesi veya Date objesi.
|
|
28
|
+
* @returns {boolean} - Tarih aralık içinde ise true, değilse false.
|
|
29
|
+
*/
|
|
30
|
+
export const isDateInRange = (date, startDate, endDate) => {
|
|
31
|
+
const d = parseDate(date);
|
|
32
|
+
const start = parseDate(startDate);
|
|
33
|
+
const end = parseDate(endDate);
|
|
34
|
+
return d >= start && d <= end;
|
|
35
35
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './Timeline.css';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Event Badge Component
|
|
6
|
+
* Önemli event'ler için badge gösterir
|
|
7
|
+
*/
|
|
8
|
+
const EventBadge = ({
|
|
9
|
+
text,
|
|
10
|
+
type = 'default', // 'default', 'important', 'urgent', 'new', 'custom'
|
|
11
|
+
position = 'top-right', // 'top-right', 'top-left', 'bottom-right', 'bottom-left'
|
|
12
|
+
className = '',
|
|
13
|
+
style = {}
|
|
14
|
+
}) => {
|
|
15
|
+
return (
|
|
16
|
+
<span
|
|
17
|
+
className={`event-badge event-badge-${type} event-badge-${position} ${className}`}
|
|
18
|
+
style={style}
|
|
19
|
+
>
|
|
20
|
+
{text}
|
|
21
|
+
</span>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default EventBadge;
|
|
26
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import './Timeline.css';
|
|
3
|
+
|
|
4
|
+
const EventDetailModal = ({
|
|
5
|
+
event,
|
|
6
|
+
isOpen,
|
|
7
|
+
onClose,
|
|
8
|
+
onSave,
|
|
9
|
+
onDelete,
|
|
10
|
+
resources = [],
|
|
11
|
+
}) => {
|
|
12
|
+
const [formData, setFormData] = useState({
|
|
13
|
+
title: '',
|
|
14
|
+
startDate: '',
|
|
15
|
+
endDate: '',
|
|
16
|
+
resourceId: '',
|
|
17
|
+
...event,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (event) {
|
|
22
|
+
setFormData({
|
|
23
|
+
title: event.title || '',
|
|
24
|
+
startDate: event.startDate
|
|
25
|
+
? new Date(event.startDate).toISOString().split('T')[0]
|
|
26
|
+
: '',
|
|
27
|
+
endDate: event.endDate
|
|
28
|
+
? new Date(event.endDate).toISOString().split('T')[0]
|
|
29
|
+
: '',
|
|
30
|
+
resourceId: event.resourceId || '',
|
|
31
|
+
...event,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}, [event]);
|
|
35
|
+
|
|
36
|
+
if (!isOpen || !event) return null;
|
|
37
|
+
|
|
38
|
+
const handleSubmit = (e) => {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
if (onSave) {
|
|
41
|
+
const updatedEvent = {
|
|
42
|
+
...event,
|
|
43
|
+
...formData,
|
|
44
|
+
startDate: new Date(formData.startDate),
|
|
45
|
+
endDate: new Date(formData.endDate),
|
|
46
|
+
};
|
|
47
|
+
onSave(updatedEvent);
|
|
48
|
+
}
|
|
49
|
+
onClose();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const handleDelete = () => {
|
|
53
|
+
if (onDelete && window.confirm('Bu event\'i silmek istediğinize emin misiniz?')) {
|
|
54
|
+
onDelete(event.id);
|
|
55
|
+
onClose();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="modal-overlay" onClick={onClose}>
|
|
61
|
+
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
|
|
62
|
+
<div className="modal-header">
|
|
63
|
+
<h2>Event Düzenle</h2>
|
|
64
|
+
<button className="modal-close-btn" onClick={onClose}>
|
|
65
|
+
×
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<form onSubmit={handleSubmit} className="modal-form">
|
|
70
|
+
<div className="form-group">
|
|
71
|
+
<label>Başlık</label>
|
|
72
|
+
<input
|
|
73
|
+
type="text"
|
|
74
|
+
value={formData.title}
|
|
75
|
+
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
|
76
|
+
required
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div className="form-group">
|
|
81
|
+
<label>Resource</label>
|
|
82
|
+
<select
|
|
83
|
+
value={formData.resourceId}
|
|
84
|
+
onChange={(e) => setFormData({ ...formData, resourceId: e.target.value })}
|
|
85
|
+
required
|
|
86
|
+
>
|
|
87
|
+
<option value="">Seçiniz...</option>
|
|
88
|
+
{resources.map((resource) => (
|
|
89
|
+
<option key={resource.id} value={resource.id}>
|
|
90
|
+
{resource.name}
|
|
91
|
+
</option>
|
|
92
|
+
))}
|
|
93
|
+
</select>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div className="form-row">
|
|
97
|
+
<div className="form-group">
|
|
98
|
+
<label>Başlangıç Tarihi</label>
|
|
99
|
+
<input
|
|
100
|
+
type="date"
|
|
101
|
+
value={formData.startDate}
|
|
102
|
+
onChange={(e) => setFormData({ ...formData, startDate: e.target.value })}
|
|
103
|
+
required
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div className="form-group">
|
|
108
|
+
<label>Bitiş Tarihi</label>
|
|
109
|
+
<input
|
|
110
|
+
type="date"
|
|
111
|
+
value={formData.endDate}
|
|
112
|
+
onChange={(e) => setFormData({ ...formData, endDate: e.target.value })}
|
|
113
|
+
required
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div className="modal-actions">
|
|
119
|
+
<button type="button" className="btn-delete" onClick={handleDelete}>
|
|
120
|
+
Sil
|
|
121
|
+
</button>
|
|
122
|
+
<div>
|
|
123
|
+
<button type="button" className="btn-cancel" onClick={onClose}>
|
|
124
|
+
İptal
|
|
125
|
+
</button>
|
|
126
|
+
<button type="submit" className="btn-save">
|
|
127
|
+
Kaydet
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</form>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export default EventDetailModal;
|
|
138
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './Timeline.css';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Event Icon Component
|
|
6
|
+
* Event'lerde gösterilecek ikonları render eder
|
|
7
|
+
*/
|
|
8
|
+
const EventIcon = ({ type, className = '' }) => {
|
|
9
|
+
const getIconContent = () => {
|
|
10
|
+
switch (type) {
|
|
11
|
+
case 'balance-warning':
|
|
12
|
+
return '⚠️'; // Bakiye uyarısı
|
|
13
|
+
case 'important-note':
|
|
14
|
+
return '📝'; // Önemli not
|
|
15
|
+
case 'payment-pending':
|
|
16
|
+
return '💳'; // Ödeme bekliyor
|
|
17
|
+
case 'confirmed':
|
|
18
|
+
return '✅'; // Onaylandı
|
|
19
|
+
case 'cancelled':
|
|
20
|
+
return '❌'; // İptal edildi
|
|
21
|
+
case 'pending':
|
|
22
|
+
return '⏳'; // Beklemede
|
|
23
|
+
case 'completed':
|
|
24
|
+
return '✓'; // Tamamlandı
|
|
25
|
+
case 'in-progress':
|
|
26
|
+
return '▶️'; // Devam ediyor
|
|
27
|
+
case 'alert':
|
|
28
|
+
return '🔔'; // Uyarı
|
|
29
|
+
case 'info':
|
|
30
|
+
return 'ℹ️'; // Bilgi
|
|
31
|
+
default:
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const iconContent = getIconContent();
|
|
37
|
+
if (!iconContent) return null;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<span className={`event-icon event-icon-${type} ${className}`}>
|
|
41
|
+
{iconContent}
|
|
42
|
+
</span>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default EventIcon;
|
|
47
|
+
|