source: src/main/resources/static/js/admin-terms.js@ 43c9090

Last change on this file since 43c9090 was 43c9090, checked in by macagaso <gasoskamarija@…>, 5 weeks ago

Updated version

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