Files
drp-48/src/routes/space/[id]/+page.svelte
2025-06-13 02:58:24 +01:00

305 lines
7.8 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts">
import Navbar from "$lib/components/Navbar.svelte";
import crossUrl from "$lib/assets/cross.svg";
import placeholder from "$lib/assets/study_space.png";
import Carousel from "$lib/components/Carousel.svelte";
import CompulsoryTags from "$lib/components/CompulsoryTags.svelte";
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 Button from "$lib/components/Button.svelte";
import TagFilter from "$lib/components/inputs/TagFilter.svelte";
const { data } = $props();
const { space, supabase, adminMode } = $derived(data);
const place = $derived(space.building_location as google.maps.places.PlaceResult);
const imgUrls = $derived(
space.study_space_images.length === 0
? [placeholder]
: space.study_space_images.map(
(img) =>
supabase.storage.from("files_bucket").getPublicUrl(img.image_path).data
.publicUrl
)
);
let isReportVisible = $state(false);
function hideReport() {
isReportVisible = false;
}
let isFeedbackPromptVisible = $state(false);
function hideFeedbackPrompt() {
isFeedbackPromptVisible = false;
}
let mapElem = $state<HTMLDivElement>();
onMount(async () => {
if (!mapElem) return console.error("Map element not found");
const loader = await gmapsLoader();
const { Map } = await loader.importLibrary("maps");
const { AdvancedMarkerElement } = await loader.importLibrary("marker");
const map = new Map(mapElem, {
center: place.geometry?.location,
zoom: 15,
mapId: "9f4993cd3fb1504d495821a5"
});
new AdvancedMarkerElement({
position: place.geometry?.location,
map
});
});
// Collect all timing entries
let timingsPerDay: Record<number, Table<"study_space_hours">[]> = {
0: [],
1: [],
2: [],
3: [],
4: [],
5: [],
6: []
};
for (const entry of space.study_space_hours) {
timingsPerDay[entry.day_of_week].push(entry);
}
</script>
<Navbar>
<a href="/">
<img src={crossUrl} alt="close" />
</a>
</Navbar>
{#if isReportVisible}<Report {data} studySpaceId={space.id} hideFunc={hideReport} />
{/if}
{#if isFeedbackPromptVisible}
<Feedback
studySpaceData={{
...space,
building_location: place
}}
{supabase}
hideFunc={hideFeedbackPrompt}
/>
{/if}
<main>
<Carousel urls={imgUrls} />
<div class="nameContainer">
{space.location}
</div>
{#if space.description != null && space.description.length > 0}
<p class="descContainer">
{space.description}
</p>
<hr />
{/if}
<div class="compulsoryContainer"><CompulsoryTags {space} /></div>
{#if space.tags.length > 0}
<div class="tagContainer">
{#each space.tags as tag (tag)}
<span class="tag">
{tag}
</span>
{/each}
</div>
{/if}
<hr />
<div class="subtitle">Opening Times:</div>
{#each Array(7) as _, idx (idx)}
{@const entries = timingsPerDay[idx]}
<div class="opening-entry">
<span class="day">{daysOfWeek[idx]}</span>
<div class="times">
{#each entries as entry (entry)}
<span class="time">
{entry.open_today_status
? "Open All Day"
: entry.open_today_status === false
? "Closed"
: `${formatTime(entry.opens_at)} ${formatTime(entry.closes_at)}`}
</span>
{:else}
<span class="time">Not known</span>
{/each}
</div>
</div>
{/each}
<hr />
<div class="subtitle">Where it is:</div>
<p class="addrContainer">
{#if place.name}
{place.name} <br />
{/if}
{#each place.formatted_address?.split(",") || [] as line (line)}
{line.trim()} <br />
{/each}
</p>
<div class="addrMap" bind:this={mapElem}></div>
<button
type="button"
class="feedbackButton"
onclick={() => {
isFeedbackPromptVisible = true;
}}
>
Help categorise this space
</button>
<div class="actions">
{#if adminMode}
<Button href="/space/{space.id}/edit" type="link">Edit</Button>
{:else}
<Button onclick={() => (isReportVisible = true)} style="red">Report</Button>
{/if}
</div>
</main>
<style>
main {
display: flex;
flex-direction: column;
padding: 0 0 1rem 0;
max-width: 32rem;
margin: 0 auto;
}
img {
display: block;
width: 100%;
aspect-ratio: 1/0.8;
object-fit: cover;
object-position: center;
}
hr {
height: 2px;
background-color: #2e3c42;
width: 70%;
border: none;
margin: 1rem auto 0;
}
.nameContainer {
z-index: 10;
display: block;
width: 100%;
padding: 0.6rem;
margin-top: -0.5rem;
object-position: center;
background-color: #49bd85;
border-radius: 8px;
font-size: 2.8rem;
font-weight: bold;
color: #ffffff;
}
.descContainer {
display: block;
width: 100%;
padding: 1.4rem;
margin-top: -0.5rem;
object-position: center;
border-radius: 8px;
font-size: 1.2rem;
}
.subtitle {
font-size: 1.2rem;
font-weight: bold;
color: #ffffff;
padding: 1.4rem;
}
.addrContainer {
font-size: 1.2rem;
padding: 0rem 1.4rem 1rem;
}
.tagContainer {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
padding: 1.4rem;
border-radius: 0.5rem;
background: none;
}
.tag {
display: flex;
align-items: center;
border-radius: 0.25rem;
background-color: #2e4653;
color: #eaffeb;
font-size: 1.1rem;
cursor: pointer;
padding: 0.2rem 0.6rem;
}
.compulsoryContainer {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
padding: 1.4rem;
font-size: 1.3rem;
padding-bottom: 0;
}
.addrMap {
width: 100%;
aspect-ratio: 1 / 1;
border-radius: 0.5rem;
border: 2px solid #eaffeb;
}
.feedbackButton {
width: 100%;
padding: 0.7rem;
border-radius: 0.5rem;
border: none;
background-color: #49bd85;
color: #ffffff;
font-size: 1rem;
cursor: pointer;
margin-top: 1rem;
text-align: center;
}
.actions {
display: flex;
flex-direction: column;
padding-top: 1rem;
}
.opening-entry {
display: flex;
gap: 0.75rem;
padding: 0.5rem 1.4rem;
align-items: center;
background-color: #2e4653;
margin: 0.2rem;
border-radius: 0.5rem;
}
.opening-entry .day {
font-weight: bold;
color: #ffffff;
align-items: center;
justify-content: center;
}
.opening-entry .times {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 0.25rem 1.5rem;
flex: 1;
align-items: end;
}
.opening-entry .time {
font-family: monospace;
color: #eaffeb;
}
</style>