import { delay } from "../delay"; import stringToHTML from "../stringToHTML"; import { animate, stagger } from "motion"; import { settingsState } from "../listeners/SettingsState"; import { addShortcuts } from "../Adders/AddShortcuts"; import browser from "webextension-polyfill"; import { GetThresholdOfColor } from "@/seqta/ui/colors/getThresholdColour"; import LogoLight from "@/resources/icons/betterseqta-light-icon.png"; import { CreateCustomShortcutDiv } from "@/seqta/utils/CreateEnable/CreateCustomShortcutDiv"; import assessmentsicon from "@/seqta/icons/assessmentsIcon"; import coursesicon from "@/seqta/icons/coursesIcon"; import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments"; import { CreateElement } from "@/seqta/utils/CreateEnable/CreateElement"; import { convertTo12HourFormat } from "../convertTo12HourFormat"; let LessonInterval: any; let currentSelectedDate = new Date(); export async function loadHomePage() { console.info("[BetterSEQTA+] Started Loading Home Page"); // Wait for the DOM to finish clearing await delay(10); document.title = "Home ― SEQTA Learn"; const element = document.querySelector("[data-key=home]"); element?.classList.add("active"); // Cache DOM queries const main = document.getElementById("main"); if (!main) { console.error("[BetterSEQTA+] Main element not found."); return; } // Create root container first const homeRoot = stringToHTML( /* html */ `
`, ); // Clear main and add home root main.innerHTML = ""; main.appendChild(homeRoot?.firstChild!); // Get reference to home container for all subsequent additions const homeContainer = document.getElementById("home-root"); if (!homeContainer) return; const skeletonStructure = stringToHTML(/* html */ `

Today's Lessons

Upcoming Assessments

Notices

`); // Add skeleton structure homeContainer.appendChild(skeletonStructure.firstChild!); // Run animations if enabled if (settingsState.animations) { animate( ".home-container > div", { opacity: [0, 1], y: [10, 0], scale: [0.99, 1] }, { delay: stagger(0.15, { startDelay: 0.1 }), type: "spring", stiffness: 341, damping: 20, mass: 1, }, ); } // Setup event listeners with cleanup const cleanup = setupTimetableListeners(); // Initialize shortcuts immediately try { addShortcuts(settingsState.shortcuts); } catch (err: any) { console.error("[BetterSEQTA+] Error adding shortcuts:", err.message || err); } AddCustomShortcutsToPage(); // Get current date const date = new Date(); const TodayFormatted = formatDate(date); // Start all data fetching in parallel const [timetablePromise, assessmentsPromise, classesPromise, prefsPromise] = [ // Timetable data fetch(`${location.origin}/seqta/student/load/timetable?`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ from: TodayFormatted, until: TodayFormatted, student: 69, }), }).then((res) => res.json()), // Assessments data GetUpcomingAssessments(), // Classes data GetActiveClasses(), // Preferences data fetch(`${location.origin}/seqta/student/load/prefs?`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ asArray: true, request: "userPrefs" }), }).then((res) => res.json()), ]; // Process all data in parallel const [timetableData, assessments, classes, prefs] = await Promise.all([ timetablePromise, assessmentsPromise, classesPromise, prefsPromise, ]); // Process timetable data const dayContainer = document.getElementById("day-container"); if (dayContainer && timetableData.payload.items.length > 0) { const lessonArray = timetableData.payload.items.sort((a: any, b: any) => a.from.localeCompare(b.from), ); const colours = await GetLessonColours(); // Process and display lessons dayContainer.innerHTML = ""; for (let i = 0; i < lessonArray.length; i++) { const lesson = lessonArray[i]; const subjectname = `timetable.subject.colour.${lesson.code}`; const subject = colours.find( (element: any) => element.name === subjectname, ); lesson.colour = subject ? `--item-colour: ${subject.value};` : "--item-colour: #8e8e8e;"; lesson.from = lesson.from.substring(0, 5); lesson.until = lesson.until.substring(0, 5); if (settingsState.timeFormat === "12") { lesson.from = convertTo12HourFormat(lesson.from); lesson.until = convertTo12HourFormat(lesson.until); } lesson.attendanceTitle = CheckUnmarkedAttendance(lesson.attendance); const div = makeLessonDiv(lesson, i + 1); if (GetThresholdOfColor(subject?.value) > 300) { const firstChild = div.firstChild as HTMLElement; if (firstChild) { firstChild.classList.add("day-inverted"); } } dayContainer.appendChild(div.firstChild!); } // Check current lessons if (currentSelectedDate.getDate() === date.getDate()) { for (let i = 0; i < lessonArray.length; i++) { CheckCurrentLesson(lessonArray[i], i + 1); } CheckCurrentLessonAll(lessonArray); } } else if (dayContainer) { dayContainer.innerHTML = /* html */ `

No lessons available.

`; } dayContainer?.classList.remove("loading"); // Process assessments data const activeClass = classes.find((c: any) => c.hasOwnProperty("active")); const activeSubjects = activeClass?.subjects || []; const activeSubjectCodes = activeSubjects.map((s: any) => s.code); const currentAssessments = assessments .filter((a: any) => activeSubjectCodes.includes(a.code)) .sort(comparedate); const upcomingItems = document.getElementById("upcoming-items"); if (upcomingItems) { await CreateUpcomingSection(currentAssessments, activeSubjects); upcomingItems.classList.remove("loading"); } // Process notices data const labelArray = prefs.payload .filter((item: any) => item.name === "notices.filters") .map((item: any) => item.value); if (labelArray.length > 0) { const noticeContainer = document.getElementById("notice-container"); if (noticeContainer) { const dateControl = document.querySelector( 'input[type="date"]', ) as HTMLInputElement; if (dateControl) { dateControl.value = TodayFormatted; setupNotices(labelArray[0].split(" "), TodayFormatted); } noticeContainer.classList.remove("loading"); } } return cleanup; } async function GetUpcomingAssessments() { let func = fetch( `${location.origin}/seqta/student/assessment/list/upcoming?`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ student: 69 }), }, ); return func .then((result) => result.json()) .then((response) => response.payload); } function setupTimetableListeners() { const listeners: Array<() => void> = []; const timetableBack = document.getElementById("home-timetable-back"); const timetableForward = document.getElementById("home-timetable-forward"); function changeTimetable(value: number) { currentSelectedDate.setDate(currentSelectedDate.getDate() + value); const formattedDate = formatDate(currentSelectedDate); callHomeTimetable(formattedDate, true); SetTimetableSubtitle(); } const backHandler = () => changeTimetable(-1); const forwardHandler = () => changeTimetable(1); timetableBack?.addEventListener("click", backHandler); timetableForward?.addEventListener("click", forwardHandler); listeners.push( () => timetableBack?.removeEventListener("click", backHandler), () => timetableForward?.removeEventListener("click", forwardHandler), ); return () => listeners.forEach((cleanup) => cleanup()); } function formatDate(date: Date): string { const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, "0"); const day = date.getDate().toString().padStart(2, "0"); return `${year}-${month}-${day}`; } async function GetActiveClasses() { try { const response = await fetch( `${location.origin}/seqta/student/load/subjects?`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8" }, body: JSON.stringify({}), }, ); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); return data.payload; } catch (error) { console.error("Oops! There was a problem fetching active classes:", error); } } function setupNotices(labelArray: string[], date: string) { const dateControl = document.querySelector( 'input[type="date"]', ) as HTMLInputElement; const fetchNotices = async (date: string) => { const response = await fetch( `${location.origin}/seqta/student/load/notices?`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8" }, body: JSON.stringify({ date }), }, ); const data = await response.json(); processNotices(data, labelArray); }; // Debounce the input handler const debouncedInputChange = debounce((e: Event) => { const target = e.target as HTMLInputElement; fetchNotices(target.value); }, 250); dateControl?.addEventListener("input", debouncedInputChange); fetchNotices(date); return () => dateControl?.removeEventListener("input", debouncedInputChange); } function debounce any>( func: T, wait: number, ): (...args: Parameters) => void { let timeout: any; return (...args: Parameters) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } function comparedate(obj1: any, obj2: any) { if (obj1.date < obj2.date) { return -1; } if (obj1.date > obj2.date) { return 1; } return 0; } async function AddCustomShortcutsToPage() { let customshortcuts: any = settingsState.customshortcuts; if (customshortcuts.length > 0) { for (let i = 0; i < customshortcuts.length; i++) { const element = customshortcuts[i]; CreateCustomShortcutDiv(element); } } } function processNotices(response: any, labelArray: string[]) { const NoticeContainer = document.getElementById("notice-container"); if (!NoticeContainer) return; // Clear existing notices NoticeContainer.innerHTML = ""; const notices = response.payload; if (!notices.length) { const dummyNotice = document.createElement("div"); dummyNotice.textContent = "No notices for today."; dummyNotice.classList.add("dummynotice"); NoticeContainer.append(dummyNotice); return; } // Create document fragment for batch DOM updates const fragment = document.createDocumentFragment(); // Process notices in batch notices.forEach((notice: any) => { if (labelArray.includes(JSON.stringify(notice.label))) { const colour = processNoticeColor(notice.colour); const noticeElement = createNoticeElement(notice, colour); fragment.appendChild(noticeElement); } }); // Single DOM update NoticeContainer.appendChild(fragment); } function processNoticeColor(colour: string): string | undefined { if (typeof colour === "string") { const rgb = GetThresholdOfColor(colour); if (rgb < 100 && settingsState.DarkMode) { return undefined; } } return colour; } function createNoticeElement(notice: any, colour: string | undefined): Node { const htmlContent = `

${notice.title}

${notice.label_title !== undefined ? `
${notice.label_title}
` : ""}
${notice.staff}
${notice.contents.replace(/\[\[[\w]+[:][\w]+[\]\]]+/g, "").replace(/ +/, " ")}
`; const element = stringToHTML(htmlContent).firstChild; if (element instanceof HTMLElement) { element.style.setProperty("--colour", colour ?? ""); } return element!; } function callHomeTimetable(date: string, change?: any) { // Creates a HTTP Post Request to the SEQTA page for the students timetable var xhr = new XMLHttpRequest(); xhr.open("POST", `${location.origin}/seqta/student/load/timetable?`, true); // Sets the response type to json xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); xhr.onreadystatechange = function () { // Once the response is ready if (xhr.readyState === 4) { var serverResponse = JSON.parse(xhr.response); let lessonArray: Array = []; const DayContainer = document.getElementById("day-container")!; // If items in response: if (serverResponse.payload.items.length > 0) { if (DayContainer.innerText || change) { for (let i = 0; i < serverResponse.payload.items.length; i++) { lessonArray.push(serverResponse.payload.items[i]); } lessonArray.sort(function (a, b) { return a.from.localeCompare(b.from); }); // If items in the response, set each corresponding value into divs // lessonArray = lessonArray.splice(1) GetLessonColours().then((colours) => { let subjects = colours; for (let i = 0; i < lessonArray.length; i++) { let subjectname = `timetable.subject.colour.${lessonArray[i].code}`; let subject = subjects.find( (element: any) => element.name === subjectname, ); if (!subject) { lessonArray[i].colour = "--item-colour: #8e8e8e;"; } else { lessonArray[i].colour = `--item-colour: ${subject.value};`; let result = GetThresholdOfColor(subject.value); if (result > 300) { lessonArray[i].invert = true; } } // Removes seconds from the start and end times lessonArray[i].from = lessonArray[i].from.substring(0, 5); lessonArray[i].until = lessonArray[i].until.substring(0, 5); if (settingsState.timeFormat === "12") { lessonArray[i].from = convertTo12HourFormat( lessonArray[i].from, ); lessonArray[i].until = convertTo12HourFormat( lessonArray[i].until, ); } // Checks if attendance is unmarked, and sets the string to " ". lessonArray[i].attendanceTitle = CheckUnmarkedAttendance( lessonArray[i].attendance, ); } // If on home page, apply each lesson to HTML with information in each div DayContainer.innerText = ""; for (let i = 0; i < lessonArray.length; i++) { var div = makeLessonDiv(lessonArray[i], i + 1); // Append each of the lessons into the day-container if (lessonArray[i].invert) { const div1 = div.firstChild! as HTMLElement; div1.classList.add("day-inverted"); } DayContainer.append(div.firstChild as HTMLElement); } const today = new Date(); if (currentSelectedDate.getDate() == today.getDate()) { for (let i = 0; i < lessonArray.length; i++) { CheckCurrentLesson(lessonArray[i], i + 1); } // For each lesson, check the start and end times CheckCurrentLessonAll(lessonArray); } }); } } else { DayContainer.innerHTML = ""; var dummyDay = document.createElement("div"); dummyDay.classList.add("day-empty"); let img = document.createElement("img"); img.src = browser.runtime.getURL(LogoLight); let text = document.createElement("p"); text.innerText = "No lessons available."; dummyDay.append(img); dummyDay.append(text); DayContainer.append(dummyDay); } } }; xhr.send( JSON.stringify({ // Information sent to SEQTA page as a request with the dates and student number from: date, until: date, // Funny number student: 69, }), ); } function CheckCurrentLessonAll(lessons: any) { // Checks each lesson and sets an interval to run every 60 seconds to continue updating LessonInterval = setInterval( function () { for (let i = 0; i < lessons.length; i++) { CheckCurrentLesson(lessons[i], i + 1); } }.bind(lessons), 60000, ); } async function CheckCurrentLesson(lesson: any, num: number) { const { from: startTime, until: endTime, code, description, room, staff, } = lesson; const currentDate = new Date(); // Create Date objects for start and end times const [startHour, startMinute] = startTime.split(":").map(Number); const [endHour, endMinute] = endTime.split(":").map(Number); const startDate = new Date(currentDate); startDate.setHours(startHour, startMinute, 0); const endDate = new Date(currentDate); endDate.setHours(endHour, endMinute, 0); // Check if the current time is within the lesson time range const isValidTime = startDate < currentDate && endDate > currentDate; const elementId = `${code}${num}`; const element = document.getElementById(elementId); if (!element) { clearInterval(LessonInterval); return; } const isCurrentDate = currentSelectedDate.toLocaleDateString("en-au") === currentDate.toLocaleDateString("en-au"); if (isCurrentDate) { if (isValidTime) { element.classList.add("activelesson"); } else { element.classList.remove("activelesson"); } } const minutesUntilStart = Math.floor( (startDate.getTime() - currentDate.getTime()) / 60000, ); if ( minutesUntilStart !== 5 || settingsState.lessonalert || !window.Notification ) return; if (Notification.permission !== "granted") await Notification.requestPermission(); try { new Notification("Next Lesson in 5 Minutes:", { body: `Subject: ${description}${room ? `\nRoom: ${room}` : ""}${staff ? `\nTeacher: ${staff}` : ""}`, }); } catch (error) { console.error(error); } } function makeLessonDiv(lesson: any, num: number) { if (!lesson) throw new Error("No lesson provided."); const { code, colour, description, staff, room, from, until, attendanceTitle, programmeID, metaID, assessments, } = lesson; // Construct the base lesson string with default values using ternary operators let lessonString = /* html */ `

${description || "Unknown"}

${staff || "Unknown"}

${room || "Unknown"}

${from || "Unknown"} - ${until || "Unknown"}

${attendanceTitle || "Unknown"}
`; // Add buttons for assessments and courses if applicable if (programmeID !== 0) { lessonString += /* html */ `
${assessmentsicon}
${coursesicon}
`; } // Add assessments if they exist if (assessments && assessments.length > 0) { const assessmentString = assessments .map( (element: any) => `

${element.title}

`, ) .join(""); lessonString += /* html */ `
${assessmentString}
`; } lessonString += "
"; return stringToHTML(lessonString); } function buildAssessmentURL(programmeID: any, metaID: any, itemID = "") { const base = "../#?page=/assessments/"; return itemID ? `${base}${programmeID}:${metaID}&item=${itemID}` : `${base}${programmeID}:${metaID}`; } function CheckUnmarkedAttendance(lessonattendance: any) { if (lessonattendance) { var lesson = lessonattendance.label; } else { lesson = " "; } return lesson; } async function CreateUpcomingSection(assessments: any, activeSubjects: any) { let upcomingitemcontainer = document.querySelector("#upcoming-items"); let overdueDates = []; let upcomingDates = {}; var Today = new Date(); // Removes overdue assessments from the upcoming assessments array and pushes to overdue array for (let i = 0; i < assessments.length; i++) { const assessment = assessments[i]; let assessmentdue = new Date(assessment.due); CheckSpecialDay(Today, assessmentdue); if (assessmentdue < Today) { if (!CheckSpecialDay(Today, assessmentdue)) { overdueDates.push(assessment); assessments.splice(i, 1); i--; } } } var TomorrowDate = new Date(); TomorrowDate.setDate(TomorrowDate.getDate() + 1); const colours = await GetLessonColours(); let subjects = colours; for (let i = 0; i < assessments.length; i++) { let subjectname = `timetable.subject.colour.${assessments[i].code}`; let subject = subjects.find((element: any) => element.name === subjectname); if (!subject) { assessments[i].colour = "--item-colour: #8e8e8e;"; } else { assessments[i].colour = `--item-colour: ${subject.value};`; GetThresholdOfColor(subject.value); // result (originally) result = GetThresholdOfColor } } for (let i = 0; i < activeSubjects.length; i++) { const element = activeSubjects[i]; let subjectname = `timetable.subject.colour.${element.code}`; let colour = colours.find((element: any) => element.name === subjectname); if (!colour) { element.colour = "--item-colour: #8e8e8e;"; } else { element.colour = `--item-colour: ${colour.value};`; let result = GetThresholdOfColor(colour.value); if (result > 300) { element.invert = true; } } } CreateFilters(activeSubjects); // @ts-ignore let type; // @ts-ignore let class_; for (let i = 0; i < assessments.length; i++) { const element: any = assessments[i]; if (!upcomingDates[element.due as keyof typeof upcomingDates]) { let dateObj: any = new Object(); dateObj.div = CreateElement( // TODO: not sure whats going on here? // eslint-disable-next-line (type = "div"), // eslint-disable-next-line (class_ = "upcoming-date-container"), ); dateObj.assessments = []; (upcomingDates[element.due as keyof typeof upcomingDates] as any) = dateObj; } let assessmentDateDiv = upcomingDates[element.due as keyof typeof upcomingDates]; if (assessmentDateDiv) { (assessmentDateDiv as any).assessments.push(element); } } for (var date in upcomingDates) { let assessmentdue = new Date( ( upcomingDates[date as keyof typeof upcomingDates] as any ).assessments[0].due, ); let specialcase = CheckSpecialDay(Today, assessmentdue); let assessmentDate; if (specialcase) { let datecase: string = specialcase!; assessmentDate = createAssessmentDateDiv( date, upcomingDates[date as keyof typeof upcomingDates], // eslint-disable-next-line datecase, ); } else { assessmentDate = createAssessmentDateDiv( date, upcomingDates[date as keyof typeof upcomingDates], ); } if (specialcase === "Yesterday") { upcomingitemcontainer!.insertBefore( assessmentDate, upcomingitemcontainer!.firstChild, ); } else { upcomingitemcontainer!.append(assessmentDate); } } FilterUpcomingAssessments(settingsState.subjectfilters); } function createAssessmentDateDiv(date: string, value: any, datecase?: any) { var options = { weekday: "long" as "long", month: "long" as "long", day: "numeric" as "numeric", }; const FormattedDate = new Date(date); const assessments = value.assessments; const container = value.div; let DateTitleDiv = document.createElement("div"); DateTitleDiv.classList.add("upcoming-date-title"); if (datecase) { let datetitle = document.createElement("h5"); datetitle.classList.add("upcoming-special-day"); datetitle.innerText = datecase; DateTitleDiv.append(datetitle); container.setAttribute("data-day", datecase); } let DateTitle = document.createElement("h5"); DateTitle.innerText = FormattedDate.toLocaleDateString("en-AU", options); DateTitleDiv.append(DateTitle); container.append(DateTitleDiv); let assessmentContainer = document.createElement("div"); assessmentContainer.classList.add("upcoming-date-assessments"); for (let i = 0; i < assessments.length; i++) { const element = assessments[i]; let item = document.createElement("div"); item.classList.add("upcoming-assessment"); item.setAttribute("data-subject", element.code); item.id = `assessment${element.id}`; item.style.cssText = element.colour; let titlediv = document.createElement("div"); titlediv.classList.add("upcoming-subject-title"); let titlesvg = stringToHTML(` `).firstChild; titlediv.append(titlesvg!); let detailsdiv = document.createElement("div"); detailsdiv.classList.add("upcoming-details"); let detailstitle = document.createElement("h5"); detailstitle.innerText = `${element.subject} assessment`; let subject = document.createElement("p"); subject.innerText = element.title; subject.classList.add("upcoming-assessment-title"); subject.onclick = function () { document.querySelector("#menu ul")!.classList.add("noscroll"); location.href = `../#?page=/assessments/${element.programmeID}:${element.metaclassID}&item=${element.id}`; }; detailsdiv.append(detailstitle); detailsdiv.append(subject); item.append(titlediv); item.append(detailsdiv); assessmentContainer.append(item); fetch(`${location.origin}/seqta/student/assessment/submissions/get`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ assessment: element.id, metaclass: element.metaclassID, student: 69, }), }) .then((result) => result.json()) .then((response) => { if (response.payload.length > 0) { const assessment = document.querySelector(`#assessment${element.id}`); // ticksvg = stringToHTML(``).firstChild // ticksvg.classList.add('upcoming-tick') // assessment.append(ticksvg) let submittedtext = document.createElement("div"); submittedtext.classList.add("upcoming-submittedtext"); submittedtext.innerText = "Submitted"; assessment!.append(submittedtext); } }); } container.append(assessmentContainer); return container; } function CheckSpecialDay(date1: Date, date2: Date) { if ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() - 1 === date2.getDate() ) { return "Yesterday"; } if ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate() ) { return "Today"; } if ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() + 1 === date2.getDate() ) { return "Tomorrow"; } } async function GetLessonColours() { let func = fetch(`${location.origin}/seqta/student/load/prefs?`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ request: "userPrefs", asArray: true, user: 69 }), }); return func .then((result) => result.json()) .then((response) => response.payload); } function CreateFilters(subjects: any) { let filteroptions = settingsState.subjectfilters; let filterdiv = document.querySelector("#upcoming-filters"); for (let i = 0; i < subjects.length; i++) { const element = subjects[i]; // eslint-disable-next-line if (!Object.prototype.hasOwnProperty.call(filteroptions, element.code)) { filteroptions[element.code] = true; settingsState.subjectfilters = filteroptions; } let elementdiv = CreateSubjectFilter( element.code, element.colour, filteroptions[element.code], ); filterdiv!.append(elementdiv); } } function CreateSubjectFilter( subjectcode: any, itemcolour: string, checked: any, ) { let label = CreateElement("label", "upcoming-checkbox-container"); label.innerText = subjectcode; let input1 = CreateElement("input"); const input = input1 as HTMLInputElement; input.type = "checkbox"; input.checked = checked; input.id = `filter-${subjectcode}`; label.style.cssText = itemcolour; let span = CreateElement("span", "upcoming-checkmark"); label.append(input); label.append(span); input.addEventListener("change", function (change) { let filters = settingsState.subjectfilters; let id = (change.target as HTMLInputElement)!.id.split("-")[1]; filters[id] = (change.target as HTMLInputElement)!.checked; settingsState.subjectfilters = filters; }); return label; } function SetTimetableSubtitle() { const homelessonsubtitle = document.getElementById("home-lesson-subtitle"); if (!homelessonsubtitle) return; const date = new Date(); const isSameMonth = date.getFullYear() === currentSelectedDate.getFullYear() && date.getMonth() === currentSelectedDate.getMonth(); if (isSameMonth) { const dayDiff = date.getDate() - currentSelectedDate.getDate(); switch (dayDiff) { case 0: homelessonsubtitle.innerText = "Today's Lessons"; break; case 1: homelessonsubtitle.innerText = "Yesterday's Lessons"; break; case -1: homelessonsubtitle.innerText = "Tomorrow's Lessons"; break; default: homelessonsubtitle.innerText = formatDateString(currentSelectedDate); } } else { homelessonsubtitle.innerText = formatDateString(currentSelectedDate); } } function formatDateString(date: Date): string { return `${date.toLocaleString("en-us", { weekday: "short" })} ${date.toLocaleDateString("en-au")}`; }