Compulsory tags #37
@@ -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?
|
||||||
|
|
||||||
|
|||||||
62
src/lib/components/CompulsoryTags.svelte
Normal file
62
src/lib/components/CompulsoryTags.svelte
Normal 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>
|
||||||
@@ -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="compulsoryContainer"><CompulsoryTags {space} /></div>
|
||||||
|
{#if space.tags.length > 0}
|
||||||
<div class="tagContainer">
|
<div class="tagContainer">
|
||||||
{#each space.tags as tag (tag)}
|
{#each space.tags as tag (tag)}
|
||||||
<span class="tag">{tag}</span>
|
<span class="tag">{tag}</span>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</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>
|
||||||
|
|||||||
9
src/lib/database.d.ts
vendored
9
src/lib/database.d.ts
vendored
@@ -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: []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"];
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,6 +36,8 @@
|
|||||||
</p>
|
</p>
|
||||||
<hr />
|
<hr />
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="compulsoryContainer"><CompulsoryTags {space} /></div>
|
||||||
|
{#if space.tags.length > 0}
|
||||||
<div class="tagContainer">
|
<div class="tagContainer">
|
||||||
{#each space.tags as tag (tag)}
|
{#each space.tags as tag (tag)}
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
@@ -42,6 +45,7 @@
|
|||||||
</span>
|
</span>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</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>
|
||||||
|
|||||||
11
supabase/migrations/20250609142130_add-compulsory-tags.sql
Normal file
11
supabase/migrations/20250609142130_add-compulsory-tags.sql
Normal 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;
|
||||||
@@ -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()
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user