From b84e23ff0667466d59dd8b947ea7d0c48e5d0081 Mon Sep 17 00:00:00 2001 From: CaiCheng-Li Date: Mon, 19 Aug 2024 09:36:02 -0400 Subject: [PATCH 1/9] DashboardOverHaul - Not Working --- package-lock.json | 26 +++ package.json | 1 + src/components/projectCard.svelte | 2 +- src/components/toasts/failToast.ts | 9 + src/config.ts | 28 ++- src/routes/(app)/dashboard/+page.server.ts | 178 ++++++++++++++++-- src/routes/(app)/dashboard/+page.svelte | 162 +++++++++++++++- .../(app)/dashboard/admin/+page.server.ts | 10 - src/routes/(app)/dashboard/admin/+page.svelte | 5 - .../(app)/dashboard/lead/+page.server.ts | 23 --- src/routes/(app)/dashboard/lead/+page.svelte | 49 ----- .../(app)/dashboard/teamlead/+page.server.ts | 9 - .../(app)/dashboard/teamlead/+page.svelte | 5 - src/routes/(main)/forgotPass/+page.server.ts | 2 +- src/routes/+layout.svelte | 3 + src/routes/api/members/+server.ts | 2 +- 16 files changed, 381 insertions(+), 133 deletions(-) create mode 100644 src/components/toasts/failToast.ts delete mode 100644 src/routes/(app)/dashboard/admin/+page.server.ts delete mode 100644 src/routes/(app)/dashboard/admin/+page.svelte delete mode 100644 src/routes/(app)/dashboard/lead/+page.server.ts delete mode 100644 src/routes/(app)/dashboard/lead/+page.svelte delete mode 100644 src/routes/(app)/dashboard/teamlead/+page.server.ts delete mode 100644 src/routes/(app)/dashboard/teamlead/+page.svelte diff --git a/package-lock.json b/package-lock.json index e9ae7f80..ec4555ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "svelte-stripe": "^1.1.4" }, "devDependencies": { + "@floating-ui/dom": "1.6.10", "@skeletonlabs/skeleton": "^2.0.0", "@skeletonlabs/tw-plugin": "^0.1.0", "@sveltejs/adapter-auto": "^2.0.0", @@ -430,6 +431,31 @@ "node": ">=14" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", + "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", + "dev": true, + "dependencies": { + "@floating-ui/utils": "^0.2.7" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", + "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", + "dev": true, + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.7" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", + "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==", + "dev": true + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", diff --git a/package.json b/package.json index 21441ddc..51debd24 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-node": "1.2.4", "@sveltejs/kit": "^1.5.0", + "@floating-ui/dom": "1.6.10", "@tailwindcss/forms": "^0.5.6", "@types/bcrypt": "^5.0.0", "@types/identicon.js": "^2.3.1", diff --git a/src/components/projectCard.svelte b/src/components/projectCard.svelte index 49438e31..fd0d5bce 100644 --- a/src/components/projectCard.svelte +++ b/src/components/projectCard.svelte @@ -5,7 +5,7 @@ let hover = false; - +
{ diff --git a/src/components/toasts/failToast.ts b/src/components/toasts/failToast.ts new file mode 100644 index 00000000..b51fb653 --- /dev/null +++ b/src/components/toasts/failToast.ts @@ -0,0 +1,9 @@ +import { getToastStore, type ToastSettings } from '@skeletonlabs/skeleton'; + +export default (message: string) => { + const ts = { + message: message, + background: 'variant-filled-error' + } satisfies ToastSettings; + getToastStore().trigger(ts); +}; diff --git a/src/config.ts b/src/config.ts index 77d69dbf..dd803ba2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -33,20 +33,28 @@ export default { * when roles in the future etc. Treasurser, come in to play, they can have a permission level to themselves, or even */ roles: { - officer: { - level: 10, - name: 'officer' + admin:{ + level: 5, + name: 'admin' }, - lead: { - level: 8, - name: 'lead' + president:{ + level: 5, + name: 'president' + }, + officers: { + level: 4, + name: 'officers' }, - committee: { - level: 6, - name: 'committee' + project_lead:{ + level: 3, + name: 'project lead' + }, + team_lead: { + level: 2, + name: 'lead' }, member: { - level: 4, + level: 1, name: 'member' }, guest: { diff --git a/src/routes/(app)/dashboard/+page.server.ts b/src/routes/(app)/dashboard/+page.server.ts index ff15a236..456b06c5 100644 --- a/src/routes/(app)/dashboard/+page.server.ts +++ b/src/routes/(app)/dashboard/+page.server.ts @@ -1,7 +1,7 @@ import { db } from '$lib/db'; import { z } from 'zod'; -import type { Actions, PageServerLoad } from './$types'; -import { superValidate } from 'sveltekit-superforms/server'; +import type { Actions, PageData, PageServerLoad } from './$types'; +import { setError, superValidate } from 'sveltekit-superforms/server'; import semesterYear from '../../../components/scripts/semesterYear'; import config from '../../../config'; @@ -10,10 +10,48 @@ const updateDuesSchema = z.object({ duesType: z.number() }); +const presidentSchema = z.object({ + presidentId: z.string().optional(), +}); + +const adminSchema = z.object({ + adminId: z.string().optional(), +}); + export const load: PageServerLoad = async ({ locals }) => { const form = await superValidate(updateDuesSchema); + const form1 = await superValidate(presidentSchema); + const form2 = await superValidate(adminSchema); const dateInfo = semesterYear(); + const currentPresident = await db.member.findFirst({ + where: { + role: { + name: 'president' + } + } + }); + + const currentAdmin = await db.member.findFirst({ + where: { + role: { + name: 'admin' + } + } + }); + + const members = await db.member.findMany({ + where: { + email: {not: locals.member?.email} + }, + select: { + id: true, + discordProfileName: true, + firstName: true, + lastName: true + } + }); + const availableProjects = await db.project.findMany({ where: { year: dateInfo.year, @@ -43,19 +81,13 @@ export const load: PageServerLoad = async ({ locals }) => { } } }, - Survey: true // Include the survey information + Survey: true, + role: true, } }); - // Log the DateUpdated value from the user's survey const surveyDateUpdated = user?.Survey?.DateUpdated; - // if (surveyDateUpdated) { - // console.log(`Survey Date Updated: ${surveyDateUpdated}`); - // } else { - // console.log('Survey Date Updated not found'); - // } - // Remove projects the user is already part of for (let i = 0; i < availableProjects.length; i++) { for (const element of user!.Projects) { if (availableProjects[i].id == element.id) { @@ -64,25 +96,25 @@ export const load: PageServerLoad = async ({ locals }) => { } } - return { user, form, availableProjects, surveyDateUpdated }; + return { user, form, availableProjects, surveyDateUpdated, form1, form2, currentPresident, members, currentAdmin}; }; export const actions: Actions = { - summerRole: async ({ request, locals }) => { + summerRole: async ({ request }) => { const form = await request.formData(); const id = form.get('id')?.toString(); if (id) { const currentYear = new Date().getFullYear(); - const august = new Date(currentYear, 7, 1); // August 1st - const dayOfWeek = august.getDay(); // Day of the week of August 1st - const firstDayOfFourthWeek = 22 + (7 - dayOfWeek) % 7; // Calculate the first day of the fourth week of August + const august = new Date(currentYear, 7, 1); + const dayOfWeek = august.getDay(); + const firstDayOfFourthWeek = 22 + (7 - dayOfWeek) % 7; await db.member.update({ where: { id: id }, data: { - membershipExpDate: new Date(currentYear, 7, firstDayOfFourthWeek), // Set the calculated date + membershipExpDate: new Date(currentYear, 7, firstDayOfFourthWeek), role: { connectOrCreate: { create: { @@ -98,7 +130,7 @@ export const actions: Actions = { }); } }, - + joinProject: async ({ request, locals }) => { const form = await request.formData(); const id = Number(form.get('projectID')); @@ -115,5 +147,117 @@ export const actions: Actions = { } } }); + }, + + changePresident: async ({ request }) => { + const formData = await request.formData(); + const form1 = await superValidate(formData, presidentSchema); + + if (!form1.valid) { + return setError(form1, 'presidentId', 'Invalid president selection.'); + } + + const newPresidentId = form1.data.presidentId; + + if (newPresidentId) { + const transaction = await db.$transaction(async (tx) => { + const currentPresident = await tx.member.findFirst({ + where: { + role: { + name: 'president' + } + } + }); + + if (currentPresident && currentPresident.id !== newPresidentId) { + await tx.member.update({ + where: { + id: currentPresident.id + }, + data: { + role: { + connect: { + name: 'member' + } + } + } + }); + } + + await tx.member.update({ + where: { + id: newPresidentId + }, + data: { + role: { + connect: { + name: 'president' + } + } + } + }); + }); + form1.message = 'OK'; + return { form1 }; + }else{ + form1.message = 'NO'; + return { form1 }; + } + }, + + changeAdmin: async ({ request }) => { + const formData = await request.formData(); + const form2 = await superValidate(formData, adminSchema); + + if (!form2.valid) { + return setError(form2, 'adminId', 'Invalid admin selection.'); + } + + const newAdminId = form2.data.adminId; + + if (newAdminId) { + const transaction = await db.$transaction(async (tx) => { + const currentAdmin = await tx.member.findFirst({ + where: { + role: { + name: 'admin' + } + } + }); + + if (currentAdmin && currentAdmin.id !== newAdminId) { + await tx.member.update({ + where: { + id: currentAdmin.id + }, + data: { + role: { + connect: { + name: 'member' + } + } + } + }); + } + + await tx.member.update({ + where: { + id: newAdminId + }, + data: { + role: { + connect: { + name: 'admin' + } + } + } + }); + }); + form2.message = 'OK'; + return { form2 }; + }else{ + form2.message = 'NO'; + return { form2 }; + } } }; diff --git a/src/routes/(app)/dashboard/+page.svelte b/src/routes/(app)/dashboard/+page.svelte index baf609f2..4651c756 100644 --- a/src/routes/(app)/dashboard/+page.svelte +++ b/src/routes/(app)/dashboard/+page.svelte @@ -4,7 +4,7 @@ AppShell, type DrawerSettings, getDrawerStore, - modeCurrent + modeCurrent, } from '@skeletonlabs/skeleton'; import type { PageServerData } from './$types'; import { superForm } from 'sveltekit-superforms/client'; @@ -13,12 +13,18 @@ import RightSideBar from '../../../components/dashboard/rightSidebar/rightSideBar.svelte'; import Payments from '../../../components/stripe/payments.svelte'; import { enhance } from '$app/forms'; + import { Autocomplete, popup } from '@skeletonlabs/skeleton'; + import type { AutocompleteOption, PopupSettings } from '@skeletonlabs/skeleton'; + import successToast from '../../../components/toasts/successToast'; + import failToast from '../../../components/toasts/failToast'; export let data: PageServerData; - const { form, errors, constraints } = superForm(data.form, { + const { form, errors, constraints, message } = superForm(data.form, { clearOnSubmit: 'errors-and-message' }); + const drawerStore = getDrawerStore(); + const drawerSettingsLeft: DrawerSettings = { id: 'dashboard1', meta: { @@ -26,6 +32,7 @@ teams: data.user?.Teams } }; + const drawerSettingsRight: DrawerSettings = { id: 'dashboard2', position: 'right', @@ -55,8 +62,57 @@ break; } } + + let selectedMemberId: string | null = null; + let change: boolean | null = false; + let input: string = ''; + + let popupSettings: PopupSettings = { + event: 'focus-click', // Trigger popup on focus and click + target: 'popupAutocomplete', // The ID to target for the popup + placement: 'bottom' // Position the popup below the input + }; + + // Map members from the database for autocomplete options + const memberOptions: AutocompleteOption[] = data.members.map(member => ({ + label: `${member.firstName} ${member.lastName}`, + value: member.id, + keywords: `${member.firstName} ${member.lastName}, ${member.discordProfileName}`, + meta: {} + })); + + // Handle member selection from autocomplete + function onMemberSelection(event: CustomEvent): void { + input = event.detail.label; // Update input field with selected member's name + selectedMemberId = event.detail.value as string | null; // Store selected member ID + console.log(selectedMemberId); + } + + let selectedAdminId: string | null = null; + let changeAdmin: boolean | null = false; + let adminInput: string = ''; + +function onAdminSelection(event: CustomEvent): void { + adminInput = event.detail.label; // Update input field with selected admin's name + selectedAdminId = event.detail.value as string | null; // Store selected admin ID + console.log(selectedAdminId); +} + +$: if ($message === 'OK') { + successToast('Configuration Updated Successfully!'); +}else if ($message === 'NO') { + failToast('Error 404, Member Not Found'); +} + + + @@ -144,6 +200,108 @@ {#if !((data.user?.membershipExpDate.getTime() ?? 0) < new Date().getTime())}
{/if} + + {#if data.user?.role.permissionLevel > 1} +
+
+

+ {data.user?.role?.name?.split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ')} Dashboard +

+ + {#if data.user?.role.permissionLevel >= 5} +
+ {#if data.user?.role.name === "admin"} +
+
+ Configure President Position +
+ {#if data.currentPresident && !change} +

Current President: {data.currentPresident.firstName} {data.currentPresident.lastName}

+ + {/if} + + {#if !data.currentPresident || change} + +
+ +
+
change = false}> + + +
+ {/if} +
+ {/if} + {#if data.user?.role.name === "president"} +
+
Configure Admin Position
+ + {#if data.currentAdmin && !changeAdmin} +

Current Admin: {data.currentAdmin.firstName} {data.currentAdmin.lastName}

+ + {/if} + + {#if !data.currentAdmin || changeAdmin} + +
+ +
+
changeAdmin = false}> + + +
+ {/if} +
+ {/if} + + {/if} + + {#if data.user?.role.permissionLevel >= 4} +
+
+ Configure Project Leads +
+ {/if} + + {#if data.user?.role.permissionLevel >= 3} +
+
+ Configure Teams & Team Leads +
+ {/if} + + {#if data.user?.role.permissionLevel >= 2} +
+
+ Configure Teams +
+ {/if} +
+
+ {/if} + {#if (data.user?.membershipExpDate.getTime() ?? 0) > new Date().getTime()} {/if} diff --git a/src/routes/(app)/dashboard/admin/+page.server.ts b/src/routes/(app)/dashboard/admin/+page.server.ts deleted file mode 100644 index e18d4ff3..00000000 --- a/src/routes/(app)/dashboard/admin/+page.server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; - -export const load = (async ({ locals }) => { - if (locals.member.permissions.level <= 5) { - // lowwer than a committee member - throw redirect(302, '/'); - } - return {}; -}) satisfies PageServerLoad; diff --git a/src/routes/(app)/dashboard/admin/+page.svelte b/src/routes/(app)/dashboard/admin/+page.svelte deleted file mode 100644 index dbaddd9c..00000000 --- a/src/routes/(app)/dashboard/admin/+page.svelte +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/src/routes/(app)/dashboard/lead/+page.server.ts b/src/routes/(app)/dashboard/lead/+page.server.ts deleted file mode 100644 index 421265a8..00000000 --- a/src/routes/(app)/dashboard/lead/+page.server.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { redirect, type Actions } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; -import { z } from 'zod'; -import { superValidate } from 'sveltekit-superforms/server'; - -export const load = (async ({ locals }) => { - const form = superValidate(blogpostSchema); - if (locals.member.permissions.level < 8) { - throw redirect(302, '/'); - } - return { form }; -}) satisfies PageServerLoad; - -const blogpostSchema = z.object({ - title: z.string().max(32, 'Titles cannot be more than 32 characters!'), - blogpost: z.string(), - picture: z.string() // string that represents the url of the picture -}); -export const actions: Actions = { - blogPost: async ({ locals, request }) => { - const form = superValidate(request, blogpostSchema); - } -}; diff --git a/src/routes/(app)/dashboard/lead/+page.svelte b/src/routes/(app)/dashboard/lead/+page.svelte deleted file mode 100644 index 101d177d..00000000 --- a/src/routes/(app)/dashboard/lead/+page.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -
-

Create new Blogpost

- -
-
- -
- + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+
+
+ \ No newline at end of file diff --git a/src/routes/(app)/dashboard/edit-projects/+page.server.ts b/src/routes/(app)/dashboard/edit-projects/+page.server.ts new file mode 100644 index 00000000..d92340d9 --- /dev/null +++ b/src/routes/(app)/dashboard/edit-projects/+page.server.ts @@ -0,0 +1,74 @@ +import { z } from "zod"; +import type { Actions, PageServerLoad } from "./$types"; +import { setError, superValidate } from "sveltekit-superforms/server"; +import { fail, redirect } from "@sveltejs/kit"; +import { db } from "$lib/db"; +import { prisma } from "$lib/server/prisma"; + +let pLevel = 0; + +const editProSchema = z.object({ + title: z.string().min(1, "Title is required."), + description: z.string().optional(), + docsLink: z.string().url("Invalid URL format."), + season: z.enum(["Fall", "Spring", "Summer"]), + year: z.string().regex(/^\d{4}$/, "Year must be a 4-digit number."), + logo: z.string().url("Invalid URL format."), + Skills: z.string().array().optional(), + id: z.number() +}); + +export const load: PageServerLoad = async ({ parent }) => { + const data = await parent(); + pLevel = data.member!.role.permissionLevel; + // check user permission level + if (!(pLevel > 3)) { + throw redirect(302, '/dashboard'); + } + + // Fetch all projects + const allProjects = await prisma.project.findMany({ + // You can include other relations if needed, like `member` or `tasks` + }); + + const form = await superValidate(editProSchema); + + const currentProject = await db.project.findUnique({ + where: {id: form.data.id} + }) + + return { + form, currentProject, + member: { + Projects: allProjects // Pass all projects to the frontend + } + }; +}; + +// Handle form submission +export const actions: Actions = { + selectProject: async ({ request }) => { + // Parse the form data + const form = await superValidate(request, editProSchema); + + // If form validation fails, return with errors + if (!form.valid) { + return fail(400, { form }); + } + + const projectId = form.data.id; + console.log(projectId); + + // + }, + + updateProject: async ({ request }) => { + // Parse the form data + const form = await superValidate(request, editProSchema); + + // If form validation fails, return with errors + if (!form.valid) { + return fail(400, { form }); + } + } +}; \ No newline at end of file diff --git a/src/routes/(app)/dashboard/edit-projects/+page.svelte b/src/routes/(app)/dashboard/edit-projects/+page.svelte new file mode 100644 index 00000000..fabe7351 --- /dev/null +++ b/src/routes/(app)/dashboard/edit-projects/+page.svelte @@ -0,0 +1,178 @@ + + +
+ +
+
+

Edit a Project

+ {#if !data.currentProject} +
+

Select a Project

+ + +
+ +
+ + +
+ {/if} + {#if data.currentProject} +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ {/if} +
+
+ \ No newline at end of file diff --git a/src/routes/(app)/dashboard/profile/update survey/+page.svelte b/src/routes/(app)/dashboard/profile/update survey/+page.svelte index 874e056d..5fd55b4f 100644 --- a/src/routes/(app)/dashboard/profile/update survey/+page.svelte +++ b/src/routes/(app)/dashboard/profile/update survey/+page.svelte @@ -1,11 +1,4 @@ @@ -290,17 +290,24 @@ $: if ($message === 'OK') { {/if} {#if data.user?.role.permissionLevel >= 3} +

Configure Teams & Team Leads
+

Create, Edit, or Mange a Team

+
Create Team + Edit Team {/if} {#if data.user?.role.permissionLevel >= 2} +

Configure Teams
+

Appoint Members to a Team

+ Appoint to Team {/if}
diff --git a/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts b/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts new file mode 100644 index 00000000..8e1c366c --- /dev/null +++ b/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts @@ -0,0 +1,79 @@ +import { z } from "zod"; +import type { Actions, PageServerLoad } from "./$types"; +import { superValidate } from "sveltekit-superforms/server"; +import { fail, redirect } from "@sveltejs/kit"; +import { db } from "$lib/db"; + +let pLevel = 0; + +const appointTeamSchema = z.object({ + members: z.string().array(), + teamId: z.string(), +}); + +export const load: PageServerLoad = async ({ parent, locals }) => { + const data = await parent(); + pLevel = data.member!.role.permissionLevel; + // check user permission level + if (!(pLevel > 1)) { + throw redirect(302, '/dashboard'); + } + + const teams = await db.team.findMany({ + where:{ + teamLead: { + firstName: locals.member.fname, + }, + }, + select:{ + id: true, + name: true, + teamLead: true, + Project: true, + } + }) + + const members = await db.member.findMany({ + where: { + email: {not: locals.member?.email} + }, + select: { + id: true, + discordProfileName: true, + firstName: true, + lastName: true + } + }); + + const form = await superValidate(appointTeamSchema); + return { form, teams, members }; +}; + +export const actions: Actions = { + default: async({ request }) => { + const form = await superValidate(request, appointTeamSchema); + // Validating forms + if (!form.valid) { + return fail(400, { form }); + } + + for(let i = 0; i < form.data.members.length; i++){ + console.log(form.data.members[i]); + console.log(form.data.teamId); + await db.team.update({ + where: { + id: form.data.teamId, + }, + data: { + members: { + connect:{ + id: form.data.members[i] + } + } + } + }) + } + + throw redirect(302, '/dashboard'); + } +}; diff --git a/src/routes/(app)/dashboard/appoint-to-team/+page.svelte b/src/routes/(app)/dashboard/appoint-to-team/+page.svelte new file mode 100644 index 00000000..0e5210f7 --- /dev/null +++ b/src/routes/(app)/dashboard/appoint-to-team/+page.svelte @@ -0,0 +1,119 @@ + + + + +
+ +
+
+
+

Appoint Members to a Team

+ +
+ + +
+ Select Members + + +
+ +
+ + + + +
+
diff --git a/src/routes/(app)/dashboard/create-team/+page.server.ts b/src/routes/(app)/dashboard/create-team/+page.server.ts new file mode 100644 index 00000000..a9392b44 --- /dev/null +++ b/src/routes/(app)/dashboard/create-team/+page.server.ts @@ -0,0 +1,91 @@ +import { z } from "zod"; +import type { Actions, PageServerLoad } from "./$types"; +import { superValidate } from "sveltekit-superforms/server"; +import { fail, redirect } from "@sveltejs/kit"; +import { db } from "$lib/db"; + +let pLevel = 0; + +const createTeamSchema = z.object({ + projectId: z.number(), + name: z.string(), + teamLead: z.string(), +}); + +export const load: PageServerLoad = async ({ parent, locals }) => { + const data = await parent(); + pLevel = data.member!.role.permissionLevel; + // check user permission level + if (!(pLevel > 2)) { + throw redirect(302, '/dashboard'); + } + + const members = await db.member.findMany({ + where: { + email: {not: locals.member?.email} + }, + select: { + id: true, + discordProfileName: true, + firstName: true, + lastName: true + } + }); + + const projects = await db.project.findMany({ + select: { + id: true, + title: true, + season: true, + year: true, + projectType: true, + } + }); + + const form = await superValidate(createTeamSchema); + return { form, projects, members }; +}; + +export const actions: Actions = { + default: async({ request }) => { + const form = await superValidate(request, createTeamSchema); + // Validating forms + if (!form.valid) { + return fail(400, { form }); + } + + await db.team.create({ + data:{ + name: form.data.name, + teamLead: { + connect: { + id: form.data.teamLead, + } + }, + Project: { + connect: { + id: form.data.projectId, + } + }, + // to be changed later down the line + maxMembers: 0, + minMembers: 0, + } + }); + + //update member acess + await db.member.update({ + where:{ + id: form.data.teamLead, + }, + data:{ + role: { + connect: { + name: 'team lead', + } + } + } + }) + throw redirect(302, '/dashboard'); + } +}; diff --git a/src/routes/(app)/dashboard/create-team/+page.svelte b/src/routes/(app)/dashboard/create-team/+page.svelte new file mode 100644 index 00000000..babe4fd9 --- /dev/null +++ b/src/routes/(app)/dashboard/create-team/+page.svelte @@ -0,0 +1,143 @@ + + + + +
+ +
+
+
+

Create a Team

+ +
+ + +
+ + +
+ + + + +
+
+
+ \ No newline at end of file From ffec992f633b3c0c79e506abecfac2a57d16474d Mon Sep 17 00:00:00 2001 From: CaiCheng-Li Date: Sun, 25 Aug 2024 19:30:36 -0400 Subject: [PATCH 4/9] working dashboard - creation --- .../dashboard/appoint-to-team/+page.server.ts | 36 ++++++----- .../dashboard/appoint-to-team/+page.svelte | 14 +++-- .../dashboard/create-project/+page.server.ts | 58 ++++++++++++++++-- .../dashboard/create-project/+page.svelte | 59 ++++++++++++++++++- 4 files changed, 139 insertions(+), 28 deletions(-) diff --git a/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts b/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts index 8e1c366c..ac3cd815 100644 --- a/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts +++ b/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts @@ -56,23 +56,29 @@ export const actions: Actions = { if (!form.valid) { return fail(400, { form }); } + console.log("Received Team ID:", form.data.teamId); // Verify received teamId + console.log("Received Members:", form.data.members); // Verify received members - for(let i = 0; i < form.data.members.length; i++){ - console.log(form.data.members[i]); - console.log(form.data.teamId); - await db.team.update({ - where: { - id: form.data.teamId, + // Split the string of member IDs into an array + const memberIdsArray = form.data.members[0].split(','); + + // Map the array of member IDs to the format required by Prisma + const memberConnections = memberIdsArray.map((memberId) => ({ + id: memberId.trim(), + })); + + // Update the team with the connected members + await db.team.update({ + where: { + id: form.data.teamId, + }, + data: { + members: { + connect: memberConnections, }, - data: { - members: { - connect:{ - id: form.data.members[i] - } - } - } - }) - } + }, + }); + throw redirect(302, '/dashboard'); } diff --git a/src/routes/(app)/dashboard/appoint-to-team/+page.svelte b/src/routes/(app)/dashboard/appoint-to-team/+page.svelte index 0e5210f7..a265d24a 100644 --- a/src/routes/(app)/dashboard/appoint-to-team/+page.svelte +++ b/src/routes/(app)/dashboard/appoint-to-team/+page.svelte @@ -6,7 +6,7 @@ import { injectDots } from "../../../../components/pixijs/dotsAnimation"; import { Autocomplete, popup } from '@skeletonlabs/skeleton'; import type { AutocompleteOption, PopupSettings } from '@skeletonlabs/skeleton'; - import { append } from "svelte/internal"; + import { append } from "svelte/internal"; export let data: PageServerData; const { form, errors, constraints, enhance, message } = superForm(data.form, { @@ -23,7 +23,7 @@ let memInput: string = ''; let inputList: string[] = []; // Initialize as an empty array let memIdList: string[] = []; // Initialize as an empty array - let teamId: string | unknown = ''; + let teamId: string = ''; let popupSettingsTeam: PopupSettings = { event: 'focus-click', target: 'popupAutocompleteTeam', @@ -40,16 +40,17 @@ const memberOptions: AutocompleteOption[] = data.members.map(member => ({ label: `${member.firstName} ${member.lastName}`, value: member.id, - keywords: `${member.firstName} ${member.lastName}, ${member.discordProfileName}`, + keywords: `${member.firstName} ${member.lastName}, ${member.firstName}, ${member.lastName}, ${member.discordProfileName}`, meta: {} })); function onTeamSelection(event: CustomEvent): void { - input = event.detail.label; // Update input field with selected admin's name - teamId = event.detail.value; - console.log(inputList) + input = event.detail.label; + teamId = event.detail.value as string; + // console.log("Selected Team ID:", teamId); // Debugging line } + function onMemberSelection(event: CustomEvent): void { memInput = event.detail.label; // Update input field with selected admin's name memIdList = [...memIdList, event.detail.value as string]; @@ -88,6 +89,7 @@ name="autocomplete-search-lead" bind:value={input} placeholder="Search..." + autocomplete="off" use:popup={popupSettingsTeam} />
diff --git a/src/routes/(app)/dashboard/create-project/+page.server.ts b/src/routes/(app)/dashboard/create-project/+page.server.ts index fd88a2bb..b9e14c56 100644 --- a/src/routes/(app)/dashboard/create-project/+page.server.ts +++ b/src/routes/(app)/dashboard/create-project/+page.server.ts @@ -14,9 +14,10 @@ const createProSchema = z.object({ year: z.string(), logo: z.string(), Skills: z.string().array(), + proLeadID: z.string() }); -export const load: PageServerLoad = async ({ parent }) => { +export const load: PageServerLoad = async ({ parent, locals }) => { const data = await parent(); pLevel = data.member!.role.permissionLevel; // check user permission level @@ -24,8 +25,20 @@ export const load: PageServerLoad = async ({ parent }) => { throw redirect(302, '/dashboard'); } + const members = await db.member.findMany({ + where: { + email: {not: locals.member?.email}, + }, + select: { + id: true, + discordProfileName: true, + firstName: true, + lastName: true + } + }); + const form = await superValidate(createProSchema); - return { form }; + return { form, members }; }; export const actions: Actions = { @@ -47,6 +60,9 @@ export const actions: Actions = { if (vaildTitle === ''){ return setError(form, 'title', 'Please Enter a Title'); } + if (form.data.proLeadID === ''){ + return setError(form, 'proLeadID', "Please Select a Project Lead") + } if (vaildLogo === ''){ return setError(form, 'logo', 'Please Enter a Logo-Link'); } @@ -59,13 +75,20 @@ export const actions: Actions = { if (selectedyear === '' || !yearNum) { return setError(form, 'year', 'Please Enter a Valid Year'); } - + console.log("raw skills: ", form.data.Skills); + const skillsArray = form.data.Skills[0].split(','); + console.log("split skills: ", skillsArray); // Creating survey entry in the database await db.project.create({ data: { title: form.data.title, description: form.data.description, + projectLead: { + connect: { + id: form.data.proLeadID, + } + }, logo: { create: { data: form.data.logo, @@ -74,13 +97,38 @@ export const actions: Actions = { docsLink: form.data.docsLink, season: form.data.season, year: yearNum, - Skills: form.data.Skills, + Skills: skillsArray, budget: 0, remainingFunds: 0, - } }); + //update permisson level if insufficent + const lead = await db.member.findFirst({ + where:{ + id: form.data.proLeadID, + }, + select: { + role: true, + } + }) + + if (lead?.role.permissionLevel && lead?.role.permissionLevel < 3){ + await db.member.update({ + where: { + id: form.data.proLeadID, + }, + data: { + role: { + connect: { + name: "project lead", + } + } + } + }) + } + + throw redirect(302, '/dashboard'); } }; diff --git a/src/routes/(app)/dashboard/create-project/+page.svelte b/src/routes/(app)/dashboard/create-project/+page.svelte index 970bc39f..81348e3f 100644 --- a/src/routes/(app)/dashboard/create-project/+page.svelte +++ b/src/routes/(app)/dashboard/create-project/+page.svelte @@ -4,6 +4,8 @@ import type { PageData } from './$types'; import { onMount } from "svelte"; import { injectDots } from "../../../../components/pixijs/dotsAnimation"; + import { Autocomplete, popup } from '@skeletonlabs/skeleton'; + import type { AutocompleteOption, PopupSettings } from '@skeletonlabs/skeleton'; export let data: PageData; const { form, errors, constraints, enhance, message } = superForm(data.form, { @@ -15,7 +17,37 @@ onMount(() => { injectDots(mainEle, 200); }); - + + let input: string = ''; + let leadId: string = ''; + let skills: string[] = []; + let popupSettings: PopupSettings = { + event: 'focus-click', + target: 'popupAutocompleteTeam', + placement: 'bottom', + }; + + const leadOptions: AutocompleteOption[] = data.members.map(member => ({ + label: `${member.firstName} ${member.lastName}`, + value: member.id, + keywords: `${member.firstName} ${member.lastName}, ${member.firstName}, ${member.lastName}, ${member.discordProfileName}`, + meta: {} + })); + + function onLeadSelection(event: CustomEvent): void { + input = event.detail.label; + leadId = event.detail.value as string; + } + + + + +
+ +
+
+ +
From 73980d422db7c455a91f10e8089a1cf0bd78a3ea Mon Sep 17 00:00:00 2001 From: CaiCheng-Li Date: Wed, 28 Aug 2024 09:51:42 -0400 Subject: [PATCH 5/9] restore update --- src/components/stripe/payments.svelte | 52 +++- src/routes/(app)/dashboard/+page.svelte | 278 +++++++++--------- .../dashboard/create-project/+page.server.ts | 4 +- .../(app)/dashboard/survey/+page.server.ts | 2 +- .../(app)/dashboard/survey/+page.svelte | 2 +- src/routes/create-payment-intent/+server.ts | 31 +- src/stores.ts | 3 +- 7 files changed, 203 insertions(+), 169 deletions(-) diff --git a/src/components/stripe/payments.svelte b/src/components/stripe/payments.svelte index f8c26207..ae2e43ab 100644 --- a/src/components/stripe/payments.svelte +++ b/src/components/stripe/payments.svelte @@ -12,7 +12,8 @@ let processing = false; export let userID: string; let thm: 'night' | 'stripe' | 'flat' | undefined = 'night'; - let duesSelection = '1'; + let duesSelection = ''; + let hide: boolean = false; const appearance = { theme: thm, @@ -22,17 +23,17 @@ onMount(async () => { stripe = await loadStripe(PUBLIC_STRIPE_KEY); - // create payment intent server side - clientSecret = await createPayment(); + // clientSecret = await createPayment(); }); async function createPayment() { + const response = await fetch('/create-payment-intent', { method: 'POST', headers: { 'content-type': 'application/json' }, - body: JSON.stringify({}) + body: JSON.stringify({ duesType: duesSelection }) }); const { clientSecret } = await response.json(); @@ -62,20 +63,44 @@ } } } + + function reloadPage(){ + location.reload(); + } + + async function test(){ + // create payment intent server side + clientSecret = await createPayment(); + hide = true; + } - {#if error}

Please try again

{error.message}

{/if} +{#if !hide} + +{:else} + {#if duesSelection == '1'} + Semesterly Dues + + + {/if} + {#if duesSelection == '2'} + Yearly Dues + + + {/if} +{/if} + {#if duesSelection} {#if clientSecret && stripe} diff --git a/src/routes/(app)/dashboard/+page.svelte b/src/routes/(app)/dashboard/+page.svelte index ceb3b650..9295c293 100644 --- a/src/routes/(app)/dashboard/+page.svelte +++ b/src/routes/(app)/dashboard/+page.svelte @@ -104,13 +104,32 @@ failToast('Error 404, Member Not Found'); } + function isSummerPeriod() { + const currentDate = new Date(); + const currentYear = currentDate.getFullYear(); + + // Calculate the date of the fourth week in August (matching the original code) + const august = new Date(currentYear, 7, 1); + const dayOfWeek = august.getDay(); + const firstDayOfFourthWeek = 22 + (7 - dayOfWeek) % 7; + const fourthWeekInAugust = new Date(currentYear, 7, firstDayOfFourthWeek); + + // Set start date to May 1st + const startDate = new Date(currentYear, 4, 1); + + // console.log("Start: ", startDate); + // console.log("End: ", fourthWeekInAugust); + + return currentDate >= startDate && currentDate <= fourthWeekInAugust; + } + @@ -172,148 +191,145 @@ {/if}
- - {#if (data.user?.membershipExpDate.getTime() ?? 0) < new Date().getTime() && new Date().getMonth() <= 8 && new Date().getMonth() >= 4} + + {#if (data.user?.membershipExpDate.getTime() ?? 0) < new Date().getTime() && isSummerPeriod()} {#if data.user?.id}
{/if} + {:else} + {#if (data.user?.membershipExpDate.getTime() ?? 0) < new Date().getTime()} +
Looks like your dues are expired!
+
+
+ {#if data.user?.id} + + {/if} {:else} - {#if (data.user?.membershipExpDate.getTime() ?? 0) < new Date().getTime()} -
Looks like your dues are expired!
-
-
- {#if data.user?.id} - - {/if} - {:else} -
Your Dues Expire On {data.user?.membershipExpDate.toDateString()}
-
- Looks like you're all set! Check back in on discord after paying dues for membership status (it can take a second or two), and look out for announcements about updates to this site! -
- {/if} +
Your Dues Expire On {data.user?.membershipExpDate.toDateString()}
+
+ Looks like you're all set! Check back in on discord after paying dues for membership status (it can take a minute or two), and look out for announcements about updates to this site! +
+ {/if} {/if}
- {#if !((data.user?.membershipExpDate.getTime() ?? 0) < new Date().getTime())} -
- {/if} - - {#if data.user?.role.permissionLevel > 1} -
-
-

- {data.user?.role?.name?.split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ')} Dashboard -

- - {#if data.user?.role.permissionLevel >= 5} -
- {#if data.user?.role.name === "admin"} -
-
- Configure President Position -
- {#if data.currentPresident && !change} -

Current President: {data.currentPresident.firstName} {data.currentPresident.lastName}

- - {/if} - - {#if !data.currentPresident || change} - -
- new Date().getTime()} +
+ {#if data.user?.role.permissionLevel > 1} +
+
+

+ {data.user?.role?.name?.split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ')} Dashboard +

+ + {#if data.user?.role.permissionLevel >= 5} +
+ {#if data.user?.role.name === "admin"} +
+
+ Configure President Position +
+ {#if data.currentPresident && !change} +

Current President: {data.currentPresident.firstName} {data.currentPresident.lastName}

+ + {/if} + + {#if !data.currentPresident || change} + -
-
change = false}> - - -
- {/if} -
- {/if} - {#if data.user?.role.name === "president"} -
-
Configure Admin Position
- - {#if data.currentAdmin && !changeAdmin} -

Current Admin: {data.currentAdmin.firstName} {data.currentAdmin.lastName}

- +
+ +
+
change = false}> + + +
+ {/if} +
{/if} - - {#if !data.currentAdmin || changeAdmin} - -
- +
Configure Admin Position
+ + {#if data.currentAdmin && !changeAdmin} +

Current Admin: {data.currentAdmin.firstName} {data.currentAdmin.lastName}

+ + {/if} + + {#if !data.currentAdmin || changeAdmin} + -
-
changeAdmin = false}> - - -
- {/if} -
- {/if} - - {/if} - - {#if data.user?.role.permissionLevel >= 4} -
-
- Configure Projects -
-

Select or Create a Project

- Create Project - Edit Project +
+ +
+
changeAdmin = false}> + + +
+ {/if} +
+ {/if} + + {/if} + + {#if data.user?.role.permissionLevel >= 4} +
+
+ Configure Projects +
+

Select or Create a Project

+ Create Project + Edit Project - {/if} - - {#if data.user?.role.permissionLevel >= 3} -
-
-
- Configure Teams & Team Leads -
-

Create, Edit, or Mange a Team

- Create Team - Edit Team - {/if} - - {#if data.user?.role.permissionLevel >= 2} -
-
-
- Configure Teams -
-

Appoint Members to a Team

- Appoint to Team - {/if} + {/if} + + {#if data.user?.role.permissionLevel >= 3} +
+
+
+ Configure Teams & Team Leads +
+

Create, Edit, or Mange a Team

+ Create Team + Edit Team + {/if} + + {#if data.user?.role.permissionLevel >= 2} +
+
+
+ Configure Teams +
+

Appoint Members to a Team

+ Appoint to Team + {/if} +
-
- {/if} - - {#if (data.user?.membershipExpDate.getTime() ?? 0) > new Date().getTime()} + {/if} {/if}
diff --git a/src/routes/(app)/dashboard/create-project/+page.server.ts b/src/routes/(app)/dashboard/create-project/+page.server.ts index b9e14c56..b8223245 100644 --- a/src/routes/(app)/dashboard/create-project/+page.server.ts +++ b/src/routes/(app)/dashboard/create-project/+page.server.ts @@ -75,9 +75,9 @@ export const actions: Actions = { if (selectedyear === '' || !yearNum) { return setError(form, 'year', 'Please Enter a Valid Year'); } - console.log("raw skills: ", form.data.Skills); + // console.log("raw skills: ", form.data.Skills); const skillsArray = form.data.Skills[0].split(','); - console.log("split skills: ", skillsArray); + // console.log("split skills: ", skillsArray); // Creating survey entry in the database await db.project.create({ diff --git a/src/routes/(app)/dashboard/survey/+page.server.ts b/src/routes/(app)/dashboard/survey/+page.server.ts index 2e2407aa..7a4b6a90 100644 --- a/src/routes/(app)/dashboard/survey/+page.server.ts +++ b/src/routes/(app)/dashboard/survey/+page.server.ts @@ -55,7 +55,7 @@ export const actions: Actions = { return fail(400, { form }); } - // Check if the user already has a survey to handle race condition + // make ucf email be unique if (await db.survey.findFirst({ where: { UCFemail: form.data.ucfEmail diff --git a/src/routes/(app)/dashboard/survey/+page.svelte b/src/routes/(app)/dashboard/survey/+page.svelte index e136b5ae..3e7d5198 100644 --- a/src/routes/(app)/dashboard/survey/+page.svelte +++ b/src/routes/(app)/dashboard/survey/+page.svelte @@ -220,7 +220,7 @@