import type { CMSFilters } from '$types/CMSFilters'; import type { MSCCEvent, MSCCRawEvent } from '$types/Events'; import { fetchItems } from '$utils/fetch'; import { handleDates } from '$utils/handleDates'; /** * * Handles the Capitol events * * @export * @param {string} API_URL * @return void */ export async function handleCapitolEvents(API_URL: string) { // Get the element with ID of 'mscc-events' const eventsElement = document.getElementById('mscc-events'); // If the element doesn't exist, return early if (!eventsElement) return false; handleDates(); // Initialize Finsweet CMSFilter window.fsAttributes = window.fsAttributes || []; window.fsAttributes.push([ 'cmsfilter', // When the CMSFilter is loaded with instances, run this function async (filtersInstances: CMSFilters[]) => { // Get the filters instance const [filtersInstance] = filtersInstances; if (!filtersInstance) return; // Get the list instance const { listInstance } = filtersInstance; // Save a copy of the template const [firstItem] = listInstance.items; const itemTemplateElement = firstItem.element; // Remove existing items listInstance.clearItems(); // Fetch external data const events = (await fetchItems(API_URL)) as MSCCRawEvent[]; // If there are no events, return early if (!events) return; // Create the new items const newItemsPromises = events.map((singleEvent: MSCCRawEvent) => { const processedEvent = processEvent(singleEvent); return createEventCard(processedEvent, itemTemplateElement); }); const newItems = await Promise.all(newItemsPromises); console.log('newItems', newItems); // Populate the list listInstance.addItems(newItems); // Get the unique event organizations const eventOrganizations = getEventOrganizations(events); const dropdownTemplateElement = document.querySelector( 'select[fs-cmsfilter-field="organization"]' ) as NonNullable; const dropdownTemplateWrapperElement = dropdownTemplateElement.parentElement as NonNullable; const dropdown = createDropdown( eventOrganizations, dropdownTemplateElement! ); dropdownTemplateWrapperElement.insertBefore( dropdown, dropdownTemplateWrapperElement.children[1] ); filtersInstance.storeFiltersData(); console.log('filtersInstance', filtersInstance.storeFiltersData()); // Trigger date range change event const dateRange = document.querySelectorAll( 'input[fs-cmsfilter-field="dates"]' ); if (dateRange) { dateRange.forEach((input) => { input.addEventListener('change', () => { filtersInstance.storeFiltersData(); }); }); } }, ]); } /** * * Creates an event card * * @param {MSCCEvent} singleEvent * @param {HTMLDivElement} templateElement * @return void */ function createEventCard( singleEvent: MSCCEvent, templateElement: HTMLDivElement ) { // Clone the template element const newItem = templateElement.cloneNode(true) as HTMLDivElement; // return newItem; // Query inner elements const title = newItem.querySelector( '[mscc-field="title"]' ) as NonNullable; const organization = newItem.querySelector( '[mscc-field="organization"]' ) as NonNullable; const startDate = newItem.querySelector( '[mscc-field="startDate"]' ) as NonNullable; const startTimestamp = newItem.querySelector( '[mscc-field="startTimestamp"]' ) as NonNullable; const endDate = newItem.querySelector( '[mscc-field="endDate"]' ) as NonNullable; const totalDate = newItem.querySelector( '[mscc-field="totalDate"]' ) as NonNullable; const attendees = newItem.querySelector( '[mscc-field="attendees"]' ) as NonNullable; const startYear = newItem.querySelector( '[mscc-field="startYear"]' ) as NonNullable; const startMonth = newItem.querySelector( '[mscc-field="startMonth"]' ) as NonNullable; const startDay = newItem.querySelector( '[mscc-field="startDay"]' ) as NonNullable; ///////////////////////////// // Populate inner elements // ///////////////////////////// if (title) title.textContent = singleEvent.title ?? 'UNKNOWN'; if (organization) organization.textContent = singleEvent.organization ?? 'UNKNOWN'; if (startDate) startDate.textContent = singleEvent.startDate; if (endDate) endDate.textContent = singleEvent.endDate; if (startTimestamp) startTimestamp.textContent = singleEvent.startTimestamp; if (totalDate) totalDate.textContent = singleEvent.totalDate; if (attendees) attendees.textContent = singleEvent.attendees.toString(); if (startYear) startYear.textContent = singleEvent.startYear; if (startMonth) startMonth.textContent = singleEvent.startMonth; if (startDay) startDay.textContent = singleEvent.startDay; return newItem; } /** * * Processes a single raw event from the API * * @param {NonNullable} singleEvent * @return {MSCCEvent} processedEvent as MSCCEvent */ function processEvent(singleEvent: NonNullable) { // Process the event data const processedEvent = {} as MSCCEvent; const dateFormat = { timeZone: 'America/Detroit', month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', } as Intl.DateTimeFormatOptions; // Event name and organization processedEvent.title = singleEvent.hso_eventname; processedEvent.organization = singleEvent[ '_hso_organization_value@OData.Community.Display.V1.FormattedValue' ]; processedEvent.attendees = singleEvent.hso_ofattendees ?? 0; // Event start and end date processedEvent.startDate = new Date( singleEvent.hso_starttime ).toLocaleDateString('en-US', dateFormat); processedEvent.startTimestamp = new Date( singleEvent.hso_starttime ).toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', }); // Remove the comma from the startTimestamp processedEvent.startTimestamp = processedEvent.startTimestamp.replace( /,/g, '' ); const endDate = new Date(singleEvent.hso_endtime); processedEvent.endDate = endDate.toLocaleString('en-US', dateFormat); if ( new Date(endDate).setHours(0, 0, 0, 0).toString() === new Date(singleEvent.hso_starttime).setHours(0, 0, 0, 0).toString() ) { processedEvent.totalDate = `${processedEvent.startDate} — ${endDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' })}`; } else { processedEvent.totalDate = `${processedEvent.startDate} — ${processedEvent.endDate}`; } processedEvent.startYear = new Date(singleEvent.hso_starttime) .getFullYear() .toString(); processedEvent.startMonth = new Date( singleEvent.hso_starttime ).toLocaleString('en-US', { month: 'short', }); processedEvent.startDay = new Date(singleEvent.hso_starttime) .getDate() .toString(); return processedEvent; } /** * * Get the unique event organizations * * @param {MSCCRawEvent[]} events * @return {string[]} eventOrganizations as string[] */ function getEventOrganizations(events: MSCCRawEvent[]) { const processedEvents = events.map((event) => processEvent(event)); const eventOrganizations = processedEvents.map((event) => event.organization); return Array.from(new Set(eventOrganizations)); } /** * * Populates the dropdown for the event organizations * * @param {string[]} eventOrganizations * @param {HTMLSelectElement} templateElement * @return {HTMLSelectElement} newDropdown as HTMLSelectElement */ const createDropdown = ( eventOrganizations: string[], templateElement: HTMLSelectElement ) => { // Clone the template element const newDropdown = templateElement.cloneNode( true ) as NonNullable; // Clear the dropdown newDropdown.innerHTML = ''; // Check query string for tour type const urlParams = new URLSearchParams(window.location.search); const queryType = urlParams.get('type'); // Populate the dropdown const dropdownItems = eventOrganizations.map((type) => { const option = document.createElement('option'); option.value = type; option.textContent = type; if (queryType === type) { option.selected = true; } return option; }); // Sort the dropdown items dropdownItems.sort((a, b) => a.textContent!.localeCompare(b.textContent!)); // Add a default option const defaultOption = document.createElement('option'); defaultOption.value = ''; defaultOption.textContent = 'Select an organization...'; newDropdown.prepend(defaultOption); // Add the dropdown items newDropdown.append(...dropdownItems); // Remove the template element templateElement.remove(); return newDropdown; };