Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: availability page functionality #85

Merged
merged 31 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3b05c80
feat: ✨ fix auth
KevinWu098 Apr 15, 2024
ad7deca
fix: 🐛 add relation info for meeting and availabilities
KevinWu098 Apr 15, 2024
0d939b3
feat: ✨ insert/update availabilities
KevinWu098 Apr 16, 2024
caa2984
feat: ✨ read and display availability from DB
KevinWu098 Apr 16, 2024
0b7b2e7
feat: ✨ (stable) saving and loading availability
KevinWu098 Apr 16, 2024
6e9af6b
feat: ✨ save guest availability
KevinWu098 Apr 16, 2024
9320a05
fix: 🐛 add in auth_methods, keep schema as notNull()
KevinWu098 Apr 22, 2024
d8fb1d7
Revert "fix: 🐛 add in auth_methods, keep schema as notNull()"
KevinWu098 Apr 22, 2024
45d0aec
Revert "Revert "fix: 🐛 add in auth_methods, keep schema as notNull()""
KevinWu098 Apr 22, 2024
6f7152f
Merge branch 'main' into kw/74-availability-page-functionality
KevinWu098 Apr 22, 2024
89a751c
fix: 🐛 revert schema changes (for real)
KevinWu098 Apr 29, 2024
36d533b
feat: ✨ correct err message for duplicate username
KevinWu098 Apr 29, 2024
4fd1f34
feat: ✨ get guest login to semi-work
KevinWu098 Apr 29, 2024
a782159
chore: 🔧 cleanup logs
KevinWu098 Apr 30, 2024
5f2bcf6
refactor: ♻️ cleanup guest form submission in login modal
KevinWu098 Apr 30, 2024
bfbfff7
refactor: ♻️ cleanup api calls and error handling in login modal
KevinWu098 Apr 30, 2024
feee263
refactor: ♻️ clean up how meeting id and data is passed around
KevinWu098 Apr 30, 2024
50fd55b
docs: 📚️ add note about .submit()
KevinWu098 Apr 30, 2024
57124bb
chore: 🔧 misc. cleanup
KevinWu098 Apr 30, 2024
04738f4
refactor: ♻️ correct var names
KevinWu098 Apr 30, 2024
5fc6a5c
docs: 📚️ add in meeting creation util
KevinWu098 Apr 30, 2024
0da71fd
feat: ✨ create meeting dates w/ meeting, fix retrieval of availabilities
KevinWu098 May 1, 2024
720797a
fix: 🐛 reference isAvailable in reactive block
KevinWu098 May 6, 2024
f3c67f0
fix: 🐛 misc. fixes
KevinWu098 May 6, 2024
6460b7a
fix: 🐛 unblock insert
KevinWu098 May 6, 2024
30e5af3
feat: ✨ wrap multi-insert with transaction
KevinWu098 May 6, 2024
0598eb4
fix: 🐛 simplify type
KevinWu098 May 6, 2024
f55f826
fix: 🐛 match parse to ZotDate[]
KevinWu098 May 6, 2024
cb4a6de
Merge branch 'main' of https://github.com/icssc/ZotMeet into kw/74-av…
seancfong May 6, 2024
bc77537
fix: 🐛 unasync map func
KevinWu098 May 7, 2024
045a64a
chore: 🔧 use pgTable
KevinWu098 May 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
"cz-conventional-changelog": "3.3.0",
"daisyui": "^4.6.2",
"devmoji": "2.3.0",
"drizzle-kit": "0.20.14",
"dotenv": "^16.4.5",
"drizzle-kit": "0.20.17",
"eslint": "8.54.0",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-import": "2.29.0",
Expand Down
39 changes: 28 additions & 11 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/lib/components/availability/AvailabilityBlock.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

$: {
updateBlockColor(selectionState);
isAvailable;
}
</script>

Expand Down
111 changes: 71 additions & 40 deletions src/lib/components/availability/LoginModal.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
<script lang="ts">
import type { ActionResult } from "@sveltejs/kit";
import { superForm } from "sveltekit-superforms/client";

import { guestSchema, userSchema } from "$lib/config/zod-schemas";
import { isEditingAvailability, isStateUnsaved } from "$lib/stores/availabilityStores";
import type { LoginModalProps } from "$lib/types/availability";
import type { PageData } from "../../../routes/availability/$types";

import { deserialize } from "$app/forms";
import { userSchema } from "$lib/config/zod-schemas";
import {
isEditingAvailability,
isStateUnsaved,
guestSession,
} from "$lib/stores/availabilityStores";
import BrightnessAlert from "~icons/material-symbols/brightness-alert-outline-rounded";
import EmailIcon from "~icons/mdi/email";
import KeyIcon from "~icons/mdi/key";
import Loader from "~icons/mdi/loading";
import UserIcon from "~icons/mdi/user";

export let data: LoginModalProps;
export let data: PageData;

const loginSchema = userSchema.pick({ email: true, password: true });
const guestLoginSchema = guestSchema.pick({ username: true });

const { form, errors, enhance, delayed } = superForm(data.form, {
taintedMessage: null,
Expand All @@ -26,36 +32,68 @@
authModal.close();
}

$isEditingAvailability = false;
$isStateUnsaved = false;
// TODO: Update DB with data
}
},
});
const availabilitySaveForm: HTMLFormElement | null = document.getElementById(
"availability-save-form",
) as HTMLFormElement;

const {
form: guestForm,
errors: guestErrors,
enhance: guestEnhance,
delayed: guestDelayed,
} = superForm(data.guestForm, {
taintedMessage: null,
validators: guestLoginSchema,
delayMs: 0,
onUpdated({ form }) {
if (form.valid) {
const authModal = document.getElementById("auth-modal") as HTMLDialogElement;
if (authModal) {
authModal.close();
if (availabilitySaveForm) {
/**
* This triggers a regular form submission (no enhancement)
*/
availabilitySaveForm.submit();
}

$isEditingAvailability = false;
$isStateUnsaved = false;

// TODO: Update DB with guest data
}
},
});

/**
* Some bespoke state for the guest form
*/
let guestForm: HTMLFormElement;
let formState: "success" | "failure";
let formError: string;

/**
* Guest form submissions are handled through a standard fetch
* This prevents the full page refresh of a non-enhanced form action,
* which would lose the current guest session (which is in a Svelte store)
*/
const handleGuestSubmit = async (meetingId: string) => {
const formData = new FormData(guestForm);
formData.append("meetingId", meetingId);

const response = await fetch("/auth/guest", {
method: "POST",
body: formData,
});

const guestData: ActionResult = deserialize(await response.text());

if (guestData.type === "failure") {
formState = "failure";

// TODO: Handle cases other than duplicate username
formError = guestData.data?.form.errors.username[0];
}

if (guestData.type === "success") {
const authModal = document.getElementById("auth-modal");
if (authModal && authModal instanceof HTMLDialogElement) {
authModal.close();
}

$guestSession = {
guestName: guestData.data?.username,
meetingId: data.meetingId,
};

$isEditingAvailability = false;
$isStateUnsaved = false;
}
};
</script>

<dialog id="auth-modal" class="modal">
Expand Down Expand Up @@ -142,19 +180,18 @@
<h3 class="h-fit px-2 text-left text-xl font-bold">Save as Guest</h3>

<form
method="POST"
action="TODO"
use:guestEnhance
bind:this={guestForm}
class="flex-center w-full grow flex-col items-center space-y-4 md:w-[250px]"
on:submit|preventDefault={() => handleGuestSubmit(data.meetingId)}
>
{#if $guestErrors._errors}
{#if formState === "failure"}
<aside class="variant-filled-error alert">
<div><BrightnessAlert /></div>

<!-- Message -->
<div class="alert-message">
<h3 class="h3">Login Problem</h3>
<p>{$guestErrors._errors}</p>
<p>{formError ?? "An error has occurred..."}</p>
</div>
</aside>
{/if}
Expand All @@ -166,17 +203,11 @@
type="text"
class="grow appearance-none border-none focus:border-none focus:outline-none focus:ring-0"
placeholder="username"
bind:value={$guestForm.username}
name="username"
/>
</label>

<button type="submit" class="variant-filled-primary btn h-10 w-full">
{#if $guestDelayed}
<Loader class="animate-spin" />
{:else}
Save
{/if}
</button>
<button type="submit" class="variant-filled-primary btn h-10 w-full">Save</button>
</div>
</form>
</div>
Expand Down
29 changes: 23 additions & 6 deletions src/lib/components/availability/PersonalAvailability.svelte
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
<script lang="ts">
import { onMount } from "svelte";

import type { PageData } from "../../../routes/availability/$types";

import LoginFlow from "./LoginModal.svelte";

import AvailabilityBlock from "$lib/components/availability/AvailabilityBlock.svelte";
import {
availabilityDates,
availabilityTimeBlocks,
guestSession,
isEditingAvailability,
isStateUnsaved,
} from "$lib/stores/availabilityStores";
import type {
AvailabilityBlockType,
LoginModalProps,
SelectionStateType,
} from "$lib/types/availability";
import type { AvailabilityBlockType, SelectionStateType } from "$lib/types/availability";
import { ZotDate } from "$lib/utils/ZotDate";
import { getGeneralAvailability } from "$lib/utils/availability";
import { cn } from "$lib/utils/utils";

export let columns: number;
export let data: LoginModalProps;
export let data: PageData;

let itemsPerPage: number = columns;
$: {
Expand All @@ -34,13 +36,15 @@
let endBlockSelection: AvailabilityBlockType | null = null;

let currentPage = 0;

let currentPageAvailability: (ZotDate | null)[];

let selectionState: SelectionStateType | null = null;

// Triggers on every pagination change and selection confirmation
$: {
const datesToOffset = currentPage * itemsPerPage;

currentPageAvailability = $availabilityDates.slice(datesToOffset, datesToOffset + itemsPerPage);

if (currentPage === lastPage) {
Expand Down Expand Up @@ -148,6 +152,19 @@
}
});
}

onMount(async () => {
$guestSession.meetingId = data.meetingId;

const generalAvailability = await getGeneralAvailability(data, $guestSession);
const defaultMeetingDates = data.defaultDates.map((item) => new ZotDate(item.date, false, []));
ZotDate.initializeAvailabilities(defaultMeetingDates);

$availabilityDates =
generalAvailability && generalAvailability.length > 0
? generalAvailability
: defaultMeetingDates;
});
</script>

<div class="flex items-center justify-between overflow-x-auto font-dm-sans">
Expand Down
1 change: 1 addition & 0 deletions src/lib/config/zod-schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ export const guestSchema = z.object({
.string({ required_error: "Username is required" })
.min(1, { message: "Username is required" })
.trim(),
meetingId: z.string().min(1, { message: "Username is required" }).trim(),
});
Loading
Loading