source: src/main/resources/static/js/admin-terms.js@ 743de55

Last change on this file since 743de55 was 743de55, checked in by macagaso <gasoskamarija@…>, 6 weeks ago

Initial commit

  • Property mode set to 100644
File size: 22.2 KB
Line 
1import { deleteAppointment, confirmCarriedOut, getUsersByTermExcept, removeRequestAndUpdateUser, removeAppointment, makeReservation ,displayDiv} from './shared.js';
2
3let calendar = document.querySelector('.calendar')
4let importantDate;
5const month_names = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
6const modal = document.getElementById('popupModal');
7const cancelBtn = document.getElementById('cancelBtn');
8const approveBtn = document.getElementById('approveBtn');
9const closeSpan = document.querySelector('.close');
10const deleteBtn=document.getElementById("temporal-deletion");
11
12function resetFields() {
13 document.getElementById('start-time').selectedIndex = 0;
14 document.getElementById('timePicker').selectedIndex=0;
15 document.getElementById('end-time').selectedIndex=0;
16 document.getElementById('time-interval').value = '';
17 document.getElementById('start-date').value = '';
18 document.getElementById('end-date').value='';
19 document.getElementById('delete-date-from').value='';
20 document.getElementById('delete-date-to').value='';
21}
22function checkOverlap(existingTimes, newTime) {
23 const [newHour, newMinutes] = newTime.split(':').map(Number);
24
25 const newStartTime = new Date(0, 0, 0, newHour, newMinutes);
26 const newEndTime = new Date(newStartTime);
27 newEndTime.setHours(newEndTime.getHours() + 1);
28
29 return existingTimes.some(existingTime => {
30 const [existingHour, existingMinutes] = existingTime.split(':').map(Number);
31 const existingStartTime = new Date(0, 0, 0, existingHour, existingMinutes);
32 const existingEndTime = new Date(existingStartTime);
33 existingEndTime.setHours(existingEndTime.getHours() + 1);
34 return newStartTime < existingEndTime && newEndTime > existingStartTime;
35 });
36}
37async function getExistingAppointmentsMapped() {
38 let existingAppointments;
39 try {
40 const response = await fetch(`/api/appointments/getAllAppointments`);
41 if (!response.ok) {
42 throw new Error('Failed to fetch appointments');
43 }
44 existingAppointments = await response.json();
45 console.log(existingAppointments);
46 } catch (error) {
47 console.error(error);
48 }
49
50 const appointmentMap = new Map();
51 existingAppointments.forEach(appointment => {
52 const dateTime = new Date(appointment.term);
53 const date = dateTime.toISOString().split('T')[0];
54 const time = dateTime.toTimeString().substring(0, 5);
55 if (appointmentMap.has(date)) {
56 appointmentMap.get(date).push(time);
57 } else {
58 appointmentMap.set(date, [time]);
59 }
60 });
61 return appointmentMap;
62}
63function createAppointments(startDate, endDate, startTime, endTime, interval) {
64 let appointments = [];
65 let currentDate = new Date(startDate);
66 let endDateObj = new Date(endDate);
67 const [startHour, startMinute] = startTime.split(':').map(Number);
68 const [endHour, endMinute] = endTime.split(':').map(Number);
69
70 while (currentDate <= endDateObj) {
71 let currentStartTime = new Date(currentDate);
72 currentStartTime.setHours(startHour, startMinute, 0, 0);
73
74 let currentEndTime = new Date(currentDate);
75 currentEndTime.setHours(endHour - 1, endMinute+interval, 0, 0);
76
77 while (currentStartTime < currentEndTime) {
78 const formattedDate = currentDate.toISOString().split('T')[0];
79 appointments.push({
80 date: formattedDate,
81 time: currentStartTime.toTimeString().substring(0, 5)
82 });
83 currentStartTime.setMinutes(currentStartTime.getMinutes() + interval + 60);
84 }
85 currentDate.setDate(currentDate.getDate() + 1);
86 }
87
88 return appointments;
89}
90
91function formatConflictAlert(conflictingAppointments) {
92 const appointmentList = Array.isArray(conflictingAppointments) ? conflictingAppointments : [conflictingAppointments];
93 const formattedAppointments = appointmentList.map(appointment =>
94 `Датум: ${appointment.date}, Време: ${appointment.time}`
95 );
96
97 const alertMessage = [
98 "Неуспешно креирање на термини:",
99 ...formattedAppointments
100 ].filter(Boolean).join('\n');
101
102 return alertMessage.trim();
103}
104
105async function createAutoAppointments(appointments) {
106 const requestBody = appointments.map(appointment => ({
107 date: appointment.date,
108 time: appointment.time,
109 }));
110
111 await fetch(`/api/appointments/create`, {
112 method: 'POST',
113 headers: {
114 'Content-Type': 'application/json',
115 },
116 body: JSON.stringify(requestBody) // Send all appointments in one request
117 });
118}
119document.getElementById('create-appointments').addEventListener('click', async function () {
120 const startDate = document.getElementById('start-date').value;
121 const endDate = document.getElementById('end-date').value;
122 const startTime = document.getElementById('start-time').value;
123 const endTime = document.getElementById('end-time').value;
124 const interval = parseInt(document.getElementById('time-interval').value);
125
126 if (!startDate || !endDate || !startTime || !endTime || !interval) {
127 alert("Please fill out all the fields.");
128 return;
129 }
130
131 const appointments = createAppointments(startDate, endDate, startTime, endTime, interval);
132 console.log('Generated Appointments:', appointments);
133
134 const existingMapped = await getExistingAppointmentsMapped();
135 const conflictingAppointments = [];
136 const successfulAppointments = [];
137
138 appointments.forEach(newAppointment => {
139 const { date, time } = newAppointment;
140
141 if (existingMapped.has(date)) {
142 const existingTimes = existingMapped.get(date);
143 if (checkOverlap(existingTimes, time)) {
144 conflictingAppointments.push(newAppointment); // Add to conflict list if overlaps
145 } else {
146 successfulAppointments.push(newAppointment);
147 }
148 } else {
149 successfulAppointments.push(newAppointment);
150 }
151 });
152 console.log(conflictingAppointments);
153 console.log(successfulAppointments);
154 if(successfulAppointments.length>0){
155 await createAutoAppointments(successfulAppointments);
156 }
157 if (conflictingAppointments.length > 0) {
158 const alertMessage = formatConflictAlert(conflictingAppointments);
159 alert(alertMessage);
160 }
161 resetFields();
162});
163
164cancelBtn.addEventListener('click', () => {
165 modal.style.display = 'none';
166});
167
168closeSpan.addEventListener('click', () => {
169 modal.style.display = 'none';
170});
171
172deleteBtn.addEventListener('click',()=>{
173 deleteAppointment(deleteBtn.getAttribute("term"),deleteBtn.getAttribute("type"));
174})
175
176window.addEventListener('click', (event) => {
177 if (event.target === modal) {
178 modal.style.display = 'none';
179 }
180});
181
182function cleanAssets() {
183 const ids = ['request-assets', 'appointment-assets'];
184 ids.forEach((id) => {
185 const element = document.getElementById(id);
186 if (element && element.style.display !== 'none') {
187 element.style.display = 'none';
188 }
189 });
190}
191
192
193async function isAppointmentReserved(dateTime) {
194 try {
195 const response = await fetch(`/api/appointments/isReserved?term=${dateTime}`);
196 return await response.json();
197 } catch (error) {
198 console.error('Error checking if appointment reserved:', error);
199 return false;
200 }
201}
202async function isAppointmentEmpty(dateTime) {
203 try {
204 const response = await fetch(`/api/requests/isEmpty?term=${dateTime}`);
205 return await response.json();
206 } catch (error) {
207 console.error('Error checking if no requests:', error);
208 return false;
209 }
210}
211function cleanData(bodyId){
212 const element = document.getElementById(bodyId);
213 while (element.firstChild) {
214 element.removeChild(element.firstChild);
215 }
216}
217function createLines(data,dateTime,whichOne){
218 cleanData(whichOne)
219 if(!Array.isArray(data)){
220 data=[data];
221 }
222
223 let requestedElement=document.getElementById(whichOne);
224 data.forEach(item => {
225 const requestedRow = document.createElement('tr');
226 const usernameTd = document.createElement('td');
227 usernameTd.textContent = item.username;
228 requestedRow.appendChild(usernameTd);
229 const nameTd = document.createElement('td');
230 nameTd.textContent = item.name;
231 requestedRow.appendChild(nameTd);
232 const surnameTd = document.createElement('td');
233 surnameTd.textContent = item.surname;
234 requestedRow.appendChild(surnameTd);
235 const additionalInfoTd = document.createElement('td');
236 additionalInfoTd.textContent = item.additionalInfo;
237 requestedRow.appendChild(additionalInfoTd);
238 const couponCodeTd = document.createElement('td');
239 couponCodeTd.textContent = item.couponCode;
240 requestedRow.appendChild(couponCodeTd);
241 requestedElement.appendChild(requestedRow);
242 displayDiv(dateTime);
243 })
244}
245function getAllRequests(dateTime,containerId){
246 let url;
247 if(containerId === "approved"){
248 url = `/api/appointments/listApprovedRequest?term=${dateTime}`;
249 } else {
250 url = `/api/requests/listRequests?term=${dateTime}`;
251 }
252
253 fetch(url)
254 .then(response => response.json())
255 .then(data => {
256 createLines(data,dateTime,containerId)
257 })
258 .catch(error => {
259 console.error('Error fetching requests:', error);
260 });
261}
262function createActiveAppointments(data){
263 const frameElement=document.getElementById("frame");
264 frameElement.innerHTML = '';
265 document.getElementById("approved-table").style.display = 'none';
266 document.getElementById("requested-table").style.display = 'none';
267 data.forEach(item => {
268 const itemDiv = document.createElement('div');
269 itemDiv.style.border = '1px solid black';
270 itemDiv.style.padding = '20px';
271 itemDiv.style.display = 'inline-block';
272 itemDiv.style.marginRight = '10px';
273
274 const appointmentDate = new Date(item.localDateTime);
275 const timeOptions = { hour: '2-digit', minute: '2-digit', hour12: false };
276 itemDiv.textContent = appointmentDate.toLocaleTimeString([], timeOptions);
277
278 itemDiv.addEventListener('click', async () => {
279 try{
280
281 const isReserved=await isAppointmentReserved(item.localDateTime);
282 const isEmpty=await isAppointmentEmpty(item.localDateTime);
283 cleanAssets();
284 cleanData("approved")
285 cleanData("requested")
286 if (isReserved) {
287 document.getElementById("approved-table").style.display = 'block';
288 document.getElementById("requested-table").style.display = 'none';
289 getAllRequests(item.localDateTime,"approved","");
290 document.getElementById("appointment-assets").style.display='block';
291 deleteBtn.style.display='block';
292 deleteBtn.setAttribute("term",item.localDateTime);
293 deleteBtn.setAttribute("type","cancelledAppointmentByAdmin");
294 //da go isprogramirash delete-approval
295 document.getElementById("delete-approval").addEventListener('click', function() {
296 removeAppointment(item.localDateTime,"cancelledAppointmentByAdmin");
297 });
298 document.getElementById("approve-carried-out").addEventListener('click', function() {
299 modal.style.display = 'flex';
300 approveBtn.addEventListener('click', () => {
301 const userInput = document.getElementById('userInput').value;
302 confirmCarriedOut(item.localDateTime,userInput);
303 modal.style.display = 'none';
304 });
305
306 });
307 }
308 else if(!isEmpty){
309 document.getElementById("approved-table").style.display = 'none';
310 document.getElementById("requested-table").style.display = 'block';
311 getAllRequests(item.localDateTime,"requested","");
312 document.getElementById("request-assets").style.display='block';
313 deleteBtn.style.display='block';
314 deleteBtn.setAttribute("term",item.localDateTime);
315 deleteBtn.setAttribute("type","rejected");
316 }
317 else{
318 document.getElementById("approved-table").style.display = 'none';
319 document.getElementById("requested-table").style.display = 'none';
320 deleteBtn.style.display='block';
321 deleteBtn.setAttribute("term",item.localDateTime);
322 }
323
324
325 }
326 catch(error){
327 console.error('Error checking appointment reservation:', error);
328 }
329
330 });
331
332 frameElement.appendChild(itemDiv);
333 });
334}
335function fetchAppointments(date){
336 deleteBtn.style.display='none';
337 fetch(`/api/appointments/listAppointments?date=${date}`)
338 .then(response => response.json())
339 .then(data => {
340 createActiveAppointments(data);
341 })
342 .catch(error => {
343 console.error('Error fetching appointments:', error);
344 });
345}
346
347const isLeapYear = (year) => {
348 return (year % 4 === 0 && year % 100 !== 0 && year % 400 !== 0) || (year % 100 === 0 && year % 400 ===0)
349}
350
351const getFebDays = (year) => {
352 return isLeapYear(year) ? 29 : 28
353}
354
355
356const generateCalendar = (month, year) => {
357
358 let calendar_days = calendar.querySelector('.calendar-days')
359 let calendar_header_year = calendar.querySelector('#year')
360
361 let days_of_month = [31, getFebDays(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
362
363 calendar_days.innerHTML = ''
364
365 let currDate = new Date()
366
367 // if (!month) month = currDate.getMonth()
368 console.log(month);
369 if (typeof month !== 'number') month = currDate.getMonth();
370 if (!year) year = currDate.getFullYear()
371
372 let curr_month = `${month_names[month]}`
373 month_picker.innerHTML = curr_month
374 calendar_header_year.innerHTML = year
375
376 // get first day of month
377
378 let first_day = new Date(year, month, 1)
379
380 for (let i = 0; i <= days_of_month[month] + first_day.getDay() - 1; i++) {
381 let day = document.createElement('div')
382 if (i >= first_day.getDay()) {
383 day.classList.add('calendar-day-hover')
384 day.innerHTML = i - first_day.getDay() + 1;
385 day.innerHTML += `<span></span>
386 <span></span>
387 <span></span>
388 <span></span>`;
389 let selectedDate = `${year}-${(month + 1).toString().padStart(2, '0')}-${(i - first_day.getDay() + 1).toString().padStart(2, '0')}`;
390 if (i - first_day.getDay() + 1 === currDate.getDate() && year === currDate.getFullYear() && month === currDate.getMonth()) {
391 day.classList.add('curr-date')
392 importantDate=selectedDate;
393 document.getElementById("insert-date").innerText=importantDate;
394 fetchAppointments(importantDate);
395 }
396 day.addEventListener('click', () => {
397 let temp=document.getElementsByClassName('curr-date');
398 Array.from(temp).forEach(element => {
399 element.classList.remove('curr-date');
400 });
401 importantDate=selectedDate;
402 let daySpan= document.getElementById("insert-date");
403 daySpan.innerText="";
404 daySpan.innerText=importantDate;
405 day.classList.add('curr-date');
406 fetchAppointments(importantDate);
407 cleanAssets();
408 cleanData("approved")
409 cleanData("requested")
410 resetFields();
411 });
412 }
413 calendar_days.appendChild(day)
414 }
415}
416
417let month_list = calendar.querySelector('.month-list')
418
419month_names.forEach((e, index) => {
420 let month = document.createElement('div')
421 month.innerHTML = `<div data-month="${index}">${e}</div>`
422 month.querySelector('div').onclick = () => {
423 month_list.classList.remove('show')
424 curr_month.value = index
425 generateCalendar(index, curr_year.value)
426 }
427 month_list.appendChild(month)
428})
429
430let month_picker = calendar.querySelector('#month-picker')
431
432month_picker.onclick = () => {
433 month_list.classList.add('show')
434}
435
436let currDate = new Date()
437
438let curr_month = {value: currDate.getMonth()}
439let curr_year = {value: currDate.getFullYear()}
440
441generateCalendar(curr_month.value, curr_year.value)
442
443document.querySelector('#prev-year').onclick = () => {
444 --curr_year.value
445 generateCalendar(curr_month.value, curr_year.value)
446}
447
448document.querySelector('#next-year').onclick = () => {
449 ++curr_year.value
450 generateCalendar(curr_month.value, curr_year.value)
451}
452function populateTimePicker() {
453 const timePicker = document.getElementById('timePicker');
454 const timePickerStart = document.getElementById('start-time');
455 const timePickerEnd = document.getElementById('end-time');
456
457 const timePickerInterval = 10;
458 const otherPickersInterval = 30;
459
460 for (let hour = 7; hour < 22; hour++) { // 0 to 23 for 24-hour format
461 for (let minutes = 0; minutes < 60; minutes++) {
462 const formattedHour = hour.toString().padStart(2, '0');
463 const formattedMinutes = minutes.toString().padStart(2, '0');
464
465
466 if (minutes % timePickerInterval === 0) {
467 let timeOption = document.createElement('option');
468 timeOption.value = `${formattedHour}:${formattedMinutes}`;
469 timeOption.text = `${formattedHour}:${formattedMinutes}`;
470 timePicker.appendChild(timeOption);
471 }
472
473 if (minutes % otherPickersInterval === 0) {
474 let timeOptionStart = document.createElement('option');
475 timeOptionStart.value = `${formattedHour}:${formattedMinutes}`;
476 timeOptionStart.text = `${formattedHour}:${formattedMinutes}`;
477 timePickerStart.appendChild(timeOptionStart);
478
479 let timeOptionEnd = document.createElement('option');
480 timeOptionEnd.value = `${formattedHour}:${formattedMinutes}`;
481 timeOptionEnd.text = `${formattedHour}:${formattedMinutes}`;
482 timePickerEnd.appendChild(timeOptionEnd);
483 }
484 }
485 }
486}
487
488function createSeparateAppointment(data){
489 fetch('/api/appointments/add', {
490 method: 'POST',
491 headers: {
492 'Content-Type': 'application/json',
493 },
494 body: JSON.stringify(data),
495 })
496 .then(response => {
497 if (!response.ok) {
498 return response.json().then(errorData => {
499 throw new Error(errorData.error || 'Unknown error');
500 });
501 }
502 return response.json();
503 })
504 .then(data => {
505 console.log(data.message);
506 })
507 .catch(error => {
508 console.error('Error:', error);
509 });
510}
511
512async function deleteFreeAppointments() {
513 const selectedDateFrom = document.getElementById('delete-date-from').value;
514 const selectedDateTo = document.getElementById('delete-date-to').value;
515 if (!selectedDateFrom && !selectedDateTo) {
516 alert("Please select dates!");
517 return;
518 }
519
520 try {
521 const response = await fetch(`/api/appointments/deleteFree?startDate=${selectedDateFrom}&endDate=${selectedDateTo}`, {
522 method: 'DELETE',
523 });
524
525 if (response.ok) {
526 alert("Free appointments for the selected date range were deleted.");
527 } else {
528 alert("An error occurred while trying to delete the appointments.");
529 }
530 } catch (error) {
531 console.error("Error deleting appointments:", error);
532 alert("A network error occurred while trying to delete the appointments.");
533 }
534 document.getElementById('delete-date-from').value='';
535 document.getElementById('delete-date-to').value='';
536}
537
538populateTimePicker();
539document.addEventListener('DOMContentLoaded', () => {
540 const today = new Date();
541 const formattedDate = today.toISOString().split('T')[0];
542 document.getElementById('start-date').setAttribute('min', formattedDate);
543 document.getElementById('end-date').setAttribute('min',formattedDate);
544 const addTermButton = document.getElementById('add-Term');
545 const timePicker = document.getElementById('timePicker');
546 timePicker.addEventListener('click',()=>{
547 document.getElementById('start-time').selectedIndex = 0;
548 document.getElementById('end-time').selectedIndex=0;
549 document.getElementById('time-interval').value = '';
550 document.getElementById('start-date').value = '';
551 document.getElementById('end-date').value='';
552 document.getElementById('delete-date-from').value='';
553 document.getElementById('delete-date-to').value='';
554 })
555 addTermButton.addEventListener('click',async () => {
556 const selectedTime = timePicker.value;
557 if (importantDate && selectedTime) {
558 console.log(`Selected Date: ${importantDate}`);
559 console.log(`Selected Time: ${selectedTime}`);
560
561 const data = {
562 date: importantDate,
563 time: selectedTime
564 };
565
566
567 const mapped = await getExistingAppointmentsMapped();
568 if (mapped.has(importantDate)) {
569 const existingTimes = mapped.get(importantDate);
570 if (checkOverlap(existingTimes, selectedTime)) {
571 const alertMessage = formatConflictAlert(data);
572 alert(alertMessage);
573 } else {
574 createSeparateAppointment(data);
575 }
576 }
577 else {
578 createSeparateAppointment(data);
579 }
580 resetFields();
581 } else {
582 console.error('Please select a date and time.');
583 }
584 });
585
586 let tempContainer=document.getElementsByClassName('appointment-section')[0];
587 tempContainer.addEventListener('click',()=>{
588 document.getElementById('timePicker').selectedIndex=0;
589 })
590 document.getElementById('delete-free-button').addEventListener('click', deleteFreeAppointments);
591});
592
593
Note: See TracBrowser for help on using the repository browser.