feat: Implemented Favouriting for a user
Co-Authored-By: Tadios Temesgen <tt2022@ic.ac.uk>
This commit is contained in:
@@ -8,7 +8,25 @@ export const load: PageServerLoad = async ({ depends, locals: { supabase } }) =>
|
||||
.select("*, study_space_images(*), study_space_hours(*)");
|
||||
if (err) error(500, "Failed to load study spaces");
|
||||
|
||||
const {
|
||||
data: { session }
|
||||
} = await supabase.auth.getSession();
|
||||
|
||||
// Fetch this user’s favourites
|
||||
let favouriteIds: string[] = [];
|
||||
if (session?.user?.id) {
|
||||
const { data: favs, error: favErr } = await supabase
|
||||
.from("favourite_study_spaces")
|
||||
.select("study_space_id")
|
||||
.eq("user_id", session.user.id);
|
||||
if (!favErr && favs) {
|
||||
favouriteIds = favs.map((f) => f.study_space_id);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
studySpaces
|
||||
studySpaces,
|
||||
session,
|
||||
favouriteIds
|
||||
};
|
||||
};
|
||||
|
||||
@@ -7,18 +7,46 @@
|
||||
import Button from "$lib/components/Button.svelte";
|
||||
|
||||
const { data } = $props();
|
||||
const { studySpaces, supabase, session, adminMode } = $derived(data);
|
||||
const {
|
||||
studySpaces,
|
||||
supabase,
|
||||
session,
|
||||
adminMode,
|
||||
favouriteIds: initialFavourites = []
|
||||
} = $derived(data);
|
||||
|
||||
let selectedTags = $state<string[]>([]);
|
||||
let tagFilter = $state("");
|
||||
let tagFilterElem = $state<HTMLInputElement>();
|
||||
|
||||
let openingFilter = $state("");
|
||||
let closingFilter = $state("");
|
||||
let tagFilterElem = $state<HTMLInputElement>();
|
||||
|
||||
let favouriteIds = $derived<string[]>(initialFavourites);
|
||||
let showFavourites = $state(false);
|
||||
|
||||
function categorySelected(category: string[]) {
|
||||
return category.some((tag) => selectedTags.includes(tag));
|
||||
}
|
||||
|
||||
// Toggle a space in/out of favourites
|
||||
async function handleToggleFavourite(id: string) {
|
||||
if (!session?.user) return;
|
||||
const already = favouriteIds.includes(id);
|
||||
if (already) {
|
||||
await supabase
|
||||
.from("favourite_study_spaces")
|
||||
.delete()
|
||||
.match({ user_id: session.user.id, study_space_id: id });
|
||||
favouriteIds = favouriteIds.filter((x) => x !== id);
|
||||
} else {
|
||||
await supabase
|
||||
.from("favourite_study_spaces")
|
||||
.insert([{ user_id: session.user.id, study_space_id: id }]);
|
||||
favouriteIds = [...favouriteIds, id];
|
||||
}
|
||||
}
|
||||
|
||||
let filteredTags = $derived(
|
||||
allTags
|
||||
.filter((tag) => tag.toLowerCase().includes(tagFilter.toLowerCase()))
|
||||
@@ -44,6 +72,8 @@
|
||||
// Combine tag and time filtering
|
||||
let filteredStudySpaces = $derived(
|
||||
studySpaces
|
||||
// only include favourites when showFavourites===true
|
||||
.filter((space) => !showFavourites || favouriteIds?.includes(space.id))
|
||||
// tag filtering
|
||||
.filter((space) => {
|
||||
if (selectedTags.length === 0) return true;
|
||||
@@ -114,6 +144,9 @@
|
||||
<a href="/space/new/edit">
|
||||
<img src={crossUrl} alt="new" class="new-space" />
|
||||
</a>
|
||||
<button class="fav-button" onclick={() => (showFavourites = !showFavourites)} type="button">
|
||||
{showFavourites ? "All spaces" : "My favourites"}
|
||||
</button>
|
||||
{/if}
|
||||
</Navbar>
|
||||
|
||||
@@ -198,6 +231,8 @@
|
||||
imgSrc={imgUrl}
|
||||
space={studySpace}
|
||||
hours={studySpace.study_space_hours}
|
||||
isFavourite={favouriteIds.includes(studySpace.id)}
|
||||
onToggleFavourite={() => handleToggleFavourite(studySpace.id)}
|
||||
/>
|
||||
{/each}
|
||||
</main>
|
||||
@@ -356,6 +391,18 @@
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.fav-button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #eaffeb;
|
||||
font-size: 1rem;
|
||||
margin-right: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.fav-button:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 20rem) {
|
||||
main {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { onMount } from "svelte";
|
||||
import { gmapsLoader, daysOfWeek, formatTime, type Table } from "$lib";
|
||||
import Button from "$lib/components/Button.svelte";
|
||||
import Favourite from "$lib/components/Favourite.svelte";
|
||||
|
||||
const { data } = $props();
|
||||
const { space, supabase, adminMode } = $derived(data);
|
||||
@@ -65,6 +66,40 @@
|
||||
for (const entry of space.study_space_hours) {
|
||||
timingsPerDay[entry.day_of_week].push(entry);
|
||||
}
|
||||
|
||||
let isFavourite = $state(false);
|
||||
onMount(async () => {
|
||||
const {
|
||||
data: { session }
|
||||
} = await supabase.auth.getSession();
|
||||
if (!session?.user) return;
|
||||
const { data: fav } = await supabase
|
||||
.from("favourite_study_spaces")
|
||||
.select("study_space_id")
|
||||
.match({ user_id: session.user.id, study_space_id: space.id })
|
||||
.single();
|
||||
isFavourite = !!fav;
|
||||
});
|
||||
|
||||
// Toggle a space in/out of favourites
|
||||
async function handleToggleFavourite() {
|
||||
const {
|
||||
data: { session }
|
||||
} = await supabase.auth.getSession();
|
||||
if (!session?.user) return;
|
||||
if (isFavourite) {
|
||||
await supabase
|
||||
.from("favourite_study_spaces")
|
||||
.delete()
|
||||
.match({ user_id: session.user.id, study_space_id: space.id });
|
||||
isFavourite = false;
|
||||
} else {
|
||||
await supabase
|
||||
.from("favourite_study_spaces")
|
||||
.insert([{ user_id: session.user.id, study_space_id: space.id }]);
|
||||
isFavourite = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Navbar>
|
||||
@@ -87,8 +122,11 @@
|
||||
{/if}
|
||||
<main>
|
||||
<Carousel urls={imgUrls} />
|
||||
<div class="nameContainer">
|
||||
{space.location}
|
||||
<div class="titleContainer">
|
||||
<div class="nameContainer">{space.location}</div>
|
||||
<div class="title-fav">
|
||||
<Favourite {isFavourite} onToggleFavourite={handleToggleFavourite} />
|
||||
</div>
|
||||
</div>
|
||||
{#if space.description != null && space.description.length > 0}
|
||||
<p class="descContainer">
|
||||
@@ -300,4 +338,18 @@
|
||||
font-family: monospace;
|
||||
color: #eaffeb;
|
||||
}
|
||||
.titleContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: -0.5rem 0 1rem;
|
||||
}
|
||||
.title-fav {
|
||||
font-size: 2rem;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border-radius: 50%;
|
||||
padding: 0.25rem;
|
||||
box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.7);
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -658,34 +658,4 @@
|
||||
background-color: #eaffeb;
|
||||
border-radius: 5rem;
|
||||
}
|
||||
.opening-times {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.opening-time-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.opening-time-item label {
|
||||
margin-top: 0;
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
.opening-time-item input[type="time"] {
|
||||
padding: 0.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 2px solid #eaffeb;
|
||||
background: none;
|
||||
color: #eaffeb;
|
||||
}
|
||||
|
||||
.opening-time-item span {
|
||||
margin: 0 0.5rem;
|
||||
color: #eaffeb;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user