diff --git a/src/lib/components/SpaceCard.svelte b/src/lib/components/SpaceCard.svelte index a2d5901..163e7c4 100644 --- a/src/lib/components/SpaceCard.svelte +++ b/src/lib/components/SpaceCard.svelte @@ -1,24 +1,24 @@ - + @@ -34,11 +34,14 @@ {#if space.tags.length > 0} {#each space.tags as tag (tag)} - {tag} + {tag} {/each} {/if} - + + {#if footer} + + {/if} @@ -46,14 +49,25 @@ .card { display: flex; flex-direction: column; - background-color: #49bd85; width: 100%; max-width: 20rem; border-radius: 0.5rem; overflow: hidden; text-decoration: none; } + .green { + background-color: #49bd85; + } + .grey { + background-color: #2e4653; + } + .spacer { + flex: 1; + } .description { + flex: 1; + display: flex; + flex-direction: column; padding: 0.5rem; color: #edebe9; } @@ -83,13 +97,20 @@ justify-content: center; text-align: center; border-radius: 0.25rem; - background-color: #2e4653; color: #eaffeb; font-size: 0.875rem; cursor: pointer; padding: 0.2rem 0.6rem; } + .tagGreen { + background-color: #2e4653; + } + + .tagGrey { + background-color: #49bd85; + } + .compulsoryContainer { display: grid; grid-template-columns: repeat(auto-fit, minmax(3.75rem, 1fr)); @@ -109,4 +130,16 @@ padding: 0.25rem; z-index: 1; } + + .footer { + width: 100%; + color: #2e4653; + background-color: #eaffeb; + align-self: flex-end; + border-radius: 0.5rem; + padding: 0.1rem; + text-align: center; + font-weight: bold; + margin-top: 0.4rem; + } diff --git a/src/lib/index.ts b/src/lib/index.ts index d9029c9..41d52c4 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -84,3 +84,21 @@ export function haversineDistance( Math.pow(Math.sin(deltaLng / 2), 2) * Math.cos(lat1) * Math.cos(lat2); return radius * 2 * Math.asin(Math.sqrt(e1)); } + +export function collectTimings(study_space_hours: Table<"study_space_hours">[]) { + // Collect all timing entries + const timingsPerDay: Record[]> = { + 0: [], + 1: [], + 2: [], + 3: [], + 4: [], + 5: [], + 6: [] + }; + + for (const entry of study_space_hours) { + timingsPerDay[entry.day_of_week].push(entry); + } + return timingsPerDay; +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 921b00e..8bc290b 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -4,10 +4,11 @@ import crossUrl from "$lib/assets/cross.svg"; import searchUrl from "$lib/assets/search.svg"; import Navbar from "$lib/components/Navbar.svelte"; - import { haversineDistance, timeToMins } from "$lib"; + import { collectTimings, timeToMins, haversineDistance } from "$lib"; import Button from "$lib/components/Button.svelte"; import { urldecodeSortFilter } from "$lib/filter.js"; import { invalidateAll } from "$app/navigation"; + import type { Table } from "$lib"; const { data } = $props(); const { @@ -123,6 +124,45 @@ }) : filteredStudySpaces ); + + // Open now + function isOpenNow(all_study_space_hours: Table<"study_space_hours">[]) { + const now = new Date(); + const time = now.toTimeString().slice(0, 5); + const day = now.getDay(); + + const timingsPerDay = collectTimings(all_study_space_hours); + for (const timing of timingsPerDay[day]) { + if (timing.open_today_status === true) { + return { isOpen: true, message: `Open all day` }; + } else if (timing.open_today_status === false) { + return { isOpen: false, message: `Closed today` }; + } else { + const opensFor = timeUntilClosing(timing.opens_at, timing.closes_at, time); + if (opensFor) { + return { + isOpen: true, + message: `Open now for: ${minsToReadableHours(timeToMins(time))}` + }; + } + } + } + + return { isOpen: false, message: "Closed right now" }; + } + + function timeUntilClosing(openingTime: string, closingTime: string, currentTime: string) { + const currTimeInMins = timeToMins(currentTime); + const OpeningTimeInMins = timeToMins(openingTime); + const closingTimeInMins = timeToMins(closingTime); + if (currTimeInMins >= OpeningTimeInMins && currTimeInMins < closingTimeInMins) { + return closingTimeInMins - currTimeInMins; + } + } + + function minsToReadableHours(mins: number) { + return `${Math.floor(mins / 60)} hrs, ${mins % 60} mins`; + } @@ -161,9 +201,14 @@ href="/space/{studySpace.id}" imgSrc={imgUrl} space={studySpace} - hours={studySpace.study_space_hours} isFavourite={favouriteIds.includes(studySpace.id)} onToggleFavourite={session ? () => handleToggleFavourite(studySpace.id) : undefined} + isAvailable={studySpace.study_space_hours.length === 0 + ? undefined + : isOpenNow(studySpace.study_space_hours).isOpen} + footer={studySpace.study_space_hours.length === 0 + ? undefined + : isOpenNow(studySpace.study_space_hours).message} /> {/each} diff --git a/src/routes/space/[id]/+page.svelte b/src/routes/space/[id]/+page.svelte index 52ed34b..1c124fa 100644 --- a/src/routes/space/[id]/+page.svelte +++ b/src/routes/space/[id]/+page.svelte @@ -7,7 +7,7 @@ import Report from "$lib/components/Report.svelte"; import Feedback from "$lib/components/Feedback.svelte"; import { onMount } from "svelte"; - import { gmapsLoader, daysOfWeek, formatTime, type Table } from "$lib"; + import { gmapsLoader, daysOfWeek, formatTime, collectTimings } from "$lib"; import Button from "$lib/components/Button.svelte"; import Favourite from "$lib/components/Favourite.svelte"; @@ -52,20 +52,7 @@ }); }); - // Collect all timing entries - let timingsPerDay: Record[]> = { - 0: [], - 1: [], - 2: [], - 3: [], - 4: [], - 5: [], - 6: [] - }; - - for (const entry of space.study_space_hours) { - timingsPerDay[entry.day_of_week].push(entry); - } + let timingsPerDay = collectTimings(space.study_space_hours); let isFavourite = $state(false); onMount(async () => { diff --git a/src/routes/space/[id]/edit/+page.svelte b/src/routes/space/[id]/edit/+page.svelte index eaf435d..f937a43 100644 --- a/src/routes/space/[id]/edit/+page.svelte +++ b/src/routes/space/[id]/edit/+page.svelte @@ -59,7 +59,6 @@ let spaceImgs = $state(); let uploading = $state(false); function checkTimings() { - console.log(studySpaceData.opening_times); let cannotExist = [] as number[]; const opensAtMinsAll = timeToMins(allDays.opens_at); @@ -91,7 +90,6 @@ function genTimings(studySpaceId: string) { const fullDayOfWeek = [0, 1, 2, 3, 4, 5, 6]; - console.log(studySpaceData.opening_times, allDays); // all day only if ( studySpaceData.opening_times.length === 0 && @@ -101,8 +99,8 @@ return fullDayOfWeek.map((day) => ({ study_space_id: studySpaceId, day_of_week: day, - opens_at: allDays.open_today_status == null ? allDays.opens_at : "00:00", - closes_at: allDays.open_today_status == null ? allDays.closes_at : "00:00", + opens_at: allDays.open_today_status !== null ? allDays.opens_at : "00:00", + closes_at: allDays.open_today_status !== null ? allDays.closes_at : "00:01", open_today_status: allDays.open_today_status })); } @@ -114,16 +112,16 @@ .map((h) => ({ study_space_id: studySpaceId, day_of_week: h.day_of_week, - opens_at: h.opens_at, - closes_at: h.closes_at, + opens_at: h.open_today_status === null ? h.opens_at : "00:00", + closes_at: h.open_today_status === null ? h.closes_at : "00:01", open_today_status: h.open_today_status })) .concat( nonDefinedDays.map((day) => ({ study_space_id: studySpaceId, day_of_week: day, - opens_at: allDays.opens_at, - closes_at: allDays.closes_at, + opens_at: allDays.open_today_status === null ? allDays.opens_at : "00:00", + closes_at: allDays.open_today_status === null ? allDays.closes_at : "00:01", open_today_status: allDays.open_today_status })) ); @@ -134,10 +132,8 @@ if (!spaceImgs || spaceImgs.length < 1) return alert("Please select an image file."); if (!studySpaceData.building_location) return alert("Please select a building location."); - const { opening_times, ...spacePayload } = studySpaceData; - - // explicitly mark opening_times as used to avoid unused warning - void opening_times; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { opening_times: _, ...spacePayload } = studySpaceData; const { data: studySpaceInsert, error: studySpaceError } = await supabase .from("study_spaces")