From b7429a13250c2b6942f8627166323b56a24b9358 Mon Sep 17 00:00:00 2001
From: Ethan Seys
Date: Tue, 12 Nov 2024 11:22:11 -0600
Subject: [PATCH 01/11] Added student rescheduling functionality
---
src/lib/emails/AppointmentBooked.svelte | 9 ++-
src/lib/emails/AppointmentBooked.txt | 2 +-
src/lib/emails/appointment_booked.ts | 4 +-
.../dash/sessions/[sessionId]/+page.svelte | 56 ++++++++++++++-----
src/routes/(authed)/schedule/+page.server.ts | 28 +++++++---
src/routes/(authed)/schedule/+page.svelte | 4 +-
6 files changed, 74 insertions(+), 29 deletions(-)
diff --git a/src/lib/emails/AppointmentBooked.svelte b/src/lib/emails/AppointmentBooked.svelte
index cafb587..48e5f3c 100644
--- a/src/lib/emails/AppointmentBooked.svelte
+++ b/src/lib/emails/AppointmentBooked.svelte
@@ -2,11 +2,14 @@
import type { AppointmentBookedProps } from '$lib/emails/appointment_booked';
import { DateTime } from 'luxon';
- let { startTime, duration, mentorName, sessionId, type, timezone }: AppointmentBookedProps =
+ let { startTime, duration, mentorName, sessionId, type, timezone, reschedule }: AppointmentBookedProps =
$props();
+
+ let reschedule_link = `https://scheddy.ztlartcc.org/sessions/${sessionId}`;
+ let title = reschedule ? 'Appointment updated' : 'Appointment booked';
-Appointment booked!
+{title}
This is your confirmation email for your upcoming session.
Session type: {type}
@@ -14,7 +17,7 @@
Timezone: {timezone}
Duration: {duration} minutes
Mentor: {mentorName}
-Please reach out to your mentor directly if you need to cancel or reschedule this lesson.
+Cancel/Reschedule
---
diff --git a/src/lib/emails/AppointmentBooked.txt b/src/lib/emails/AppointmentBooked.txt
index 69d9216..8b92e2b 100644
--- a/src/lib/emails/AppointmentBooked.txt
+++ b/src/lib/emails/AppointmentBooked.txt
@@ -8,7 +8,7 @@ This is your confirmation email for your upcoming session.
-- Duration: {duration} minutes
-- Mentor: {mentorName}
-Please reach out to your mentor directly if you need to cancel or reschedule this lesson.
+Cancel/Reschedule
---
diff --git a/src/lib/emails/appointment_booked.ts b/src/lib/emails/appointment_booked.ts
index 5fab21b..2c7607c 100644
--- a/src/lib/emails/appointment_booked.ts
+++ b/src/lib/emails/appointment_booked.ts
@@ -11,6 +11,7 @@ export interface AppointmentBookedProps {
mentorName: string;
sessionId: string;
timezone: string;
+ reschedule?: boolean;
}
export function appointment_booked(props: AppointmentBookedProps): EmailContent {
@@ -21,7 +22,8 @@ export function appointment_booked(props: AppointmentBookedProps): EmailContent
duration: props.duration.toString(),
mentorName: props.mentorName,
sessionId: props.sessionId,
- timezone: props.timezone
+ timezone: props.timezone,
+ reschedule: props.reschedule ? 'rescheduled' : ''
}),
html: render(AppointmentBooked, {
props: props
diff --git a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
index a307db9..ecf9955 100644
--- a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
+++ b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
@@ -21,6 +21,18 @@
let hour: number = $state(0);
let minute: number = $state(0);
+ function check_time(t: string) {
+ const start_time = DateTime.fromISO(t);
+ const now = DateTime.now();
+ const interval = start_time.diff(now, 'hours');
+
+ if (interval.hours < 24) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
async function cancel() {
await fetch('?/cancel', {
method: 'POST',
@@ -31,6 +43,19 @@
await goto('/dash');
await invalidateAll();
}
+
+ function reschedule_helper() {
+ if (!data.isMentor) {
+ rescheduleOpen = true;
+ } else {
+ const queryParam = new URLSearchParams();
+ queryParam.set('sessionId', data.sessionInfo.session.id);
+ queryParam.set('reschedule', 'true');
+ queryParam.set('type', data.sessionInfo.sessionType.id);
+ goto(`/schedule?${queryParam.toString()}`);
+ }
+ }
+
async function reschedule() {
let udata = new URLSearchParams();
@@ -91,8 +116,8 @@
{data.sessionInfo.sessionType.category} - {data.sessionInfo.sessionType.name}
Duration: {data.sessionInfo.sessionType.length} minutes
- {#if data.isMentor}
- Mentor/Staff Actions
+ {#if data.isMentor || check_time(data.sessionInfo.session.start)}
+ Session Actions
{
@@ -103,18 +128,21 @@
Cancel
{
- rescheduleOpen = true;
- }}
+ onclick={reschedule_helper}
variant="danger"
>
Reschedule
+ {:else}
+
+ You cannot cancel or reschedule this session as it is less than 24 hours away. Please contact
+ your instructor if you need to cancel or reschedule.
+
{/if}
-{#if data.isMentor}
+{#if data.isMentor || check_time(data.sessionInfo.session.start)}
{
cancelOpen = false;
@@ -127,13 +155,15 @@
}}
title="Confirm cancellation"
/>
-
-
-
- It is your responsibility to inform the student of the cancellation.
-
-
-
+ {#if data.isMentor}
+
+
+
+ It is your responsibility to inform the student of the cancellation.
+
+
+
+ {/if}
{
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index 1d9a13c..35de363 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -185,7 +185,7 @@ function slottificate(
return slotData;
}
-export const load: PageServerLoad = async ({ cookies }) => {
+export const load: PageServerLoad = async ({ cookies, url }) => {
const { user } = (await loadUserData(cookies))!;
const sTypes = await db.select().from(sessionTypes);
@@ -198,6 +198,8 @@ export const load: PageServerLoad = async ({ cookies }) => {
const slotData = slottificate(sTypes, mentors, allSessions);
+ const originalSessionType = url.searchParams.has('reschedule') ? url.searchParams.get('type') : null;
+
return {
user,
role: roleString(roleOf(user)),
@@ -205,18 +207,21 @@ export const load: PageServerLoad = async ({ cookies }) => {
isStaff: roleOf(user) >= ROLE_STAFF,
isDeveloper: roleOf(user) >= ROLE_DEVELOPER,
sessionTypes: sTypes,
- slotData
+ slotData,
+ originalSessionType
};
};
export const actions: Actions = {
- default: async ({ cookies, request }) => {
+ default: async ({ cookies, request, url }) => {
const { user } = (await loadUserData(cookies))!;
const formData = await request.formData();
const requestedSlotId = formData.get('timeslot')!;
const requestedType = formData.get('type')!;
const timezone = formData.get('timezone')!;
+ const orginalSessionId = url.searchParams.get('sessionId')!;
+ const reschedule = url.searchParams.has('reschedule')!;
const sTypes = await db.select().from(sessionTypes);
const mentors = await db
@@ -290,12 +295,17 @@ export const actions: Actions = {
timezone
});
- await sendEmail(
- user.email,
- 'Appointment booked - ' + start.setZone(timezone).toLocaleString(DateTime.DATETIME_HUGE),
- studentEmailContent.raw,
- studentEmailContent.html
- );
+ const subject = reschedule
+ ? 'Appointment updated'
+ : 'Appointment booked' +
+ ' - ' +
+ start.setZone(timezone).toLocaleString(DateTime.DATETIME_HUGE);
+
+ if (reschedule) {
+ await db.delete(sessions).where(eq(sessions.id, orginalSessionId));
+ }
+
+ await sendEmail(user.email, subject, studentEmailContent.raw, studentEmailContent.html);
await sendEmail(
mentor.email,
'New session booked - ' +
diff --git a/src/routes/(authed)/schedule/+page.svelte b/src/routes/(authed)/schedule/+page.svelte
index 6a05143..d70cf43 100644
--- a/src/routes/(authed)/schedule/+page.svelte
+++ b/src/routes/(authed)/schedule/+page.svelte
@@ -41,7 +41,7 @@
return c;
});
- let sessionType: string | null = $state(null);
+ let sessionType: string | null = $state(data.originalSessionType);
let timezones = $state(getTimeZones());
timezones.sort((a, b) => {
@@ -58,7 +58,7 @@
return 0;
});
- let step = $state(1);
+ let step = $state(data.originalSessionType ? 2 : 1);
let timezone = $state(DateTime.local().zoneName);
let timeslot: string | null = $state(null);
let interval = $derived(timeslot ? Interval.fromISO(timeslot.split('@')[0]) : null);
From 34259ad67c9612f879cf6f1a63321bc8dd8fb184 Mon Sep 17 00:00:00 2001
From: Ethan Seys
Date: Tue, 12 Nov 2024 12:04:48 -0600
Subject: [PATCH 02/11] fmt
---
.../dash/sessions/[sessionId]/+page.svelte | 6 +----
src/routes/(authed)/schedule/+page.server.ts | 24 ++++++++++++-------
2 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
index ecf9955..2453d6f 100644
--- a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
+++ b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
@@ -26,11 +26,7 @@
const now = DateTime.now();
const interval = start_time.diff(now, 'hours');
- if (interval.hours < 24) {
- return false;
- } else {
- return true;
- }
+ return interval.hours < 24;
}
async function cancel() {
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index 35de363..34152cd 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -198,7 +198,9 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
const slotData = slottificate(sTypes, mentors, allSessions);
- const originalSessionType = url.searchParams.has('reschedule') ? url.searchParams.get('type') : null;
+ const originalSessionType = url.searchParams.has('reschedule')
+ ? url.searchParams.get('type')
+ : null;
return {
user,
@@ -208,7 +210,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
isDeveloper: roleOf(user) >= ROLE_DEVELOPER,
sessionTypes: sTypes,
slotData,
- originalSessionType
+ originalSessionType
};
};
@@ -295,17 +297,21 @@ export const actions: Actions = {
timezone
});
- const subject = reschedule
- ? 'Appointment updated'
- : 'Appointment booked' +
- ' - ' +
- start.setZone(timezone).toLocaleString(DateTime.DATETIME_HUGE);
-
if (reschedule) {
await db.delete(sessions).where(eq(sessions.id, orginalSessionId));
}
- await sendEmail(user.email, subject, studentEmailContent.raw, studentEmailContent.html);
+ await sendEmail(
+ user.email,
+ reschedule
+ ? 'Appointment updated'
+ : 'Appointment booked' +
+ ' - ' +
+ start.setZone(timezone).toLocaleString(DateTime.DATETIME_HUGE),
+ studentEmailContent.raw,
+ studentEmailContent.html
+ );
+
await sendEmail(
mentor.email,
'New session booked - ' +
From e011d2357bbab222d387c37a9072e743ec672bc4 Mon Sep 17 00:00:00 2001
From: Ethan Seys
Date: Tue, 12 Nov 2024 12:10:06 -0600
Subject: [PATCH 03/11] Fixed email
---
src/routes/(authed)/schedule/+page.server.ts | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index 34152cd..d93529f 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -277,7 +277,8 @@ export const actions: Actions = {
mentorName: mentor.firstName + ' ' + mentor.lastName,
duration,
sessionId: id,
- type: typename
+ type: typename,
+ reschedule
});
const mentorEmailContent = new_session({
startTime: start.setZone(mentor.timezone),
@@ -311,7 +312,7 @@ export const actions: Actions = {
studentEmailContent.raw,
studentEmailContent.html
);
-
+
await sendEmail(
mentor.email,
'New session booked - ' +
From 319faf8ab043ee911a8902f15f640cea53f43438 Mon Sep 17 00:00:00 2001
From: Ethan Seys
Date: Wed, 13 Nov 2024 12:47:54 -0600
Subject: [PATCH 04/11] Fixed POST
---
src/routes/(authed)/schedule/+page.server.ts | 8 +++++---
src/routes/(authed)/schedule/+page.svelte | 6 +++++-
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index d93529f..58a893e 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -202,6 +202,8 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
? url.searchParams.get('type')
: null;
+ const orginalSessionId = url.searchParams.get('sessionId')!;
+
return {
user,
role: roleString(roleOf(user)),
@@ -210,7 +212,8 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
isDeveloper: roleOf(user) >= ROLE_DEVELOPER,
sessionTypes: sTypes,
slotData,
- originalSessionType
+ originalSessionType,
+ orginalSessionId
};
};
@@ -294,8 +297,7 @@ export const actions: Actions = {
mentor: slotObj.mentor,
student: user.id,
start: start.toISO(),
- type: requestedType,
- timezone
+ type: requestedType
});
if (reschedule) {
diff --git a/src/routes/(authed)/schedule/+page.svelte b/src/routes/(authed)/schedule/+page.svelte
index d70cf43..289cfcd 100644
--- a/src/routes/(authed)/schedule/+page.svelte
+++ b/src/routes/(authed)/schedule/+page.svelte
@@ -69,6 +69,10 @@
let bookingState: 'success' | 'fail' | 'loading' = $state('loading');
+ const bookingUrl = data.originalSessionType
+ ? `?sessionId=${data.originalSessionId}&reschedule=true&type=${data.originalSessionType}`
+ : '?';
+
async function book() {
if (!timeslot) return;
@@ -80,7 +84,7 @@
data.set('type', sessionType!);
data.set('timezone', timezone);
- let r = await fetch('?', {
+ let r = await fetch(bookingUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
From ae5b68e931839e69dc0caece8b45dae362e1d239 Mon Sep 17 00:00:00 2001
From: EMcNugget
Date: Wed, 13 Nov 2024 18:22:59 -0600
Subject: [PATCH 05/11] fixed typos
---
src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte | 2 +-
src/routes/(authed)/schedule/+page.server.ts | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
index 2453d6f..f578780 100644
--- a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
+++ b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
@@ -41,7 +41,7 @@
}
function reschedule_helper() {
- if (!data.isMentor) {
+ if (data.isMentor) {
rescheduleOpen = true;
} else {
const queryParam = new URLSearchParams();
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index 58a893e..291c625 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -202,7 +202,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
? url.searchParams.get('type')
: null;
- const orginalSessionId = url.searchParams.get('sessionId')!;
+ const originalSessionId = url.searchParams.get('sessionId')!;
return {
user,
@@ -213,7 +213,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
sessionTypes: sTypes,
slotData,
originalSessionType,
- orginalSessionId
+ originalSessionId
};
};
@@ -297,7 +297,8 @@ export const actions: Actions = {
mentor: slotObj.mentor,
student: user.id,
start: start.toISO(),
- type: requestedType
+ type: requestedType,
+ timezone
});
if (reschedule) {
From ca1491c209d2dca4849e4f141ea7999ea6f278bd Mon Sep 17 00:00:00 2001
From: Ethan Seys
Date: Thu, 14 Nov 2024 13:28:27 -0600
Subject: [PATCH 06/11] Fixed check_time()
---
src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
index f578780..e8964d7 100644
--- a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
+++ b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
@@ -112,7 +112,7 @@
{data.sessionInfo.sessionType.category} - {data.sessionInfo.sessionType.name}
Duration: {data.sessionInfo.sessionType.length} minutes
- {#if data.isMentor || check_time(data.sessionInfo.session.start)}
+ {#if data.isMentor || !check_time(data.sessionInfo.session.start)}
Session Actions
Date: Wed, 20 Nov 2024 18:59:53 -0600
Subject: [PATCH 07/11] Moved reschedule interface to /schedule
---
src/lib/emails/AppointmentBooked.svelte | 14 ++++-
src/lib/emails/appointment_booked.ts | 2 +
.../dash/sessions/[sessionId]/+page.svelte | 52 +++++-------------
src/routes/(authed)/schedule/+page.server.ts | 27 ++++++++-
src/routes/(authed)/schedule/+page.svelte | 55 +++++++++++++++++++
5 files changed, 105 insertions(+), 45 deletions(-)
diff --git a/src/lib/emails/AppointmentBooked.svelte b/src/lib/emails/AppointmentBooked.svelte
index 48e5f3c..591c652 100644
--- a/src/lib/emails/AppointmentBooked.svelte
+++ b/src/lib/emails/AppointmentBooked.svelte
@@ -2,10 +2,18 @@
import type { AppointmentBookedProps } from '$lib/emails/appointment_booked';
import { DateTime } from 'luxon';
- let { startTime, duration, mentorName, sessionId, type, timezone, reschedule }: AppointmentBookedProps =
- $props();
+ let {
+ startTime,
+ duration,
+ mentorName,
+ sessionId,
+ type,
+ timezone,
+ link_params,
+ reschedule
+ }: AppointmentBookedProps = $props();
- let reschedule_link = `https://scheddy.ztlartcc.org/sessions/${sessionId}`;
+ let reschedule_link = `https://scheddy.ztlartcc.org/schedule${link_params}`;
let title = reschedule ? 'Appointment updated' : 'Appointment booked';
diff --git a/src/lib/emails/appointment_booked.ts b/src/lib/emails/appointment_booked.ts
index 2c7607c..9885b17 100644
--- a/src/lib/emails/appointment_booked.ts
+++ b/src/lib/emails/appointment_booked.ts
@@ -11,6 +11,7 @@ export interface AppointmentBookedProps {
mentorName: string;
sessionId: string;
timezone: string;
+ link_params: string;
reschedule?: boolean;
}
@@ -23,6 +24,7 @@ export function appointment_booked(props: AppointmentBookedProps): EmailContent
mentorName: props.mentorName,
sessionId: props.sessionId,
timezone: props.timezone,
+ link_params: props.link_params,
reschedule: props.reschedule ? 'rescheduled' : ''
}),
html: render(AppointmentBooked, {
diff --git a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
index e8964d7..a307db9 100644
--- a/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
+++ b/src/routes/(authed)/dash/sessions/[sessionId]/+page.svelte
@@ -21,14 +21,6 @@
let hour: number = $state(0);
let minute: number = $state(0);
- function check_time(t: string) {
- const start_time = DateTime.fromISO(t);
- const now = DateTime.now();
- const interval = start_time.diff(now, 'hours');
-
- return interval.hours < 24;
- }
-
async function cancel() {
await fetch('?/cancel', {
method: 'POST',
@@ -39,19 +31,6 @@
await goto('/dash');
await invalidateAll();
}
-
- function reschedule_helper() {
- if (data.isMentor) {
- rescheduleOpen = true;
- } else {
- const queryParam = new URLSearchParams();
- queryParam.set('sessionId', data.sessionInfo.session.id);
- queryParam.set('reschedule', 'true');
- queryParam.set('type', data.sessionInfo.sessionType.id);
- goto(`/schedule?${queryParam.toString()}`);
- }
- }
-
async function reschedule() {
let udata = new URLSearchParams();
@@ -112,8 +91,8 @@
{data.sessionInfo.sessionType.category} - {data.sessionInfo.sessionType.name}
Duration: {data.sessionInfo.sessionType.length} minutes
- {#if data.isMentor || !check_time(data.sessionInfo.session.start)}
- Session Actions
+ {#if data.isMentor}
+ Mentor/Staff Actions
{
@@ -124,21 +103,18 @@
Cancel
{
+ rescheduleOpen = true;
+ }}
variant="danger"
>
Reschedule
- {:else}
-
- You cannot cancel or reschedule this session as it is less than 24 hours away. Please contact
- your instructor if you need to cancel or reschedule.
-
{/if}
-{#if data.isMentor || check_time(data.sessionInfo.session.start)}
+{#if data.isMentor}
{
cancelOpen = false;
@@ -151,15 +127,13 @@
}}
title="Confirm cancellation"
/>
- {#if data.isMentor}
-
-
-
- It is your responsibility to inform the student of the cancellation.
-
-
-
- {/if}
+
+
+
+ It is your responsibility to inform the student of the cancellation.
+
+
+
{
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index 291c625..d74f349 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -3,11 +3,11 @@ import { loadUserData } from '$lib/userInfo';
import { ROLE_DEVELOPER, ROLE_MENTOR, ROLE_STAFF, roleString } from '$lib/utils';
import { roleOf } from '$lib';
import { db } from '$lib/server/db';
-import { sessions, sessionTypes, users } from '$lib/server/db/schema';
+import { mentors, sessions, sessionTypes, students, users } from '$lib/server/db/schema';
import { eq, gte, or } from 'drizzle-orm';
import type { DayAvailability, MentorAvailability } from '$lib/availability';
import { DateTime, Duration, Interval } from 'luxon';
-import { fail } from '@sveltejs/kit';
+import { fail, redirect } from '@sveltejs/kit';
import { MAX_BOOKING_AHEAD_DAYS } from '$env/static/private';
import { ulid } from 'ulid';
import { appointment_booked } from '$lib/emails/appointment_booked';
@@ -177,7 +177,7 @@ function slottificate(
} else {
return 0;
}
- })
+ });
slotData[typ.id] = slots;
}
@@ -281,6 +281,7 @@ export const actions: Actions = {
duration,
sessionId: id,
type: typename,
+ link_params: `?sessionId=${id}?reschedule=true?type=${requestedType}`,
reschedule
});
const mentorEmailContent = new_session({
@@ -323,5 +324,25 @@ export const actions: Actions = {
mentorEmailContent.raw,
mentorEmailContent.html
);
+ },
+ cancel: async ({ cookies, request }) => {
+ const { user } = (await loadUserData(cookies))!;
+ const sessionList = await db
+ .select()
+ .from(sessions)
+ .leftJoin(sessionTypes, eq(sessionTypes.id, sessions.type))
+ .leftJoin(mentors, eq(mentors.id, sessions.mentor))
+ .leftJoin(students, eq(students.id, sessions.student))
+ .where(eq(sessions.id, request.sessionId));
+ const sessionAndFriends = sessionList[0];
+
+ if (
+ roleOf(user) < ROLE_STAFF &&
+ !(user.id == sessionAndFriends.session.student || user.id == sessionAndFriends.session.mentor)
+ ) {
+ redirect(307, '/schedule');
+ }
+
+ await db.delete(sessions).where(eq(sessions.id, request.sessionId));
}
};
diff --git a/src/routes/(authed)/schedule/+page.svelte b/src/routes/(authed)/schedule/+page.svelte
index 289cfcd..0c4cce0 100644
--- a/src/routes/(authed)/schedule/+page.svelte
+++ b/src/routes/(authed)/schedule/+page.svelte
@@ -9,6 +9,9 @@
import Button from '$lib/ui/Button.svelte';
import { DateTime, Interval } from 'luxon';
import { getTimeZones } from '@vvo/tzdb';
+ import Modal from '$lib/ui/modal/Modal.svelte';
+ import ModalHeader from '$lib/ui/modal/ModalHeader.svelte';
+ import ModalFooter from '$lib/ui/modal/ModalFooter.svelte';
interface Props {
data: PageData;
@@ -73,6 +76,23 @@
? `?sessionId=${data.originalSessionId}&reschedule=true&type=${data.originalSessionType}`
: '?';
+ let cancelOpen = $state(false);
+
+ async function cancel() {
+ let udata = new URLSearchParams();
+ udata.set('sessionId', data.originalSessionId);
+
+ await fetch('?/cancel', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ body: udata.toString()
+ });
+ await goto('/dash');
+ await invalidateAll();
+ }
+
async function book() {
if (!timeslot) return;
@@ -132,6 +152,16 @@
>
Next
+ {#if data.originalSessionType}
+ {
+ cancelOpen = true;
+ }}
+ variant="danger"
+ >
+ Cancel
+
+ {/if}
{/if}
{:else if step === 2}
@@ -270,4 +300,29 @@
>
+ {#if data.originalSessionType}
+ {
+ cancelOpen = false;
+ }}
+ bind:open={cancelOpen}
+ >
+ {
+ cancelOpen = false;
+ }}
+ title="Confirm cancellation"
+ />
+
+ {
+ cancelOpen = false;
+ }}
+ variant="ghost"
+ size="sm">Nevermind
+ Yes, cancel
+
+
+ {/if}
From 175b5ece0f21be2e45acaf0362e71a1a1c78f837 Mon Sep 17 00:00:00 2001
From: EMcNugget
Date: Wed, 20 Nov 2024 19:03:57 -0600
Subject: [PATCH 08/11] Added time checker
---
src/routes/(authed)/schedule/+page.svelte | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/routes/(authed)/schedule/+page.svelte b/src/routes/(authed)/schedule/+page.svelte
index 0c4cce0..699a250 100644
--- a/src/routes/(authed)/schedule/+page.svelte
+++ b/src/routes/(authed)/schedule/+page.svelte
@@ -78,6 +78,13 @@
let cancelOpen = $state(false);
+ function check_time(t: string) {
+ const start_time = DateTime.fromISO(t);
+ const now = DateTime.now();
+ const interval = start_time.diff(now, 'hours');
+ return interval.hours < 24;
+ }
+
async function cancel() {
let udata = new URLSearchParams();
udata.set('sessionId', data.originalSessionId);
@@ -300,7 +307,7 @@
>
- {#if data.originalSessionType}
+ {#if data.originalSessionType && check_time(timeslot)}
{
cancelOpen = false;
From f14adea644b1c7678b13336ca31059e0cca49750 Mon Sep 17 00:00:00 2001
From: EMcNugget
Date: Wed, 20 Nov 2024 22:52:50 -0600
Subject: [PATCH 09/11] Fixed schedule +page.server.ts
---
src/routes/(authed)/schedule/+page.server.ts | 2 +-
src/routes/(authed)/schedule/+page.svelte | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index d74f349..54a485a 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -218,7 +218,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
};
export const actions: Actions = {
- default: async ({ cookies, request, url }) => {
+ book: async ({ cookies, request, url }) => {
const { user } = (await loadUserData(cookies))!;
const formData = await request.formData();
diff --git a/src/routes/(authed)/schedule/+page.svelte b/src/routes/(authed)/schedule/+page.svelte
index 699a250..cc86dd1 100644
--- a/src/routes/(authed)/schedule/+page.svelte
+++ b/src/routes/(authed)/schedule/+page.svelte
@@ -73,8 +73,8 @@
let bookingState: 'success' | 'fail' | 'loading' = $state('loading');
const bookingUrl = data.originalSessionType
- ? `?sessionId=${data.originalSessionId}&reschedule=true&type=${data.originalSessionType}`
- : '?';
+ ? `?/book?sessionId=${data.originalSessionId}&reschedule=true&type=${data.originalSessionType}`
+ : '?/book';
let cancelOpen = $state(false);
From f3ee9030f9604f188d0ad94bdfb9cc01459c46d1 Mon Sep 17 00:00:00 2001
From: EMcNugget
Date: Sat, 23 Nov 2024 18:07:11 -0600
Subject: [PATCH 10/11] Fixed reschedule link
---
src/routes/(authed)/schedule/+page.server.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index 54a485a..5688a5b 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -281,7 +281,7 @@ export const actions: Actions = {
duration,
sessionId: id,
type: typename,
- link_params: `?sessionId=${id}?reschedule=true?type=${requestedType}`,
+ link_params: `?sessionId=${id}&reschedule=true&type=${requestedType}`,
reschedule
});
const mentorEmailContent = new_session({
From c905f81f45e3c459e76b4e8a20a4d663e387e466 Mon Sep 17 00:00:00 2001
From: core
Date: Sat, 23 Nov 2024 23:54:08 -0500
Subject: [PATCH 11/11] hot: resolve issues
---
.github/workflows/build.yml | 1 +
package.json | 3 +-
src/lib/emails/AppointmentBooked.svelte | 3 +-
src/lib/emails/AppointmentBooked.txt | 4 +-
src/lib/emails/appointment_booked.ts | 4 +-
src/routes/(authed)/schedule/+page.server.ts | 16 ++--
src/routes/(authed)/schedule/+page.svelte | 92 ++++++++++++--------
7 files changed, 75 insertions(+), 48 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cf16c34..1947731 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -27,6 +27,7 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
PUBLIC_SENTRY_DSN: ${{ secrets.PUBLIC_SENTRY_DSN }}
API_MASTER_KEY: ${{ secrets.API_MASTER_KEY }}
+ BASE_URL: ${{ vars.BASE_URL }}
steps:
- name: Checkout source
uses: actions/checkout@v4
diff --git a/package.json b/package.json
index cdab51a..33b2d5c 100644
--- a/package.json
+++ b/package.json
@@ -53,5 +53,6 @@
"sveltekit-superforms": "^2.20.0",
"tailwind-merge": "^2.5.4",
"ulid": "^2.3.0"
- }
+ },
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
diff --git a/src/lib/emails/AppointmentBooked.svelte b/src/lib/emails/AppointmentBooked.svelte
index 591c652..74c0e45 100644
--- a/src/lib/emails/AppointmentBooked.svelte
+++ b/src/lib/emails/AppointmentBooked.svelte
@@ -1,6 +1,7 @@
diff --git a/src/lib/emails/AppointmentBooked.txt b/src/lib/emails/AppointmentBooked.txt
index 8b92e2b..df00a83 100644
--- a/src/lib/emails/AppointmentBooked.txt
+++ b/src/lib/emails/AppointmentBooked.txt
@@ -8,9 +8,9 @@ This is your confirmation email for your upcoming session.
-- Duration: {duration} minutes
-- Mentor: {mentorName}
-Cancel/Reschedule
+Cancel/Reschedule: {rescheduleLink}
---
Confirmation ID {sessionId}.
-You are receiving this email because you have booked a session with the ZTL ARTCC. If you believe to have received this email in error, please contact wm@ztlartcc.org.
\ No newline at end of file
+You are receiving this email because you have booked a session with the ZTL ARTCC. If you believe to have received this email in error, please contact wm@ztlartcc.org.
diff --git a/src/lib/emails/appointment_booked.ts b/src/lib/emails/appointment_booked.ts
index 9885b17..712921a 100644
--- a/src/lib/emails/appointment_booked.ts
+++ b/src/lib/emails/appointment_booked.ts
@@ -3,6 +3,7 @@ import { type EmailContent, templateOut } from '$lib/email';
import plaintextTemplate from './AppointmentBooked.txt?raw';
import AppointmentBooked from './AppointmentBooked.svelte';
import { render } from 'svelte/server';
+import { BASE_URL } from '$env/static/private';
export interface AppointmentBookedProps {
startTime: DateTime;
@@ -25,7 +26,8 @@ export function appointment_booked(props: AppointmentBookedProps): EmailContent
sessionId: props.sessionId,
timezone: props.timezone,
link_params: props.link_params,
- reschedule: props.reschedule ? 'rescheduled' : ''
+ reschedule: props.reschedule ? 'rescheduled' : '',
+ reschedule_link: `${BASE_URL}schedule/${props.link_params}`
}),
html: render(AppointmentBooked, {
props: props
diff --git a/src/routes/(authed)/schedule/+page.server.ts b/src/routes/(authed)/schedule/+page.server.ts
index 5688a5b..4faf0b8 100644
--- a/src/routes/(authed)/schedule/+page.server.ts
+++ b/src/routes/(authed)/schedule/+page.server.ts
@@ -203,6 +203,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
: null;
const originalSessionId = url.searchParams.get('sessionId')!;
+ const ogSession = await db.select().from(sessions).where(eq(sessions.id, originalSessionId));
return {
user,
@@ -213,20 +214,23 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
sessionTypes: sTypes,
slotData,
originalSessionType,
- originalSessionId
+ originalSessionId,
+ ogSession
};
};
export const actions: Actions = {
- book: async ({ cookies, request, url }) => {
+ book: async ({ cookies, request }) => {
const { user } = (await loadUserData(cookies))!;
const formData = await request.formData();
const requestedSlotId = formData.get('timeslot')!;
const requestedType = formData.get('type')!;
const timezone = formData.get('timezone')!;
- const orginalSessionId = url.searchParams.get('sessionId')!;
- const reschedule = url.searchParams.has('reschedule')!;
+ const orginalSessionId = formData.get('sessionId');
+ const reschedule = formData.has('reschedule') || false;
+
+ console.log(formData);
const sTypes = await db.select().from(sessionTypes);
const mentors = await db
@@ -343,6 +347,8 @@ export const actions: Actions = {
redirect(307, '/schedule');
}
- await db.delete(sessions).where(eq(sessions.id, request.sessionId));
+ const formData = await request.formData();
+
+ await db.delete(sessions).where(eq(sessions.id, formData.get('sessionId')!.toString()));
}
};
diff --git a/src/routes/(authed)/schedule/+page.svelte b/src/routes/(authed)/schedule/+page.svelte
index cc86dd1..eb7e258 100644
--- a/src/routes/(authed)/schedule/+page.svelte
+++ b/src/routes/(authed)/schedule/+page.svelte
@@ -72,19 +72,23 @@
let bookingState: 'success' | 'fail' | 'loading' = $state('loading');
- const bookingUrl = data.originalSessionType
- ? `?/book?sessionId=${data.originalSessionId}&reschedule=true&type=${data.originalSessionType}`
- : '?/book';
-
- let cancelOpen = $state(false);
-
- function check_time(t: string) {
+ function check_time(t: string): boolean {
const start_time = DateTime.fromISO(t);
const now = DateTime.now();
const interval = start_time.diff(now, 'hours');
- return interval.hours < 24;
+ return interval.hours >= 24;
}
+ let canCancelReschedule = $derived.by(() => {
+ console.log(data.originalSessionType, data.ogSession);
+ if (data.originalSessionType && data.ogSession.length !== 0) {
+ return check_time(data.ogSession[0].start);
+ } else {
+ return true;
+ }
+ });
+ let cancelOpen = $state(false);
+
async function cancel() {
let udata = new URLSearchParams();
udata.set('sessionId', data.originalSessionId);
@@ -96,8 +100,8 @@
},
body: udata.toString()
});
- await goto('/dash');
await invalidateAll();
+ await goto('/');
}
async function book() {
@@ -106,17 +110,19 @@
step = 4;
bookingState = 'loading';
- let data = new URLSearchParams();
- data.set('timeslot', timeslot);
- data.set('type', sessionType!);
- data.set('timezone', timezone);
+ let rdata = new URLSearchParams();
+ rdata.set('timeslot', timeslot);
+ rdata.set('type', sessionType!);
+ rdata.set('timezone', timezone);
+ rdata.set('sessionId', data.originalSessionId);
+ rdata.set('reschedule', true);
- let r = await fetch(bookingUrl, {
+ let r = await fetch('?/book', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
- body: data.toString()
+ body: rdata.toString()
});
if (r.ok) {
bookingState = 'success';
@@ -137,11 +143,16 @@
- Schedule appointment at {PUBLIC_FACILITY_NAME}
+ {data.originalSessionType ? 'Reschedule' : 'Schedule'} appointment at {PUBLIC_FACILITY_NAME}
- {#if step === 1}
+ {#if !canCancelReschedule}
+
+ You cannot cancel or reschedule this session, as it is within 24 hours. Contact your
+ mentor.
+
+ {:else if step === 1}
{#each Object.entries(categories) as [k, v]}
@@ -159,16 +170,6 @@
>
Next
- {#if data.originalSessionType}
- {
- cancelOpen = true;
- }}
- variant="danger"
- >
- Cancel
-
- {/if}
{/if}
{:else if step === 2}
@@ -177,7 +178,11 @@
{/each}
{#if sessionType}
-
+
{#each data.slotData[sessionType] as slot}
{Interval.fromISO(slot.slot)
@@ -191,15 +196,25 @@
{/if}
- {
- step = 1;
- }}
- >
- Back
-
+ {#if data.originalSessionType}
+ {
+ cancelOpen = true;
+ }}>Cancel Appointment
+ {:else}
+ {
+ step = 1;
+ }}
+ >
+ Back
+
+ {/if}
{#if timeslot !== null}
- {#if data.originalSessionType && check_time(timeslot)}
+ {#if data.originalSessionType}
{
cancelOpen = false;
@@ -320,6 +335,7 @@
}}
title="Confirm cancellation"
/>
+ You'll need to re-book another session.
{