From 0c2a7fe49db91e90d509f54fcb66ca4bc876435a Mon Sep 17 00:00:00 2001 From: Kevin Wu <95104238+kevinwu15@users.noreply.github.com> Date: Wed, 18 Oct 2023 18:43:12 -0400 Subject: [PATCH 01/42] Task 573 next previous buttons (#103) Demo of changes: ![PrevNextPR](https://github.com/HacktheHill/track-the-hack/assets/95104238/6e7964eb-7b64-463c-9655-65bdc246a11d) Changes: Added new hacker routers to get the next and previous hacker from a unique id (in the url) using cursor-based pagination. Buttons are just preliminary and should be changed based on desired design later --- src/pages/hackers/hacker.tsx | 21 ++++++++++++ src/server/api/routers/hackers.ts | 54 +++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/src/pages/hackers/hacker.tsx b/src/pages/hackers/hacker.tsx index 0009f983..50bd8ea1 100644 --- a/src/pages/hackers/hacker.tsx +++ b/src/pages/hackers/hacker.tsx @@ -27,6 +27,9 @@ const Hacker: NextPage = () => { const hackerQuery = trpc.hackers.get.useQuery({ id: id ?? "" }, { enabled: !!id }); const presenceQuery = trpc.presence.getFromHackerId.useQuery({ id: id ?? "" }, { enabled: !!id }); + + const nextHackerQuery = trpc.hackers.getNext.useQuery({ id: id ?? "" }, { enabled: !!id }); + const prevHackerQuery = trpc.hackers.getPrev.useQuery({ id: id ?? "" }, { enabled: !!id }); if (hackerQuery.isLoading || hackerQuery.data == null) { return ( @@ -82,6 +85,24 @@ const Hacker: NextPage = () => { >
role === Role.ORGANIZER || role === Role.SPONSOR}> +
+ {prevHackerQuery.data ? ( + Previous + + ) : ( + + )} + {nextHackerQuery.data && ( + Next + + )} +
role === Role.HACKER}>{t("not-authorized-to-view-this-page")} diff --git a/src/server/api/routers/hackers.ts b/src/server/api/routers/hackers.ts index 6f0ed750..7c20d096 100644 --- a/src/server/api/routers/hackers.ts +++ b/src/server/api/routers/hackers.ts @@ -42,6 +42,60 @@ export const hackerRouter = createTRPCRouter({ return hacker; }), + + // Get next hacker in db from an id + getNext: publicProcedure + .input( + z + .object({ + id: z.string(), + }), + ) + .query(async ({ ctx, input }) => { + let hacker: HackerInfo | null = null; + if ("id" in input) { + hacker = await ctx.prisma.hackerInfo.findFirst({ + take: 1, + skip: 1, + cursor: { + id: input.id, + }, + }); + } + + if (!hacker) { + throw new Error("Hacker not found"); + } + + return hacker; + }), + + // Get prev hacker in db from an id + getPrev: publicProcedure + .input( + z + .object({ + id: z.string(), + }), + ) + .query(async ({ ctx, input }) => { + let hacker: HackerInfo | null = null; + if ("id" in input) { + hacker = await ctx.prisma.hackerInfo.findFirst({ + take: -1, + skip: 1, + cursor: { + id: input.id, + }, + }); + } + + if (!hacker) { + throw new Error("Hacker not found"); + } + + return hacker; + }), // Get all hackers all: protectedProcedure From 17841bb70d424a926ae46c19c138350d2674928f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 06:40:16 +0000 Subject: [PATCH 02/42] chore(deps): bump @babel/traverse from 7.22.17 to 7.23.2 Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.17 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 60 +++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index d4414d48..7397b53c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -218,11 +218,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.22.15", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -359,20 +359,20 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -522,9 +522,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", - "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -641,9 +641,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", - "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1797,18 +1797,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz", - "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.16", - "@babel/types": "^7.22.17", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1825,12 +1825,12 @@ } }, "node_modules/@babel/types": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", - "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.15", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { From 2bc3e47c976853cc20d16df600f89723fa1ff9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=BBartin?= <51332188+martin0024@users.noreply.github.com> Date: Thu, 23 Nov 2023 00:05:22 -0500 Subject: [PATCH 03/42] feat: editable profiles (#100) https://project.hackthehill.com/projects/track-the-hack/work_packages/46/activity --------- Co-authored-by: macsaheen --- prisma/schema.prisma | 265 +++++++++-------- public/locales/en/hacker.json | 32 ++- public/locales/fr/hacker.json | 35 ++- src/pages/hackers/hacker.tsx | 456 +++++++++++++++++++++++++----- src/server/api/routers/hackers.ts | 33 +++ 5 files changed, 635 insertions(+), 186 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f1aaf3e3..e6c3eef4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,40 +1,32 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" } datasource db { - provider = "mysql" - // NOTE: When using postgresql, mysql or sqlserver, uncomment the @db.Text annotations in model Account below - // Further reading: - // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema - // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string - url = env("DATABASE_URL") - relationMode = "prisma" + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "prisma" } -// Necessary for Next auth model Account { - id String @id @default(cuid()) - userId String - type String - provider String - providerAccountId String - refresh_token String? @db.Text - refresh_token_expires_in Int? - access_token String? @db.Text - ext_expires_in Int? - expires_at Int? - token_type String? - scope String? - id_token String? @db.Text - session_state String? - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@unique([provider, providerAccountId]) - @@index([userId]) + id String @id @default(cuid()) + userId String + type String + provider String + providerAccountId String + refresh_token String? @db.Text + refresh_token_expires_in Int? + access_token String? @db.Text + expires_at Int? + token_type String? + scope String? + id_token String? @db.Text + session_state String? + ext_expires_in Int? + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([provider, providerAccountId]) + @@index([userId]) } model AuditLog { @@ -75,120 +67,157 @@ model User { } model VerificationToken { - identifier String - token String @unique - expires DateTime + identifier String + token String @unique + expires DateTime - @@unique([identifier, token]) + @@unique([identifier, token]) } model HackerInfo { - id String @id @default(cuid()) @map("submissionID") - preferredLanguage Language @default(EN) - email String - firstName String - lastName String - gender String @default("Prefer not to say") - phoneNumber String - university String? - studyLevel String? - studyProgram String? - graduationYear Int? - attendanceType AttendanceType @default(IN_PERSON) - location String? @map("attendanceLocation") - transportationRequired Boolean @default(false) - dietaryRestrictions String @db.Text - accessibilityRequirements String @db.Text - shirtSize ShirtSize? - emergencyContactName String - emergencyContactRelationship String - emergencyContactPhoneNumber String - numberOfPreviousHackathons Int? - linkGithub String? - linkLinkedin String? - linkPersonalSite String? - linkResume String? @db.Text - lookingForwardTo String? @db.Text - formStartDate DateTime? - formEndDate DateTime? - confirmed Boolean @default(false) - user User? @relation(fields: [userId], references: [id]) - userId String? - unsubscribed Boolean @default(false) - unsubscribeToken String? @unique @default(cuid()) - onlyOnline Boolean @default(false) - acceptanceExpiry DateTime? - walkIn Boolean @default(false) - winner Boolean @default(false) - presenceInfo PresenceInfo? - - @@index([userId]) + id String @id @default(cuid()) @map("submissionID") + preferredLanguage Language @default(EN) + email String + firstName String + lastName String + gender String @default("Prefer not to say") + phoneNumber String + university String? + studyLevel String? + studyProgram String? + graduationYear Int? + attendanceType AttendanceType @default(IN_PERSON) + location String? @map("attendanceLocation") + transportationRequired Boolean @default(false) + dietaryRestrictions String @db.Text + accessibilityRequirements String @db.Text + shirtSize ShirtSize? + emergencyContactName String + emergencyContactRelationship String + emergencyContactPhoneNumber String + numberOfPreviousHackathons Int? + linkGithub String? + linkLinkedin String? + linkPersonalSite String? + linkResume String? @db.Text + lookingForwardTo String? @db.Text + formStartDate DateTime? + formEndDate DateTime? + confirmed Boolean @default(false) + userId String? + unsubscribed Boolean @default(false) + unsubscribeToken String? @unique @default(cuid()) + onlyOnline Boolean @default(false) + acceptanceExpiry DateTime? + walkIn Boolean @default(false) + winner Boolean @default(false) + user User? @relation(fields: [userId], references: [id]) + presenceInfo PresenceInfo? + + @@index([userId]) } model PresenceInfo { - id String @id @default(cuid()) - checkedIn Boolean @default(false) - breakfast1 Boolean @default(false) - lunch1 Boolean @default(false) - dinner1 Boolean @default(false) - snacks Boolean @default(false) - snacks2 Boolean @default(false) - redbull Boolean @default(false) - breakfast2 Boolean @default(false) - lunch2 Boolean @default(false) - lunch22 Boolean @default(false) - hackerInfo HackerInfo @relation(fields: [hackerInfoId], references: [id]) - hackerInfoId String @unique + id String @id @default(cuid()) + checkedIn Boolean @default(false) + breakfast1 Boolean @default(false) + lunch1 Boolean @default(false) + dinner1 Boolean @default(false) + breakfast2 Boolean @default(false) + lunch2 Boolean @default(false) + hackerInfoId String @unique + snacks Boolean @default(false) + redbull Boolean @default(false) + snacks2 Boolean @default(false) + lunch22 Boolean @default(false) + hackerInfo HackerInfo @relation(fields: [hackerInfoId], references: [id]) +} + +model Payment { + id String @id @default(cuid()) + company_name String + amount Int + reps_name String + tier Tiers + logo String + paid String + date DateTime + invoice String } model Event { - id String @id @default(cuid()) - start DateTime - end DateTime - name String - type EventType @default(ALL) - host String? - description String @db.Text - room String - image String? - link String? - linkText String? - tiktok String? + id String @id @default(cuid()) + start DateTime + end DateTime + name String + type EventType @default(ALL) + host String? + description String @db.Text + room String + tiktok String? + image String? + link String? + linkText String? } model Follow { - id String @id @default(cuid()) - email String @unique + id String @id @default(cuid()) + email String @unique +} + + +model AuditLogToUser { + A Int + B String + + @@unique([A, B], map: "_AuditLogToUser_AB_unique") + @@index([B], map: "_AuditLogToUser_B_index") + @@map("_AuditLogToUser") +} + +model Link { + id String @id + label String + link String } enum Language { - EN - FR + EN + FR } enum AttendanceType { - IN_PERSON - ONLINE + IN_PERSON + ONLINE } enum ShirtSize { - S - M - L - XL - XXL + S + M + L + XL + XXL } enum Role { - HACKER - ORGANIZER - SPONSOR + HACKER + ORGANIZER + SPONSOR } enum EventType { - ALL - WORKSHOP - SOCIAL - CAREER_FAIR - FOOD + ALL + WORKSHOP + SOCIAL + CAREER_FAIR + FOOD +} + +enum Tiers { + STARTUP + MAYOR + PREMIER + GOVERNOR + PRIME_MINISTER + CUSTOM } diff --git a/public/locales/en/hacker.json b/public/locales/en/hacker.json index 0967ef42..a42cd2c6 100644 --- a/public/locales/en/hacker.json +++ b/public/locales/en/hacker.json @@ -1 +1,31 @@ -{} +{ + "gender": "Gender", + "firstName": "First Name", + "lastName": "Last Name", + "university": "University", + "study": "Study", + "studyLevel": "Study Level", + "studyProgram": "Study Program", + "graduationYear": "Graduation Year", + "phoneNumber": "Phone Number", + "email": "Email", + "emergencyContactName": "Emergency Contact Name", + "emergencyContactRelationship": "Emergency Contact Relationship", + "emergencyContactPhoneNumber": "Emergency Contact Phone Number", + "dietaryRestrictions": "Dietary Restrictions", + "accessibilityRequirements": "Accessibility Requirements", + "preferredLanguage": "Preferred Language", + "shirtSize": "Shirt Size", + "walkIn": "Walk In", + "subscribeToMailingList": "Subscribe to Mailing List", + "attendanceType": "Attendance Type", + "location": "Location", + "transportationRequired": "Transportation Required", + "category_personal_information": "Personal Information", + "category_emergency_contact": "Emergency Contact", + "category_general_information": "General Information", + "category_links_information": "Links Information", + "edit_description": "Careful - you have unsaved changes!", + "edit_reset_button": "Reset", + "edit_save_button": "Save" +} diff --git a/public/locales/fr/hacker.json b/public/locales/fr/hacker.json index 0967ef42..d2917d9a 100644 --- a/public/locales/fr/hacker.json +++ b/public/locales/fr/hacker.json @@ -1 +1,34 @@ -{} +{ + "gender": "Genre", + "firstName": "Prénom", + "lastName": "Nom de famille", + "university": "Université", + "study": "Études", + "studyLevel": "Niveau d'études", + "studyProgram": "Programme d'études", + "graduationYear": "Année de diplomation", + "phoneNumber": "Numéro de téléphone", + "email": "Adresse e-mail", + "emergencyContactName": "Nom du contact en cas d'urgence", + "emergencyContactRelationship": "Relation avec le contact en cas d'urgence", + "emergencyContactPhoneNumber": "Numéro de téléphone du contact en cas d'urgence", + "dietaryRestrictions": "Restrictions alimentaires", + "accessibilityRequirements": "Exigences en matière d'accessibilité", + "preferredLanguage": "Langue préférée", + "shirtSize": "Taille de chemise", + "walkIn": "Marcher", + "subscribeToMailingList": "S'abonner à la liste de diffusion", + "attendanceType": "Type de participation", + "location": "Emplacement", + "transportationRequired": "Transport requis", + "linkGithub": "Lien vers Github", + "linkLinkedin": "Lien vers LinkedIn", + "uploadResume": "Télécharger le CV", + "category_personal_information": "Informations personnelles", + "category_emergency_contact": "Contact en cas d'urgence", + "category_general_information": "Informations générales", + "category_links_information": "Liens", + "edit_description": "Modifications non enregistrées!", + "edit_reset_button": "Réinitialiser", + "edit_save_button": "Sauvegarder" +} diff --git a/src/pages/hackers/hacker.tsx b/src/pages/hackers/hacker.tsx index 50bd8ea1..deb3e38c 100644 --- a/src/pages/hackers/hacker.tsx +++ b/src/pages/hackers/hacker.tsx @@ -10,6 +10,8 @@ import App from "../../components/App"; import Error from "../../components/Error"; import Loading from "../../components/Loading"; import OnlyRole from "../../components/OnlyRole"; +import { walkInSchema } from "../../utils/common"; +import { z } from "zod"; type HackerInfo = Prisma.HackerInfoGetPayload; type PresenceInfo = Prisma.PresenceInfoGetPayload; @@ -116,17 +118,32 @@ type HackerViewProps = { presenceData: PresenceInfo; }; +type Field = { + label: string; + name: string; + default_value: string | null | undefined; + type: string; + category: string; + options?: string[]; +}; + +interface Patterns { + [key: string]: string | undefined; +} + // eslint-disable-next-line @typescript-eslint/no-unused-vars const HackerView = ({ hackerData, presenceData: { id: _, hackerInfoId, ...presenceData } }: HackerViewProps) => { const router = useRouter(); const [id] = [router.query.id].flat(); + const { t } = useTranslation("hacker"); const presenceMutation = trpc.presence.update.useMutation(); const [presenceState, setPresenceState] = useState(presenceData); - const paragraphClass = "flex justify-between gap-4 text-right"; + const paragraphClass = "flex justify-between gap-4 text-right py-1.5 "; const boldClass = "text-left font-bold"; + const [edit, setEdit] = useState(false); const keyToLabel = { checkedIn: "Checked In", breakfast1: "Breakfast March 4th", @@ -140,13 +157,260 @@ const HackerView = ({ hackerData, presenceData: { id: _, hackerInfoId, ...presen lunch22: "Lunch March 5th 2", } as const satisfies Record, string>; + const fields = [ + { + label: t("gender"), + name: "gender", + default_value: hackerData.gender, + type: "text", + category: t("category_personal_information"), + }, + { + label: t("firstName"), + name: "firstName", + default_value: hackerData.firstName, + type: "text", + category: t("category_personal_information"), + }, + { + label: t("lastName"), + name: "lastName", + default_value: hackerData.lastName, + type: "text", + category: t("category_personal_information"), + }, + { + label: t("university"), + name: "university", + default_value: hackerData.university, + type: "text", + category: t("category_personal_information"), + }, + { + label: t("studyLevel"), + name: "studyLevel", + default_value: hackerData.studyLevel?.toUpperCase(), + type: "text", + category: t("category_personal_information"), + }, + { + label: t("studyProgram"), + name: "studyProgram", + default_value: hackerData.studyProgram, + type: "text", + category: t("category_personal_information"), + }, + { + label: t("graduationYear"), + name: "graduationYear", + default_value: hackerData.graduationYear, + type: "number", + category: t("category_personal_information"), + }, + { + label: t("phoneNumber"), + name: "phoneNumber", + default_value: hackerData.phoneNumber, + type: "number", + category: t("category_personal_information"), + }, + { + label: t("email"), + name: "email", + default_value: hackerData.email, + type: "email", + category: t("category_personal_information"), + }, + { + label: t("emergencyContactName"), + name: "emergencyContactName", + default_value: hackerData.emergencyContactName, + type: "text", + category: t("category_emergency_contact"), + }, + { + label: t("emergencyContactRelationship"), + name: "emergencyContactRelationship", + default_value: hackerData.emergencyContactRelationship, + type: "text", + category: t("category_emergency_contact"), + }, + { + label: t("emergencyContactPhoneNumber"), + name: "emergencyContactPhoneNumber", + default_value: hackerData.emergencyContactPhoneNumber, + type: "number", + category: t("category_emergency_contact"), + }, + { + label: t("dietaryRestrictions"), + name: "dietaryRestrictions", + default_value: hackerData.dietaryRestrictions, + type: "text", + category: t("category_general_information"), + }, + { + label: t("accessibilityRequirements"), + name: "accessibilityRequirements", + default_value: hackerData.accessibilityRequirements, + type: "text", + category: t("category_general_information"), + }, + { + label: t("preferredLanguage"), + name: "preferredLanguage", + default_value: hackerData.preferredLanguage, + type: "select", + options: ["EN", "FR"], + category: t("category_general_information"), + }, + { + label: t("shirtSize"), + name: "shirtSize", + default_value: hackerData.shirtSize, + type: "select", + options: ["S", "M", "L", "XL", "XXL"], + category: t("category_general_information"), + }, + { + label: t("walkIn"), + name: "walkIn", + default_value: hackerData.walkIn, + type: "select", + options: ["true", "false"], + category: t("category_general_information"), + }, + { + label: t("subscribeToMailingList"), + name: "subscribed", + default_value: hackerData.unsubscribed, + type: "select", + options: ["true", "false"], + category: t("category_general_information"), + }, + { + label: t("attendanceType"), + name: "attendanceType", + default_value: hackerData.attendanceType, + type: "select", + options: ["IN_PERSON", "ONLINE"], + category: t("category_general_information"), + }, + { + label: t("location"), + name: "location", + default_value: hackerData.location, + type: "text", + category: t("category_general_information"), + }, + { + label: t("transportationRequired"), + name: "transportationRequired", + default_value: hackerData.transportationRequired, + type: "select", + options: ["true", "false"], + category: t("category_general_information"), + }, + { + label: "Linkedin", + name: "linkLinkedin", + default_value: hackerData.linkLinkedin, + type: "url", + category: t("category_links_information"), + }, + { + label: "Github", + name: "linkGithub", + default_value: hackerData.linkGithub, + type: "url", + category: t("category_links_information"), + }, + ]; + const patterns: Patterns = { + tel: "^\\s*(?:\\+?(\\d{1,3}))?[-. (]*(\\d{3})[-. )]*(\\d{3})[-. ]*(\\d{4})(?: *x(\\d+))?\\s*$", + url: undefined, + number: "^\\d+$", + email: undefined, + text: undefined, + }; + + const initialInputValues: Record = {}; + const [inputValues, setInputValues] = useState<{ [key: string]: string }>(initialInputValues); + const groupedData: { [key: string]: Field[] } = {}; + const mutation = trpc.hackers.update.useMutation(); + + const handleInputChange = (name: string, value: string) => { + setInputValues(prevValues => ({ + ...prevValues, + [name]: value, + })); + setEdit(true); + }; + + const resetInputFields = () => { + setInputValues(initialInputValues); + setEdit(false); + }; + + fields.forEach(field => { + initialInputValues[field.name] = String(field.default_value) || ""; + }); + + fields.forEach(item => { + const field = item as Field; + if (!groupedData[field.category]) { + groupedData[field.category] = []; + } + groupedData[field.category]?.push(field); + }); + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + const formData = new FormData(event.currentTarget); + const data = Object.fromEntries(formData) as Record; + data.id = id; + + if (data.linkLinkedin !== hackerData.linkLinkedin || data.linkGithub !== hackerData.linkGithub) { + window.location.reload(); + } + + if (typeof data.graduationYear === "string") { + const parsedGraduationYear = parseInt(data.graduationYear); + if (!isNaN(parsedGraduationYear)) { + data.graduationYear = parsedGraduationYear; + } else { + data.graduationYear = undefined; + } + } + + const parse = walkInSchema + .extend({ + id: z.string(), + }) + .safeParse(data); + + if (!parse.success) { + console.error("Validation Error:", parse.error); + } else { + console.log("Data parsed", parse.data); + mutation.mutate(parse.data); + if (!mutation.error) { + event.currentTarget.reset(); + } else { + console.log("Error", mutation.error.message); + } + } + setEdit(false); + }; + return ( <>

{hackerData.firstName} {hackerData.lastName} ({hackerData.gender})

+

- {hackerData.studyLevel} {hackerData.studyProgram}{" "} + {hackerData.studyLevel?.toUpperCase()} {hackerData.studyProgram}{" "} {hackerData.university && `at ${hackerData.university}`}{" "} {hackerData.graduationYear && `(${hackerData.graduationYear})`}

@@ -155,6 +419,7 @@ const HackerView = ({ hackerData, presenceData: { id: _, hackerInfoId, ...presen {Object.entries(presenceState).map(([key, value]) => (

-

General Information

-

- Contact Information {hackerData.email} — {hackerData.phoneNumber} -

-

- Emergency Contact {hackerData.emergencyContactName} ( - {hackerData.emergencyContactRelationship}) {hackerData.emergencyContactPhoneNumber} -

-

- Requirements - {hackerData.dietaryRestrictions - ? hackerData.dietaryRestrictions - : "No Dietary Restrictions"} —{" "} - {hackerData.accessibilityRequirements - ? hackerData.accessibilityRequirements - : "No Accessibility Requirements"}{" "} - — {hackerData.preferredLanguage} — {hackerData.shirtSize} -

-

- Attendance - {hackerData.confirmed ? "Confirmed attendance" : "Unconfirmed attendance"} - {hackerData.walkIn ? " (walk-in)" : ""} —{" "} - {hackerData.unsubscribed ? "Unsubscribed from emails" : "Subscribed to emails"} — Attends{" "} - {hackerData.attendanceType} at {hackerData.location} with{" "} - {hackerData.transportationRequired ? "" : "no"} transportation required -

-

- {Object.entries({ - Resume: hackerData.linkResume, - LinkedIn: hackerData.linkLinkedin, - GitHub: hackerData.linkGithub, - "Personal Website": hackerData.linkPersonalSite, - }).map( - ([key, value]) => - value && ( - - {key} - - ), + +

+ {Object.keys(groupedData).map((category, index) => ( +
+
+

+ {category} +

+
+ {groupedData[category]?.map((item, itemIndex) => ( +
+ {item.label} + {item.type === "select" ? ( + + ) : ( + { + handleInputChange(item.name, e.target.value); + }} + pattern={patterns[item.type]} + /> + )} +
+ ))} +
+ ))} + +
+ + +
+ +

+ {Object.entries({ + Resume: hackerData.linkResume, + LinkedIn: hackerData.linkLinkedin, + GitHub: hackerData.linkGithub, + "Personal Website": hackerData.linkPersonalSite, + }).map( + ([key, value]) => + value && ( + + {key} + + ), + )} +

+ + role === Role.ORGANIZER}> + <> +
+

+ Debug Information +

+
+

+ HackerInfo ID {hackerData.id ?? "NULL"} +

+

+ User ID {hackerData.userId ?? "NULL"} +

+

+ Unsubscribe Token {hackerData.unsubscribeToken ?? "NULL"} +

+

+ Acceptance Expiry{" "} + {(hackerData.acceptanceExpiry ?? "NULL").toString()} +

+ +
+ + {edit && ( +
+
+
+

{t("edit_description")}

+
+
+ + +
+
+
)} -

- role === Role.ORGANIZER}> - <> -

Debug Information

-

- HackerInfo ID {hackerData.id ?? "NULL"} -

-

- User ID {hackerData.userId ?? "NULL"} -

-

- Unsubscribe Token {hackerData.unsubscribeToken ?? "NULL"} -

-

- Acceptance Expiry{" "} - {(hackerData.acceptanceExpiry ?? "NULL").toString()} -

- -
+
); }; diff --git a/src/server/api/routers/hackers.ts b/src/server/api/routers/hackers.ts index 7c20d096..d350e465 100644 --- a/src/server/api/routers/hackers.ts +++ b/src/server/api/routers/hackers.ts @@ -277,6 +277,39 @@ export const hackerRouter = createTRPCRouter({ }, }); + return hacker; + }), + + // Update a hacker's info + update: protectedProcedure + .input( + walkInSchema.extend({ + id: z.string(), + }), + ) + .mutation(async ({ ctx, input }) => { + const userId = ctx.session.user.id; + const user = await ctx.prisma.user.findUnique({ + where: { + id: userId, + }, + }); + + if (!user) { + throw new Error("User not found"); + } + + if (input.id !== userId && !hasRoles(user, [Role.ORGANIZER])) { + throw new Error("You do not have permission to do this"); + } + + const hacker = await ctx.prisma.hackerInfo.update({ + where: { + id: input.id, + }, + data: input, + }); + return hacker; }), }); From ac21f3efcfcd4889aa78d037e75ee16076b08d1a Mon Sep 17 00:00:00 2001 From: ouyix Date: Thu, 23 Nov 2023 08:18:39 -0500 Subject: [PATCH 04/42] chore: update email template dates to fall 2024 --- src/pages/sponsorship/gmail-drafts.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/sponsorship/gmail-drafts.tsx b/src/pages/sponsorship/gmail-drafts.tsx index f9ba1bab..7ecd7ecb 100644 --- a/src/pages/sponsorship/gmail-drafts.tsx +++ b/src/pages/sponsorship/gmail-drafts.tsx @@ -47,7 +47,7 @@ const templates = [ reaching out to companies like yours to join us in making this event possible.

- Hack the Hill is an event that will run from February 2nd to 4th 2024, along with a series of monthly + Hack the Hill is an event that will run in the Fall of 2024, along with a series of monthly events throughout the year. We are shaping the future of technology through the joint effort of notable student organizations from both the University of Ottawa and Carleton University.

@@ -94,7 +94,7 @@ const templates = [ 1000 passionate North American students who are ready to push the boundaries of software and hardware solutions. We are shaping the future of technology through the joint effort of notable student organizations from both the University of Ottawa and - Carleton University. From February 2nd to 4th 2024, we will transform + Carleton University. In the Fall of 2024, we will transform the campus into a hub for new ideas and flourishing projects. This is where ideas come to life, where technology thrives, and where your company's brand can truly shine.

@@ -240,9 +240,9 @@ const templates = [ throughout the year.

- This year, we/they will be hosting about 1,000 participants from across North America - who will receive the opportunity to innovate software and hardware solutions from February 2nd to 4th, - 2024. Events like these could not happen without the support of our sponsors.we/they + In the Fall of 2024, we/they will be hosting about 1,000 participants from across North America + who will receive the opportunity to innovate software and hardware solutions. Events like these could + not happen without the support of our sponsors.we/they encourage you to take a look at the sponsorship package, which I have attached. we/they are more than happy to answer any questions you may have!

From b9c22d76ee6aa375be2c6ee7f24504d8444c818d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CF=BBartin?= <51332188+martin0024@users.noreply.github.com> Date: Fri, 24 Nov 2023 16:31:17 -0500 Subject: [PATCH 05/42] feat: audit logging (#101) https://project.hackthehill.com/projects/track-the-hack/work_packages/585/activity --------- Co-authored-by: macsaheen --- .env.example | 5 - README.md | 10 +- package-lock.json | 108 ++++----- package.json | 10 +- prisma/schema.prisma | 98 +++----- prisma/seeders/events.mts | 1 + prisma/seeders/index.mts | 1 - prisma/seeders/users.mts | 16 +- prisma/seeders/utils.mts | 22 +- public/locales/en/common.json | 1 + public/locales/en/hacker.json | 32 +-- public/locales/en/index.json | 4 +- public/locales/en/logs.json | 7 + public/locales/en/navbar.json | 13 - public/locales/en/qr.json | 3 +- .../locales/en/sponsorship-gmail-drafts.json | 7 - public/locales/fr/hacker.json | 35 +-- public/locales/fr/index.json | 4 +- public/locales/fr/logs.json | 7 + public/locales/fr/navbar.json | 13 - public/locales/fr/qr.json | 3 +- .../locales/fr/sponsorship-gmail-drafts.json | 8 - src/components/Navigation.tsx | 189 ++++++++------- src/components/PhysicalScanner.tsx | 34 --- src/components/QRCode.tsx | 2 - src/env/schema.mjs | 6 - src/pages/api/auth/[...nextauth].ts | 7 - src/pages/follow/index.tsx | 2 +- src/pages/hackers/hacker.tsx | 157 +++++------- src/pages/hackers/index.tsx | 5 +- src/pages/index.tsx | 4 +- src/pages/logs/index.tsx | 118 +++++++++ src/pages/maps/index.tsx | 2 +- src/pages/qr/index.tsx | 27 +-- src/pages/resources/en-2023.md | 226 ----------------- src/pages/resources/en.md | 227 +++++++++++++++++- src/pages/resources/fr-2023.md | 226 ----------------- src/pages/resources/fr.md | 227 +++++++++++++++++- src/pages/resources/index.tsx | 2 +- src/pages/schedule/event.tsx | 2 +- src/pages/schedule/index.tsx | 2 +- src/server/api/root.ts | 5 +- src/server/api/routers/auditLog.ts | 57 +++++ src/server/api/routers/hackers.ts | 65 +---- src/server/api/routers/sponsorship.ts | 2 +- src/server/audit.ts | 31 +++ src/utils/common.ts | 1 + src/utils/redirects.ts | 38 ++- 48 files changed, 1016 insertions(+), 1056 deletions(-) create mode 100644 public/locales/en/logs.json create mode 100644 public/locales/fr/logs.json delete mode 100644 src/components/PhysicalScanner.tsx create mode 100644 src/pages/logs/index.tsx delete mode 100644 src/pages/resources/en-2023.md delete mode 100644 src/pages/resources/fr-2023.md create mode 100644 src/server/api/routers/auditLog.ts create mode 100644 src/server/audit.ts diff --git a/.env.example b/.env.example index 5af36fd3..1aefaa87 100644 --- a/.env.example +++ b/.env.example @@ -19,8 +19,3 @@ DISCORD_CLIENT_SECRET= # Next Auth GitHub Provider GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= - -# Sponsorship Gmail Drafts -SPONSORSHIP_GOOGLE_CLIENT_ID= -SPONSORSHIP_GOOGLE_CLIENT_SECRET= -SPONSORSHIP_GOOGLE_REFRESH_TOKEN= diff --git a/README.md b/README.md index 970c738f..3d20dd6f 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,10 @@ Track the Hack is a comprehensive event management solution designed to streamli 1. Clone this repository and navigate to the project directory. 2. Install the required dependencies by running `npm install`. -3. Copy the `.env.example` file to create a `.env` file and configure the database and environment settings as per the provided guidelines. +3. Create a `.env` file and onfigure the database and environment settings as per the provided guidelines. 4. Start the development server using `npm run dev`. 5. Access the application through your web browser at `http://localhost:3000`. -## Self-host the database (optional) - -1. Install [Docker](https://docs.docker.com/get-docker/). -2. Add `DATABASE_URL="mysql://root:root@localhost/track-the-hack"` to the `.env` file. -3. Run `npx prisma db push` to push the database schema and create the tables. -4. Run `docker compose up -d` to start the database. -5. Run `node --loader ts-node/esm prisma/seeders/index.mts` to seed the database. - ## Contributing We appreciate your interest, but please note that we currently do not accept external contributions. diff --git a/package-lock.json b/package-lock.json index 7397b53c..88320cbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@google-cloud/local-auth": "^2.1.1", "@next-auth/prisma-adapter": "^1.0.7", - "@prisma/client": "^5.3.1", + "@prisma/client": "^5.2.0", "@tanstack/react-query": "^4.35.0", "@trpc/client": "^10.38.2", "@trpc/next": "^10.38.2", @@ -37,7 +37,7 @@ "rehype-slug": "^5.1.0", "remark-toc": "^8.0.1", "superjson": "1.12.2", - "zod": "^3.22.3" + "zod": "^3.20.6" }, "devDependencies": { "@faker-js/faker": "^8.0.2", @@ -54,10 +54,10 @@ "autoprefixer": "^10.4.13", "eslint": "^8.34.0", "eslint-config-next": "13.1.6", - "postcss": "^8.4.31", + "postcss": "^8.4.21", "prettier": "^2.8.4", "prettier-plugin-tailwindcss": "^0.2.2", - "prisma": "^5.3.1", + "prisma": "^5.2.0", "tailwindcss": "^3.2.6", "ts-node": "^10.9.1", "typescript": "^4.9.5" @@ -218,11 +218,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.22.15", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -359,20 +359,20 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -522,9 +522,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", + "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", "engines": { "node": ">=6.9.0" } @@ -641,9 +641,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.22.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", + "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1797,18 +1797,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz", + "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==", "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", + "@babel/generator": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.22.16", + "@babel/types": "^7.22.17", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1825,12 +1825,12 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", + "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.22.15", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2289,12 +2289,12 @@ } }, "node_modules/@prisma/client": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.3.1.tgz", - "integrity": "sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.2.0.tgz", + "integrity": "sha512-AiTjJwR4J5Rh6Z/9ZKrBBLel3/5DzUNntMohOy7yObVnVoTNVFi2kvpLZlFuKO50d7yDspOtW6XBpiAd0BVXbQ==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" + "@prisma/engines-version": "5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f" }, "engines": { "node": ">=16.13" @@ -2309,16 +2309,16 @@ } }, "node_modules/@prisma/engines": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.3.1.tgz", - "integrity": "sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.2.0.tgz", + "integrity": "sha512-dT7FOLUCdZmq+AunLqB1Iz+ZH/IIS1Fz2THmKZQ6aFONrQD/BQ5ecJ7g2wGS2OgyUFf4OaLam6/bxmgdOBDqig==", "devOptional": true, "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz", - "integrity": "sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w==" + "version": "5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f.tgz", + "integrity": "sha512-jsnKT5JIDIE01lAeCj2ghY9IwxkedhKNvxQeoyLs6dr4ZXynetD0vTy7u6wMJt8vVPv8I5DPy/I4CFaoXAgbtg==" }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", @@ -7730,9 +7730,9 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "funding": [ { @@ -7991,13 +7991,13 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prisma": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.3.1.tgz", - "integrity": "sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.2.0.tgz", + "integrity": "sha512-FfFlpjVCkZwrqxDnP4smlNYSH1so+CbfjgdpioFzGGqlQAEm6VHAYSzV7jJgC3ebtY9dNOhDMS2+4/1DDSM7bQ==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.3.1" + "@prisma/engines": "5.2.0" }, "bin": { "prisma": "build/index.js" @@ -10397,9 +10397,9 @@ } }, "node_modules/zod": { - "version": "3.22.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", - "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.2.tgz", + "integrity": "sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index b083b17c..1b31b317 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dependencies": { "@google-cloud/local-auth": "^2.1.1", "@next-auth/prisma-adapter": "^1.0.7", - "@prisma/client": "^5.3.1", + "@prisma/client": "^5.2.0", "@tanstack/react-query": "^4.35.0", "@trpc/client": "^10.38.2", "@trpc/next": "^10.38.2", @@ -23,7 +23,7 @@ "googleapis": "^120.0.0", "i18next": "^22.4.9", "mimetext": "^3.0.15", - "next": "13.1.6", + "next": "^13.5.6", "next-auth": "^4.20.1", "next-i18next": "^13.1.5", "next-pwa": "^5.6.0", @@ -39,7 +39,7 @@ "rehype-slug": "^5.1.0", "remark-toc": "^8.0.1", "superjson": "1.12.2", - "zod": "^3.22.3" + "zod": "^3.20.6" }, "devDependencies": { "@faker-js/faker": "^8.0.2", @@ -56,10 +56,10 @@ "autoprefixer": "^10.4.13", "eslint": "^8.34.0", "eslint-config-next": "13.1.6", - "postcss": "^8.4.31", + "postcss": "^8.4.21", "prettier": "^2.8.4", "prettier-plugin-tailwindcss": "^0.2.2", - "prisma": "^5.3.1", + "prisma": "^5.2.0", "tailwindcss": "^3.2.6", "ts-node": "^10.9.1", "typescript": "^4.9.5" diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e6c3eef4..7ae08bd3 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,13 +1,21 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" + // NOTE: When using postgresql, mysql or sqlserver, uncomment the @db.Text annotations in model Account below + // Further reading: + // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema + // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string url = env("DATABASE_URL") relationMode = "prisma" } +// Necessary for Next auth model Account { id String @id @default(cuid()) userId String @@ -17,12 +25,12 @@ model Account { refresh_token String? @db.Text refresh_token_expires_in Int? access_token String? @db.Text + ext_expires_in Int? expires_at Int? token_type String? scope String? id_token String? @db.Text session_state String? - ext_expires_in Int? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) @@ -37,33 +45,34 @@ model AuditLog { route String action String details String? - user User[] + user User[] @@index([user_id]) } model Session { - id String @id @default(cuid()) - sessionToken String @unique - userId String - expires DateTime - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@index([userId]) + id String @id @default(cuid()) + sessionToken String @unique + userId String + expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) } model User { - id String @id @default(cuid()) - name String? - email String? @unique - emailVerified DateTime? - image String? - passwordHash String? @db.Text - salt String? - role Role @default(HACKER) - accounts Account[] - sessions Session[] - hackerInfo HackerInfo[] - auditLog AuditLog[] + id String @id @default(cuid()) + name String? + email String? @unique + emailVerified DateTime? + image String? + passwordHash String? @db.Text + salt String? + role Role @default(HACKER) + accounts Account[] + sessions Session[] + hackerInfo HackerInfo[] + auditLog AuditLog[] } model VerificationToken { @@ -104,6 +113,7 @@ model HackerInfo { formStartDate DateTime? formEndDate DateTime? confirmed Boolean @default(false) + user User? @relation(fields: [userId], references: [id]) userId String? unsubscribed Boolean @default(false) unsubscribeToken String? @unique @default(cuid()) @@ -111,7 +121,6 @@ model HackerInfo { acceptanceExpiry DateTime? walkIn Boolean @default(false) winner Boolean @default(false) - user User? @relation(fields: [userId], references: [id]) presenceInfo PresenceInfo? @@index([userId]) @@ -123,26 +132,14 @@ model PresenceInfo { breakfast1 Boolean @default(false) lunch1 Boolean @default(false) dinner1 Boolean @default(false) - breakfast2 Boolean @default(false) - lunch2 Boolean @default(false) - hackerInfoId String @unique snacks Boolean @default(false) - redbull Boolean @default(false) snacks2 Boolean @default(false) + redbull Boolean @default(false) + breakfast2 Boolean @default(false) + lunch2 Boolean @default(false) lunch22 Boolean @default(false) hackerInfo HackerInfo @relation(fields: [hackerInfoId], references: [id]) -} - -model Payment { - id String @id @default(cuid()) - company_name String - amount Int - reps_name String - tier Tiers - logo String - paid String - date DateTime - invoice String + hackerInfoId String @unique } model Event { @@ -154,10 +151,10 @@ model Event { host String? description String @db.Text room String - tiktok String? image String? link String? linkText String? + tiktok String? } model Follow { @@ -165,22 +162,6 @@ model Follow { email String @unique } - -model AuditLogToUser { - A Int - B String - - @@unique([A, B], map: "_AuditLogToUser_AB_unique") - @@index([B], map: "_AuditLogToUser_B_index") - @@map("_AuditLogToUser") -} - -model Link { - id String @id - label String - link String -} - enum Language { EN FR @@ -212,12 +193,3 @@ enum EventType { CAREER_FAIR FOOD } - -enum Tiers { - STARTUP - MAYOR - PREMIER - GOVERNOR - PRIME_MINISTER - CUSTOM -} diff --git a/prisma/seeders/events.mts b/prisma/seeders/events.mts index f6a2190d..ffa0403d 100644 --- a/prisma/seeders/events.mts +++ b/prisma/seeders/events.mts @@ -1,3 +1,4 @@ +import { faker } from "@faker-js/faker"; import { EventType } from "@prisma/client"; const day1 = "2023-08-8"; diff --git a/prisma/seeders/index.mts b/prisma/seeders/index.mts index c4154200..cd3229c6 100644 --- a/prisma/seeders/index.mts +++ b/prisma/seeders/index.mts @@ -17,7 +17,6 @@ async function main() { console.log("Creating dummy events..."); await insertRecords(prisma.event, events); } - main() .then(async () => { await prisma.$disconnect(); diff --git a/prisma/seeders/users.mts b/prisma/seeders/users.mts index 5555a26d..57850bdd 100644 --- a/prisma/seeders/users.mts +++ b/prisma/seeders/users.mts @@ -1,8 +1,8 @@ import { faker } from "@faker-js/faker"; import { Role, Language, PrismaClient } from "@prisma/client"; -// Generates dummy users -const user = () => { +//generates dummy users +let user = () => { const firstName = faker.person.firstName(); const lastName = faker.person.lastName(); const name = `${firstName} ${lastName}`; @@ -21,23 +21,21 @@ const user = () => { preferredLanguage: Language.EN, phoneNumber, email, - emergencyContactName: "", - emergencyContactRelationship: "", - emergencyContactPhoneNumber: "", - dietaryRestrictions: "", - accessibilityRequirements: "", + emergencyContactName: "null", + emergencyContactRelationship: "null", + emergencyContactPhoneNumber: "null", }, }, }; }; /** - * Generates an array of users + * generates an array of users * @param n number of users to generate * @returns [] array of users */ function generateUsers(n = 10) { - const users = []; + let users = []; for (let i = 0; i < n; i++) { users.push(user()); } diff --git a/prisma/seeders/utils.mts b/prisma/seeders/utils.mts index 2b36efd8..33a393cb 100644 --- a/prisma/seeders/utils.mts +++ b/prisma/seeders/utils.mts @@ -1,13 +1,21 @@ -async function insertRecords(db: any, rows: object[]) { +async function insertRecords(db: iDatabaseTable, rows: object[]) { try { - rows.forEach(row => - db.create({ + rows.map(async row => { + await db.create({ data: row, - }), - ); - } catch (error) { - console.log(error); + }); + }); + } catch (e) { + console.log(e); } } +export interface iDatabaseTable { + findMany(fields: object): any; + findUnique(criteria: object): any; + update(data: object): any; + delete(data: object): any; + create(data: object): any; +} + export { insertRecords }; diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c5aed613..74896a4d 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -4,5 +4,6 @@ "loading": "Loading...", "error": "Error: {{message}}", "contact-us": "Contact us for help", + "unknown-error": "Unknown error", "not-authorized-to-view-this-page": "You are not authorized to view this page." } diff --git a/public/locales/en/hacker.json b/public/locales/en/hacker.json index a42cd2c6..0967ef42 100644 --- a/public/locales/en/hacker.json +++ b/public/locales/en/hacker.json @@ -1,31 +1 @@ -{ - "gender": "Gender", - "firstName": "First Name", - "lastName": "Last Name", - "university": "University", - "study": "Study", - "studyLevel": "Study Level", - "studyProgram": "Study Program", - "graduationYear": "Graduation Year", - "phoneNumber": "Phone Number", - "email": "Email", - "emergencyContactName": "Emergency Contact Name", - "emergencyContactRelationship": "Emergency Contact Relationship", - "emergencyContactPhoneNumber": "Emergency Contact Phone Number", - "dietaryRestrictions": "Dietary Restrictions", - "accessibilityRequirements": "Accessibility Requirements", - "preferredLanguage": "Preferred Language", - "shirtSize": "Shirt Size", - "walkIn": "Walk In", - "subscribeToMailingList": "Subscribe to Mailing List", - "attendanceType": "Attendance Type", - "location": "Location", - "transportationRequired": "Transportation Required", - "category_personal_information": "Personal Information", - "category_emergency_contact": "Emergency Contact", - "category_general_information": "General Information", - "category_links_information": "Links Information", - "edit_description": "Careful - you have unsaved changes!", - "edit_reset_button": "Reset", - "edit_save_button": "Save" -} +{} diff --git a/public/locales/en/index.json b/public/locales/en/index.json index b20a687d..2210caf9 100644 --- a/public/locales/en/index.json +++ b/public/locales/en/index.json @@ -1,5 +1,5 @@ { "welcome": "Welcome to the Hack the Hill's Track the Hack app!", - "explanation": "The event is over now, but we hope to see you next year!", - "get-started": "Get Started" + "get-started": "Get Started", + "description": "The event is over now, but we hope to see you next year!" } diff --git a/public/locales/en/logs.json b/public/locales/en/logs.json new file mode 100644 index 00000000..42b4bdc0 --- /dev/null +++ b/public/locales/en/logs.json @@ -0,0 +1,7 @@ +{ + "user": "User", + "action": "Action", + "timestamp": "Timestamp", + "details": "Details", + "userid": "User ID", +} \ No newline at end of file diff --git a/public/locales/en/navbar.json b/public/locales/en/navbar.json index 5ef122b6..e69de29b 100644 --- a/public/locales/en/navbar.json +++ b/public/locales/en/navbar.json @@ -1,13 +0,0 @@ -{ - "home": "Home", - "qr": "QR", - "schedule": "Schedule", - "maps": "Maps", - "resources": "Resources", - "hackers": "Hackers", - "navigation": "Navigation", - "logo": "Logo", - "sign-out": "Sign Out", - "sign-in": "Sign In", - "bottom-navigation": "Bottom Navigation" -} diff --git a/public/locales/en/qr.json b/public/locales/en/qr.json index fe2f50e4..732d6eb3 100644 --- a/public/locales/en/qr.json +++ b/public/locales/en/qr.json @@ -2,6 +2,5 @@ "title": "QR Code", "use-qr": "Show this QR code to organizers to check in", "scan-qr": "Ready to scan a QR code", - "mascot-choco-alt": "Hack the Hill mascot with hot chocolate", - "sign-in-to-access": "You need to sign in to access the QR page." + "mascot-choco-alt": "Hack the Hill mascot with hot chocolate" } diff --git a/public/locales/en/sponsorship-gmail-drafts.json b/public/locales/en/sponsorship-gmail-drafts.json index 92d2b41e..c78ef89b 100644 --- a/public/locales/en/sponsorship-gmail-drafts.json +++ b/public/locales/en/sponsorship-gmail-drafts.json @@ -6,12 +6,5 @@ "company-name": "Company Name", "company-rep-name": "Company Representative's Name", "company-email": "Company Email", - "select-template": "Select Template", - "subject": "Subject", - "custom-template": "Custom Template", - "copy-to-clipboard": "Copy to Clipboard", - "copied-to-clipboard": "Copied to Clipboard", - "create-draft-email": "Create Draft Email", - "created-draft-email": "Created Draft Email", "invalid-form": "Please fill out all required fields correctly." } diff --git a/public/locales/fr/hacker.json b/public/locales/fr/hacker.json index d2917d9a..0967ef42 100644 --- a/public/locales/fr/hacker.json +++ b/public/locales/fr/hacker.json @@ -1,34 +1 @@ -{ - "gender": "Genre", - "firstName": "Prénom", - "lastName": "Nom de famille", - "university": "Université", - "study": "Études", - "studyLevel": "Niveau d'études", - "studyProgram": "Programme d'études", - "graduationYear": "Année de diplomation", - "phoneNumber": "Numéro de téléphone", - "email": "Adresse e-mail", - "emergencyContactName": "Nom du contact en cas d'urgence", - "emergencyContactRelationship": "Relation avec le contact en cas d'urgence", - "emergencyContactPhoneNumber": "Numéro de téléphone du contact en cas d'urgence", - "dietaryRestrictions": "Restrictions alimentaires", - "accessibilityRequirements": "Exigences en matière d'accessibilité", - "preferredLanguage": "Langue préférée", - "shirtSize": "Taille de chemise", - "walkIn": "Marcher", - "subscribeToMailingList": "S'abonner à la liste de diffusion", - "attendanceType": "Type de participation", - "location": "Emplacement", - "transportationRequired": "Transport requis", - "linkGithub": "Lien vers Github", - "linkLinkedin": "Lien vers LinkedIn", - "uploadResume": "Télécharger le CV", - "category_personal_information": "Informations personnelles", - "category_emergency_contact": "Contact en cas d'urgence", - "category_general_information": "Informations générales", - "category_links_information": "Liens", - "edit_description": "Modifications non enregistrées!", - "edit_reset_button": "Réinitialiser", - "edit_save_button": "Sauvegarder" -} +{} diff --git a/public/locales/fr/index.json b/public/locales/fr/index.json index 84846327..e0993e57 100644 --- a/public/locales/fr/index.json +++ b/public/locales/fr/index.json @@ -1,5 +1,5 @@ { "welcome": "Bienvenue sur l'application Track the Hack de Hack the Hill !", - "explanation": "L'événement est terminé maintenant, mais nous espérons vous voir l'année prochaine !", - "get-started": "Commencez" + "get-started": "Commencez", + "description": "L'événement est terminé maintenant, mais nous espérons vous voir l'année prochaine !" } diff --git a/public/locales/fr/logs.json b/public/locales/fr/logs.json new file mode 100644 index 00000000..e784331e --- /dev/null +++ b/public/locales/fr/logs.json @@ -0,0 +1,7 @@ +{ + "user": "utilisateur", + "action": "Action", + "timestamp": "Date", + "details": "Détails", + "userid": "ID de l'utilisateur", +} \ No newline at end of file diff --git a/public/locales/fr/navbar.json b/public/locales/fr/navbar.json index f3d31c74..e69de29b 100644 --- a/public/locales/fr/navbar.json +++ b/public/locales/fr/navbar.json @@ -1,13 +0,0 @@ -{ - "home": "Acceuil", - "qr": "QR", - "schedule": "Horaire", - "maps": "Carte", - "resources": "Ressources", - "hackers": "Hackers", - "navigation": "Navigation", - "logo": "Logo", - "sign-out": "Déconnexion", - "sign-in": "Connexion", - "bottom-navigation": "Navigation du bas" -} diff --git a/public/locales/fr/qr.json b/public/locales/fr/qr.json index 1ae0c4cf..65e91281 100644 --- a/public/locales/fr/qr.json +++ b/public/locales/fr/qr.json @@ -2,6 +2,5 @@ "title": "Code QR", "use-qr": "Montrer ce code QR aux organisateurs pour participer", "scan-qr": "Prêt à scanner un code QR", - "mascot-choco-alt": "Mascotte Hack the Hill avec du chocolat chaud", - "sign-in-to-access": "Vous devez vous connecter pour accéder à la page QR." + "mascot-choco-alt": "Mascotte Hack the Hill avec du chocolat chaud" } diff --git a/public/locales/fr/sponsorship-gmail-drafts.json b/public/locales/fr/sponsorship-gmail-drafts.json index 1a8f862d..12a64795 100644 --- a/public/locales/fr/sponsorship-gmail-drafts.json +++ b/public/locales/fr/sponsorship-gmail-drafts.json @@ -6,13 +6,5 @@ "company-name": "Nom de l'entreprise", "company-rep-name": "Nom du représentant de l'entreprise", "company-email": "Courriel de l'entreprise", - "select-template": "Sélectionner un modèle", - "subject": "Sujet", - "custom-template": "Modèle personnalisé", - "custom-template-note": "Veuillez noter que toutes les modifications seront perdues si vous changez de modèle ou décochez la case « Personnaliser le modèle ».", - "copy-to-clipboard": "Copier dans le presse-papiers", - "copied-to-clipboard": "Copié dans le presse-papiers", - "create-draft-email": "Créer un brouillon de courriel", - "created-draft-email": "Brouillon de courriel créé", "invalid-form": "Veuillez remplir correctement tous les champs obligatoires." } diff --git a/src/components/Navigation.tsx b/src/components/Navigation.tsx index 3ba99819..43686f4d 100644 --- a/src/components/Navigation.tsx +++ b/src/components/Navigation.tsx @@ -6,89 +6,6 @@ import Link from "next/link"; import { trpc } from "../utils/api"; import OnlyRole from "./OnlyRole"; -type LinkProps = { - bottom?: boolean; -}; - -const Links = ({ bottom }: LinkProps) => { - const { t } = useTranslation("navbar"); - const { data: sessionData } = useSession(); - - const hackerId = trpc.users.getHackerId.useQuery( - { id: sessionData?.user?.id ?? "" }, - { enabled: !!sessionData?.user?.id }, - ); - - const role = trpc.users.getRole.useQuery({ id: sessionData?.user?.id ?? "" }, { enabled: !!sessionData?.user?.id }); - - return ( - <> - - {bottom ? {t("home")} : t("home")} - - {sessionData?.user && ((role.data === Role.HACKER && hackerId.data) || role.data === Role.ORGANIZER) && ( - - {bottom ? {t("qr")} : t("qr")} - - )} - - {bottom ? ( - {t("schedule")} - ) : ( - t("schedule") - )} - - - {bottom ? {t("maps")} : t("maps")} - - - {bottom ? ( - Resources - ) : ( - t("resources") - )} - - {sessionData?.user && ( - <> - role === Role.ORGANIZER || role === Role.SPONSOR}> - - {bottom ? ( - {t("hackers")} - ) : ( - t("hackers") - )} - - - - )} - - ); -}; - type NavbarProps = { integrated?: boolean; }; @@ -96,13 +13,12 @@ type NavbarProps = { const Navbar = ({ integrated }: NavbarProps) => { const { t } = useTranslation("navbar"); const { data: sessionData } = useSession(); - return ( ); @@ -141,15 +57,108 @@ const Navbar = ({ integrated }: NavbarProps) => { const BottomMenu = () => { const { t } = useTranslation("navbar"); + const { data: sessionData } = useSession(); + const hackerQuery = trpc.users.getHackerId.useQuery( + { id: sessionData?.user?.id ?? "" }, + { enabled: !!sessionData?.user?.id }, + ); return ( ); }; +const Links = () => { + const { t } = useTranslation("navbar"); + const { data: sessionData } = useSession(); + + const hackerQuery = trpc.users.getHackerId.useQuery( + { id: sessionData?.user?.id ?? "" }, + { enabled: !!sessionData?.user?.id }, + ); + + return ( + <> + + Home + + {sessionData?.user && hackerQuery.data && ( + role === Role.HACKER}> + + QR + + + )} + + Schedule + + + Maps + + + Resources + + {sessionData?.user && ( + <> + role === Role.ORGANIZER || role === Role.SPONSOR}> + + Hackers + + + role === Role.ORGANIZER}> + + Walk-In + + + + )} + + ); +}; + export { BottomMenu, Navbar }; diff --git a/src/components/PhysicalScanner.tsx b/src/components/PhysicalScanner.tsx deleted file mode 100644 index bc0bef3d..00000000 --- a/src/components/PhysicalScanner.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useState, type FormEvent } from "react"; - -type PhysicalScannerProps = { - onScan: (data: string) => void; -}; - -const PhysicalScanner = (props: PhysicalScannerProps) => { - const [scannedCode, setScannedCode] = useState(""); - - const onSubmit = (event: FormEvent) => { - event.preventDefault(); - props.onScan(scannedCode); - setScannedCode(""); - }; - - const onChange = (event: FormEvent) => { - setScannedCode(event.currentTarget.value); - }; - - return ( -
- -
- ); -}; - -export default PhysicalScanner; diff --git a/src/components/QRCode.tsx b/src/components/QRCode.tsx index 2c40b4b8..8b569fa8 100644 --- a/src/components/QRCode.tsx +++ b/src/components/QRCode.tsx @@ -55,9 +55,7 @@ const QRCode = ({ setError, id }: QRCodeProps) => { setError?.(true); return ; } - setError?.(false); - return ( { return { - props: await serverSideTranslations(locale ?? "en", ["common", "navbar", "follow"]), + props: await serverSideTranslations(locale ?? "en", ["common", "follow"]), }; }; diff --git a/src/pages/hackers/hacker.tsx b/src/pages/hackers/hacker.tsx index deb3e38c..aff6a8b9 100644 --- a/src/pages/hackers/hacker.tsx +++ b/src/pages/hackers/hacker.tsx @@ -3,7 +3,7 @@ import type { GetStaticProps, NextPage } from "next"; import { useTranslation } from "next-i18next"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import { useRouter } from "next/router"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { trpc } from "../../utils/api"; import App from "../../components/App"; @@ -18,7 +18,7 @@ type PresenceInfo = Prisma.PresenceInfoGetPayload; export const getStaticProps: GetStaticProps = async ({ locale }) => { return { - props: await serverSideTranslations(locale ?? "en", ["common", "navbar", "hacker"]), + props: await serverSideTranslations(locale ?? "en", ["common", "hacker"]), }; }; @@ -29,9 +29,6 @@ const Hacker: NextPage = () => { const hackerQuery = trpc.hackers.get.useQuery({ id: id ?? "" }, { enabled: !!id }); const presenceQuery = trpc.presence.getFromHackerId.useQuery({ id: id ?? "" }, { enabled: !!id }); - - const nextHackerQuery = trpc.hackers.getNext.useQuery({ id: id ?? "" }, { enabled: !!id }); - const prevHackerQuery = trpc.hackers.getPrev.useQuery({ id: id ?? "" }, { enabled: !!id }); if (hackerQuery.isLoading || hackerQuery.data == null) { return ( @@ -87,24 +84,6 @@ const Hacker: NextPage = () => { >
role === Role.ORGANIZER || role === Role.SPONSOR}> -
- {prevHackerQuery.data ? ( - Previous - - ) : ( - - )} - {nextHackerQuery.data && ( - Next - - )} -
role === Role.HACKER}>{t("not-authorized-to-view-this-page")} @@ -135,7 +114,6 @@ interface Patterns { const HackerView = ({ hackerData, presenceData: { id: _, hackerInfoId, ...presenceData } }: HackerViewProps) => { const router = useRouter(); const [id] = [router.query.id].flat(); - const { t } = useTranslation("hacker"); const presenceMutation = trpc.presence.update.useMutation(); const [presenceState, setPresenceState] = useState(presenceData); @@ -159,171 +137,167 @@ const HackerView = ({ hackerData, presenceData: { id: _, hackerInfoId, ...presen const fields = [ { - label: t("gender"), + label: "Gender", name: "gender", default_value: hackerData.gender, type: "text", - category: t("category_personal_information"), + category: "Personal Information", }, { - label: t("firstName"), + label: "First Name", name: "firstName", default_value: hackerData.firstName, type: "text", - category: t("category_personal_information"), + category: "Personal Information", }, { - label: t("lastName"), + label: "Last Name", name: "lastName", default_value: hackerData.lastName, type: "text", - category: t("category_personal_information"), + category: "Personal Information", }, { - label: t("university"), + label: "University", name: "university", default_value: hackerData.university, type: "text", - category: t("category_personal_information"), + category: "Personal Information", }, { - label: t("studyLevel"), + label: "Study Level", name: "studyLevel", default_value: hackerData.studyLevel?.toUpperCase(), type: "text", - category: t("category_personal_information"), + category: "Personal Information", }, { - label: t("studyProgram"), + label: "Study Program", name: "studyProgram", default_value: hackerData.studyProgram, type: "text", - category: t("category_personal_information"), + category: "Personal Information", }, { - label: t("graduationYear"), + label: "Graduation Year", name: "graduationYear", default_value: hackerData.graduationYear, type: "number", - category: t("category_personal_information"), + category: "Personal Information", }, { - label: t("phoneNumber"), + label: "Phone Number", name: "phoneNumber", default_value: hackerData.phoneNumber, - type: "number", - category: t("category_personal_information"), + type: "tel", + category: "Personal Information", }, { - label: t("email"), + label: "Email", name: "email", default_value: hackerData.email, - type: "email", - category: t("category_personal_information"), + type: "text", + category: "Personal Information", }, { - label: t("emergencyContactName"), + label: "Emergency Contact Name", name: "emergencyContactName", default_value: hackerData.emergencyContactName, type: "text", - category: t("category_emergency_contact"), + category: "Emergency Contact", }, { - label: t("emergencyContactRelationship"), + label: "Emergency Contact Relationship", name: "emergencyContactRelationship", default_value: hackerData.emergencyContactRelationship, type: "text", - category: t("category_emergency_contact"), + category: "Emergency Contact", }, { - label: t("emergencyContactPhoneNumber"), + label: "Emergency Contact Phone Number", name: "emergencyContactPhoneNumber", default_value: hackerData.emergencyContactPhoneNumber, - type: "number", - category: t("category_emergency_contact"), + type: "tel", + category: "Emergency Contact", }, { - label: t("dietaryRestrictions"), + label: "Dietary Restrictions", name: "dietaryRestrictions", default_value: hackerData.dietaryRestrictions, type: "text", - category: t("category_general_information"), + category: "General Information", }, { - label: t("accessibilityRequirements"), + label: "Accessibility Requirements", name: "accessibilityRequirements", default_value: hackerData.accessibilityRequirements, type: "text", - category: t("category_general_information"), + category: "General Information", }, { - label: t("preferredLanguage"), + label: "Preferred Language", name: "preferredLanguage", default_value: hackerData.preferredLanguage, type: "select", options: ["EN", "FR"], - category: t("category_general_information"), + category: "General Information", }, { - label: t("shirtSize"), + label: "Shirt Size", name: "shirtSize", default_value: hackerData.shirtSize, type: "select", options: ["S", "M", "L", "XL", "XXL"], - category: t("category_general_information"), + category: "General Information", }, { - label: t("walkIn"), + label: "Walk In", name: "walkIn", default_value: hackerData.walkIn, - type: "select", - options: ["true", "false"], - category: t("category_general_information"), + type: "text", + category: "General Information", }, { - label: t("subscribeToMailingList"), + label: "Subscribed", name: "subscribed", default_value: hackerData.unsubscribed, - type: "select", - options: ["true", "false"], - category: t("category_general_information"), + type: "text", + category: "General Information", }, { - label: t("attendanceType"), + label: "Attendance Type", name: "attendanceType", default_value: hackerData.attendanceType, - type: "select", - options: ["IN_PERSON", "ONLINE"], - category: t("category_general_information"), + type: "text", + category: "General Information", }, { - label: t("location"), + label: "Location", name: "location", default_value: hackerData.location, type: "text", - category: t("category_general_information"), + category: "General Information", }, { - label: t("transportationRequired"), + label: "Transportation Required", name: "transportationRequired", default_value: hackerData.transportationRequired, - type: "select", - options: ["true", "false"], - category: t("category_general_information"), + type: "text", + category: "General Information", }, { label: "Linkedin", name: "linkLinkedin", default_value: hackerData.linkLinkedin, type: "url", - category: t("category_links_information"), + category: "Links Information", }, { label: "Github", name: "linkGithub", default_value: hackerData.linkGithub, type: "url", - category: t("category_links_information"), + category: "Links Information", }, ]; const patterns: Patterns = { @@ -335,6 +309,7 @@ const HackerView = ({ hackerData, presenceData: { id: _, hackerInfoId, ...presen }; const initialInputValues: Record = {}; + const { t } = useTranslation("walk-in"); const [inputValues, setInputValues] = useState<{ [key: string]: string }>(initialInputValues); const groupedData: { [key: string]: Field[] } = {}; const mutation = trpc.hackers.update.useMutation(); @@ -460,6 +435,7 @@ const HackerView = ({ hackerData, presenceData: { id: _, hackerInfoId, ...presen handleInputChange(item.name, e.target.value); }} > + {item.options?.map(option => (