Merge branch 'compulsory-tags' into 'master'

Compulsory tags

See merge request gk1623/drp-48!7

Co-authored-by: Barf-Vader <47476490+Barf-Vader@users.noreply.github.com>
This commit is contained in:
Ling, Alex
2025-06-09 18:30:56 +00:00
9 changed files with 210 additions and 27 deletions

View File

@@ -35,7 +35,7 @@
- `npx supabase stop` will stop the local dev database (data is persisted unless you do a reset). - `npx supabase stop` will stop the local dev database (data is persisted unless you do a reset).
- `npx supabase db push --local` will apply migrations to your local dev database. Useful if someone else has made new SQL migrations. - `npx supabase db push --local` will apply migrations to your local dev database. Useful if someone else has made new SQL migrations.
- `npx supabase db reset` will completely reset the local dev database. - `npx supabase db reset` will completely reset the local dev database.
- `npx supabase diff db -f <migration-name>` will generate a new migration file based on the current state of the database. This isn't 100% foolproof, so don't use it blindly. - `npx supabase db diff -f <migration-name>` will generate a new migration file based on the current state of the database. This isn't 100% foolproof, so don't use it blindly.
### What's where? ### What's where?

View File

@@ -0,0 +1,62 @@
<script lang="ts">
import type { Table } from "$lib";
interface Props {
space: Table<"study_spaces">;
}
const { space }: Props = $props();
const tagToColor: Record<string, string> = {
"Many Outlets": "compulsoryTagGreen",
"No Outlets": "compulsoryTagRed",
"Some Outlets": "compulsoryTagYellow",
"Good WiFi": "compulsoryTagGreen",
"Bad WiFi": "compulsoryTagRed",
"Moderate WiFi": "compulsoryTagYellow",
"No WiFi": "compulsoryTagRed"
};
</script>
<span class="compulsoryTagGreen">{space.volume}</span>
<span class={tagToColor[space.power]}>{space.power}</span>
<span class={tagToColor[space.wifi]}>{space.wifi}</span>
<style>
.compulsoryTagGreen {
display: flex;
font-weight: bold;
align-items: center;
justify-content: center;
text-align: center;
border-radius: 0.25rem;
background-color: #eaffeb;
color: #2e4653;
cursor: pointer;
padding: 0.2rem 0.6rem;
}
.compulsoryTagYellow {
display: flex;
font-weight: bold;
align-items: center;
justify-content: center;
text-align: center;
border-radius: 0.25rem;
background-color: #ffffd4;
color: #534b2e;
cursor: pointer;
padding: 0.2rem 0.6rem;
}
.compulsoryTagRed {
display: flex;
font-weight: bold;
align-items: center;
justify-content: center;
text-align: center;
border-radius: 0.25rem;
background-color: #ffcece;
color: #532e2e;
cursor: pointer;
padding: 0.2rem 0.6rem;
}
</style>

View File

@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import CompulsoryTags from "./CompulsoryTags.svelte";
import type { Table } from "$lib"; import type { Table } from "$lib";
interface Props { interface Props {
@@ -15,11 +16,14 @@
<img src={imgSrc} {alt} /> <img src={imgSrc} {alt} />
<div class="description"> <div class="description">
<h1>{space.location}</h1> <h1>{space.location}</h1>
<div class="tagContainer"> <div class="compulsoryContainer"><CompulsoryTags {space} /></div>
{#each space.tags as tag (tag)} {#if space.tags.length > 0}
<span class="tag">{tag}</span> <div class="tagContainer">
{/each} {#each space.tags as tag (tag)}
</div> <span class="tag">{tag}</span>
{/each}
</div>
{/if}
</div> </div>
</a> </a>
@@ -55,11 +59,14 @@
gap: 0.4rem; gap: 0.4rem;
border-radius: 0.5rem; border-radius: 0.5rem;
background: none; background: none;
padding-top: 0.5rem;
} }
.tag { .tag {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center;
text-align: center;
border-radius: 0.25rem; border-radius: 0.25rem;
background-color: #2e4653; background-color: #2e4653;
color: #eaffeb; color: #eaffeb;
@@ -67,4 +74,11 @@
cursor: pointer; cursor: pointer;
padding: 0.2rem 0.6rem; padding: 0.2rem 0.6rem;
} }
.compulsoryContainer {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
font-size: 0.875rem;
}
</style> </style>

View File

@@ -70,8 +70,11 @@ export type Database = {
description: string | null description: string | null
id: string id: string
location: string | null location: string | null
power: string
tags: string[] tags: string[]
updated_at: string | null updated_at: string | null
volume: string
wifi: string
} }
Insert: { Insert: {
building_location?: string | null building_location?: string | null
@@ -79,8 +82,11 @@ export type Database = {
description?: string | null description?: string | null
id?: string id?: string
location?: string | null location?: string | null
power: string
tags?: string[] tags?: string[]
updated_at?: string | null updated_at?: string | null
volume: string
wifi: string
} }
Update: { Update: {
building_location?: string | null building_location?: string | null
@@ -88,8 +94,11 @@ export type Database = {
description?: string | null description?: string | null
id?: string id?: string
location?: string | null location?: string | null
power?: string
tags?: string[] tags?: string[]
updated_at?: string | null updated_at?: string | null
volume?: string
wifi?: string
} }
Relationships: [] Relationships: []
} }

View File

@@ -5,26 +5,22 @@ export type Table<T extends keyof Database["public"]["Tables"]> =
export type Enum<T extends keyof Database["public"]["Enums"]> = Database["public"]["Enums"][T]; export type Enum<T extends keyof Database["public"]["Enums"]> = Database["public"]["Enums"][T];
export const availableStudySpaceTags = [ export const availableStudySpaceTags = [
"Quiet",
"Loud",
"Silent",
"Crowded", "Crowded",
"Group study", "Group study",
"Power outlets",
"No power outlets",
"24/7", "24/7",
"Food allowed", "Food allowed",
"No food allowed", "No food allowed",
"Well lit", "Well lit",
"Poorly lit", "Poorly lit",
"Good wifi",
"Bad wifi",
"No wifi",
"Whiteboard", "Whiteboard",
"Restricted access", "Restricted access",
"Hot", "Hot",
"Air conditioned", "Air conditioned",
"Cold", "Cold",
"Cringe", "PCs",
"PCs" "Cringe"
]; ];
export const volumeTags = ["Silent", "Quiet", "Some Noise", "Loud"];
export const wifiTags = ["Good WiFi", "Moderate WiFi", "Bad WiFi", "No WiFi"];
export const powerOutletTags = ["Many Outlets", "Some Outlets", "No Outlets"];

View File

@@ -7,7 +7,7 @@
import Button from "$lib/components/Button.svelte"; import Button from "$lib/components/Button.svelte";
import Images from "$lib/components/inputs/Images.svelte"; import Images from "$lib/components/inputs/Images.svelte";
import type { Table } from "$lib"; import type { Table } from "$lib";
import { availableStudySpaceTags } from "$lib"; import { availableStudySpaceTags, wifiTags, powerOutletTags, volumeTags } from "$lib";
const { data } = $props(); const { data } = $props();
const { supabase } = $derived(data); const { supabase } = $derived(data);
@@ -17,7 +17,10 @@
description: "", description: "",
building_location: "", building_location: "",
location: "", location: "",
tags: [] tags: [],
volume: "",
power: "",
wifi: ""
}); });
async function uploadStudySpace() { async function uploadStudySpace() {
@@ -115,7 +118,41 @@
required required
/> />
<label for="tags">Add tags:</label> <div class="compulsoryTags">
<div class="compulsoryContainer">
<label for="volume">Sound level:</label>
<select bind:value={studySpaceData.volume} name="volume" class="compulsoryTagSelect">
<option value="" disabled selected>How noisy is it?</option>
{#each volumeTags as volumeTag (volumeTag)}
<option value={volumeTag}>{volumeTag}</option>
{/each}
</select>
</div>
<div class="compulsoryContainer">
<label for="powerOutlets">Power outlets:</label>
<select
bind:value={studySpaceData.power}
name="poweOutlets"
class="compulsoryTagSelect"
>
<option value="" disabled selected>Power outlets?</option>
{#each powerOutletTags as powerOutletTag (powerOutletTag)}
<option value={powerOutletTag}>{powerOutletTag}</option>
{/each}
</select>
</div>
<div class="compulsoryContainer">
<label for="wifi">Wifi:</label>
<select bind:value={studySpaceData.wifi} name="wifi" class="compulsoryTagSelect">
<option value="" disabled selected>How's the wifi?</option>
{#each wifiTags as wifiTag (wifiTag)}
<option value={wifiTag}>{wifiTag}</option>
{/each}
</select>
</div>
</div>
<label for="tags">Additional tags:</label>
<div class="tagDisplay"> <div class="tagDisplay">
{#each studySpaceData.tags as tagName (tagName)} {#each studySpaceData.tags as tagName (tagName)}
<button class="tag" onclick={deleteTag(tagName)} type="button"> <button class="tag" onclick={deleteTag(tagName)} type="button">
@@ -184,7 +221,9 @@
type="submit" type="submit"
disabled={(spaceImgs?.length || 0) === 0 || disabled={(spaceImgs?.length || 0) === 0 ||
!studySpaceData.location || !studySpaceData.location ||
studySpaceData.tags.length === 0 || !studySpaceData.wifi ||
!studySpaceData.volume ||
!studySpaceData.power ||
!studySpaceData.building_location || !studySpaceData.building_location ||
uploading} uploading}
> >
@@ -294,4 +333,40 @@
.avaliableTag:last-child { .avaliableTag:last-child {
padding-bottom: 0.6rem; padding-bottom: 0.6rem;
} }
.compulsoryTags {
display: grid;
gap: 0.4rem;
border-radius: 0.5rem;
background-color: none;
width: 100%;
font-size: 1rem;
grid-template-columns: repeat(3, 1fr);
}
.compulsoryContainer {
display: flex;
flex-direction: column;
align-items: left;
justify-content: top;
border-radius: 0.5rem;
background-color: none;
}
.compulsoryTagSelect {
width: 100%;
height: 100%;
padding: 0.5rem;
border-radius: 0.5rem;
border: 2px solid #eaffeb;
background: none;
color: #eaffeb;
font-size: 0.9rem;
text-align: left;
}
.compulsoryTagSelect option {
background-color: #2e4653;
color: #eaffeb;
}
</style> </style>

View File

@@ -3,6 +3,7 @@
import crossUrl from "$lib/assets/cross.svg"; import crossUrl from "$lib/assets/cross.svg";
import placeholder from "$lib/assets/study_space.png"; import placeholder from "$lib/assets/study_space.png";
import Carousel from "$lib/components/Carousel.svelte"; import Carousel from "$lib/components/Carousel.svelte";
import CompulsoryTags from "$lib/components/CompulsoryTags.svelte";
const { data } = $props(); const { data } = $props();
const { space, supabase } = $derived(data); const { space, supabase } = $derived(data);
@@ -35,13 +36,16 @@
</p> </p>
<hr /> <hr />
{/if} {/if}
<div class="tagContainer"> <div class="compulsoryContainer"><CompulsoryTags {space} /></div>
{#each space.tags as tag (tag)} {#if space.tags.length > 0}
<span class="tag"> <div class="tagContainer">
{tag} {#each space.tags as tag (tag)}
</span> <span class="tag">
{/each} {tag}
</div> </span>
{/each}
</div>
{/if}
<hr /> <hr />
<div class="subtitle">Where it is:</div> <div class="subtitle">Where it is:</div>
<p class="addrContainer"> <p class="addrContainer">
@@ -128,4 +132,13 @@
cursor: pointer; cursor: pointer;
padding: 0.2rem 0.6rem; 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;
}
</style> </style>

View File

@@ -0,0 +1,11 @@
alter table "public"."study_spaces" add column "power" text;
alter table "public"."study_spaces" add column "volume" text;
alter table "public"."study_spaces" add column "wifi" text;
update "public"."study_spaces" set "power" = 'Many' where "power" is null;
update "public"."study_spaces" set "volume" = 'Quiet' where "volume" is null;
update "public"."study_spaces" set "wifi" = 'Good' where "wifi" is null;
alter table "public"."study_spaces" alter column "power" set not null;
alter table "public"."study_spaces" alter column "volume" set not null;
alter table "public"."study_spaces" alter column "wifi" set not null;

View File

@@ -13,6 +13,9 @@ CREATE TABLE study_spaces (
location text, location text,
building_location text, building_location text,
tags text[] NOT NULL DEFAULT array[]::text[], tags text[] NOT NULL DEFAULT array[]::text[],
volume text NOT NULL,
wifi text NOT NULL,
power text NOT NULL,
created_at timestamp with time zone DEFAULT now(), created_at timestamp with time zone DEFAULT now(),
updated_at timestamp with time zone DEFAULT now() updated_at timestamp with time zone DEFAULT now()
); );