diff --git a/app/logic/events.py b/app/logic/events.py index db921d149..52600cfa6 100644 --- a/app/logic/events.py +++ b/app/logic/events.py @@ -129,8 +129,9 @@ def attemptSaveMultipleOfferings(eventData, attachmentFiles = None): 'startDate': event['eventDate'], 'timeStart': event['startTime'], 'timeEnd': event['endTime'], + 'location': eventData['location'], 'seriesId': seriesId, - 'isRepeating': bool(isRepeating) + 'isRepeating': bool(isRepeating), }) # Try to save each offering savedEvents, validationErrorMessage = attemptSaveEvent(eventInfo, attachmentFiles) @@ -458,7 +459,9 @@ def getRepeatingEventsData(eventData): return [ {'name': f"{eventData['name']} Week {counter+1}", 'date': eventData['startDate'] + timedelta(days=7*counter), - "week": counter+1} + "week": counter+1, + 'location': eventData['location'] + } for counter in range(0, ((eventData['endDate']-eventData['startDate']).days//7)+1)] def preprocessEventData(eventData): diff --git a/app/static/js/createEvents.js b/app/static/js/createEvents.js index 1a734a73c..a934a8174 100644 --- a/app/static/js/createEvents.js +++ b/app/static/js/createEvents.js @@ -21,9 +21,9 @@ function format12to24HourTime(timeStr) { let [hours, minutes] = timePart.split(":").map(Number); if (meridian === "PM" && hours !== 12) { - hours += 12; + hours += 12; } else if (meridian === "AM" && hours === 12) { - hours = 0; // midnight + hours = 0; // midnight } // format hours and minutes to always be 2 digits @@ -33,43 +33,54 @@ function format12to24HourTime(timeStr) { return `${formattedHours}:${formattedMinutes}`; } -function calculateRepeatingEventFrequency(){ - var eventDatesAndName = {name:$("#repeatingEventsNamePicker").val(), - isRepeating: true, - startDate:$("#repeatingEventsStartDate").val(), - endDate:$("#repeatingEventsEndDate").val()} +function calculateRepeatingEventFrequency() { + var eventDatesAndName = { + name: $("#repeatingEventsNamePicker").val(), + isRepeating: true, + startDate: $("#repeatingEventsStartDate").val(), + endDate: $("#repeatingEventsEndDate").val(), + location: $("#repeatingEventsLocationPicker").val() || '' + } + $.ajax({ - type:"POST", + type: "POST", url: "/makeRepeatingEvents", //get the startDate, endDate and name as a dictionary data: eventDatesAndName, - success: function(jsonData){ + success: function (jsonData) { var generatedEvents = JSON.parse(jsonData) $("#generatedEventsTable tbody tr").remove(); - for(var event of generatedEvents){ + for (var event of generatedEvents) { loadRepeatingOfferingToModal(event) } $("#generatedEvents").removeClass("d-none"); }, - error: function(error){ - console.log(error) + error: function (error) { + displayNotification("Failed to generate events."); } }); } -function setViewForSingleOffering(){ +function setViewForSingleOffering() { $(".startDatePicker").prop('required', true); + $(".startDatePicker").show(); $("#multipleOfferingTableDiv").addClass('d-none'); - $('#eventTime, #eventDate').removeClass('d-none'); - $('#checkIsSeriesToggleContainer').addClass('col-md-6') + $("#eventLocation-main").show(); + $("#inputEventLocation-main").attr('readonly', false); + $("#inputEventLocation-main").prop('required', true); + $('#eventTime, #eventDate').show(); $('#checkIsSeriesToggleContainer').removeClass('col-md-12') + $('#checkIsSeriesToggleContainer').addClass('col-md-6') } -function setViewForSeries(){ +function setViewForSeries() { $(".startDatePicker").prop('required', false); + $(".startDatePicker").hide(); $("#multipleOfferingTableDiv").removeClass('d-none'); - $('#eventTime, #eventDate').addClass('d-none'); + $("#eventLocation-main").show(); + $("#inputEventLocation-main").prop('required', false); + $('#eventTime, #eventDate').hide(); $('#checkIsSeriesToggleContainer').removeClass('col-md-6') $('#checkIsSeriesToggleContainer').addClass('col-md-12') $("#pastDateWarningText").text("") @@ -78,13 +89,14 @@ function setViewForSeries(){ function displayNotification(message) { $('#textNotifierPadding').addClass('pt-5'); $('.invalidFeedback').text(message); - $('.invalidFeedback').css('display', 'block'); - $('.invalidFeedback').on('animationend', function() { - $('.invalidFeedback').css('display', 'none'); - $('#textNotifierPadding').removeClass('pt-5') + $('.invalidFeedback').css('display', 'block'); + $('.invalidFeedback').on('animationend', function () { + $('.invalidFeedback').css('display', 'none'); + $('#textNotifierPadding').removeClass('pt-5'); }); } + function isDateInPast(dateString, timeString) { const combineDateTime = `${dateString}T${timeString}:00`; const setDate = new Date(combineDateTime).getTime(); @@ -102,20 +114,23 @@ function initializeFlatpickr(obj) { minTime: "08:00", maxTime: "22:00", minuteIncrement: 15, - allowInput: true + allowInput: true }); } -function createOfferingModalRow({eventName=null, eventDate=null, startTime=null, endTime=null}={}){ - +function createOfferingModalRow({ eventName = null, eventDate = null, startTime = null, endTime = null, eventLocation = null } = {}) { let clonedOffering = $("#multipleOfferingEvent").clone().removeClass('d-none').removeAttr("id"); + const Name = $('#inputEventName').val(); + clonedOffering.find('.multipleOfferingNameField').val(Name); + // insert values for the newly created row - if (eventName) {clonedOffering.find('.multipleOfferingNameField').val(eventName)} - if (eventDate) {clonedOffering.find('.multipleOfferingDatePicker').val(eventDate)} - if (startTime) {clonedOffering.find('.multipleOfferingStartTime').val(startTime)} - if (endTime) {clonedOffering.find('.multipleOfferingEndTime').val(endTime)} - + if (eventName) { clonedOffering.find('.multipleOfferingNameField').val(eventName) } + if (eventDate) { clonedOffering.find('.multipleOfferingDatePicker').val(eventDate) } + if (startTime) { clonedOffering.find('.multipleOfferingStartTime').val(startTime) } + if (endTime) { clonedOffering.find('.multipleOfferingEndTime').val(endTime) } + if (eventLocation) { clonedOffering.find('.multipleOfferingLocationField').val(eventLocation) } + $("#multipleOfferingSlots").append(clonedOffering); pendingmultipleEvents.push(clonedOffering); @@ -189,6 +204,7 @@ $('#saveSeries').on('click', function(e) { enableLiveCustomValidityClearing([".multipleOfferingNameField"]) let eventOfferings = $('#multipleOfferingSlots .eventOffering'); let eventNameInputs = $('#multipleOfferingSlots .multipleOfferingNameField'); + let eventLocationInputs = $('#multipleOfferingSlots .multipleOfferingLocationField'); let datePickerInputs = $('#multipleOfferingSlots .multipleOfferingDatePicker'); let startTimeInputs = $('#multipleOfferingSlots .multipleOfferingStartTime'); let endTimeInputs = $('#multipleOfferingSlots .multipleOfferingEndTime'); @@ -220,6 +236,7 @@ $('#saveSeries').on('click', function(e) { } else { // Validate individual event offerings for non-repeating events + // Check event name fields eventNameInputs.each((index, eventNameInput) => { if (eventNameInput.value.trim() === '') { @@ -229,6 +246,18 @@ $('#saveSeries').on('click', function(e) { } else { $(eventNameInput)[0].setCustomValidity(""); } + }); + + + // Check location fields + eventLocationInputs.each((index, eventLocationInput) => { + if (eventLocationInput.value.trim() === '') { + hasErrors = true; + $(eventLocationInput)[0].setCustomValidity("Please enter an event location"); + $(eventLocationInput)[0].reportValidity(); + } else { + $(eventLocationInput)[0].setCustomValidity(""); + } }); // Check date picker fields @@ -303,24 +332,11 @@ $('#saveSeries').on('click', function(e) { // Populate the Event Name field in the main page with the entered repeating events function updateEventNameField() { - let offerings = JSON.parse($("#seriesData").val()) let isSeries = $("#checkIsRepeating").is(":checked") - - // Check if the event is weekly - if (!isSeries) { - // if not weeekly, add them to a set to remove duplicates, then put them in a string to populate the field - let names = new Set() - offerings.forEach(offering => { - names.add(offering.eventName) - }); - let offeringsText = Array.from(names).join(", ") - $('#inputEventName').val(offeringsText) - } - else { - // if weekly, take the name of the first item (which is the same for all) and take the word 'week' + if (isSeries) { let offeringText = $("#repeatingEventsNamePicker").val() $('#inputEventName').val(offeringText) - } + } } // Save the offerings from the modal to the hidden input field @@ -329,32 +345,34 @@ function saveOfferingsFromModal() { let isRepeatingStatus = $("#checkIsRepeating").is(":checked"); $("#formIsRepeating").prop("checked", isRepeatingStatus); let dataTable = isRepeatingStatus ? "#generatedEventsList" : "#multipleOfferingSlots"; - $(dataTable).children().each(function(index, element) { + $(dataTable).children().each(function (index, element) { let rowData; - if (isRepeatingStatus){ - rowData = $.map($(element).find("td"), function(td){ + if (isRepeatingStatus) { + rowData = $.map($(element).find("td"), function (td) { let input = $(td).find("input"); - if (input.length){ + if (input.length) { return input.val(); } else { return $(td).text().trim(); } - })} + }) + } else { rowData = $.map($(element).find("input"), (el) => $(el).val()); } - let startTime = isRepeatingStatus ? $("#repeatingEventsStartTime").val() : rowData[2] - let endTime = isRepeatingStatus ? $("#repeatingEventsEndTime").val() : rowData[3] + let startTime = isRepeatingStatus ? $("#repeatingEventsStartTime").val() : rowData[3] + let endTime = isRepeatingStatus ? $("#repeatingEventsEndTime").val() : rowData[4] if (navigator.userAgent.indexOf("Chrome") == -1) { - startTime = format12to24HourTime(startTime) - endTime = format12to24HourTime(endTime) + startTime = format12to24HourTime(startTime) + endTime = format12to24HourTime(endTime) } offerings.push({ - eventName: rowData[0], - eventDate: rowData[1], - startTime: startTime, - endTime: endTime, + eventName: rowData[0], + eventLocation: isRepeatingStatus ? rowData[2] : rowData[1], + eventDate: isRepeatingStatus ? rowData[1] : rowData[2], + startTime: startTime, + endTime: endTime, }) }); @@ -364,29 +382,37 @@ function saveOfferingsFromModal() { $("#seriesData").val(offeringsJson); } -function loadOfferingsToModal(){ +function loadOfferingsToModal() { let offerings = JSON.parse($("#seriesData").val()) - if (offerings.length < 1) {return;} + if (offerings.length < 1) { return; } let isRepeatingStatus = $("#checkIsRepeating").is(":checked"); - if (isRepeatingStatus) {$("#generatedEvents").removeClass("d-none"); $("#generatedEventsTable tbody tr").remove();}; - offerings.forEach((offering, i) =>{ - if (isRepeatingStatus){ + let mainLocation = $('#inputEventLocation-main').val(); + if (isRepeatingStatus) { $("#generatedEvents").removeClass("d-none"); $("#generatedEventsTable tbody tr").remove(); }; + offerings.forEach((offering, i) => { + if (isRepeatingStatus) { loadRepeatingOfferingToModal(offering); } else { - let newOfferingModalRow = createOfferingModalRow(offering); + let newOfferingModalRow = createOfferingModalRow({ + ...offering, + eventLocation: offering.eventLocation || mainLocation + }); //stripes odd event sections in event modal - newOfferingModalRow.css('background-color', i % 2 ?'#f2f2f2':'#fff'); - }}) + newOfferingModalRow.css('background-color', i % 2 ? '#f2f2f2' : '#fff'); + } + }); } function loadRepeatingOfferingToModal(offering){ var seriesTable = $("#generatedEventsTable"); var eventDate = new Date(offering.date || offering.eventDate).toLocaleDateString(); + + seriesTable.append( "