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/prisma/schema.prisma b/prisma/schema.prisma
index 6f610aea..3ab9e7dd 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -121,8 +121,8 @@ model Picture {
id Int @id @default(autoincrement())
title String?
caption String?
- data String?
- isLocal Boolean @default(value: true)
+ data String?
+ isLocal Boolean @default(value: false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Article Article[]
@@ -165,6 +165,8 @@ model Article {
model Team {
id String @id @unique @default(uuid())
name String
+ teamLead Member? @relation("TeamLead", fields: [teamLeadId], references: [id])
+ teamLeadId String? // Foreign key for team lead
members Member[]
maxMembers Int
minMembers Int
@@ -186,10 +188,10 @@ model Project {
updatedAt DateTime @updatedAt
description String
logo Picture? @relation(fields: [pictureId], references: [id])
- budget Float
+ budget Float?
docsLink String @default("")
extraLinks Link[]
- remainingFunds Float
+ remainingFunds Float?
season Season @default(value: Fall)
year Int
articles Article[]
@@ -200,6 +202,8 @@ model Project {
Tags Tag[]
Skills String[]
projectType projectType @default(value: standard)
+ projectLead Member? @relation("ProjectLead", fields: [projectLeadId], references: [id])
+ projectLeadId String? // Foreign key for project lead
teams Team[] // possibility for no teams created if project does not require them
}
@@ -217,14 +221,16 @@ model Member {
role Role @relation(fields: [roleId], references: [id])
roleId Int
Articles Article[]
- Projects Project[]
BlogPost BlogPost[]
Account Account? @relation(fields: [accountId], references: [id])
accountId Int?
+ Projects Project[]
Teams Team[]
Survey Survey? @relation(fields: [surveyId], references: [id])
surveyId Int?
PasswordResetToken PasswordResetToken?
+ teamLead Team[] @relation("TeamLead")
+ projectLead Project[] @relation("ProjectLead")
}
model Sponsor {
diff --git a/src/components/projectCard.svelte b/src/components/projectCard.svelte
index 49438e31..931daa28 100644
--- a/src/components/projectCard.svelte
+++ b/src/components/projectCard.svelte
@@ -3,11 +3,19 @@
export let project: Project & { logo: Picture };
let hover = false;
+
+ // Function to truncate description to 2 sentences
+ function truncateDescription(description: string): string {
+ const sentences = description.split('. ').filter((s) => s.trim() !== '');
+ return sentences.length > 2
+ ? `${sentences.slice(0, 2).join('. ')}...`
+ : description;
+ }
-
+
{
hover = true;
}}
@@ -15,27 +23,35 @@
hover = false;
}}
>
- {#if !hover}
-
- {#if project.logo.isLocal}
-
+
+
+
{project.title}
+
+
+
+
+ {#if !hover}
+
+ {#if project.logo.isLocal}
+
+ {:else}
+
+ {/if}
{:else}
-
{project.title}
-
+
+
+
{truncateDescription(project.description)}
+
+ {#each project.Skills as skill}
+ {skill}
+ {/each}
+
+
{/if}
- {:else}
-
-
-
{project.title}
-
{project.description}
- {#each project.Skills as skill}
-
{skill}
- {/each}
-
- {/if}
-
+
+
+
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}
+
+ Pay Dues For The Semester or Year
+
+ Semester: $25
+ Year: $50
+
+
+{:else}
+ {#if duesSelection == '1'}
+ Semesterly Dues
+ Cancel
+
+ {/if}
+ {#if duesSelection == '2'}
+ Yearly Dues
+ Cancel
+
+ {/if}
+{/if}
+
{#if duesSelection}
{#if clientSecret && stripe}
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..f328df4b 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'
},
- committee: {
- level: 6,
- name: 'committee'
+ officers: {
+ level: 4,
+ name: 'officers'
+ },
+ project_lead:{
+ level: 3,
+ name: 'project lead'
+ },
+ team_lead: {
+ level: 2,
+ name: ' team 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..b85c91ce 100644
--- a/src/routes/(app)/dashboard/+page.server.ts
+++ b/src/routes/(app)/dashboard/+page.server.ts
@@ -1,19 +1,57 @@
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';
+import config from '../../../config.ts';
const updateDuesSchema = z.object({
email: z.string().email(),
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..d39e725a 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,76 @@
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');
+ }
+
+ 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;
+ }
+
+
+
@@ -116,35 +191,147 @@
{/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?.membershipExpDate.getTime() ?? 0) > 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}
+
selectedMemberId = null} on:click={() => change = true}>Change
+ {/if}
+
+ {#if !data.currentPresident || change}
+
+
+
+ {/if}
+
+ {/if}
+ {#if data.user?.role.name === "president"}
+
+
Configure Admin Position
+
+ {#if data.currentAdmin && !changeAdmin}
+
Current Admin: {data.currentAdmin.firstName} {data.currentAdmin.lastName}
+
selectedAdminId = null} on:click={() => changeAdmin = true}>Change Admin
+ {/if}
+
+ {#if !data.currentAdmin || changeAdmin}
+
+
+
+ {/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}
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/appoint-to-team/+page.server.ts b/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts
new file mode 100644
index 00000000..ac3cd815
--- /dev/null
+++ b/src/routes/(app)/dashboard/appoint-to-team/+page.server.ts
@@ -0,0 +1,85 @@
+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 });
+ }
+ console.log("Received Team ID:", form.data.teamId); // Verify received teamId
+ console.log("Received Members:", form.data.members); // Verify received members
+
+ // 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,
+ },
+ },
+ });
+
+
+ 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..a265d24a
--- /dev/null
+++ b/src/routes/(app)/dashboard/appoint-to-team/+page.svelte
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
diff --git a/src/routes/(app)/dashboard/create-project/+page.server.ts b/src/routes/(app)/dashboard/create-project/+page.server.ts
new file mode 100644
index 00000000..b8223245
--- /dev/null
+++ b/src/routes/(app)/dashboard/create-project/+page.server.ts
@@ -0,0 +1,134 @@
+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";
+
+let pLevel = 0;
+
+const createProSchema = z.object({
+ title: z.string(),
+ description: z.string(),
+ docsLink: z.string(),
+ season: z.custom(),
+ year: z.string(),
+ logo: z.string(),
+ Skills: z.string().array(),
+ proLeadID: z.string()
+});
+
+export const load: PageServerLoad = async ({ parent, locals }) => {
+ const data = await parent();
+ pLevel = data.member!.role.permissionLevel;
+ // check user permission level
+ if (!(pLevel > 3)) {
+ 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, members };
+};
+
+export const actions: Actions = {
+ default: async({ request }) => {
+ const form = await superValidate(request, createProSchema);
+ // Validating forms
+ if (!form.valid) {
+ return fail(400, { form });
+ }
+
+ const selectedyear = form.data.year;
+ const vaildTitle = form.data.title;
+ const vaildDocsLink = form.data.docsLink;
+ const vaildSeason = form.data.season;
+ const vaildLogo = form.data.logo;
+ const yearNum = parseInt(selectedyear);
+
+
+ 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');
+ }
+ if (vaildDocsLink === ''){
+ return setError(form, 'docsLink', 'Please Enter a Docs-Link');
+ }
+ if (vaildSeason === ''){
+ return setError(form, 'season', 'Please Enter a Season');
+ }
+ 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,
+ }
+ },
+ docsLink: form.data.docsLink,
+ season: form.data.season,
+ year: yearNum,
+ 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
new file mode 100644
index 00000000..81348e3f
--- /dev/null
+++ b/src/routes/(app)/dashboard/create-project/+page.svelte
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
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 @@
+
+
+
+
+
+
+
+
\ 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..9dbb894a
--- /dev/null
+++ b/src/routes/(app)/dashboard/edit-projects/+page.server.ts
@@ -0,0 +1,93 @@
+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()
+});
+
+const projectSelectionSchema = z.object({
+ 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 selectionForm = await superValidate(projectSelectionSchema);
+
+ console.log("On load Project id is: " + form.data.id);
+
+ const currentProject = await db.project.findUnique({
+ where: {id: form.data.id}
+ })
+
+ return {
+ form, selectionForm, currentProject,
+ member: {
+ Projects: allProjects // Pass all projects to the frontend
+ }
+ };
+};
+
+// Handle form submission
+export const actions: Actions = {
+ selectProject: async ({ request }) => {
+ console.log("Project select has been triggered!");
+ // Parse the form data
+ const form = await superValidate(request, projectSelectionSchema);
+
+ if (!form.valid) {
+ return fail(400, { form });
+ }
+
+ const projectId = form.data.id;
+
+ // Find the project
+ const project = await db.project.findUnique({
+ where: { id: projectId },
+ });
+
+ console.log(project);
+
+ if (!project) {
+ return setError(form, 'id', 'Project not found.');
+ }else{
+ return { form, currentProject: project };
+ }
+ },
+
+
+ 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 });
+ }
+ }
+};
+
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..aaa91299
--- /dev/null
+++ b/src/routes/(app)/dashboard/edit-projects/+page.svelte
@@ -0,0 +1,185 @@
+
+
+
+
+
+
\ No newline at end of file
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
-
-
-
-
- Blogpost Title
-
-
-
-
- Blogpost
-
-
-
-
-
-
-
-
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 @@
diff --git a/src/routes/(main)/forgotPass/+page.server.ts b/src/routes/(main)/forgotPass/+page.server.ts
index 9cb51a8a..23113237 100644
--- a/src/routes/(main)/forgotPass/+page.server.ts
+++ b/src/routes/(main)/forgotPass/+page.server.ts
@@ -104,7 +104,7 @@ export const actions: Actions = {
// Send an email with the reset link
const client = new postmark.ServerClient(POSTMARK_API_TOKEN);
- const resetLink = `http://localhost:5173/forgotPass/reset-password?token=${token}`;
+ const resetLink = `http://rccf.club/forgotPass/reset-password?token=${token}`;
try {
await client.sendEmailWithTemplate({
diff --git a/src/routes/(main)/register/+page.server.ts b/src/routes/(main)/register/+page.server.ts
index 81ab5be0..b61d2245 100644
--- a/src/routes/(main)/register/+page.server.ts
+++ b/src/routes/(main)/register/+page.server.ts
@@ -4,7 +4,7 @@ import { randomUUID } from 'crypto';
import { setError, superValidate } from 'sveltekit-superforms/server';
import { z } from 'zod';
import generatePassword from '../../../components/scripts/generatePass';
-import config from '../../../config';
+import config from '../../../config.ts';
import type { Actions, PageServerLoad } from './$types';
const registerSchema = z.object({
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 040a8f92..53e5e59b 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,6 +1,9 @@
diff --git a/src/routes/api/members/+server.ts b/src/routes/api/members/+server.ts
index 7f80583b..8adc11d7 100644
--- a/src/routes/api/members/+server.ts
+++ b/src/routes/api/members/+server.ts
@@ -7,7 +7,7 @@ export async function GET({ url }) {
where: {
role: {
permissionLevel: {
- gte: 4
+ gte: 1
}
}
}
diff --git a/src/routes/create-payment-intent/+server.ts b/src/routes/create-payment-intent/+server.ts
index 58822ab3..2bc46a2e 100644
--- a/src/routes/create-payment-intent/+server.ts
+++ b/src/routes/create-payment-intent/+server.ts
@@ -1,34 +1,21 @@
import Stripe from 'stripe';
import { SECRET_STRIPE_KEY } from '$env/static/private';
import config from '../../config';
-import { duesType } from '../../stores';
-import { get } from 'svelte/store';
// initialize Stripe
const stripe = new Stripe(SECRET_STRIPE_KEY);
-// handle POST /create-payment-intent
-export async function POST() {
- // create the payment intent
+
+export async function POST({ request }) {
+ const { duesType } = await request.json();
+
+ const amount = duesType === '1' ? config.paypal.semester_cost : config.paypal.year_cost;
+ // console.log(amount);
+
const paymentIntent = await stripe.paymentIntents.create({
- amount: Number(config.paypal.semester_cost) * 100,
- // note, for some EU-only payment methods it must be EUR
+ amount: Number(amount) * 100, // Stripe amounts are in cents
currency: 'USD',
- // specify what payment methods are allowed
- // can be card, sepa_debit, ideal, etc...
payment_method_types: ['card']
});
- // return the clientSecret to the client
return new Response(JSON.stringify({ clientSecret: paymentIntent.client_secret }));
}
-
-// "variable" is the variable configured in the stores.ts
-// function figureOutDues() {
-// const ds = get(duesType);
-// console.log(ds);
-// if (ds == '1') {
-// return Number(config.paypal.year_cost);
-// } else {
-// return Number(config.paypal.semester_cost);
-// }
-// }
diff --git a/src/stores.ts b/src/stores.ts
index 1cad7f30..5956a586 100644
--- a/src/stores.ts
+++ b/src/stores.ts
@@ -1,3 +1,4 @@
import { writable, type Writable } from 'svelte/store';
-export const duesType = writable('1');
+export const duesType: Writable = writable('1');
+