From bbac553adea10311ac08ccb4c08532059d45a5c1 Mon Sep 17 00:00:00 2001 From: Thandi Menelas Date: Mon, 17 Nov 2025 06:02:07 -0500 Subject: [PATCH 1/7] Changes to schema, redid migrations, and update deps --- ...zy_exodus.sql => 0000_chilly_bullseye.sql} | 111 ++- drizzle/meta/0000_snapshot.json | 736 ++++++++++++--- drizzle/meta/_journal.json | 4 +- drizzle/relations.ts | 87 -- drizzle/schema.ts | 271 ------ package.json | 13 +- pnpm-lock.yaml | 836 +++++++----------- src/lib/database/schema.ts | 142 ++- src/lib/mongodb.js | 31 - 9 files changed, 1148 insertions(+), 1083 deletions(-) rename drizzle/{0000_jazzy_exodus.sql => 0000_chilly_bullseye.sql} (68%) delete mode 100644 drizzle/relations.ts delete mode 100644 drizzle/schema.ts delete mode 100644 src/lib/mongodb.js diff --git a/drizzle/0000_jazzy_exodus.sql b/drizzle/0000_chilly_bullseye.sql similarity index 68% rename from drizzle/0000_jazzy_exodus.sql rename to drizzle/0000_chilly_bullseye.sql index 7999ba4..f6fd5a4 100644 --- a/drizzle/0000_jazzy_exodus.sql +++ b/drizzle/0000_chilly_bullseye.sql @@ -1,8 +1,24 @@ +CREATE TYPE "public"."context_type_enum" AS ENUM('global', 'committee', 'project');--> statement-breakpoint CREATE TYPE "public"."event_host_type_enum" AS ENUM('club', 'committee', 'project', 'member');--> statement-breakpoint CREATE TYPE "public"."gender_enum" AS ENUM('M', 'F', 'NB', 'O', 'PNTS');--> statement-breakpoint -CREATE TYPE "public"."officer_role_enum" AS ENUM('executive_chair', 'executive_vice_chair', 'executive_secretary', 'executive_treasurer', 'committee_lead');--> statement-breakpoint +CREATE TYPE "public"."officer_role_enum" AS ENUM('Executive Chair', 'Vice Chair', 'Treasurer', 'Secretary', 'Project Chair', 'Workshop Chair', 'Conference Chair', 'Outreach Chair', 'Service Chair', 'Social Chair', 'Professional Development Chair', 'Marketing Chair', 'Software Chair');--> statement-breakpoint CREATE TYPE "public"."permission_enum" AS ENUM('scan_attendance', 'view_statistics', 'manage_context');--> statement-breakpoint -CREATE TYPE "public"."sponsorship_tier_enum" AS ENUM('bronze', 'silver', 'gold');--> statement-breakpoint +CREATE TYPE "public"."sponsorship_tier_enum" AS ENUM('Bronze', 'Silver', 'Gold');--> statement-breakpoint +CREATE TABLE "accounts" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "type" varchar(255) NOT NULL, + "provider" varchar(255) NOT NULL, + "provider_account_id" varchar(255) NOT NULL, + "refresh_token" text, + "access_token" text, + "expires_at" integer, + "token_type" varchar(255), + "scope" varchar(255), + "id_token" text, + "session_state" varchar(255) +); +--> statement-breakpoint CREATE TABLE "committee_members" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "committee_id" uuid NOT NULL, @@ -16,7 +32,6 @@ CREATE TABLE "committees" ( "title" varchar(255) NOT NULL, "slug" varchar(64), "about" text NOT NULL, - "chair_id" uuid NOT NULL, "discord_role_id" varchar(64), "active" boolean DEFAULT true NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, @@ -36,20 +51,19 @@ CREATE TABLE "events" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "title" varchar(255) NOT NULL, "location" varchar(255) NOT NULL, - "host_type" "event_host_type_enum" NOT NULL, + "host_type" "event_host_type_enum" DEFAULT 'committee' NOT NULL, "host_id" uuid, - "slug" varchar(64), - "start_time" timestamp with time zone NOT NULL, - "end_time" timestamp with time zone, - "requires_dues" boolean DEFAULT false NOT NULL, - "active" boolean DEFAULT true NOT NULL, "description" text NOT NULL, "flyer_url" varchar(500), "rsvp_link" varchar(500), "photo_urls" text, - "duration" integer, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "slug" varchar(64), + "start_time" timestamp with time zone NOT NULL, + "end_time" timestamp with time zone, + "requires_dues" boolean DEFAULT false NOT NULL, + "active" boolean DEFAULT true NOT NULL, CONSTRAINT "events_slug_unique" UNIQUE("slug") ); --> statement-breakpoint @@ -57,7 +71,7 @@ CREATE TABLE "member_permissions" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "member_id" uuid NOT NULL, "granted_by_id" uuid, - "context_type" varchar(32) NOT NULL, + "context_type" "context_type_enum" NOT NULL, "context_id" uuid, "permission" "permission_enum" NOT NULL, "active" boolean DEFAULT true NOT NULL, @@ -68,16 +82,18 @@ CREATE TABLE "member_permissions" ( --> statement-breakpoint CREATE TABLE "members" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, "first_name" varchar(255) NOT NULL, "middle_name" varchar(255), "last_name" varchar(255) NOT NULL, "officer_role" "officer_role_enum", "administrator" boolean DEFAULT false NOT NULL, + "officer_status" boolean DEFAULT false NOT NULL, "biography" text, "dues_paid" boolean DEFAULT false NOT NULL, - "discord_id" varchar(64) NOT NULL, "date_of_birth" date NOT NULL, - "email" varchar(255) NOT NULL, + "personal_email" varchar(255) NOT NULL, + "ucf_email" varchar(255) NOT NULL, "phone_number" varchar(20), "major" varchar(255) NOT NULL, "gender" "gender_enum" NOT NULL, @@ -90,8 +106,9 @@ CREATE TABLE "members" ( "active" boolean DEFAULT true NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, - CONSTRAINT "members_discord_id_unique" UNIQUE("discord_id"), - CONSTRAINT "members_email_unique" UNIQUE("email") + CONSTRAINT "members_user_id_unique" UNIQUE("user_id"), + CONSTRAINT "members_personal_email_unique" UNIQUE("personal_email"), + CONSTRAINT "members_ucf_email_unique" UNIQUE("ucf_email") ); --> statement-breakpoint CREATE TABLE "project_members" ( @@ -118,6 +135,21 @@ CREATE TABLE "projects" ( CONSTRAINT "projects_slug_unique" UNIQUE("slug") ); --> statement-breakpoint +CREATE TABLE "scanning_sessions" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "event_id" uuid NOT NULL, + "scanner_id" uuid, + "scanned_member_id" uuid NOT NULL, + "method" varchar(32) DEFAULT 'qr' NOT NULL, + "timestamp" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "sessions" ( + "session_token" varchar(255) PRIMARY KEY NOT NULL, + "user_id" uuid NOT NULL, + "expires" timestamp with time zone NOT NULL +); +--> statement-breakpoint CREATE TABLE "sponsorships" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "company_name" varchar(255) NOT NULL, @@ -132,15 +164,31 @@ CREATE TABLE "sponsorships" ( "updated_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint +CREATE TABLE "users" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text, + "email" varchar(255) NOT NULL, + "email_verified" timestamp with time zone, + "image" text, + "discordId" varchar(64), + CONSTRAINT "users_email_unique" UNIQUE("email"), + CONSTRAINT "users_discordId_unique" UNIQUE("discordId") +); +--> statement-breakpoint +ALTER TABLE "accounts" ADD CONSTRAINT "accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "committee_members" ADD CONSTRAINT "committee_members_committee_id_committees_id_fk" FOREIGN KEY ("committee_id") REFERENCES "public"."committees"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "committee_members" ADD CONSTRAINT "committee_members_member_id_members_id_fk" FOREIGN KEY ("member_id") REFERENCES "public"."members"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "committees" ADD CONSTRAINT "committees_chair_id_members_id_fk" FOREIGN KEY ("chair_id") REFERENCES "public"."members"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "event_attendees" ADD CONSTRAINT "event_attendees_event_id_events_id_fk" FOREIGN KEY ("event_id") REFERENCES "public"."events"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "event_attendees" ADD CONSTRAINT "event_attendees_member_id_members_id_fk" FOREIGN KEY ("member_id") REFERENCES "public"."members"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "member_permissions" ADD CONSTRAINT "member_permissions_member_id_members_id_fk" FOREIGN KEY ("member_id") REFERENCES "public"."members"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "member_permissions" ADD CONSTRAINT "member_permissions_granted_by_id_members_id_fk" FOREIGN KEY ("granted_by_id") REFERENCES "public"."members"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "members" ADD CONSTRAINT "members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "project_members" ADD CONSTRAINT "project_members_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "project_members" ADD CONSTRAINT "project_members_member_id_members_id_fk" FOREIGN KEY ("member_id") REFERENCES "public"."members"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "scanning_sessions" ADD CONSTRAINT "scanning_sessions_event_id_events_id_fk" FOREIGN KEY ("event_id") REFERENCES "public"."events"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "scanning_sessions" ADD CONSTRAINT "scanning_sessions_scanner_id_members_id_fk" FOREIGN KEY ("scanner_id") REFERENCES "public"."members"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "scanning_sessions" ADD CONSTRAINT "scanning_sessions_scanned_member_id_members_id_fk" FOREIGN KEY ("scanned_member_id") REFERENCES "public"."members"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint CREATE INDEX "committee_members_idx_id" ON "committee_members" USING btree ("id");--> statement-breakpoint CREATE INDEX "committee_members_idx_committee_id" ON "committee_members" USING btree ("committee_id");--> statement-breakpoint CREATE INDEX "committee_members_idx_member_id" ON "committee_members" USING btree ("member_id");--> statement-breakpoint @@ -148,25 +196,26 @@ CREATE INDEX "committee_members_idx_is_chair" ON "committee_members" USING btree CREATE INDEX "committees_idx_id" ON "committees" USING btree ("id");--> statement-breakpoint CREATE INDEX "committees_idx_title" ON "committees" USING btree ("title");--> statement-breakpoint CREATE INDEX "committees_idx_slug" ON "committees" USING btree ("slug");--> statement-breakpoint -CREATE INDEX "committees_idx_chair_id" ON "committees" USING btree ("chair_id");--> statement-breakpoint CREATE INDEX "committees_idx_created_at" ON "committees" USING btree ("created_at");--> statement-breakpoint CREATE INDEX "committees_idx_updated_at" ON "committees" USING btree ("updated_at");--> statement-breakpoint CREATE INDEX "event_attendees_idx_id" ON "event_attendees" USING btree ("id");--> statement-breakpoint CREATE INDEX "event_attendees_idx_event_id" ON "event_attendees" USING btree ("event_id");--> statement-breakpoint CREATE INDEX "event_attendees_idx_member_id" ON "event_attendees" USING btree ("member_id");--> statement-breakpoint -CREATE INDEX "events_idx_id" ON "events" USING btree ("id");--> statement-breakpoint -CREATE INDEX "events_idx_host" ON "events" USING btree ("host_type","host_id");--> statement-breakpoint -CREATE INDEX "events_idx_start_time" ON "events" USING btree ("start_time");--> statement-breakpoint -CREATE INDEX "events_idx_time_desc" ON "events" USING btree (start_time DESC);--> statement-breakpoint -CREATE INDEX "events_idx_title" ON "events" USING btree ("title");--> statement-breakpoint -CREATE INDEX "events_idx_location" ON "events" USING btree ("location");--> statement-breakpoint -CREATE INDEX "events_idx_created_at" ON "events" USING btree ("created_at");--> statement-breakpoint -CREATE INDEX "events_idx_updated_at" ON "events" USING btree ("updated_at");--> statement-breakpoint +CREATE INDEX "events_idx_host" ON "events" USING btree ("host_type","host_id" uuid_ops);--> statement-breakpoint +CREATE INDEX "events_idx_created_at" ON "events" USING btree ("created_at" timestamptz_ops);--> statement-breakpoint +CREATE INDEX "events_idx_id" ON "events" USING btree ("id" uuid_ops);--> statement-breakpoint +CREATE INDEX "events_idx_location" ON "events" USING btree ("location" text_ops);--> statement-breakpoint +CREATE INDEX "events_idx_start_time" ON "events" USING btree ("start_time" timestamptz_ops);--> statement-breakpoint +CREATE INDEX "events_idx_time_desc" ON "events" USING btree ("start_time" timestamptz_ops);--> statement-breakpoint +CREATE INDEX "events_idx_title" ON "events" USING btree ("title" text_ops);--> statement-breakpoint +CREATE INDEX "events_idx_updated_at" ON "events" USING btree ("updated_at" timestamptz_ops);--> statement-breakpoint CREATE INDEX "member_permissions_idx_member" ON "member_permissions" USING btree ("member_id");--> statement-breakpoint CREATE INDEX "member_permissions_idx_context" ON "member_permissions" USING btree ("context_type","context_id");--> statement-breakpoint CREATE INDEX "members_idx_id" ON "members" USING btree ("id");--> statement-breakpoint -CREATE INDEX "members_idx_discord_id" ON "members" USING btree ("discord_id");--> statement-breakpoint -CREATE INDEX "members_idx_email" ON "members" USING btree ("email");--> statement-breakpoint +CREATE INDEX "members_idx_user_id" ON "members" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "members_idx_personal_email" ON "members" USING btree ("personal_email");--> statement-breakpoint +CREATE INDEX "members_idx_ucf_email" ON "members" USING btree ("ucf_email");--> statement-breakpoint +CREATE INDEX "members_idx_officer_status" ON "members" USING btree ("officer_status");--> statement-breakpoint CREATE INDEX "members_idx_officer_role" ON "members" USING btree ("officer_role");--> statement-breakpoint CREATE INDEX "members_idx_administrator" ON "members" USING btree ("administrator");--> statement-breakpoint CREATE INDEX "members_idx_dues_paid" ON "members" USING btree ("dues_paid");--> statement-breakpoint @@ -185,8 +234,14 @@ CREATE INDEX "projects_idx_title" ON "projects" USING btree ("title");--> statem CREATE INDEX "projects_idx_slug" ON "projects" USING btree ("slug");--> statement-breakpoint CREATE INDEX "projects_idx_created_at" ON "projects" USING btree ("created_at");--> statement-breakpoint CREATE INDEX "projects_idx_updated_at" ON "projects" USING btree ("updated_at");--> statement-breakpoint +CREATE INDEX "scanning_sessions_idx_event" ON "scanning_sessions" USING btree ("event_id");--> statement-breakpoint +CREATE INDEX "scanning_sessions_idx_scanner" ON "scanning_sessions" USING btree ("scanner_id");--> statement-breakpoint +CREATE INDEX "scanning_sessions_idx_scanned_member" ON "scanning_sessions" USING btree ("scanned_member_id");--> statement-breakpoint +CREATE INDEX "scanning_sessions_idx_timestamp" ON "scanning_sessions" USING btree ("timestamp");--> statement-breakpoint CREATE INDEX "sponsorships_idx_id" ON "sponsorships" USING btree ("id");--> statement-breakpoint CREATE INDEX "sponsorships_idx_company_name" ON "sponsorships" USING btree ("company_name");--> statement-breakpoint CREATE INDEX "sponsorships_idx_tier" ON "sponsorships" USING btree ("tier");--> statement-breakpoint CREATE INDEX "sponsorships_idx_created_at" ON "sponsorships" USING btree ("created_at");--> statement-breakpoint -CREATE INDEX "sponsorships_idx_updated_at" ON "sponsorships" USING btree ("updated_at"); \ No newline at end of file +CREATE INDEX "sponsorships_idx_updated_at" ON "sponsorships" USING btree ("updated_at");--> statement-breakpoint +CREATE INDEX "users_idx_id" ON "users" USING btree ("id");--> statement-breakpoint +CREATE INDEX "users_idx_discord_id" ON "users" USING btree ("discordId"); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index 5a631ad..50116af 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,9 +1,109 @@ { - "id": "5a351107-6b3c-4c52-b8ff-ac12a55158ce", + "id": "c987dc40-43b8-4ac0-b629-05d7cc41a32c", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.committee_members": { "name": "committee_members", "schema": "", @@ -169,12 +269,6 @@ "primaryKey": false, "notNull": true }, - "chair_id": { - "name": "chair_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, "discord_role_id": { "name": "discord_role_id", "type": "varchar(64)", @@ -249,21 +343,6 @@ "method": "btree", "with": {} }, - "committees_idx_chair_id": { - "name": "committees_idx_chair_id", - "columns": [ - { - "expression": "chair_id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - }, "committees_idx_created_at": { "name": "committees_idx_created_at", "columns": [ @@ -295,21 +374,7 @@ "with": {} } }, - "foreignKeys": { - "committees_chair_id_members_id_fk": { - "name": "committees_chair_id_members_id_fk", - "tableFrom": "committees", - "tableTo": "members", - "columnsFrom": [ - "chair_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, + "foreignKeys": {}, "compositePrimaryKeys": {}, "uniqueConstraints": { "committees_slug_unique": { @@ -473,7 +538,8 @@ "type": "event_host_type_enum", "typeSchema": "public", "primaryKey": false, - "notNull": true + "notNull": true, + "default": "'committee'" }, "host_id": { "name": "host_id", @@ -481,38 +547,6 @@ "primaryKey": false, "notNull": false }, - "slug": { - "name": "slug", - "type": "varchar(64)", - "primaryKey": false, - "notNull": false - }, - "start_time": { - "name": "start_time", - "type": "timestamp with time zone", - "primaryKey": false, - "notNull": true - }, - "end_time": { - "name": "end_time", - "type": "timestamp with time zone", - "primaryKey": false, - "notNull": false - }, - "requires_dues": { - "name": "requires_dues", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "active": { - "name": "active", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": true - }, "description": { "name": "description", "type": "text", @@ -537,12 +571,6 @@ "primaryKey": false, "notNull": false }, - "duration": { - "name": "duration", - "type": "integer", - "primaryKey": false, - "notNull": false - }, "created_at": { "name": "created_at", "type": "timestamp with time zone", @@ -556,17 +584,56 @@ "primaryKey": false, "notNull": true, "default": "now()" + }, + "slug": { + "name": "slug", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "start_time": { + "name": "start_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "requires_dues": { + "name": "requires_dues", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true } }, "indexes": { - "events_idx_id": { - "name": "events_idx_id", + "events_idx_host": { + "name": "events_idx_host", "columns": [ { - "expression": "id", + "expression": "host_type", "isExpression": false, "asc": true, "nulls": "last" + }, + { + "expression": "host_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" } ], "isUnique": false, @@ -574,20 +641,15 @@ "method": "btree", "with": {} }, - "events_idx_host": { - "name": "events_idx_host", + "events_idx_created_at": { + "name": "events_idx_created_at", "columns": [ { - "expression": "host_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "host_id", + "expression": "created_at", "isExpression": false, "asc": true, - "nulls": "last" + "nulls": "last", + "opclass": "timestamptz_ops" } ], "isUnique": false, @@ -595,14 +657,15 @@ "method": "btree", "with": {} }, - "events_idx_start_time": { - "name": "events_idx_start_time", + "events_idx_id": { + "name": "events_idx_id", "columns": [ { - "expression": "start_time", + "expression": "id", "isExpression": false, "asc": true, - "nulls": "last" + "nulls": "last", + "opclass": "uuid_ops" } ], "isUnique": false, @@ -610,14 +673,15 @@ "method": "btree", "with": {} }, - "events_idx_time_desc": { - "name": "events_idx_time_desc", + "events_idx_location": { + "name": "events_idx_location", "columns": [ { - "expression": "start_time DESC", + "expression": "location", + "isExpression": false, "asc": true, - "isExpression": true, - "nulls": "last" + "nulls": "last", + "opclass": "text_ops" } ], "isUnique": false, @@ -625,14 +689,15 @@ "method": "btree", "with": {} }, - "events_idx_title": { - "name": "events_idx_title", + "events_idx_start_time": { + "name": "events_idx_start_time", "columns": [ { - "expression": "title", + "expression": "start_time", "isExpression": false, "asc": true, - "nulls": "last" + "nulls": "last", + "opclass": "timestamptz_ops" } ], "isUnique": false, @@ -640,14 +705,15 @@ "method": "btree", "with": {} }, - "events_idx_location": { - "name": "events_idx_location", + "events_idx_time_desc": { + "name": "events_idx_time_desc", "columns": [ { - "expression": "location", + "expression": "start_time", "isExpression": false, - "asc": true, - "nulls": "last" + "asc": false, + "nulls": "first", + "opclass": "timestamptz_ops" } ], "isUnique": false, @@ -655,14 +721,15 @@ "method": "btree", "with": {} }, - "events_idx_created_at": { - "name": "events_idx_created_at", + "events_idx_title": { + "name": "events_idx_title", "columns": [ { - "expression": "created_at", + "expression": "title", "isExpression": false, "asc": true, - "nulls": "last" + "nulls": "last", + "opclass": "text_ops" } ], "isUnique": false, @@ -677,7 +744,8 @@ "expression": "updated_at", "isExpression": false, "asc": true, - "nulls": "last" + "nulls": "last", + "opclass": "timestamptz_ops" } ], "isUnique": false, @@ -726,7 +794,8 @@ }, "context_type": { "name": "context_type", - "type": "varchar(32)", + "type": "context_type_enum", + "typeSchema": "public", "primaryKey": false, "notNull": true }, @@ -858,6 +927,12 @@ "notNull": true, "default": "gen_random_uuid()" }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, "first_name": { "name": "first_name", "type": "varchar(255)", @@ -890,6 +965,13 @@ "notNull": true, "default": false }, + "officer_status": { + "name": "officer_status", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, "biography": { "name": "biography", "type": "text", @@ -903,20 +985,20 @@ "notNull": true, "default": false }, - "discord_id": { - "name": "discord_id", - "type": "varchar(64)", - "primaryKey": false, - "notNull": true - }, "date_of_birth": { "name": "date_of_birth", "type": "date", "primaryKey": false, "notNull": true }, - "email": { - "name": "email", + "personal_email": { + "name": "personal_email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "ucf_email": { + "name": "ucf_email", "type": "varchar(255)", "primaryKey": false, "notNull": true @@ -1014,11 +1096,11 @@ "method": "btree", "with": {} }, - "members_idx_discord_id": { - "name": "members_idx_discord_id", + "members_idx_user_id": { + "name": "members_idx_user_id", "columns": [ { - "expression": "discord_id", + "expression": "user_id", "isExpression": false, "asc": true, "nulls": "last" @@ -1029,11 +1111,11 @@ "method": "btree", "with": {} }, - "members_idx_email": { - "name": "members_idx_email", + "members_idx_personal_email": { + "name": "members_idx_personal_email", "columns": [ { - "expression": "email", + "expression": "personal_email", "isExpression": false, "asc": true, "nulls": "last" @@ -1044,11 +1126,11 @@ "method": "btree", "with": {} }, - "members_idx_officer_role": { - "name": "members_idx_officer_role", + "members_idx_ucf_email": { + "name": "members_idx_ucf_email", "columns": [ { - "expression": "officer_role", + "expression": "ucf_email", "isExpression": false, "asc": true, "nulls": "last" @@ -1059,7 +1141,37 @@ "method": "btree", "with": {} }, - "members_idx_administrator": { + "members_idx_officer_status": { + "name": "members_idx_officer_status", + "columns": [ + { + "expression": "officer_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_idx_officer_role": { + "name": "members_idx_officer_role", + "columns": [ + { + "expression": "officer_role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_idx_administrator": { "name": "members_idx_administrator", "columns": [ { @@ -1186,21 +1298,42 @@ "with": {} } }, - "foreignKeys": {}, + "foreignKeys": { + "members_user_id_users_id_fk": { + "name": "members_user_id_users_id_fk", + "tableFrom": "members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": { - "members_discord_id_unique": { - "name": "members_discord_id_unique", + "members_user_id_unique": { + "name": "members_user_id_unique", "nullsNotDistinct": false, "columns": [ - "discord_id" + "user_id" ] }, - "members_email_unique": { - "name": "members_email_unique", + "members_personal_email_unique": { + "name": "members_personal_email_unique", "nullsNotDistinct": false, "columns": [ - "email" + "personal_email" + ] + }, + "members_ucf_email_unique": { + "name": "members_ucf_email_unique", + "nullsNotDistinct": false, + "columns": [ + "ucf_email" ] } }, @@ -1517,6 +1650,204 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.scanning_sessions": { + "name": "scanning_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "event_id": { + "name": "event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scanner_id": { + "name": "scanner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "scanned_member_id": { + "name": "scanned_member_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'qr'" + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scanning_sessions_idx_event": { + "name": "scanning_sessions_idx_event", + "columns": [ + { + "expression": "event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scanning_sessions_idx_scanner": { + "name": "scanning_sessions_idx_scanner", + "columns": [ + { + "expression": "scanner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scanning_sessions_idx_scanned_member": { + "name": "scanning_sessions_idx_scanned_member", + "columns": [ + { + "expression": "scanned_member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scanning_sessions_idx_timestamp": { + "name": "scanning_sessions_idx_timestamp", + "columns": [ + { + "expression": "timestamp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scanning_sessions_event_id_events_id_fk": { + "name": "scanning_sessions_event_id_events_id_fk", + "tableFrom": "scanning_sessions", + "tableTo": "events", + "columnsFrom": [ + "event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scanning_sessions_scanner_id_members_id_fk": { + "name": "scanning_sessions_scanner_id_members_id_fk", + "tableFrom": "scanning_sessions", + "tableTo": "members", + "columnsFrom": [ + "scanner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "scanning_sessions_scanned_member_id_members_id_fk": { + "name": "scanning_sessions_scanned_member_id_members_id_fk", + "tableFrom": "scanning_sessions", + "tableTo": "members", + "columnsFrom": [ + "scanned_member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "session_token": { + "name": "session_token", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.sponsorships": { "name": "sponsorships", "schema": "", @@ -1676,9 +2007,114 @@ "policies": {}, "checkConstraints": {}, "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_idx_id": { + "name": "users_idx_id", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_idx_discord_id": { + "name": "users_idx_discord_id", + "columns": [ + { + "expression": "discordId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_discordId_unique": { + "name": "users_discordId_unique", + "nullsNotDistinct": false, + "columns": [ + "discordId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false } }, "enums": { + "public.context_type_enum": { + "name": "context_type_enum", + "schema": "public", + "values": [ + "global", + "committee", + "project" + ] + }, "public.event_host_type_enum": { "name": "event_host_type_enum", "schema": "public", @@ -1704,11 +2140,19 @@ "name": "officer_role_enum", "schema": "public", "values": [ - "executive_chair", - "executive_vice_chair", - "executive_secretary", - "executive_treasurer", - "committee_lead" + "Executive Chair", + "Vice Chair", + "Treasurer", + "Secretary", + "Project Chair", + "Workshop Chair", + "Conference Chair", + "Outreach Chair", + "Service Chair", + "Social Chair", + "Professional Development Chair", + "Marketing Chair", + "Software Chair" ] }, "public.permission_enum": { @@ -1724,9 +2168,9 @@ "name": "sponsorship_tier_enum", "schema": "public", "values": [ - "bronze", - "silver", - "gold" + "Bronze", + "Silver", + "Gold" ] } }, diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index c82ae7a..5fc9a10 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "7", - "when": 1762386143733, - "tag": "0000_jazzy_exodus", + "when": 1763375319710, + "tag": "0000_chilly_bullseye", "breakpoints": true } ] diff --git a/drizzle/relations.ts b/drizzle/relations.ts deleted file mode 100644 index 4cd9bf4..0000000 --- a/drizzle/relations.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { relations } from "drizzle-orm/relations"; -import { members, committees, committeeMembers, projects, projectMembers, events, users, eventAttendees, sessions, accounts } from "./schema"; - -export const committeesRelations = relations(committees, ({one, many}) => ({ - member: one(members, { - fields: [committees.chairId], - references: [members.id] - }), - committeeMembers: many(committeeMembers), - events: many(events), -})); - -export const membersRelations = relations(members, ({one, many}) => ({ - committees: many(committees), - committeeMembers: many(committeeMembers), - projectMembers: many(projectMembers), - user: one(users, { - fields: [members.userId], - references: [users.id] - }), - eventAttendees: many(eventAttendees), -})); - -export const committeeMembersRelations = relations(committeeMembers, ({one}) => ({ - committee: one(committees, { - fields: [committeeMembers.committeeId], - references: [committees.id] - }), - member: one(members, { - fields: [committeeMembers.memberId], - references: [members.id] - }), -})); - -export const projectMembersRelations = relations(projectMembers, ({one}) => ({ - project: one(projects, { - fields: [projectMembers.projectId], - references: [projects.id] - }), - member: one(members, { - fields: [projectMembers.memberId], - references: [members.id] - }), -})); - -export const projectsRelations = relations(projects, ({many}) => ({ - projectMembers: many(projectMembers), -})); - -export const eventsRelations = relations(events, ({one, many}) => ({ - committee: one(committees, { - fields: [events.committeeId], - references: [committees.id] - }), - eventAttendees: many(eventAttendees), -})); - -export const usersRelations = relations(users, ({many}) => ({ - members: many(members), - sessions: many(sessions), - accounts: many(accounts), -})); - -export const eventAttendeesRelations = relations(eventAttendees, ({one}) => ({ - event: one(events, { - fields: [eventAttendees.eventId], - references: [events.id] - }), - member: one(members, { - fields: [eventAttendees.memberId], - references: [members.id] - }), -})); - -export const sessionsRelations = relations(sessions, ({one}) => ({ - user: one(users, { - fields: [sessions.userId], - references: [users.id] - }), -})); - -export const accountsRelations = relations(accounts, ({one}) => ({ - user: one(users, { - fields: [accounts.userId], - references: [users.id] - }), -})); \ No newline at end of file diff --git a/drizzle/schema.ts b/drizzle/schema.ts deleted file mode 100644 index 941aba5..0000000 --- a/drizzle/schema.ts +++ /dev/null @@ -1,271 +0,0 @@ -import { pgTable, index, foreignKey, unique, uuid, varchar, text, timestamp, boolean, date, integer, pgEnum } from "drizzle-orm/pg-core" -import { sql } from "drizzle-orm" - -export const genderEnum = pgEnum("gender_enum", ['M', 'F', 'NB', 'O', 'PNTS']) -export const officerRoleEnum = pgEnum("officer_role_enum", ['Executive Chair', 'Vice Chair', 'Treasurer', 'Secretary', 'Project Chair', 'Workshop Chair', 'Conference Chair', 'Outreach Chair', 'Service Chair', 'Social Chair', 'Professional Development Chair', 'Marketing Chair', 'Software Chair']) -export const sponsorshipTierEnum = pgEnum("sponsorship_tier_enum", ['Bronze', 'Silver', 'Gold']) - - -export const committees = pgTable("committees", { - id: uuid().defaultRandom().primaryKey().notNull(), - title: varchar({ length: 255 }).notNull(), - about: text().notNull(), - chairId: uuid("chair_id").notNull(), - createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - slug: varchar({ length: 64 }), - discordRoleId: varchar("discord_role_id", { length: 64 }), - active: boolean().default(true).notNull(), -}, (table) => [ - index("committees_idx_chair_id").using("btree", table.chairId.asc().nullsLast().op("uuid_ops")), - index("committees_idx_created_at").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")), - index("committees_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), - index("committees_idx_slug").using("btree", table.slug.asc().nullsLast().op("text_ops")), - index("committees_idx_title").using("btree", table.title.asc().nullsLast().op("text_ops")), - index("committees_idx_updated_at").using("btree", table.updatedAt.asc().nullsLast().op("timestamptz_ops")), - foreignKey({ - columns: [table.chairId], - foreignColumns: [members.id], - name: "committees_chair_id_members_id_fk" - }).onDelete("cascade"), - unique("committees_slug_unique").on(table.slug), -]); - -export const committeeMembers = pgTable("committee_members", { - id: uuid().defaultRandom().primaryKey().notNull(), - committeeId: uuid("committee_id").notNull(), - memberId: uuid("member_id").notNull(), - isChair: boolean("is_chair").default(false).notNull(), -}, (table) => [ - index("committee_members_idx_committee_id").using("btree", table.committeeId.asc().nullsLast().op("uuid_ops")), - index("committee_members_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), - index("committee_members_idx_is_chair").using("btree", table.isChair.asc().nullsLast().op("bool_ops")), - index("committee_members_idx_member_id").using("btree", table.memberId.asc().nullsLast().op("uuid_ops")), - foreignKey({ - columns: [table.committeeId], - foreignColumns: [committees.id], - name: "committee_members_committee_id_committees_id_fk" - }).onDelete("cascade"), - foreignKey({ - columns: [table.memberId], - foreignColumns: [members.id], - name: "committee_members_member_id_members_id_fk" - }).onDelete("cascade"), - unique("committee_member_unique").on(table.committeeId, table.memberId), -]); - -export const projectMembers = pgTable("project_members", { - id: uuid().defaultRandom().primaryKey().notNull(), - projectId: uuid("project_id").notNull(), - memberId: uuid("member_id").notNull(), - isLead: boolean("is_lead").default(false).notNull(), -}, (table) => [ - index("project_members_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), - index("project_members_idx_is_lead").using("btree", table.isLead.asc().nullsLast().op("bool_ops")), - index("project_members_idx_member_id").using("btree", table.memberId.asc().nullsLast().op("uuid_ops")), - index("project_members_idx_project_id").using("btree", table.projectId.asc().nullsLast().op("uuid_ops")), - foreignKey({ - columns: [table.projectId], - foreignColumns: [projects.id], - name: "project_members_project_id_projects_id_fk" - }).onDelete("cascade"), - foreignKey({ - columns: [table.memberId], - foreignColumns: [members.id], - name: "project_members_member_id_members_id_fk" - }).onDelete("cascade"), - unique("project_member_unique").on(table.projectId, table.memberId), -]); - -export const events = pgTable("events", { - id: uuid().defaultRandom().primaryKey().notNull(), - title: varchar({ length: 255 }).notNull(), - location: varchar({ length: 255 }).notNull(), - committeeId: uuid("committee_id"), - description: text().notNull(), - flyerUrl: varchar("flyer_url", { length: 500 }), - rsvpLink: varchar("rsvp_link", { length: 500 }), - photoUrls: text("photo_urls"), - createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - slug: varchar({ length: 64 }), - startTime: timestamp("start_time", { withTimezone: true, mode: 'string' }).notNull(), - endTime: timestamp("end_time", { withTimezone: true, mode: 'string' }), - requiresDues: boolean("requires_dues").default(false).notNull(), - active: boolean().default(true).notNull(), -}, (table) => [ - index("events_idx_committee_id").using("btree", table.committeeId.asc().nullsLast().op("uuid_ops")), - index("events_idx_created_at").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")), - index("events_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), - index("events_idx_location").using("btree", table.location.asc().nullsLast().op("text_ops")), - index("events_idx_start_time").using("btree", table.startTime.asc().nullsLast().op("timestamptz_ops")), - index("events_idx_time_desc").using("btree", table.startTime.desc().nullsFirst().op("timestamptz_ops")), - index("events_idx_title").using("btree", table.title.asc().nullsLast().op("text_ops")), - index("events_idx_updated_at").using("btree", table.updatedAt.asc().nullsLast().op("timestamptz_ops")), - foreignKey({ - columns: [table.committeeId], - foreignColumns: [committees.id], - name: "events_committee_id_committees_id_fk" - }).onDelete("cascade"), - unique("events_slug_unique").on(table.slug), -]); - -export const members = pgTable("members", { - id: uuid().defaultRandom().primaryKey().notNull(), - firstName: varchar("first_name", { length: 255 }).notNull(), - middleName: varchar("middle_name", { length: 255 }), - lastName: varchar("last_name", { length: 255 }).notNull(), - officerStatus: boolean("officer_status").default(false).notNull(), - officerRole: officerRoleEnum("officer_role"), - administrator: boolean().default(false).notNull(), - biography: text(), - duesPaid: boolean("dues_paid").default(false).notNull(), - discordId: varchar({ length: 64 }).notNull(), - dateOfBirth: date("date_of_birth").notNull(), - personalEmail: varchar("personal_email", { length: 255 }).notNull(), - phoneNumber: varchar("phone_number", { length: 20 }), - major: varchar({ length: 255 }).notNull(), - gender: genderEnum().notNull(), - graduationYear: integer("graduation_year").notNull(), - resumeUrl: text("resume_url"), - linkedinUrl: text("linkedin_url"), - githubUrl: text("github_url"), - websiteUrl: text("website_url"), - createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - active: boolean().default(true).notNull(), - userId: uuid("user_id"), - ucfEmail: varchar("ucf_email", { length: 255 }).notNull(), -}, (table) => [ - index("members_idx_active_officer").using("btree", table.officerStatus.asc().nullsLast().op("bool_ops"), table.administrator.asc().nullsLast().op("bool_ops")), - index("members_idx_administrator").using("btree", table.administrator.asc().nullsLast().op("bool_ops")), - index("members_idx_created_at").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")), - index("members_idx_discordId").using("btree", table.discordId.asc().nullsLast().op("text_ops")), - index("members_idx_dues_paid").using("btree", table.duesPaid.asc().nullsLast().op("bool_ops")), - index("members_idx_full_name").using("btree", table.firstName.asc().nullsLast().op("text_ops"), table.lastName.asc().nullsLast().op("text_ops")), - index("members_idx_gender").using("btree", table.gender.asc().nullsLast().op("enum_ops")), - index("members_idx_graduation_year").using("btree", table.graduationYear.asc().nullsLast().op("int4_ops")), - index("members_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), - index("members_idx_major").using("btree", table.major.asc().nullsLast().op("text_ops")), - index("members_idx_officer_role").using("btree", table.officerRole.asc().nullsLast().op("enum_ops")), - index("members_idx_officer_status").using("btree", table.officerStatus.asc().nullsLast().op("bool_ops")), - index("members_idx_personal_email").using("btree", table.personalEmail.asc().nullsLast().op("text_ops")), - index("members_idx_ucf_email").using("btree", table.ucfEmail.asc().nullsLast().op("text_ops")), - index("members_idx_updated_at").using("btree", table.updatedAt.asc().nullsLast().op("timestamptz_ops")), - foreignKey({ - columns: [table.userId], - foreignColumns: [users.id], - name: "members_user_id_users_id_fk" - }).onDelete("cascade"), - unique("members_discordId_unique").on(table.discordId), - unique("members_personal_email_unique").on(table.personalEmail), - unique("members_ucf_email_unique").on(table.ucfEmail), -]); - -export const projects = pgTable("projects", { - id: uuid().defaultRandom().primaryKey().notNull(), - title: varchar({ length: 255 }).notNull(), - overview: text().notNull(), - hardwareInfo: text("hardware_info"), - softwareInfo: text("software_info"), - skills: text(), - photoUrls: text("photo_urls"), - createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - slug: varchar({ length: 64 }), - discordRoleId: varchar("discord_role_id", { length: 64 }), - active: boolean().default(true).notNull(), -}, (table) => [ - index("projects_idx_created_at").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")), - index("projects_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), - index("projects_idx_slug").using("btree", table.slug.asc().nullsLast().op("text_ops")), - index("projects_idx_title").using("btree", table.title.asc().nullsLast().op("text_ops")), - index("projects_idx_updated_at").using("btree", table.updatedAt.asc().nullsLast().op("timestamptz_ops")), - unique("projects_slug_unique").on(table.slug), -]); - -export const eventAttendees = pgTable("event_attendees", { - id: uuid().defaultRandom().primaryKey().notNull(), - eventId: uuid("event_id").notNull(), - memberId: uuid("member_id").notNull(), - timestamp: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), -}, (table) => [ - index("event_attendees_idx_event_id").using("btree", table.eventId.asc().nullsLast().op("uuid_ops")), - index("event_attendees_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), - index("event_attendees_idx_member_id").using("btree", table.memberId.asc().nullsLast().op("uuid_ops")), - foreignKey({ - columns: [table.eventId], - foreignColumns: [events.id], - name: "event_attendees_event_id_events_id_fk" - }).onDelete("cascade"), - foreignKey({ - columns: [table.memberId], - foreignColumns: [members.id], - name: "event_attendees_member_id_members_id_fk" - }).onDelete("cascade"), - unique("event_attendee_unique").on(table.eventId, table.memberId), -]); - -export const sessions = pgTable("sessions", { - sessionToken: varchar("session_token", { length: 255 }).primaryKey().notNull(), - userId: uuid("user_id").notNull(), - expires: timestamp({ withTimezone: true, mode: 'string' }).notNull(), -}, (table) => [ - foreignKey({ - columns: [table.userId], - foreignColumns: [users.id], - name: "sessions_user_id_users_id_fk" - }).onDelete("cascade"), -]); - -export const users = pgTable("users", { - id: uuid().defaultRandom().primaryKey().notNull(), - name: text(), - email: varchar({ length: 255 }).notNull(), - emailVerified: timestamp("email_verified", { withTimezone: true, mode: 'string' }), - image: text(), - discordId: varchar({ length: 64 }).notNull(), -}, (table) => [ - unique("users_email_unique").on(table.email), -]); - -export const sponsorships = pgTable("sponsorships", { - id: uuid().defaultRandom().primaryKey().notNull(), - companyName: varchar("company_name", { length: 255 }).notNull(), - moneyDonated: integer("money_donated").notNull(), - description: text(), - tier: sponsorshipTierEnum().notNull(), - companyLogoUrl: varchar("company_logo_url", { length: 500 }), - websiteUrl: varchar("website_url", { length: 500 }), - contactEmail: varchar("contact_email", { length: 255 }).notNull(), - createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), - active: boolean().default(true).notNull(), -}, (table) => [ - index("sponsorships_idx_company_name").using("btree", table.companyName.asc().nullsLast().op("text_ops")), - index("sponsorships_idx_created_at").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")), - index("sponsorships_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), - index("sponsorships_idx_tier").using("btree", table.tier.asc().nullsLast().op("enum_ops")), - index("sponsorships_idx_updated_at").using("btree", table.updatedAt.asc().nullsLast().op("timestamptz_ops")), -]); - -export const accounts = pgTable("accounts", { - id: uuid().defaultRandom().primaryKey().notNull(), - userId: uuid("user_id").notNull(), - type: varchar({ length: 255 }).notNull(), - provider: varchar({ length: 255 }).notNull(), - providerAccountId: varchar("provider_account_id", { length: 255 }).notNull(), - refreshToken: text("refresh_token"), - accessToken: text("access_token"), - expiresAt: integer("expires_at"), - tokenType: varchar("token_type", { length: 255 }), - scope: varchar({ length: 255 }), - idToken: text("id_token"), - sessionState: varchar("session_state", { length: 255 }), -}, (table) => [ - foreignKey({ - columns: [table.userId], - foreignColumns: [users.id], - name: "accounts_user_id_users_id_fk" - }).onDelete("cascade"), -]); diff --git a/package.json b/package.json index 38aae62..c9eade0 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,9 @@ "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tooltip": "^1.2.8", - "@tanstack/react-query": "^5.90.7", + "@tanstack/react-query": "^5.90.10", "@trpc/client": "^11.7.1", "@trpc/react-query": "^11.7.1", "@trpc/server": "^11.7.1", @@ -42,20 +42,21 @@ "lucide-react": "^0.510.0", "luxon": "^3.7.2", "mongodb": "^6.21.0", - "mongoose": "^8.19.3", + "mongoose": "^8.19.4", "next": "15.3.2", "next-auth": "^4.24.13", "next-sitemap": "^4.2.3", "pg": "8.16.3", + "pg-core@latest": "link:drizzle-orm/pg-core@latest", "postgres": "^3.4.7", "qrcode": "^1.5.4", - "pg-core@latest": "link:drizzle-orm/pg-core@latest", "react": "^19.2.0", "react-dom": "^19.2.0", "react-icons": "^5.5.0", "react-pdf": "^10.2.0", "react-vertical-timeline-component": "^3.5.3", "recharts": "^2.15.4", + "server-only": "^0.0.1", "superjson": "^2.2.5", "tailwind-merge": "^3.4.0", "yargs": "^18.0.0", @@ -69,10 +70,10 @@ "@types/node": "^20.19.25", "@types/pg": "^8.15.6", "@types/qrcode": "^1.5.6", - "@types/react": "^19.2.4", + "@types/react": "^19.2.5", "@types/react-dom": "^19.2.3", "@types/react-vertical-timeline-component": "^3.3.6", - "@types/yargs": "^17.0.34", + "@types/yargs": "^17.0.35", "autoprefixer": "^10.4.22", "dotenv": "^17.2.3", "drizzle-kit": "^0.31.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9be9114..b105168 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,55 +16,55 @@ importers: version: 1.11.1(pg@8.16.3) '@emotion/react': specifier: ^11.14.0 - version: 11.14.0(@types/react@19.2.4)(react@19.2.0) + version: 11.14.0(@types/react@19.2.5)(react@19.2.0) '@emotion/styled': specifier: ^11.14.1 - version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0) + version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0) '@heroicons/react': specifier: ^2.2.0 version: 2.2.0(react@19.2.0) '@mui/icons-material': specifier: ^7.3.5 - version: 7.3.5(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.4)(react@19.2.0) + version: 7.3.5(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.5)(react@19.2.0) '@neondatabase/serverless': specifier: ^1.0.2 version: 1.0.2 '@radix-ui/react-accordion': specifier: ^1.2.12 - version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-dialog': specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-hover-card': specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-label': specifier: ^2.1.8 - version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-navigation-menu': specifier: ^1.2.14 - version: 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-select': specifier: ^2.2.6 - version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-separator': specifier: ^1.1.8 - version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-slot': - specifier: ^1.2.3 - version: 1.2.3(@types/react@19.2.4)(react@19.2.0) + specifier: ^1.2.4 + version: 1.2.4(@types/react@19.2.5)(react@19.2.0) '@radix-ui/react-tooltip': specifier: ^1.2.8 - version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tanstack/react-query': - specifier: ^5.90.7 - version: 5.90.7(react@19.2.0) + specifier: ^5.90.10 + version: 5.90.10(react@19.2.0) '@trpc/client': specifier: ^11.7.1 version: 11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3) '@trpc/react-query': specifier: ^11.7.1 - version: 11.7.1(@tanstack/react-query@5.90.7(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + version: 11.7.1(@tanstack/react-query@5.90.10(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) '@trpc/server': specifier: ^11.7.1 version: 11.7.1(typescript@5.9.3) @@ -99,7 +99,7 @@ importers: specifier: ^6.21.0 version: 6.21.0 mongoose: - specifier: ^8.19.3 + specifier: ^8.19.4 version: 8.19.4 next: specifier: 15.3.2 @@ -133,13 +133,16 @@ importers: version: 5.5.0(react@19.2.0) react-pdf: specifier: ^10.2.0 - version: 10.2.0(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 10.2.0(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react-vertical-timeline-component: specifier: ^3.5.3 version: 3.5.3(@babel/core@7.0.0-beta.56)(react@19.2.0) recharts: specifier: ^2.15.4 version: 2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + server-only: + specifier: ^0.0.1 + version: 0.0.1 superjson: specifier: ^2.2.5 version: 2.2.5 @@ -175,16 +178,16 @@ importers: specifier: ^1.5.6 version: 1.5.6 '@types/react': - specifier: ^19.2.4 - version: 19.2.4 + specifier: ^19.2.5 + version: 19.2.5 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.4) + version: 19.2.3(@types/react@19.2.5) '@types/react-vertical-timeline-component': specifier: ^3.3.6 version: 3.3.6 '@types/yargs': - specifier: ^17.0.34 + specifier: ^17.0.35 version: 17.0.35 autoprefixer: specifier: ^10.4.22 @@ -526,11 +529,11 @@ packages: '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - '@emnapi/core@1.7.0': - resolution: {integrity: sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.7.0': - resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -1149,8 +1152,8 @@ packages: '@types/react': optional: true - '@mui/private-theming@7.3.3': - resolution: {integrity: sha512-OJM+9nj5JIyPUvsZ5ZjaeC9PfktmK+W5YaVLToLR8L0lB/DGmv1gcKE43ssNLSvpoW71Hct0necfade6+kW3zQ==} + '@mui/private-theming@7.3.5': + resolution: {integrity: sha512-cTx584W2qrLonwhZLbEN7P5pAUu0nZblg8cLBlTrZQ4sIiw8Fbvg7GvuphQaSHxPxrCpa7FDwJKtXdbl2TSmrA==} engines: {node: '>=14.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -1159,8 +1162,8 @@ packages: '@types/react': optional: true - '@mui/styled-engine@7.3.3': - resolution: {integrity: sha512-CmFxvRJIBCEaWdilhXMw/5wFJ1+FT9f3xt+m2pPXhHPeVIbBg9MnMvNSJjdALvnQJMPw8jLhrUtXmN7QAZV2fw==} + '@mui/styled-engine@7.3.5': + resolution: {integrity: sha512-zbsZ0uYYPndFCCPp2+V3RLcAN6+fv4C8pdwRx6OS3BwDkRCN8WBehqks7hWyF3vj1kdQLIWrpdv/5Y0jHRxYXQ==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.4.1 @@ -1172,8 +1175,8 @@ packages: '@emotion/styled': optional: true - '@mui/system@7.3.3': - resolution: {integrity: sha512-Lqq3emZr5IzRLKaHPuMaLBDVaGvxoh6z7HMWd1RPKawBM5uMRaQ4ImsmmgXWtwJdfZux5eugfDhXJUo2mliS8Q==} + '@mui/system@7.3.5': + resolution: {integrity: sha512-yPaf5+gY3v80HNkJcPi6WT+r9ebeM4eJzrREXPxMt7pNTV/1eahyODO4fbH3Qvd8irNxDFYn5RQ3idHW55rA6g==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -1188,16 +1191,16 @@ packages: '@types/react': optional: true - '@mui/types@7.4.7': - resolution: {integrity: sha512-8vVje9rdEr1rY8oIkYgP+Su5Kwl6ik7O3jQ0wl78JGSmiZhRHV+vkjooGdKD8pbtZbutXFVTWQYshu2b3sG9zw==} + '@mui/types@7.4.8': + resolution: {integrity: sha512-ZNXLBjkPV6ftLCmmRCafak3XmSn8YV0tKE/ZOhzKys7TZXUiE0mZxlH8zKDo6j6TTUaDnuij68gIG+0Ucm7Xhw==} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true - '@mui/utils@7.3.3': - resolution: {integrity: sha512-kwNAUh7bLZ7mRz9JZ+6qfRnnxbE4Zuc+RzXnhSpRSxjTlSTj7b4JxRLXpG+MVtPVtqks5k/XC8No1Vs3x4Z2gg==} + '@mui/utils@7.3.5': + resolution: {integrity: sha512-jisvFsEC3sgjUjcPnR4mYfhzjCDIudttSGSbe1o/IXFNu0kZuR+7vqQI0jg8qtcVZBHWrwTfvAZj9MNMumcq1g==} engines: {node: '>=14.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -1847,11 +1850,11 @@ packages: '@tailwindcss/postcss@4.1.17': resolution: {integrity: sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==} - '@tanstack/query-core@5.90.7': - resolution: {integrity: sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ==} + '@tanstack/query-core@5.90.10': + resolution: {integrity: sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==} - '@tanstack/react-query@5.90.7': - resolution: {integrity: sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ==} + '@tanstack/react-query@5.90.10': + resolution: {integrity: sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==} peerDependencies: react: ^18 || ^19 @@ -1949,8 +1952,8 @@ packages: '@types/react-vertical-timeline-component@3.3.6': resolution: {integrity: sha512-OUvyPXRjXvUD/SNLO0CW0GbIxVF32Ios5qHecMSfw6kxnK1cPULD9NV80EuqZ3WmS/s6BgbcwmN8k4ISb3akhQ==} - '@types/react@19.2.4': - resolution: {integrity: sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A==} + '@types/react@19.2.5': + resolution: {integrity: sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw==} '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -1964,14 +1967,6 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.46.2': - resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.46.2 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/eslint-plugin@8.46.4': resolution: {integrity: sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1980,13 +1975,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.46.2': - resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.46.4': resolution: {integrity: sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1994,45 +1982,22 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.46.2': - resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.46.4': resolution: {integrity: sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.46.2': - resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.46.4': resolution: {integrity: sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.46.2': - resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.46.4': resolution: {integrity: sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.46.2': - resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.46.4': resolution: {integrity: sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2040,33 +2005,16 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.46.2': - resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.46.4': resolution: {integrity: sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.46.2': - resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.46.4': resolution: {integrity: sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.46.2': - resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.46.4': resolution: {integrity: sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2074,10 +2022,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.46.2': - resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.46.4': resolution: {integrity: sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2336,9 +2280,6 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001753: - resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==} - caniuse-lite@1.0.30001755: resolution: {integrity: sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==} @@ -2405,8 +2346,8 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.2: + resolution: {integrity: sha512-D80T+tiqkd/8B0xNlbstWDG4x6aqVfO52+OlSUNIdkTvmNw0uQpJLeos2J/2XvpyidAFuTPmpad+tUxLndwj6g==} d3-array@3.2.4: resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} @@ -3188,8 +3129,8 @@ packages: jose@4.15.9: resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - jose@6.1.1: - resolution: {integrity: sha512-GWSqjfOPf4cWOkBzw5THBjtGPhXKqYnfRBzh4Ni+ArTrQQ9unvmsA3oFLqaYKoKe5sjWmGu5wVKg9Ft1i+LQfg==} + jose@6.1.2: + resolution: {integrity: sha512-MpcPtHLE5EmztuFIqB0vzHAWJPpmN1E6L4oo+kze56LIs3MyXIj9ZHMDxqOvkP38gBR7K1v3jqd4WU2+nrfONQ==} js-tokens@3.0.2: resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} @@ -3953,6 +3894,9 @@ packages: engines: {node: '>=10'} hasBin: true + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -4348,7 +4292,7 @@ snapshots: '@auth/core@0.41.1': dependencies: '@panva/hkdf': 1.2.1 - jose: 6.1.1 + jose: 6.1.2 oauth4webapi: 3.8.2 preact: 10.24.3 preact-render-to-string: 6.5.11(preact@10.24.3) @@ -4823,13 +4767,13 @@ snapshots: '@drizzle-team/brocli@0.10.2': {} - '@emnapi/core@1.7.0': + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.7.0': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true @@ -4871,7 +4815,7 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0)': + '@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 @@ -4883,7 +4827,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 transitivePeerDependencies: - supports-color @@ -4893,22 +4837,22 @@ snapshots: '@emotion/memoize': 0.9.0 '@emotion/unitless': 0.10.0 '@emotion/utils': 1.4.2 - csstype: 3.1.3 + csstype: 3.2.2 '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)': + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@19.2.4)(react@19.2.0) + '@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0) '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.0) '@emotion/utils': 1.4.2 react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 transitivePeerDependencies: - supports-color @@ -5239,7 +5183,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.7.0 + '@emnapi/runtime': 1.7.1 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -5276,90 +5220,90 @@ snapshots: '@mui/core-downloads-tracker@7.3.5': {} - '@mui/icons-material@7.3.5(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)': + '@mui/icons-material@7.3.5(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/material': 7.3.4(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@mui/material': 7.3.4(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 '@mui/core-downloads-tracker': 7.3.5 - '@mui/system': 7.3.3(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0) - '@mui/types': 7.4.7(@types/react@19.2.4) - '@mui/utils': 7.3.3(@types/react@19.2.4)(react@19.2.0) + '@mui/system': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0) + '@mui/types': 7.4.8(@types/react@19.2.5) + '@mui/utils': 7.3.5(@types/react@19.2.5)(react@19.2.0) '@popperjs/core': 2.11.8 - '@types/react-transition-group': 4.4.12(@types/react@19.2.4) + '@types/react-transition-group': 4.4.12(@types/react@19.2.5) clsx: 2.1.1 - csstype: 3.1.3 + csstype: 3.2.2 prop-types: 15.8.1 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) react-is: 19.2.0 react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.4)(react@19.2.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0) - '@types/react': 19.2.4 + '@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0) + '@types/react': 19.2.5 - '@mui/private-theming@7.3.3(@types/react@19.2.4)(react@19.2.0)': + '@mui/private-theming@7.3.5(@types/react@19.2.5)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/utils': 7.3.3(@types/react@19.2.4)(react@19.2.0) + '@mui/utils': 7.3.5(@types/react@19.2.5)(react@19.2.0) prop-types: 15.8.1 react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@mui/styled-engine@7.3.3(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(react@19.2.0)': + '@mui/styled-engine@7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 '@emotion/sheet': 1.4.0 - csstype: 3.1.3 + csstype: 3.2.2 prop-types: 15.8.1 react: 19.2.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.4)(react@19.2.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0) + '@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0) - '@mui/system@7.3.3(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0)': + '@mui/system@7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/private-theming': 7.3.3(@types/react@19.2.4)(react@19.2.0) - '@mui/styled-engine': 7.3.3(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0))(react@19.2.0) - '@mui/types': 7.4.7(@types/react@19.2.4) - '@mui/utils': 7.3.3(@types/react@19.2.4)(react@19.2.0) + '@mui/private-theming': 7.3.5(@types/react@19.2.5)(react@19.2.0) + '@mui/styled-engine': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0))(react@19.2.0) + '@mui/types': 7.4.8(@types/react@19.2.5) + '@mui/utils': 7.3.5(@types/react@19.2.5)(react@19.2.0) clsx: 2.1.1 - csstype: 3.1.3 + csstype: 3.2.2 prop-types: 15.8.1 react: 19.2.0 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.4)(react@19.2.0) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.4)(react@19.2.0))(@types/react@19.2.4)(react@19.2.0) - '@types/react': 19.2.4 + '@emotion/react': 11.14.0(@types/react@19.2.5)(react@19.2.0) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.5)(react@19.2.0))(@types/react@19.2.5)(react@19.2.0) + '@types/react': 19.2.5 - '@mui/types@7.4.7(@types/react@19.2.4)': + '@mui/types@7.4.8(@types/react@19.2.5)': dependencies: '@babel/runtime': 7.28.4 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@mui/utils@7.3.3(@types/react@19.2.4)(react@19.2.0)': + '@mui/utils@7.3.5(@types/react@19.2.5)(react@19.2.0)': dependencies: '@babel/runtime': 7.28.4 - '@mui/types': 7.4.7(@types/react@19.2.4) + '@mui/types': 7.4.8(@types/react@19.2.5) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 react: 19.2.0 react-is: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 '@napi-rs/canvas-android-arm64@0.1.82': optional: true @@ -5407,8 +5351,8 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.7.0 - '@emnapi/runtime': 1.7.0 + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true @@ -5471,375 +5415,375 @@ snapshots: '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.5)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-context@1.1.2(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-context@1.1.2(@types/react@19.2.5)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.5)(react@19.2.0) aria-hidden: 1.2.6 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.4)(react@19.2.0) + react-remove-scroll: 2.7.1(@types/react@19.2.5)(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-direction@1.1.1(@types/react@19.2.5)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.5)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-id@1.1.1(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-id@1.1.1(@types/react@19.2.5)(react@19.2.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.5)(react@19.2.0) '@radix-ui/rect': 1.1.1 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) aria-hidden: 1.2.6 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.4)(react@19.2.0) + react-remove-scroll: 2.7.1(@types/react@19.2.5)(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.5)(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-slot@1.2.4(@types/react@19.2.5)(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.5)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.5)(react@19.2.0)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.4)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.5)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.5)(react@19.2.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.5)(react@19.2.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.5)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.5)(react@19.2.0)': dependencies: react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.5)(react@19.2.0)': dependencies: '@radix-ui/rect': 1.1.1 react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.4)(react@19.2.0)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.5)(react@19.2.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.5)(react@19.2.0) react: 19.2.0 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 - '@types/react-dom': 19.2.3(@types/react@19.2.4) + '@types/react': 19.2.5 + '@types/react-dom': 19.2.3(@types/react@19.2.5) '@radix-ui/rect@1.1.1': {} @@ -5922,11 +5866,11 @@ snapshots: postcss: 8.5.6 tailwindcss: 4.1.17 - '@tanstack/query-core@5.90.7': {} + '@tanstack/query-core@5.90.10': {} - '@tanstack/react-query@5.90.7(react@19.2.0)': + '@tanstack/react-query@5.90.10(react@19.2.0)': dependencies: - '@tanstack/query-core': 5.90.7 + '@tanstack/query-core': 5.90.10 react: 19.2.0 '@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3)': @@ -5934,9 +5878,9 @@ snapshots: '@trpc/server': 11.7.1(typescript@5.9.3) typescript: 5.9.3 - '@trpc/react-query@11.7.1(@tanstack/react-query@5.90.7(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': + '@trpc/react-query@11.7.1(@tanstack/react-query@5.90.10(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': dependencies: - '@tanstack/react-query': 5.90.7(react@19.2.0) + '@tanstack/react-query': 5.90.10(react@19.2.0) '@trpc/client': 11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3) '@trpc/server': 11.7.1(typescript@5.9.3) react: 19.2.0 @@ -6006,21 +5950,21 @@ snapshots: dependencies: '@types/node': 20.19.25 - '@types/react-dom@19.2.3(@types/react@19.2.4)': + '@types/react-dom@19.2.3(@types/react@19.2.5)': dependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@types/react-transition-group@4.4.12(@types/react@19.2.4)': + '@types/react-transition-group@4.4.12(@types/react@19.2.5)': dependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 '@types/react-vertical-timeline-component@3.3.6': dependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - '@types/react@19.2.4': + '@types/react@19.2.5': dependencies: - csstype: 3.1.3 + csstype: 3.2.2 '@types/webidl-conversions@7.0.3': {} @@ -6034,23 +5978,6 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/type-utils': 8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.2 - eslint: 9.39.1(jiti@2.6.1) - graphemer: 1.4.0 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/eslint-plugin@8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -6068,18 +5995,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.2 - debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.46.4 @@ -6092,15 +6007,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.2(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) - '@typescript-eslint/types': 8.46.2 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/project-service@8.46.4(typescript@5.9.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) @@ -6110,36 +6016,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.46.2': - dependencies: - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/visitor-keys': 8.46.2 - '@typescript-eslint/scope-manager@8.46.4': dependencies: '@typescript-eslint/types': 8.46.4 '@typescript-eslint/visitor-keys': 8.46.4 - '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.46.4(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/type-utils@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.46.4 @@ -6152,26 +6037,8 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.46.2': {} - '@typescript-eslint/types@8.46.4': {} - '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/visitor-keys': 8.46.2 - debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@8.46.4(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.46.4(typescript@5.9.3) @@ -6188,17 +6055,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -6210,11 +6066,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.46.2': - dependencies: - '@typescript-eslint/types': 8.46.2 - eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.46.4': dependencies: '@typescript-eslint/types': 8.46.4 @@ -6463,8 +6314,6 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001753: {} - caniuse-lite@1.0.30001755: {} chalk@2.4.2: @@ -6536,7 +6385,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - csstype@3.1.3: {} + csstype@3.2.2: {} d3-array@3.2.4: dependencies: @@ -6637,7 +6486,7 @@ snapshots: dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.28.4 - csstype: 3.1.3 + csstype: 3.2.2 dotenv@17.2.3: {} @@ -6868,12 +6717,12 @@ snapshots: dependencies: '@next/eslint-plugin-next': 15.3.2 '@rushstack/eslint-patch': 1.15.0 - '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.46.2(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) @@ -6903,21 +6752,22 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -6928,7 +6778,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -7396,7 +7246,7 @@ snapshots: jose@4.15.9: {} - jose@6.1.1: {} + jose@6.1.2: {} js-tokens@3.0.2: {} @@ -7539,9 +7389,9 @@ snapshots: memory-pager@1.5.0: {} - merge-refs@2.0.0(@types/react@19.2.4): + merge-refs@2.0.0(@types/react@19.2.5): optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 merge2@1.4.1: {} @@ -7647,7 +7497,7 @@ snapshots: '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 - caniuse-lite: 1.0.30001753 + caniuse-lite: 1.0.30001755 postcss: 8.4.31 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) @@ -7908,39 +7758,39 @@ snapshots: react-is@19.2.0: {} - react-pdf@10.2.0(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + react-pdf@10.2.0(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: clsx: 2.1.1 dequal: 2.0.3 make-cancellable-promise: 2.0.0 make-event-props: 2.0.0 - merge-refs: 2.0.0(@types/react@19.2.4) + merge-refs: 2.0.0(@types/react@19.2.5) pdfjs-dist: 5.4.296 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) tiny-invariant: 1.3.3 warning: 4.0.3 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - react-remove-scroll-bar@2.3.8(@types/react@19.2.4)(react@19.2.0): + react-remove-scroll-bar@2.3.8(@types/react@19.2.5)(react@19.2.0): dependencies: react: 19.2.0 - react-style-singleton: 2.2.3(@types/react@19.2.4)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.5)(react@19.2.0) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - react-remove-scroll@2.7.1(@types/react@19.2.4)(react@19.2.0): + react-remove-scroll@2.7.1(@types/react@19.2.5)(react@19.2.0): dependencies: react: 19.2.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.4)(react@19.2.0) - react-style-singleton: 2.2.3(@types/react@19.2.4)(react@19.2.0) + react-remove-scroll-bar: 2.3.8(@types/react@19.2.5)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.5)(react@19.2.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.4)(react@19.2.0) - use-sidecar: 1.1.3(@types/react@19.2.4)(react@19.2.0) + use-callback-ref: 1.3.3(@types/react@19.2.5)(react@19.2.0) + use-sidecar: 1.1.3(@types/react@19.2.5)(react@19.2.0) optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 react-smooth@4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: @@ -7950,13 +7800,13 @@ snapshots: react-dom: 19.2.0(react@19.2.0) react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react-style-singleton@2.2.3(@types/react@19.2.4)(react@19.2.0): + react-style-singleton@2.2.3(@types/react@19.2.5)(react@19.2.0): dependencies: get-nonce: 1.0.1 react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: @@ -8094,6 +7944,8 @@ snapshots: semver@7.7.3: {} + server-only@0.0.1: {} + set-blocking@2.0.0: {} set-function-length@1.2.2: @@ -8453,20 +8305,20 @@ snapshots: dependencies: punycode: 2.3.1 - use-callback-ref@1.3.3(@types/react@19.2.4)(react@19.2.0): + use-callback-ref@1.3.3(@types/react@19.2.5)(react@19.2.0): dependencies: react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 - use-sidecar@1.1.3(@types/react@19.2.4)(react@19.2.0): + use-sidecar@1.1.3(@types/react@19.2.5)(react@19.2.0): dependencies: detect-node-es: 1.1.0 react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.4 + '@types/react': 19.2.5 uuid@8.3.2: {} diff --git a/src/lib/database/schema.ts b/src/lib/database/schema.ts index ca6a1e9..3996d10 100644 --- a/src/lib/database/schema.ts +++ b/src/lib/database/schema.ts @@ -28,6 +28,13 @@ export const permissionEnum = pgEnum('permission_enum', [ 'manage_context', ]); +// Context Types: global, committee, project +export const contextTypeEnum = pgEnum('context_type_enum', [ + 'global', + 'committee', + 'project', +]); + // Gender: Male (M), Female (F), Non-Binary (NB), Other (O), Prefer Not to Say (PNTS) export const genderEnum = pgEnum('gender_enum', [ 'M', 'F', 'NB', 'O', 'PNTS', @@ -50,16 +57,18 @@ export const eventHostTypeEnum = pgEnum('event_host_type_enum', [ // ==== Schemas ==== -// basically we need this for authentication with nextauth and drizzle, and we need to link it in members +// NextAuth Tables export const Users = pgTable("users", { id: uuid("id").primaryKey().defaultRandom(), name: text("name"), email: varchar("email", { length: 255 }).notNull().unique(), emailVerified: timestamp("email_verified", { withTimezone: true }), image: text("image"), // pull from discord - discordId: varchar("discordId", { length: 64 }), -}); - + discordID: varchar('discordId', { length: 64 }).unique(), +}, (table) => [ + index('users_idx_id').on(table.id), + index('users_idx_discord_id').on(table.discordID), +]); export const Accounts = pgTable("accounts", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id").notNull().references(() => Users.id, { onDelete: "cascade" }), @@ -74,7 +83,6 @@ export const Accounts = pgTable("accounts", { id_token: text("id_token"), session_state: varchar("session_state", { length: 255 }), }); - export const Sessions = pgTable("sessions", { sessionToken: varchar("session_token", { length: 255 }).primaryKey(), userId: uuid("user_id").notNull().references(() => Users.id, { onDelete: "cascade" }), @@ -84,7 +92,7 @@ export const Sessions = pgTable("sessions", { // Members export const Members = pgTable('members', { id: uuid('id').primaryKey().defaultRandom(), - userId: uuid("user_id").references(() => Users.id, { onDelete: "cascade" }), // we reference that authentication information + userId: uuid("user_id").notNull().unique().references(() => Users.id, { onDelete: "cascade" }), // 1:1 relationship with auth user firstName: varchar('first_name', { length: 255 }).notNull(), middleName: varchar('middle_name', { length: 255 }), lastName: varchar('last_name', { length: 255 }).notNull(), @@ -93,7 +101,7 @@ export const Members = pgTable('members', { officerStatus: boolean('officer_status').notNull().default(false), biography: text('biography'), duesPaid: boolean('dues_paid').notNull().default(false), - discordID: varchar('discordId', { length: 64 }).unique(), + // discordID: varchar('discordId', { length: 64 }).unique(), // Removed in favor of Users.discordID for now dateOfBirth: date('date_of_birth').notNull(), personalEmail: varchar('personal_email', { length: 255 }).notNull().unique(), ucfEmail: varchar('ucf_email', { length: 255 }).notNull().unique(), @@ -111,7 +119,8 @@ export const Members = pgTable('members', { updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow().$onUpdate(() => sql`now()`), }, (table) => [ index('members_idx_id').on(table.id), - index('members_idx_discordId').on(table.discordID), + index('members_idx_user_id').on(table.userId), + // index('members_idx_discordId').on(table.discordID), index('members_idx_personal_email').on(table.personalEmail), index('members_idx_ucf_email').on(table.ucfEmail), index('members_idx_officer_status').on(table.officerStatus), @@ -132,7 +141,6 @@ export const Committees = pgTable('committees', { title: varchar('title', { length: 255 }).notNull(), slug: varchar('slug', { length: 64 }).unique(), // URL-friendly identifier, smth like "software" committee or "solarcar" project about: text('about').notNull(), - chairId: uuid('chair_id').notNull().references(() => Members.id, { onDelete: 'cascade' }), discordRoleId: varchar('discord_role_id', { length: 64 }), active: boolean('active').notNull().default(true), createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), @@ -141,7 +149,6 @@ export const Committees = pgTable('committees', { index('committees_idx_id').on(table.id), index('committees_idx_title').on(table.title), index('committees_idx_slug').on(table.slug), - index('committees_idx_chair_id').on(table.chairId), index('committees_idx_created_at').on(table.createdAt), index('committees_idx_updated_at').on(table.updatedAt), ]); @@ -164,7 +171,8 @@ export const Events = pgTable("events", { id: uuid().defaultRandom().primaryKey().notNull(), title: varchar({ length: 255 }).notNull(), location: varchar({ length: 255 }).notNull(), - committeeId: uuid("committee_id"), + hostType: eventHostTypeEnum('host_type').notNull().default('committee'), // club, committee, project, or member + hostId: uuid('host_id'), // Points to committee/project/member ID based on hostType description: text().notNull(), flyerUrl: varchar("flyer_url", { length: 500 }), rsvpLink: varchar("rsvp_link", { length: 500 }), @@ -177,7 +185,7 @@ export const Events = pgTable("events", { requiresDues: boolean("requires_dues").default(false).notNull(), active: boolean().default(true).notNull(), }, (table) => [ - index("events_idx_committee_id").using("btree", table.committeeId.asc().nullsLast().op("uuid_ops")), + index("events_idx_host").using("btree", table.hostType.asc().nullsLast(), table.hostId.asc().nullsLast().op("uuid_ops")), index("events_idx_created_at").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")), index("events_idx_id").using("btree", table.id.asc().nullsLast().op("uuid_ops")), index("events_idx_location").using("btree", table.location.asc().nullsLast().op("text_ops")), @@ -185,11 +193,6 @@ export const Events = pgTable("events", { index("events_idx_time_desc").using("btree", table.startTime.desc().nullsFirst().op("timestamptz_ops")), index("events_idx_title").using("btree", table.title.asc().nullsLast().op("text_ops")), index("events_idx_updated_at").using("btree", table.updatedAt.asc().nullsLast().op("timestamptz_ops")), - foreignKey({ - columns: [table.committeeId], - foreignColumns: [Committees.id], - name: "events_committee_id_committees_id_fk", - }).onDelete("cascade"), unique("events_slug_unique").on(table.slug), ]); // EventAttendees: Join table for many-to-many relation between Events and Members @@ -270,12 +273,19 @@ export const UsersRelations = relations(Users, ({ one }) => ({ }), })); -export const MembersRelations = relations(Members, ({ one }) => ({ +export const MembersRelations = relations(Members, ({ one, many }) => ({ user: one(Users, { fields: [Members.userId], references: [Users.id], }), -})) + committeeMembers: many(CommitteeMembers), + projectMembers: many(ProjectMembers), + eventAttendees: many(EventAttendees), + permissionsGranted: many(MemberPermissions, { relationName: 'grantedBy' }), + permissions: many(MemberPermissions, { relationName: 'hasPermission' }), + scannedSessions: many(ScanningSessions, { relationName: 'scanned' }), + scannerSessions: many(ScanningSessions, { relationName: 'scanner' }), +})); export const AccountRelations = relations(Accounts, ({ one }) => ({ user: one(Users, { @@ -291,6 +301,51 @@ export const SessionRelations = relations(Sessions, ({ one }) => ({ }), })); +export const CommitteesRelations = relations(Committees, ({ many }) => ({ + members: many(CommitteeMembers), +})); + +export const CommitteeMembersRelations = relations(CommitteeMembers, ({ one }) => ({ + committee: one(Committees, { + fields: [CommitteeMembers.committeeId], + references: [Committees.id], + }), + member: one(Members, { + fields: [CommitteeMembers.memberId], + references: [Members.id], + }), +})); + +export const EventsRelations = relations(Events, ({ many }) => ({ + attendees: many(EventAttendees), + scanningSessions: many(ScanningSessions), +})); + +export const EventAttendeesRelations = relations(EventAttendees, ({ one }) => ({ + event: one(Events, { + fields: [EventAttendees.eventId], + references: [Events.id], + }), + member: one(Members, { + fields: [EventAttendees.memberId], + references: [Members.id], + }), +})); + +export const ProjectsRelations = relations(Projects, ({ many }) => ({ + members: many(ProjectMembers), +})); + +export const ProjectMembersRelations = relations(ProjectMembers, ({ one }) => ({ + project: one(Projects, { + fields: [ProjectMembers.projectId], + references: [Projects.id], + }), + member: one(Members, { + fields: [ProjectMembers.memberId], + references: [Members.id], + }), +})); // MemberPermissions: Delegated or custom permissions for members @@ -298,7 +353,7 @@ export const MemberPermissions = pgTable('member_permissions', { id: uuid('id').primaryKey().defaultRandom(), memberId: uuid('member_id').notNull().references(() => Members.id, { onDelete: 'cascade' }), grantedById: uuid('granted_by_id').references(() => Members.id, { onDelete: 'set null' }), // who granted the permission - contextType: varchar('context_type', { length: 32 }).notNull(), // e.g., 'committee', 'project', 'global' + contextType: contextTypeEnum('context_type').notNull(), // 'global', 'committee', or 'project' contextId: uuid('context_id'), // links to a specific committee/project if applicable permission: permissionEnum('permission').notNull(), active: boolean('active').notNull().default(true), @@ -310,6 +365,51 @@ export const MemberPermissions = pgTable('member_permissions', { unique('member_permission_unique').on(table.memberId, table.contextType, table.contextId, table.permission), ]); +// ScanningSessions: Audit trail for attendance scanning +export const ScanningSessions = pgTable('scanning_sessions', { + id: uuid('id').primaryKey().defaultRandom(), + eventId: uuid('event_id').notNull().references(() => Events.id, { onDelete: 'cascade' }), + scannerId: uuid('scanner_id').references(() => Members.id, { onDelete: 'set null' }), // Who performed the scan + scannedMemberId: uuid('scanned_member_id').notNull().references(() => Members.id, { onDelete: 'cascade' }), // Who was scanned + method: varchar('method', { length: 32 }).notNull().default('qr'), // 'qr', 'manual', 'api' + timestamp: timestamp('timestamp', { withTimezone: true }).notNull().defaultNow(), +}, (table) => [ + index('scanning_sessions_idx_event').on(table.eventId), + index('scanning_sessions_idx_scanner').on(table.scannerId), + index('scanning_sessions_idx_scanned_member').on(table.scannedMemberId), + index('scanning_sessions_idx_timestamp').on(table.timestamp), +]); + +export const MemberPermissionsRelations = relations(MemberPermissions, ({ one }) => ({ + member: one(Members, { + relationName: 'hasPermission', + fields: [MemberPermissions.memberId], + references: [Members.id], + }), + grantedBy: one(Members, { + relationName: 'grantedBy', + fields: [MemberPermissions.grantedById], + references: [Members.id], + }), +})); + +export const ScanningSessionsRelations = relations(ScanningSessions, ({ one }) => ({ + event: one(Events, { + fields: [ScanningSessions.eventId], + references: [Events.id], + }), + scanner: one(Members, { + relationName: 'scanner', + fields: [ScanningSessions.scannerId], + references: [Members.id], + }), + scannedMember: one(Members, { + relationName: 'scanned', + fields: [ScanningSessions.scannedMemberId], + references: [Members.id], + }), +})); + // Infer Types export type Member = typeof Members.$inferSelect; export type NewMember = typeof Members.$inferInsert; @@ -329,3 +429,5 @@ export type Sponsorship = typeof Sponsorships.$inferSelect; export type NewSponsorship = typeof Sponsorships.$inferInsert; export type MemberPermission = typeof MemberPermissions.$inferSelect; export type NewMemberPermission = typeof MemberPermissions.$inferInsert; +export type ScanningSession = typeof ScanningSessions.$inferSelect; +export type NewScanningSession = typeof ScanningSessions.$inferInsert; diff --git a/src/lib/mongodb.js b/src/lib/mongodb.js deleted file mode 100644 index 8dc2f6d..0000000 --- a/src/lib/mongodb.js +++ /dev/null @@ -1,31 +0,0 @@ -import mongoose from 'mongoose'; - -const MONGODB_URI = process.env.MONGODB_URI; - -if (!MONGODB_URI) { - throw new Error('oh no we done messed up'); -} - -let cached = global.mongoose; - -if (!cached) { - cached = global.mongoose = { conn: null, promise: null }; -} - -export async function dbConnect() { - if (cached.conn) { - return cached.conn; - } - - if (!cached.promise) { - cached.promise = mongoose.connect(MONGODB_URI).then((mongoose) => { - return mongoose; - }) - .catch((error) => { - throw error; - }); - } - cached.conn = await cached.promise; - return cached.conn; - -} From ba61acae66b122dddb0c73cb65fca41c0ecf9358 Mon Sep 17 00:00:00 2001 From: Thandi Menelas Date: Mon, 17 Nov 2025 06:28:32 -0500 Subject: [PATCH 2/7] Updated infer types --- src/lib/database/schema.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/database/schema.ts b/src/lib/database/schema.ts index 3996d10..7911ccd 100644 --- a/src/lib/database/schema.ts +++ b/src/lib/database/schema.ts @@ -411,6 +411,12 @@ export const ScanningSessionsRelations = relations(ScanningSessions, ({ one }) = })); // Infer Types +export type User = typeof Users.$inferSelect; +export type NewUser = typeof Users.$inferInsert; +export type Account = typeof Accounts.$inferSelect; +export type NewAccount = typeof Accounts.$inferInsert; +export type Session = typeof Sessions.$inferSelect; +export type NewSession = typeof Sessions.$inferInsert; export type Member = typeof Members.$inferSelect; export type NewMember = typeof Members.$inferInsert; export type Event = typeof Events.$inferSelect; From 5ac1a896845002191e65c2f27fa1921747ca90cf Mon Sep 17 00:00:00 2001 From: Thandi Menelas Date: Mon, 17 Nov 2025 21:27:04 -0500 Subject: [PATCH 3/7] leaving mongodb lib for now until migration is done --- src/lib/mongodb.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/lib/mongodb.js diff --git a/src/lib/mongodb.js b/src/lib/mongodb.js new file mode 100644 index 0000000..7f6ff85 --- /dev/null +++ b/src/lib/mongodb.js @@ -0,0 +1,32 @@ +import mongoose from 'mongoose'; + +const MONGODB_URI = process.env.MONGODB_URI; + +if (!MONGODB_URI) { + throw new Error('oh no we done messed up'); +} + +let cached = global.mongoose; + +if (!cached) { + cached = global.mongoose = { conn: null, promise: null }; +} + +export async function dbConnect() { + if (cached.conn) { + return cached.conn; + } + + if (!cached.promise) { + cached.promise = mongoose.connect(MONGODB_URI).then((mongoose) => { + return mongoose; + }) + .catch((error) => { + throw error; + }); + } + cached.conn = await cached.promise; + return cached.conn; + +} + From 4520da0e3597622914ce0e6cc725abe65283a6b4 Mon Sep 17 00:00:00 2001 From: Thandi Menelas Date: Mon, 17 Nov 2025 22:00:07 -0500 Subject: [PATCH 4/7] Fixed some discordid inconsistencies --- src/app/auth/signin/page.tsx | 4 ++-- src/lib/auth.ts | 2 +- src/lib/database/schema.ts | 6 +++--- src/lib/trpc/routers/auth.ts | 6 +++--- src/lib/trpc/routers/member.ts | 2 +- src/types/next-auth.d.ts | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/app/auth/signin/page.tsx b/src/app/auth/signin/page.tsx index 7148d84..f44f732 100644 --- a/src/app/auth/signin/page.tsx +++ b/src/app/auth/signin/page.tsx @@ -12,7 +12,7 @@ export default async function SignInPage() { const session = await getServerSession(authOptions); if (session) { - if (!session.user.discordId) { + if (!session.user.discordID) { redirect("/auth/error"); return; } @@ -20,7 +20,7 @@ export default async function SignInPage() { const [member] = await db .select() .from(Members) - .where(eq(Members.discordID, session.user.discordId)) + .where(eq(Members.discordID, session.user.discordID)) .limit(1); if (member) redirect("/"); diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 34709bb..8cc7a7f 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -62,7 +62,7 @@ export const authOptions: NextAuthOptions = { user: { ...session.user, id: user.id, - discordId: account?.providerAccountId || null, + discordID: account?.providerAccountId || null, memberId: member?.id || null, officerStatus: member?.officerStatus || false, officerRole: member?.officerRole || null, diff --git a/src/lib/database/schema.ts b/src/lib/database/schema.ts index 7911ccd..1dbde7c 100644 --- a/src/lib/database/schema.ts +++ b/src/lib/database/schema.ts @@ -64,7 +64,7 @@ export const Users = pgTable("users", { email: varchar("email", { length: 255 }).notNull().unique(), emailVerified: timestamp("email_verified", { withTimezone: true }), image: text("image"), // pull from discord - discordID: varchar('discordId', { length: 64 }).unique(), + discordID: varchar('discordID', { length: 64 }).unique(), }, (table) => [ index('users_idx_id').on(table.id), index('users_idx_discord_id').on(table.discordID), @@ -101,7 +101,7 @@ export const Members = pgTable('members', { officerStatus: boolean('officer_status').notNull().default(false), biography: text('biography'), duesPaid: boolean('dues_paid').notNull().default(false), - // discordID: varchar('discordId', { length: 64 }).unique(), // Removed in favor of Users.discordID for now + discordID: varchar('discordID', { length: 64 }).unique(), dateOfBirth: date('date_of_birth').notNull(), personalEmail: varchar('personal_email', { length: 255 }).notNull().unique(), ucfEmail: varchar('ucf_email', { length: 255 }).notNull().unique(), @@ -120,7 +120,7 @@ export const Members = pgTable('members', { }, (table) => [ index('members_idx_id').on(table.id), index('members_idx_user_id').on(table.userId), - // index('members_idx_discordId').on(table.discordID), + index('members_idx_discordID').on(table.discordID), index('members_idx_personal_email').on(table.personalEmail), index('members_idx_ucf_email').on(table.ucfEmail), index('members_idx_officer_status').on(table.officerStatus), diff --git a/src/lib/trpc/routers/auth.ts b/src/lib/trpc/routers/auth.ts index 5ae5bb3..ba0cd85 100644 --- a/src/lib/trpc/routers/auth.ts +++ b/src/lib/trpc/routers/auth.ts @@ -123,8 +123,8 @@ export const authRouter = { let discordAvatar = userWithDiscord?.image || null; - if (!discordAvatar && userWithDiscord?.discordId) { - discordAvatar = `https://cdn.discordapp.com/embed/avatars/${parseInt(userWithDiscord.discordId) % 5}.png`; + if (!discordAvatar && userWithDiscord?.discordID) { + discordAvatar = `https://cdn.discordapp.com/embed/avatars/${parseInt(userWithDiscord.discordID) % 5}.png`; } return { @@ -136,7 +136,7 @@ export const authRouter = { officerRole: member?.officerRole || null, user: ctx.session.user, member: member || null, - profile: ctx.session?.user.discordId, + profile: ctx.session?.user.discordID, discordAvatar, }; }), diff --git a/src/lib/trpc/routers/member.ts b/src/lib/trpc/routers/member.ts index 019d542..c07f1c7 100644 --- a/src/lib/trpc/routers/member.ts +++ b/src/lib/trpc/routers/member.ts @@ -64,7 +64,7 @@ export const memberRouter = createTRPCRouter({ } // collect the discord id from the user or session created when they logged in with discord auth - const discordID = ctx.session.user.discordId || ctx.session.user.id; + const discordID = ctx.session.user.discordID || ctx.session.user.id; // insert the new member const newMember = await db diff --git a/src/types/next-auth.d.ts b/src/types/next-auth.d.ts index 40584cf..e37e4fd 100644 --- a/src/types/next-auth.d.ts +++ b/src/types/next-auth.d.ts @@ -8,7 +8,7 @@ declare module "next-auth" { name?: string | null; email?: string | null; image?: string | null; - discordId?: string; + discordID?: string; memberId?: string; officerStatus: boolean; officerRole?: string | null; @@ -21,6 +21,6 @@ declare module "next-auth" { name?: string | null; email?: string | null; image?: string | null; - discordId?: string; + discordID?: string; } } From a9ce0e234abfd5f234f19a1476a964456fb4a8dd Mon Sep 17 00:00:00 2001 From: Thandi Menelas Date: Mon, 17 Nov 2025 22:00:39 -0500 Subject: [PATCH 5/7] Updated migrations --- ..._bullseye.sql => 0000_modern_valkyrie.sql} | 9 ++-- drizzle/meta/0000_snapshot.json | 42 +++++++++++++++---- drizzle/meta/_journal.json | 4 +- 3 files changed, 43 insertions(+), 12 deletions(-) rename drizzle/{0000_chilly_bullseye.sql => 0000_modern_valkyrie.sql} (98%) diff --git a/drizzle/0000_chilly_bullseye.sql b/drizzle/0000_modern_valkyrie.sql similarity index 98% rename from drizzle/0000_chilly_bullseye.sql rename to drizzle/0000_modern_valkyrie.sql index f6fd5a4..6cef2b1 100644 --- a/drizzle/0000_chilly_bullseye.sql +++ b/drizzle/0000_modern_valkyrie.sql @@ -91,6 +91,7 @@ CREATE TABLE "members" ( "officer_status" boolean DEFAULT false NOT NULL, "biography" text, "dues_paid" boolean DEFAULT false NOT NULL, + "discordID" varchar(64), "date_of_birth" date NOT NULL, "personal_email" varchar(255) NOT NULL, "ucf_email" varchar(255) NOT NULL, @@ -107,6 +108,7 @@ CREATE TABLE "members" ( "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "members_user_id_unique" UNIQUE("user_id"), + CONSTRAINT "members_discordID_unique" UNIQUE("discordID"), CONSTRAINT "members_personal_email_unique" UNIQUE("personal_email"), CONSTRAINT "members_ucf_email_unique" UNIQUE("ucf_email") ); @@ -170,9 +172,9 @@ CREATE TABLE "users" ( "email" varchar(255) NOT NULL, "email_verified" timestamp with time zone, "image" text, - "discordId" varchar(64), + "discordID" varchar(64), CONSTRAINT "users_email_unique" UNIQUE("email"), - CONSTRAINT "users_discordId_unique" UNIQUE("discordId") + CONSTRAINT "users_discordID_unique" UNIQUE("discordID") ); --> statement-breakpoint ALTER TABLE "accounts" ADD CONSTRAINT "accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint @@ -213,6 +215,7 @@ CREATE INDEX "member_permissions_idx_member" ON "member_permissions" USING btree CREATE INDEX "member_permissions_idx_context" ON "member_permissions" USING btree ("context_type","context_id");--> statement-breakpoint CREATE INDEX "members_idx_id" ON "members" USING btree ("id");--> statement-breakpoint CREATE INDEX "members_idx_user_id" ON "members" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "members_idx_discordID" ON "members" USING btree ("discordID");--> statement-breakpoint CREATE INDEX "members_idx_personal_email" ON "members" USING btree ("personal_email");--> statement-breakpoint CREATE INDEX "members_idx_ucf_email" ON "members" USING btree ("ucf_email");--> statement-breakpoint CREATE INDEX "members_idx_officer_status" ON "members" USING btree ("officer_status");--> statement-breakpoint @@ -244,4 +247,4 @@ CREATE INDEX "sponsorships_idx_tier" ON "sponsorships" USING btree ("tier");--> CREATE INDEX "sponsorships_idx_created_at" ON "sponsorships" USING btree ("created_at");--> statement-breakpoint CREATE INDEX "sponsorships_idx_updated_at" ON "sponsorships" USING btree ("updated_at");--> statement-breakpoint CREATE INDEX "users_idx_id" ON "users" USING btree ("id");--> statement-breakpoint -CREATE INDEX "users_idx_discord_id" ON "users" USING btree ("discordId"); \ No newline at end of file +CREATE INDEX "users_idx_discord_id" ON "users" USING btree ("discordID"); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index 50116af..c30cf8b 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,5 +1,5 @@ { - "id": "c987dc40-43b8-4ac0-b629-05d7cc41a32c", + "id": "74fd9606-4f85-4bfb-982f-54b478de7d8c", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", @@ -985,6 +985,12 @@ "notNull": true, "default": false }, + "discordID": { + "name": "discordID", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, "date_of_birth": { "name": "date_of_birth", "type": "date", @@ -1111,6 +1117,21 @@ "method": "btree", "with": {} }, + "members_idx_discordID": { + "name": "members_idx_discordID", + "columns": [ + { + "expression": "discordID", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, "members_idx_personal_email": { "name": "members_idx_personal_email", "columns": [ @@ -1322,6 +1343,13 @@ "user_id" ] }, + "members_discordID_unique": { + "name": "members_discordID_unique", + "nullsNotDistinct": false, + "columns": [ + "discordID" + ] + }, "members_personal_email_unique": { "name": "members_personal_email_unique", "nullsNotDistinct": false, @@ -2043,8 +2071,8 @@ "primaryKey": false, "notNull": false }, - "discordId": { - "name": "discordId", + "discordID": { + "name": "discordID", "type": "varchar(64)", "primaryKey": false, "notNull": false @@ -2070,7 +2098,7 @@ "name": "users_idx_discord_id", "columns": [ { - "expression": "discordId", + "expression": "discordID", "isExpression": false, "asc": true, "nulls": "last" @@ -2092,11 +2120,11 @@ "email" ] }, - "users_discordId_unique": { - "name": "users_discordId_unique", + "users_discordID_unique": { + "name": "users_discordID_unique", "nullsNotDistinct": false, "columns": [ - "discordId" + "discordID" ] } }, diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 5fc9a10..307d2d7 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "7", - "when": 1763375319710, - "tag": "0000_chilly_bullseye", + "when": 1763434828774, + "tag": "0000_modern_valkyrie", "breakpoints": true } ] From 8731ba619efcc07855e4d4a17b156dad91e8dce8 Mon Sep 17 00:00:00 2001 From: Thandi Menelas Date: Tue, 18 Nov 2025 23:24:37 -0500 Subject: [PATCH 6/7] Updated schema and referenced code --- src/lib/database/schema.ts | 46 +++++++----------------------------- src/lib/trpc/routers/auth.ts | 2 +- 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/src/lib/database/schema.ts b/src/lib/database/schema.ts index 1dbde7c..e065c61 100644 --- a/src/lib/database/schema.ts +++ b/src/lib/database/schema.ts @@ -200,11 +200,14 @@ export const EventAttendees = pgTable('event_attendees', { id: uuid('id').primaryKey().defaultRandom(), eventId: uuid('event_id').notNull().references(() => Events.id, { onDelete: 'cascade' }), memberId: uuid('member_id').notNull().references(() => Members.id, { onDelete: 'cascade' }), + scannerId: uuid('scanner_id').references(() => Members.id, { onDelete: 'set null' }), // Who performed the scan timestamp: timestamp('timestamp', { withTimezone: true }).notNull().defaultNow(), }, (table) => [ index('event_attendees_idx_id').on(table.id), index('event_attendees_idx_event_id').on(table.eventId), index('event_attendees_idx_member_id').on(table.memberId), + index('event_attendees_idx_scanner_id').on(table.scannerId), + index('event_attendees_idx_timestamp').on(table.timestamp), unique('event_attendee_unique').on(table.eventId, table.memberId), ]); @@ -283,8 +286,7 @@ export const MembersRelations = relations(Members, ({ one, many }) => ({ eventAttendees: many(EventAttendees), permissionsGranted: many(MemberPermissions, { relationName: 'grantedBy' }), permissions: many(MemberPermissions, { relationName: 'hasPermission' }), - scannedSessions: many(ScanningSessions, { relationName: 'scanned' }), - scannerSessions: many(ScanningSessions, { relationName: 'scanner' }), + scannedAttendees: many(EventAttendees, { relationName: 'scanned' }), })); export const AccountRelations = relations(Accounts, ({ one }) => ({ @@ -318,7 +320,6 @@ export const CommitteeMembersRelations = relations(CommitteeMembers, ({ one }) = export const EventsRelations = relations(Events, ({ many }) => ({ attendees: many(EventAttendees), - scanningSessions: many(ScanningSessions), })); export const EventAttendeesRelations = relations(EventAttendees, ({ one }) => ({ @@ -330,6 +331,11 @@ export const EventAttendeesRelations = relations(EventAttendees, ({ one }) => ({ fields: [EventAttendees.memberId], references: [Members.id], }), + scanner: one(Members, { + relationName: 'scanned', + fields: [EventAttendees.scannerId], + references: [Members.id], + }), })); export const ProjectsRelations = relations(Projects, ({ many }) => ({ @@ -365,21 +371,6 @@ export const MemberPermissions = pgTable('member_permissions', { unique('member_permission_unique').on(table.memberId, table.contextType, table.contextId, table.permission), ]); -// ScanningSessions: Audit trail for attendance scanning -export const ScanningSessions = pgTable('scanning_sessions', { - id: uuid('id').primaryKey().defaultRandom(), - eventId: uuid('event_id').notNull().references(() => Events.id, { onDelete: 'cascade' }), - scannerId: uuid('scanner_id').references(() => Members.id, { onDelete: 'set null' }), // Who performed the scan - scannedMemberId: uuid('scanned_member_id').notNull().references(() => Members.id, { onDelete: 'cascade' }), // Who was scanned - method: varchar('method', { length: 32 }).notNull().default('qr'), // 'qr', 'manual', 'api' - timestamp: timestamp('timestamp', { withTimezone: true }).notNull().defaultNow(), -}, (table) => [ - index('scanning_sessions_idx_event').on(table.eventId), - index('scanning_sessions_idx_scanner').on(table.scannerId), - index('scanning_sessions_idx_scanned_member').on(table.scannedMemberId), - index('scanning_sessions_idx_timestamp').on(table.timestamp), -]); - export const MemberPermissionsRelations = relations(MemberPermissions, ({ one }) => ({ member: one(Members, { relationName: 'hasPermission', @@ -393,23 +384,6 @@ export const MemberPermissionsRelations = relations(MemberPermissions, ({ one }) }), })); -export const ScanningSessionsRelations = relations(ScanningSessions, ({ one }) => ({ - event: one(Events, { - fields: [ScanningSessions.eventId], - references: [Events.id], - }), - scanner: one(Members, { - relationName: 'scanner', - fields: [ScanningSessions.scannerId], - references: [Members.id], - }), - scannedMember: one(Members, { - relationName: 'scanned', - fields: [ScanningSessions.scannedMemberId], - references: [Members.id], - }), -})); - // Infer Types export type User = typeof Users.$inferSelect; export type NewUser = typeof Users.$inferInsert; @@ -435,5 +409,3 @@ export type Sponsorship = typeof Sponsorships.$inferSelect; export type NewSponsorship = typeof Sponsorships.$inferInsert; export type MemberPermission = typeof MemberPermissions.$inferSelect; export type NewMemberPermission = typeof MemberPermissions.$inferInsert; -export type ScanningSession = typeof ScanningSessions.$inferSelect; -export type NewScanningSession = typeof ScanningSessions.$inferInsert; diff --git a/src/lib/trpc/routers/auth.ts b/src/lib/trpc/routers/auth.ts index dc81f37..98e59d9 100644 --- a/src/lib/trpc/routers/auth.ts +++ b/src/lib/trpc/routers/auth.ts @@ -122,7 +122,7 @@ export const authRouter = { let discordAvatar = userWithDiscord?.image || null; - + if (!discordAvatar && userWithDiscord?.discordID) { discordAvatar = `https://cdn.discordapp.com/embed/avatars/${parseInt(userWithDiscord.discordID) % 5}.png`; } From fc90530580c0d982538e877acd6c74225256f7d7 Mon Sep 17 00:00:00 2001 From: Thandi Menelas Date: Wed, 19 Nov 2025 00:38:47 -0500 Subject: [PATCH 7/7] Update test database setup and documentation --- src/lib/database/SCHEMA.md | 520 ++++++++++++++++++++ src/lib/database/schema.dbml | 308 ++++++++++++ src/testing/db/README.md | 323 +++++------- src/testing/db/data/accounts.json | 114 +++++ src/testing/db/data/committee_members.json | 42 +- src/testing/db/data/committees.json | 26 +- src/testing/db/data/event_attendees.json | 65 ++- src/testing/db/data/events.json | 122 ++++- src/testing/db/data/member_permissions.json | 36 +- src/testing/db/data/members.json | 196 +++++++- src/testing/db/data/project_members.json | 56 ++- src/testing/db/data/projects.json | 44 +- src/testing/db/data/sponsorships.json | 59 ++- src/testing/db/data/users.json | 66 +++ src/testing/db/drizzle.config.ts | 4 +- src/testing/db/seed.ts | 18 +- src/testing/db/test-db.sh | 94 ++++ 17 files changed, 1823 insertions(+), 270 deletions(-) create mode 100644 src/lib/database/SCHEMA.md create mode 100644 src/lib/database/schema.dbml create mode 100644 src/testing/db/data/accounts.json create mode 100644 src/testing/db/data/users.json create mode 100755 src/testing/db/test-db.sh diff --git a/src/lib/database/SCHEMA.md b/src/lib/database/SCHEMA.md new file mode 100644 index 0000000..2d0ae34 --- /dev/null +++ b/src/lib/database/SCHEMA.md @@ -0,0 +1,520 @@ +# Database Schema Reference +#### Last Updated: November 18, 2025 + +This document provides a detailed reference for the IEEE-UCF website database schema. All schema definitions are maintained in `src/lib/database/schema.ts`. + +--- + +## Table of Contents +- [Enums](#enums) +- [Tables](#tables) + - [Authentication Tables](#authentication-tables) + - [Member Tables](#member-tables) + - [Committee Tables](#committee-tables) + - [Event Tables](#event-tables) + - [Project Tables](#project-tables) + - [Sponsorship Tables](#sponsorship-tables) + - [Permission Tables](#permission-tables) +- [Relationships](#relationships) +- [Indexes](#indexes) + +--- + +## Enums + +### `officer_role_enum` +Defines the various officer positions within the organization. + +**Values:** +- `Executive Chair` - Top executive officer +- `Vice Chair` - Second-in-command +- `Treasurer` - Financial officer +- `Secretary` - Administrative officer +- `Project Chair` - Project management lead +- `Workshop Chair` - Workshop coordination lead +- `Conference Chair` - Conference organization lead +- `Outreach Chair` - Community outreach lead +- `Service Chair` - Service activities lead +- `Social Chair` - Social events lead +- `Professional Development Chair` - Career development lead +- `Marketing Chair` - Marketing and communications lead +- `Software Chair` - Software development lead + +### `permission_enum` +Defines the types of permissions that can be granted to members. + +**Values:** +- `scan_attendance` - Ability to scan QR codes and mark event attendance +- `view_statistics` - Ability to view analytics and statistics +- `manage_context` - Ability to manage committee/project settings + +### `context_type_enum` +Defines the scope/context where permissions can be applied. + +**Values:** +- `global` - System-wide permissions +- `committee` - Committee-specific permissions +- `project` - Project-specific permissions + +### `gender_enum` +Gender identification options for member profiles. + +**Values:** +- `M` - Male +- `F` - Female +- `NB` - Non-Binary +- `O` - Other +- `PNTS` - Prefer Not To Say + +### `sponsorship_tier_enum` +Sponsorship levels for corporate sponsors. + +**Values:** +- `Bronze` - Entry-level sponsorship +- `Silver` - Mid-tier sponsorship +- `Gold` - Premium sponsorship + +### `event_host_type_enum` +Defines which entity is hosting an event. + +**Values:** +- `club` - Event hosted by the club organization +- `committee` - Event hosted by a specific committee +- `project` - Event hosted by a project team +- `member` - Event hosted by an individual member + +--- + +## Tables + +### Authentication Tables + +#### `users` +_NextAuth.js authentication users - stores OAuth login information._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique user identifier | +| `name` | `text` | | Display name from OAuth provider | +| `email` | `varchar(255)` | Not Null, Unique | User's email address | +| `email_verified` | `timestamp` | | Email verification timestamp | +| `image` | `text` | | Profile image URL from Discord | +| `discordID` | `varchar(64)` | Unique | Discord user ID | + +**Indexes:** +- `users_idx_id` on `id` +- `users_idx_discord_id` on `discordID` + +**Relationships:** +- Has one `members` record (1:1 relationship via `user_id`) + +--- + +#### `accounts` +_NextAuth.js OAuth account information._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique account identifier | +| `user_id` | `uuid` | Not Null, FK → `users.id` | Associated user | +| `type` | `varchar(255)` | Not Null | Account type (e.g., "oauth") | +| `provider` | `varchar(255)` | Not Null | OAuth provider name (e.g., "discord") | +| `provider_account_id` | `varchar(255)` | Not Null | Provider's user ID | +| `refresh_token` | `text` | | OAuth refresh token | +| `access_token` | `text` | | OAuth access token | +| `expires_at` | `integer` | | Token expiration timestamp | +| `token_type` | `varchar(255)` | | Token type (e.g., "Bearer") | +| `scope` | `varchar(255)` | | OAuth scope | +| `id_token` | `text` | | JWT ID token | +| `session_state` | `varchar(255)` | | OAuth session state | + +**Relationships:** +- Belongs to `users` via `user_id` + +--- + +#### `sessions` +_NextAuth.js session management._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `session_token` | `varchar(255)` | Primary Key | Unique session token | +| `user_id` | `uuid` | Not Null, FK → `users.id` | Associated user | +| `expires` | `timestamp` | Not Null | Session expiration timestamp | + +**Relationships:** +- Belongs to `users` via `user_id` + +--- + +### Member Tables + +#### `members` +_Core member information and profiles._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique member identifier | +| `user_id` | `uuid` | Not Null, Unique, FK → `users.id` | Associated authentication user (1:1) | +| `first_name` | `varchar(255)` | Not Null | Member's first name | +| `middle_name` | `varchar(255)` | | Member's middle name | +| `last_name` | `varchar(255)` | Not Null | Member's last name | +| `officer_role` | `officer_role_enum` | | Officer position (if applicable) | +| `administrator` | `boolean` | Not Null, Default: `false` | System administrator flag | +| `officer_status` | `boolean` | Not Null, Default: `false` | Current officer status | +| `biography` | `text` | | Member biography/description | +| `dues_paid` | `boolean` | Not Null, Default: `false` | Membership dues payment status | +| `discordID` | `varchar(64)` | Unique | Discord user ID | +| `date_of_birth` | `date` | Not Null | Date of birth | +| `personal_email` | `varchar(255)` | Not Null, Unique | Personal email address | +| `ucf_email` | `varchar(255)` | Not Null, Unique | UCF student email | +| `phone_number` | `varchar(20)` | | Contact phone number | +| `major` | `varchar(255)` | Not Null | Academic major | +| `gender` | `gender_enum` | Not Null | Gender identification | +| `graduation_year` | `integer` | Not Null | Expected graduation year | +| `portrait_url` | `varchar(500)` | | Profile photo URL | +| `resume_url` | `text` | | Resume/CV URL | +| `linkedin_url` | `text` | | LinkedIn profile URL | +| `github_url` | `text` | | GitHub profile URL | +| `website_url` | `text` | | Personal website URL | +| `active` | `boolean` | Not Null, Default: `true` | Active membership status | +| `created_at` | `timestamp` | Not Null, Default: `now()` | Record creation timestamp | +| `updated_at` | `timestamp` | Not Null, Default: `now()` | Last update timestamp | + +**Indexes:** +- `members_idx_id` on `id` +- `members_idx_user_id` on `user_id` +- `members_idx_discordID` on `discordID` +- `members_idx_personal_email` on `personal_email` +- `members_idx_ucf_email` on `ucf_email` +- `members_idx_officer_status` on `officer_status` +- `members_idx_officer_role` on `officer_role` +- `members_idx_administrator` on `administrator` +- `members_idx_dues_paid` on `dues_paid` +- `members_idx_graduation_year` on `graduation_year` +- `members_idx_major` on `major` +- `members_idx_gender` on `gender` +- `members_idx_created_at` on `created_at` +- `members_idx_updated_at` on `updated_at` +- `members_idx_full_name` on `(first_name, last_name)` + +**Relationships:** +- Belongs to one `users` record via `user_id` (1:1) +- Has many `committee_members` records +- Has many `project_members` records +- Has many `event_attendees` records +- Has many `member_permissions` records +- Can scan attendance for other members via `event_attendees.scanner_id` + +--- + +### Committee Tables + +#### `committees` +_Organization committees (e.g., Software, Workshop, etc.)._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique committee identifier | +| `title` | `varchar(255)` | Not Null | Committee name | +| `slug` | `varchar(64)` | Unique | URL-friendly identifier | +| `about` | `text` | Not Null | Committee description | +| `discord_role_id` | `varchar(64)` | | Associated Discord role ID | +| `active` | `boolean` | Not Null, Default: `true` | Active status | +| `created_at` | `timestamp` | Not Null, Default: `now()` | Record creation timestamp | +| `updated_at` | `timestamp` | Not Null, Default: `now()` | Last update timestamp | + +**Indexes:** +- `committees_idx_id` on `id` +- `committees_idx_title` on `title` +- `committees_idx_slug` on `slug` +- `committees_idx_created_at` on `created_at` +- `committees_idx_updated_at` on `updated_at` + +**Relationships:** +- Has many `committee_members` records +- Can host `events` (polymorphic via `host_type` = 'committee') + +--- + +#### `committee_members` +_Junction table linking members to committees._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique record identifier | +| `committee_id` | `uuid` | Not Null, FK → `committees.id` | Committee reference | +| `member_id` | `uuid` | Not Null, FK → `members.id` | Member reference | +| `is_chair` | `boolean` | Not Null, Default: `false` | Committee chair status | + +**Indexes:** +- `committee_members_idx_id` on `id` +- `committee_members_idx_committee_id` on `committee_id` +- `committee_members_idx_member_id` on `member_id` +- `committee_members_idx_is_chair` on `is_chair` + +**Unique Constraints:** +- `(committee_id, member_id)` - A member can only join a committee once + +**Relationships:** +- Belongs to `committees` via `committee_id` +- Belongs to `members` via `member_id` + +--- + +### Event Tables + +#### `events` +_Events hosted by the organization._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique event identifier | +| `title` | `varchar(255)` | Not Null | Event title | +| `location` | `varchar(255)` | Not Null | Event location | +| `host_type` | `event_host_type_enum` | Not Null, Default: `'committee'` | Type of host entity | +| `host_id` | `uuid` | | Polymorphic FK to host entity | +| `slug` | `varchar(64)` | Unique | URL-friendly identifier | +| `start_time` | `timestamp` | Not Null | Event start time | +| `end_time` | `timestamp` | | Event end time | +| `requires_dues` | `boolean` | Not Null, Default: `false` | Requires paid dues for attendance | +| `active` | `boolean` | Not Null, Default: `true` | Active status | +| `description` | `text` | Not Null | Event description | +| `flyer_url` | `varchar(500)` | | Event flyer image URL | +| `rsvp_link` | `varchar(500)` | | RSVP/registration link | +| `photo_urls` | `text` | | Comma-separated photo URLs | +| `created_at` | `timestamp` | Not Null, Default: `now()` | Record creation timestamp | +| `updated_at` | `timestamp` | Not Null, Default: `now()` | Last update timestamp | + +**Indexes:** +- `events_idx_id` on `id` +- `events_idx_host` on `(host_type, host_id)` +- `events_idx_start_time` on `start_time` (ascending) +- `events_idx_time_desc` on `start_time` (descending) +- `events_idx_title` on `title` +- `events_idx_location` on `location` +- `events_idx_created_at` on `created_at` +- `events_idx_updated_at` on `updated_at` + +**Relationships:** +- Has many `event_attendees` records +- Hosted by polymorphic entity via `(host_type, host_id)` + +--- + +#### `event_attendees` +_Junction table tracking event attendance with QR code scanning info._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique record identifier | +| `event_id` | `uuid` | Not Null, FK → `events.id` | Event reference | +| `member_id` | `uuid` | Not Null, FK → `members.id` | Attending member | +| `scanner_id` | `uuid` | FK → `members.id` (nullable) | Member who scanned QR code | +| `timestamp` | `timestamp` | Not Null, Default: `now()` | Attendance timestamp | + +**Indexes:** +- `event_attendees_idx_id` on `id` +- `event_attendees_idx_event_id` on `event_id` +- `event_attendees_idx_member_id` on `member_id` +- `event_attendees_idx_scanner_id` on `scanner_id` +- `event_attendees_idx_timestamp` on `timestamp` + +**Unique Constraints:** +- `(event_id, member_id)` - A member can only attend an event once + +**Relationships:** +- Belongs to `events` via `event_id` +- Belongs to `members` via `member_id` (attendee) +- Belongs to `members` via `scanner_id` (scanner, nullable) + +--- + +### Project Tables + +#### `projects` +_Student projects undertaken by the organization._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique project identifier | +| `title` | `varchar(255)` | Not Null | Project name | +| `slug` | `varchar(64)` | Unique | URL-friendly identifier | +| `overview` | `text` | Not Null | Project description | +| `hardware_info` | `text` | | Hardware specifications/requirements | +| `software_info` | `text` | | Software stack information | +| `skills` | `text` | | Required/learned skills (comma-separated) | +| `photo_urls` | `text` | | Project photo URLs (array) | +| `discord_role_id` | `varchar(64)` | | Associated Discord role ID | +| `active` | `boolean` | Not Null, Default: `true` | Active status | +| `created_at` | `timestamp` | Not Null, Default: `now()` | Record creation timestamp | +| `updated_at` | `timestamp` | Not Null, Default: `now()` | Last update timestamp | + +**Indexes:** +- `projects_idx_id` on `id` +- `projects_idx_title` on `title` +- `projects_idx_slug` on `slug` +- `projects_idx_created_at` on `created_at` +- `projects_idx_updated_at` on `updated_at` + +**Relationships:** +- Has many `project_members` records +- Can host `events` (polymorphic via `host_type` = 'project') + +--- + +#### `project_members` +_Junction table linking members to projects._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique record identifier | +| `project_id` | `uuid` | Not Null, FK → `projects.id` | Project reference | +| `member_id` | `uuid` | Not Null, FK → `members.id` | Member reference | +| `is_lead` | `boolean` | Not Null, Default: `false` | Project lead status | + +**Indexes:** +- `project_members_idx_id` on `id` +- `project_members_idx_project_id` on `project_id` +- `project_members_idx_member_id` on `member_id` +- `project_members_idx_is_lead` on `is_lead` + +**Unique Constraints:** +- `(project_id, member_id)` - A member can only join a project once + +**Relationships:** +- Belongs to `projects` via `project_id` +- Belongs to `members` via `member_id` + +--- + +### Sponsorship Tables + +#### `sponsorships` +_Corporate sponsors and their contribution details._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique sponsorship identifier | +| `company_name` | `varchar(255)` | Not Null | Company name | +| `money_donated` | `integer` | Not Null | Donation amount (cents) | +| `description` | `text` | | Sponsorship description | +| `tier` | `sponsorship_tier_enum` | Not Null | Sponsorship tier level | +| `company_logo_url` | `varchar(500)` | | Company logo image URL | +| `website_url` | `varchar(500)` | | Company website URL | +| `contact_email` | `varchar(255)` | Not Null | Contact email address | +| `active` | `boolean` | Not Null, Default: `true` | Active sponsorship status | +| `created_at` | `timestamp` | Not Null, Default: `now()` | Record creation timestamp | +| `updated_at` | `timestamp` | Not Null, Default: `now()` | Last update timestamp | + +**Indexes:** +- `sponsorships_idx_id` on `id` +- `sponsorships_idx_company_name` on `company_name` +- `sponsorships_idx_tier` on `tier` +- `sponsorships_idx_created_at` on `created_at` +- `sponsorships_idx_updated_at` on `updated_at` + +--- + +### Permission Tables + +#### `member_permissions` +_Granular permission management for members in specific contexts._ + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| `id` | `uuid` | Primary Key, Default: `random()` | Unique permission identifier | +| `member_id` | `uuid` | Not Null, FK → `members.id` | Member receiving permission | +| `granted_by_id` | `uuid` | FK → `members.id` (nullable) | Member who granted permission | +| `context_type` | `context_type_enum` | Not Null | Permission scope (global/committee/project) | +| `context_id` | `uuid` | | Polymorphic FK to context entity | +| `permission` | `permission_enum` | Not Null | Type of permission granted | +| `active` | `boolean` | Not Null, Default: `true` | Active permission status | +| `created_at` | `timestamp` | Not Null, Default: `now()` | Permission grant timestamp | +| `expires_at` | `timestamp` | | Optional expiration timestamp | + +**Indexes:** +- `member_permissions_idx_member` on `member_id` +- `member_permissions_idx_context` on `(context_type, context_id)` + +**Unique Constraints:** +- `(member_id, context_type, context_id, permission)` - Prevents duplicate permissions + +**Relationships:** +- Belongs to `members` via `member_id` (permission holder) +- Belongs to `members` via `granted_by_id` (granter, nullable) +- Polymorphic relationship to context entity via `(context_type, context_id)` + +--- + +## Relationships + +### Key Relationship Patterns + +#### One-to-One +- `users` ↔ `members` via `user_id` - Each auth user has exactly one member profile + +#### One-to-Many +- `users` → `accounts` - A user can have multiple OAuth accounts +- `users` → `sessions` - A user can have multiple active sessions +- `members` → `committee_members` - A member can be in multiple committees +- `members` → `project_members` - A member can be in multiple projects +- `members` → `event_attendees` - A member can attend multiple events +- `members` → `member_permissions` - A member can have multiple permissions +- `members` → `event_attendees` (as scanner) - A member can scan multiple attendees +- `committees` → `committee_members` - A committee has multiple members +- `projects` → `project_members` - A project has multiple members +- `events` → `event_attendees` - An event has multiple attendees + +#### Polymorphic Relationships +- **Event Hosts**: `events.host_id` references different tables based on `events.host_type` + - `'club'` → General organization events + - `'committee'` → `committees.id` + - `'project'` → `projects.id` + - `'member'` → `members.id` + +- **Permission Context**: `member_permissions.context_id` references different tables based on `context_type` + - `'global'` → System-wide permission (context_id is NULL) + - `'committee'` → `committees.id` + - `'project'` → `projects.id` + +--- + +## Indexes + +The schema includes comprehensive indexing for optimal query performance: + +### Primary Key Indexes +All tables use UUID primary keys with default B-tree indexes. + +### Foreign Key Indexes +All foreign key columns are indexed for efficient joins and cascading operations. + +### Query Optimization Indexes +- **Full-text search**: `members_idx_full_name` on `(first_name, last_name)` +- **Temporal queries**: Multiple indexes on timestamp columns (`created_at`, `updated_at`, `start_time`) +- **Filtering**: Indexes on commonly filtered columns (`active`, `dues_paid`, `tier`, etc.) +- **Polymorphic relationships**: Composite indexes on `(type, id)` pairs +- **Event ordering**: Dual indexes on `start_time` (ascending and descending) for different query patterns + +### Unique Constraints +- **Natural keys**: Email addresses, Discord IDs, slugs +- **Junction tables**: Composite unique constraints prevent duplicate memberships/attendance +- **Permission system**: Prevents duplicate permission grants + +--- + +## Notes + +### Cascade Behaviors +- **ON DELETE CASCADE**: Deleting a user/member automatically removes all related records +- **ON DELETE SET NULL**: Deleting a scanner/granter preserves attendance/permission records but nullifies the reference + +### Timestamp Management +- All `updated_at` fields automatically update via `$onUpdate(() => sql\`now()\`)` trigger +- `created_at` fields are immutable and set on record creation + +### Data Types +- **UUIDs**: Used for all primary keys and foreign keys for security and scalability +- **Enums**: Enforced at database level for data integrity +- **Text vs Varchar**: Text used for unlimited content, varchar for constrained fields diff --git a/src/lib/database/schema.dbml b/src/lib/database/schema.dbml new file mode 100644 index 0000000..8142f8f --- /dev/null +++ b/src/lib/database/schema.dbml @@ -0,0 +1,308 @@ +// IEEE-UCF Website Database Schema +// Generate visual diagram at: https://dbdiagram.io +// Last Updated: November 18, 2025 + +// ==== Enums ==== + +enum officer_role_enum { + "Executive Chair" + "Vice Chair" + "Treasurer" + "Secretary" + "Project Chair" + "Workshop Chair" + "Conference Chair" + "Outreach Chair" + "Service Chair" + "Social Chair" + "Professional Development Chair" + "Marketing Chair" + "Software Chair" +} + +enum permission_enum { + scan_attendance + view_statistics + manage_context +} + +enum context_type_enum { + global + committee + project +} + +enum gender_enum { + M + F + NB + O + PNTS +} + +enum sponsorship_tier_enum { + Bronze + Silver + Gold +} + +enum event_host_type_enum { + club + committee + project + member +} + +// ==== Authentication Tables (NextAuth) ==== + +Table users { + id uuid [pk] + name text + email varchar(255) [not null, unique] + email_verified timestamp + image text + discordID varchar(64) [unique] + + indexes { + id [name: 'users_idx_id'] + discordID [name: 'users_idx_discord_id'] + } +} + +Table accounts { + id uuid [pk] + user_id uuid [not null, ref: > users.id] + type varchar(255) [not null] + provider varchar(255) [not null] + provider_account_id varchar(255) [not null] + refresh_token text + access_token text + expires_at integer + token_type varchar(255) + scope varchar(255) + id_token text + session_state varchar(255) +} + +Table sessions { + session_token varchar(255) [pk] + user_id uuid [not null, ref: > users.id] + expires timestamp [not null] +} + +// ==== Core Member Tables ==== + +Table members { + id uuid [pk] + user_id uuid [not null, unique, ref: - users.id] + first_name varchar(255) [not null] + middle_name varchar(255) + last_name varchar(255) [not null] + officer_role officer_role_enum + administrator boolean [not null, default: false] + officer_status boolean [not null, default: false] + biography text + dues_paid boolean [not null, default: false] + discordID varchar(64) [unique] + date_of_birth date [not null] + personal_email varchar(255) [not null, unique] + ucf_email varchar(255) [not null, unique] + phone_number varchar(20) + major varchar(255) [not null] + gender gender_enum [not null] + graduation_year integer [not null] + portrait_url varchar(500) + resume_url text + linkedin_url text + github_url text + website_url text + active boolean [not null, default: true] + created_at timestamp [not null, default: `now()`] + updated_at timestamp [not null, default: `now()`] + + indexes { + id [name: 'members_idx_id'] + user_id [name: 'members_idx_user_id'] + discordID [name: 'members_idx_discordID'] + personal_email [name: 'members_idx_personal_email'] + ucf_email [name: 'members_idx_ucf_email'] + officer_status [name: 'members_idx_officer_status'] + officer_role [name: 'members_idx_officer_role'] + administrator [name: 'members_idx_administrator'] + dues_paid [name: 'members_idx_dues_paid'] + graduation_year [name: 'members_idx_graduation_year'] + major [name: 'members_idx_major'] + gender [name: 'members_idx_gender'] + created_at [name: 'members_idx_created_at'] + updated_at [name: 'members_idx_updated_at'] + (first_name, last_name) [name: 'members_idx_full_name'] + } +} + +// ==== Committee Tables ==== + +Table committees { + id uuid [pk] + title varchar(255) [not null] + slug varchar(64) [unique] + about text [not null] + discord_role_id varchar(64) + active boolean [not null, default: true] + created_at timestamp [not null, default: `now()`] + updated_at timestamp [not null, default: `now()`] + + indexes { + id [name: 'committees_idx_id'] + title [name: 'committees_idx_title'] + slug [name: 'committees_idx_slug'] + created_at [name: 'committees_idx_created_at'] + updated_at [name: 'committees_idx_updated_at'] + } +} + +Table committee_members { + id uuid [pk] + committee_id uuid [not null, ref: > committees.id] + member_id uuid [not null, ref: > members.id] + is_chair boolean [not null, default: false] + + indexes { + id [name: 'committee_members_idx_id'] + committee_id [name: 'committee_members_idx_committee_id'] + member_id [name: 'committee_members_idx_member_id'] + is_chair [name: 'committee_members_idx_is_chair'] + (committee_id, member_id) [unique, name: 'committee_member_unique'] + } +} + +// ==== Event Tables ==== + +Table events { + id uuid [pk] + title varchar(255) [not null] + location varchar(255) [not null] + host_type event_host_type_enum [not null, default: 'committee'] + host_id uuid [note: 'Polymorphic FK: committee/project/member based on host_type'] + slug varchar(64) [unique] + start_time timestamp [not null] + end_time timestamp + requires_dues boolean [not null, default: false] + active boolean [not null, default: true] + description text [not null] + flyer_url varchar(500) + rsvp_link varchar(500) + photo_urls text + created_at timestamp [not null, default: `now()`] + updated_at timestamp [not null, default: `now()`] + + indexes { + id [name: 'events_idx_id'] + (host_type, host_id) [name: 'events_idx_host'] + start_time [name: 'events_idx_start_time'] + title [name: 'events_idx_title'] + location [name: 'events_idx_location'] + created_at [name: 'events_idx_created_at'] + updated_at [name: 'events_idx_updated_at'] + } +} + +Table event_attendees { + id uuid [pk] + event_id uuid [not null, ref: > events.id] + member_id uuid [not null, ref: > members.id] + scanner_id uuid [ref: > members.id, note: 'Member who scanned QR code (nullable for self-check-in)'] + timestamp timestamp [not null, default: `now()`] + + indexes { + id [name: 'event_attendees_idx_id'] + event_id [name: 'event_attendees_idx_event_id'] + member_id [name: 'event_attendees_idx_member_id'] + scanner_id [name: 'event_attendees_idx_scanner_id'] + timestamp [name: 'event_attendees_idx_timestamp'] + (event_id, member_id) [unique, name: 'event_attendee_unique'] + } +} + +// ==== Project Tables ==== + +Table projects { + id uuid [pk] + title varchar(255) [not null] + slug varchar(64) [unique] + overview text [not null] + hardware_info text + software_info text + skills text [note: 'Comma-separated skills list'] + photo_urls text + discord_role_id varchar(64) + active boolean [not null, default: true] + created_at timestamp [not null, default: `now()`] + updated_at timestamp [not null, default: `now()`] + + indexes { + id [name: 'projects_idx_id'] + title [name: 'projects_idx_title'] + slug [name: 'projects_idx_slug'] + created_at [name: 'projects_idx_created_at'] + updated_at [name: 'projects_idx_updated_at'] + } +} + +Table project_members { + id uuid [pk] + project_id uuid [not null, ref: > projects.id] + member_id uuid [not null, ref: > members.id] + is_lead boolean [not null, default: false] + + indexes { + id [name: 'project_members_idx_id'] + project_id [name: 'project_members_idx_project_id'] + member_id [name: 'project_members_idx_member_id'] + is_lead [name: 'project_members_idx_is_lead'] + (project_id, member_id) [unique, name: 'project_member_unique'] + } +} + +// ==== Sponsorship Tables ==== + +Table sponsorships { + id uuid [pk] + company_name varchar(255) [not null] + money_donated integer [not null, note: 'Amount in cents'] + description text + tier sponsorship_tier_enum [not null] + company_logo_url varchar(500) + website_url varchar(500) + contact_email varchar(255) [not null] + active boolean [not null, default: true] + created_at timestamp [not null, default: `now()`] + updated_at timestamp [not null, default: `now()`] + + indexes { + id [name: 'sponsorships_idx_id'] + company_name [name: 'sponsorships_idx_company_name'] + tier [name: 'sponsorships_idx_tier'] + created_at [name: 'sponsorships_idx_created_at'] + updated_at [name: 'sponsorships_idx_updated_at'] + } +} + +// ==== Permission Tables ==== + +Table member_permissions { + id uuid [pk] + member_id uuid [not null, ref: > members.id] + granted_by_id uuid [ref: > members.id, note: 'Member who granted permission (nullable)'] + context_type context_type_enum [not null] + context_id uuid [note: 'Polymorphic FK: committee/project based on context_type (NULL for global)'] + permission permission_enum [not null] + active boolean [not null, default: true] + created_at timestamp [not null, default: `now()`] + expires_at timestamp [note: 'Optional expiration for temporary permissions'] + + indexes { + member_id [name: 'member_permissions_idx_member'] + (context_type, context_id) [name: 'member_permissions_idx_context'] + (member_id, context_type, context_id, permission) [unique, name: 'member_permission_unique'] + } +} diff --git a/src/testing/db/README.md b/src/testing/db/README.md index a9d5060..a8a0d34 100644 --- a/src/testing/db/README.md +++ b/src/testing/db/README.md @@ -1,211 +1,128 @@ # Database Testing -## DB Setup +## Quick Start + +### Using the Management Script (Recommended) +```bash +# Full setup (start container + push schema + seed data) +./test-db.sh setup + +# Or step by step: +./test-db.sh start # Start container +./test-db.sh seed # Seed data +./test-db.sh status # Check status +``` + +### Manual Setup ```bash +# Start PostgreSQL container +docker compose up -d + +# Generate schema migrations (if schema changed) +pnpm db:generate + +# Push schema to database +pnpm db:push + +# Seed the database with test data +npx tsx seed.ts +``` + +## Management Script + +The `test-db.sh` script provides convenient commands for managing the test database: + +```bash +./test-db.sh {start|stop|restart|logs|setup|reset|seed|shell|status} +``` + +**Commands:** +- `start` - Start the database container +- `stop` - Stop the database container +- `restart` - Restart the database container +- `logs` - Show database logs (follow mode) +- `setup` - Full setup: start + push schema + seed +- `reset` - Reset database: wipe all data + push schema + seed +- `seed` - Seed database with test data +- `shell` - Open PostgreSQL shell +- `status` - Check database container status + +## DB Setup (Detailed) +```bash +# Start PostgreSQL in detached mode docker compose up --detach -bun seed.ts --wipe -drizzle-kit generate --config drizzle.config.ts # (if schema changed) -drizzle-kit migrate --config drizzle.config.ts -bun seed.ts --all + +# Option 1: Quick setup (recommended) +npx tsx seed.ts + +# Option 2: Step-by-step +pnpm db:generate # Generate migrations (only if schema.ts changed) +pnpm db:push # Push schema to database +npx tsx seed.ts # Seed with test data +``` + +## Seed Script Usage + +### Basic Commands +```bash +# Seed all tables with test data (default) +npx tsx seed.ts + +# Wipe all tables (clean slate) +npx tsx seed.ts --wipe + +# Seed specific tables only +npx tsx seed.ts --seed users,members,events + +# Use custom database URL +npx tsx seed.ts --dburl "postgres://user:pass@host:5432/dbname" ``` ### Seed Script Flags -- `--wipe`: wipe all tables before seeding -- `--seed`: seed all tables (default) -- `--seed [comma-separated table names]`: seed only specified tables (e.g., `--seed members,events`) - - -## dbdiagram.io Code -**DBML for Database Diagram (dbdiagram.io)** - -Copy and paste the code below into the editor at https://dbdiagram.io to generate a visual Entity-Relationship Diagram (ERD) of the database. - -```dbml -// ==== Enums ==== - -enum officer_role_enum { - executive_chair - executive_vice_chair - executive_secretary - executive_treasurer - committee_lead -} - -enum permission_enum { - scan_attendance - view_statistics - manage_context -} - -enum gender_enum { - M - F - NB - O - PNTS -} - -enum sponsorship_tier_enum { - bronze - silver - gold -} - -enum event_host_type_enum { - club - committee - project - member -} - - -// ==== Tables ==== - -Table members { - id uuid [pk] - first_name varchar(255) [not null] - middle_name varchar(255) - last_name varchar(255) [not null] - officer_role officer_role_enum - administrator boolean [not null, default: false] - biography text - dues_paid boolean [not null, default: false] - discord_id varchar(64) [not null, unique] - date_of_birth date [not null] - email varchar(255) [not null, unique] - phone_number varchar(20) - major varchar(255) [not null] - gender gender_enum [not null] - graduation_year integer [not null] - portrait_url varchar(500) - resume_url text - linkedin_url text - github_url text - website_url text - active boolean [not null, default: true] - created_at timestamp [not null, default: `now()`] - updated_at timestamp [not null, default: `now()`] -} - -Table committees { - id uuid [pk] - title varchar(255) [not null] - slug varchar(64) [unique] - about text [not null] - chair_id uuid [not null] - discord_role_id varchar(64) - active boolean [not null, default: true] - created_at timestamp [not null, default: `now()`] - updated_at timestamp [not null, default: `now()`] -} - -Table committee_members { - id uuid [pk] - committee_id uuid [not null] - member_id uuid [not null] - is_chair boolean [not null, default: false] - Note: 'Composite unique key on (committee_id, member_id)' -} - -Table events { - id uuid [pk] - title varchar(255) [not null] - location varchar(255) [not null] - host_type event_host_type_enum [not null] - host_id uuid - slug varchar(64) [unique] - start_time timestamp [not null] - end_time timestamp - requires_dues boolean [not null, default: false] - active boolean [not null, default: true] - description text [not null] - flyer_url varchar(500) - rsvp_link varchar(500) - photo_urls text - duration integer - created_at timestamp [not null, default: `now()`] - updated_at timestamp [not null, default: `now()`] -} - -Table event_attendees { - id uuid [pk] - event_id uuid [not null] - member_id uuid [not null] - timestamp timestamp [not null, default: `now()`] - Note: 'Composite unique key on (event_id, member_id)' -} - -Table projects { - id uuid [pk] - title varchar(255) [not null] - slug varchar(64) [unique] - overview text [not null] - hardware_info text - software_info text - skills text - photo_urls text - discord_role_id varchar(64) - active boolean [not null, default: true] - created_at timestamp [not null, default: `now()`] - updated_at timestamp [not null, default: `now()`] -} - -Table project_members { - id uuid [pk] - project_id uuid [not null] - member_id uuid [not null] - is_lead boolean [not null, default: false] - Note: 'Composite unique key on (project_id, member_id)' -} - -Table sponsorships { - id uuid [pk] - company_name varchar(255) [not null] - money_donated integer [not null] - description text - tier sponsorship_tier_enum [not null] - company_logo_url varchar(500) - website_url varchar(500) - contact_email varchar(255) [not null] - active boolean [not null, default: true] - created_at timestamp [not null, default: `now()`] - updated_at timestamp [not null, default: `now()`] -} - -Table member_permissions { - id uuid [pk] - member_id uuid [not null] - granted_by_id uuid - context_type varchar(32) [not null] - context_id uuid - permission permission_enum [not null] - active boolean [not null, default: true] - created_at timestamp [not null, default: `now()`] - expires_at timestamp - Note: 'Composite unique key on (member_id, context_type, context_id, permission)' -} - - -// ==== Relationships ==== - -Ref: committees.chair_id > members.id - -Ref: committee_members.committee_id > committees.id -Ref: committee_members.member_id > members.id - -Ref: event_attendees.event_id > events.id -Ref: event_attendees.member_id > members.id - -Ref: project_members.project_id > projects.id -Ref: project_members.member_id > members.id - -Ref: member_permissions.member_id > members.id -Ref: member_permissions.granted_by_id > members.id - -Ref: events.host_id > committees.id -Ref: events.host_id > projects.id -Ref: events.host_id > members.id - -Ref: member_permissions.context_id > committees.id -Ref: member_permissions.context_id > projects.id +- `--wipe`: Drop all tables before seeding (clean database) +- `--seed`: Seed all tables (default behavior) +- `--seed [table1,table2,...]`: Seed only specified tables (comma-separated, no spaces) +- `--dburl [url]`: Use custom database connection string + +## Environment Variables +Add to your `.env` file: +```env +DATABASE_URL="postgres://postgres:postgres@localhost:5432/ieee-website" +POSTGRES_URL="postgres://postgres:postgres@localhost:5432/ieee-website" ``` + +## Troubleshooting + +### Connection Issues +```bash +# Check if container is running +docker ps + +# Check container logs +docker compose logs + +# Restart container +docker compose restart +``` + +### Schema Sync Issues +```bash +# Drop all tables and start fresh +npx tsx seed.ts --wipe +pnpm db:push +npx tsx seed.ts +``` + +### Port Conflicts +If port 5432 is already in use, modify `docker-compose.yml`: +```yaml +ports: + - '5433:5432' # Change left side to available port +``` +Then update DATABASE_URL accordingly. + +## Schema Documentation + +For detailed schema documentation including all tables, relationships, and indexes, see: +- **[SCHEMA.md](../../lib/database/SCHEMA.md)** - Comprehensive schema reference +- **[schema.dbml](../../lib/database/schema.dbml)** - DBML for visual diagram generation at [dbdiagram.io](https://dbdiagram.io) diff --git a/src/testing/db/data/accounts.json b/src/testing/db/data/accounts.json new file mode 100644 index 0000000..05389ff --- /dev/null +++ b/src/testing/db/data/accounts.json @@ -0,0 +1,114 @@ +[ + { + "id": "c1aabc99-2a2b-4ef8-aa2d-2aa2ad222a11", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a11", + "type": "oauth", + "provider": "discord", + "provider_account_id": "123456789012345678", + "refresh_token": "refresh_token_1", + "access_token": "access_token_1", + "expires_at": 1735689600, + "token_type": "Bearer", + "scope": "identify email", + "id_token": null, + "session_state": null + }, + { + "id": "c1aabc99-2a2b-4ef8-aa2d-2aa2ad222a12", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a12", + "type": "oauth", + "provider": "discord", + "provider_account_id": "234567890123456789", + "refresh_token": "refresh_token_2", + "access_token": "access_token_2", + "expires_at": 1735689600, + "token_type": "Bearer", + "scope": "identify email", + "id_token": null, + "session_state": null + }, + { + "id": "c1aabc99-2a2b-4ef8-aa2d-2aa2ad222a13", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a13", + "type": "oauth", + "provider": "discord", + "provider_account_id": "345678901234567890", + "refresh_token": "refresh_token_3", + "access_token": "access_token_3", + "expires_at": 1735689600, + "token_type": "Bearer", + "scope": "identify email", + "id_token": null, + "session_state": null + }, + { + "id": "c1aabc99-2a2b-4ef8-aa2d-2aa2ad222a14", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a14", + "type": "oauth", + "provider": "discord", + "provider_account_id": "456789012345678901", + "refresh_token": "refresh_token_4", + "access_token": "access_token_4", + "expires_at": 1735689600, + "token_type": "Bearer", + "scope": "identify email", + "id_token": null, + "session_state": null + }, + { + "id": "c1aabc99-2a2b-4ef8-aa2d-2aa2ad222a15", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a15", + "type": "oauth", + "provider": "discord", + "provider_account_id": "567890123456789012", + "refresh_token": "refresh_token_5", + "access_token": "access_token_5", + "expires_at": 1735689600, + "token_type": "Bearer", + "scope": "identify email", + "id_token": null, + "session_state": null + }, + { + "id": "c1aabc99-2a2b-4ef8-aa2d-2aa2ad222a16", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a16", + "type": "oauth", + "provider": "discord", + "provider_account_id": "678901234567890123", + "refresh_token": "refresh_token_6", + "access_token": "access_token_6", + "expires_at": 1735689600, + "token_type": "Bearer", + "scope": "identify email", + "id_token": null, + "session_state": null + }, + { + "id": "c1aabc99-2a2b-4ef8-aa2d-2aa2ad222a17", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a17", + "type": "oauth", + "provider": "discord", + "provider_account_id": "789012345678901234", + "refresh_token": "refresh_token_7", + "access_token": "access_token_7", + "expires_at": 1735689600, + "token_type": "Bearer", + "scope": "identify email", + "id_token": null, + "session_state": null + }, + { + "id": "c1aabc99-2a2b-4ef8-aa2d-2aa2ad222a18", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a18", + "type": "oauth", + "provider": "discord", + "provider_account_id": "890123456789012345", + "refresh_token": "refresh_token_8", + "access_token": "access_token_8", + "expires_at": 1735689600, + "token_type": "Bearer", + "scope": "identify email", + "id_token": null, + "session_state": null + } +] diff --git a/src/testing/db/data/committee_members.json b/src/testing/db/data/committee_members.json index bf13674..4d8a330 100644 --- a/src/testing/db/data/committee_members.json +++ b/src/testing/db/data/committee_members.json @@ -1,20 +1,50 @@ [ { - "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a51", + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a41", "committee_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a31", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", "is_chair": true }, { - "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a52", + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a42", "committee_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a31", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13", + "is_chair": false + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a43", + "committee_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a31", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16", "is_chair": false }, { - "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a53", + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a44", "committee_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a32", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "is_chair": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a45", + "committee_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a32", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16", + "is_chair": false + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a46", + "committee_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a33", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "is_chair": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a47", + "committee_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a33", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18", + "is_chair": false + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a48", + "committee_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a34", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18", "is_chair": true } ] diff --git a/src/testing/db/data/committees.json b/src/testing/db/data/committees.json index e5eea20..b5d436d 100644 --- a/src/testing/db/data/committees.json +++ b/src/testing/db/data/committees.json @@ -3,14 +3,32 @@ "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a31", "title": "Software Committee", "slug": "software", - "about": "Build and maintain software projects.", - "chair_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" + "about": "Build and maintain software projects including web development, mobile apps, and automation tools.", + "discord_role_id": "1234567890123456789", + "active": true }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a32", "title": "Hardware Committee", "slug": "hardware", - "about": "Design and build hardware.", - "chair_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12" + "about": "Design and build hardware projects including PCBs, embedded systems, and robotics.", + "discord_role_id": "2345678901234567890", + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a33", + "title": "Workshops Committee", + "slug": "workshops", + "about": "Organize and conduct technical workshops on various engineering topics.", + "discord_role_id": "3456789012345678901", + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a34", + "title": "Outreach Committee", + "slug": "outreach", + "about": "Connect with the community and promote STEM education.", + "discord_role_id": "4567890123456789012", + "active": true } ] diff --git a/src/testing/db/data/event_attendees.json b/src/testing/db/data/event_attendees.json index b602e54..4004d28 100644 --- a/src/testing/db/data/event_attendees.json +++ b/src/testing/db/data/event_attendees.json @@ -2,21 +2,78 @@ { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a81", "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a71", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "scanner_id": null, + "timestamp": "2025-09-01T18:05:23-04:00" }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a82", "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a71", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12" + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", + "scanner_id": null, + "timestamp": "2025-09-01T18:07:45-04:00" }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a83", "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a71", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13" + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13", + "scanner_id": null, + "timestamp": "2025-09-01T18:12:10-04:00" }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a84", + "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a71", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "scanner_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "timestamp": "2025-09-01T18:15:33-04:00" + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a85", + "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a71", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a15", + "scanner_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "timestamp": "2025-09-01T18:18:22-04:00" + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a86", + "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a72", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", + "scanner_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "timestamp": "2025-09-15T19:02:15-04:00" + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a87", "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a72", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12" + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16", + "scanner_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "timestamp": "2025-09-15T19:05:40-04:00" + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a88", + "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a72", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13", + "scanner_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "timestamp": "2025-09-15T19:08:12-04:00" + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a89", + "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a74", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "scanner_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "timestamp": "2025-10-08T18:03:50-04:00" + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a90", + "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a74", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a15", + "scanner_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "timestamp": "2025-10-08T18:05:20-04:00" + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a91", + "event_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a74", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16", + "scanner_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "timestamp": "2025-10-08T18:07:35-04:00" } ] diff --git a/src/testing/db/data/events.json b/src/testing/db/data/events.json index a5b0e11..8524c6f 100644 --- a/src/testing/db/data/events.json +++ b/src/testing/db/data/events.json @@ -1,13 +1,19 @@ [ { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a71", - "title": "IEEE GBM 1", + "title": "IEEE GBM 1 - Fall 2025", "location": "HEC-101", "host_type": "club", "host_id": null, - "start_time": "2025-09-01 18:00:00", - "description": "First General Body Meeting of the Fall 2025 semester.", - "duration": 60 + "description": "First General Body Meeting of the Fall 2025 semester. Come learn about IEEE and meet the officers!", + "flyer_url": "https://example.com/flyers/gbm1.png", + "rsvp_link": null, + "photo_urls": null, + "slug": "gbm-1-fall-2025", + "start_time": "2025-09-01T18:00:00-04:00", + "end_time": "2025-09-01T19:30:00-04:00", + "requires_dues": false, + "active": true }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a72", @@ -15,8 +21,110 @@ "location": "ENG1-286", "host_type": "committee", "host_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a32", - "start_time": "2025-09-15 19:00:00", - "description": "Learn the basics of PCB design.", - "duration": 90 + "description": "Learn the basics of PCB design using KiCad. Bring your laptop!", + "flyer_url": "https://example.com/flyers/pcb-workshop.png", + "rsvp_link": "https://forms.gle/example1", + "photo_urls": null, + "slug": "intro-pcb-design", + "start_time": "2025-09-15T19:00:00-04:00", + "end_time": "2025-09-15T20:30:00-04:00", + "requires_dues": false, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a73", + "title": "IEEE GBM 2 - Fall 2025", + "location": "HEC-101", + "host_type": "club", + "host_id": null, + "description": "Second General Body Meeting. Project showcases and free pizza!", + "flyer_url": "https://example.com/flyers/gbm2.png", + "rsvp_link": null, + "photo_urls": null, + "slug": "gbm-2-fall-2025", + "start_time": "2025-10-01T18:00:00-04:00", + "end_time": "2025-10-01T19:30:00-04:00", + "requires_dues": false, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a74", + "title": "Soldering Workshop", + "location": "ENG1-286", + "host_type": "committee", + "host_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a33", + "description": "Hands-on soldering workshop. All materials provided. Learn to solder through-hole and SMD components.", + "flyer_url": "https://example.com/flyers/soldering.png", + "rsvp_link": "https://forms.gle/example2", + "photo_urls": null, + "slug": "soldering-workshop", + "start_time": "2025-10-08T18:00:00-04:00", + "end_time": "2025-10-08T20:00:00-04:00", + "requires_dues": true, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a75", + "title": "Industry Night", + "location": "Student Union Pegasus Ballroom", + "host_type": "club", + "host_id": null, + "description": "Network with companies hiring UCF engineers. Bring resumes! Business casual attire recommended.", + "flyer_url": "https://example.com/flyers/industry-night.png", + "rsvp_link": "https://forms.gle/example3", + "photo_urls": null, + "slug": "industry-night-fall-2025", + "start_time": "2025-10-20T17:00:00-04:00", + "end_time": "2025-10-20T20:00:00-04:00", + "requires_dues": false, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a76", + "title": "Git & GitHub Workshop", + "location": "ENG2-101", + "host_type": "committee", + "host_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a31", + "description": "Master version control with Git and GitHub. Essential for all developers!", + "flyer_url": null, + "rsvp_link": "https://forms.gle/example4", + "photo_urls": null, + "slug": "git-github-workshop", + "start_time": "2025-11-05T19:00:00-05:00", + "end_time": "2025-11-05T20:30:00-05:00", + "requires_dues": false, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a77", + "title": "Solar Car Team Meeting", + "location": "Engineering Shop", + "host_type": "project", + "host_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a54", + "description": "Weekly team meeting to discuss progress on solar car development.", + "flyer_url": null, + "rsvp_link": null, + "photo_urls": null, + "slug": "solar-car-meeting-nov-12", + "start_time": "2025-11-12T18:00:00-05:00", + "end_time": "2025-11-12T19:30:00-05:00", + "requires_dues": true, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a78", + "title": "IEEE GBM 3 - Fall 2025", + "location": "HEC-101", + "host_type": "club", + "host_id": null, + "description": "Final GBM of the semester. Year-end recap and elections for next semester!", + "flyer_url": "https://example.com/flyers/gbm3.png", + "rsvp_link": null, + "photo_urls": null, + "slug": "gbm-3-fall-2025", + "start_time": "2025-12-03T18:00:00-05:00", + "end_time": "2025-12-03T19:30:00-05:00", + "requires_dues": false, + "active": true } ] diff --git a/src/testing/db/data/member_permissions.json b/src/testing/db/data/member_permissions.json index 2b4bff5..e95f9b7 100644 --- a/src/testing/db/data/member_permissions.json +++ b/src/testing/db/data/member_permissions.json @@ -1,10 +1,42 @@ [ { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a91", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "granted_by_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "context_type": "global", + "context_id": null, + "permission": "scan_attendance", + "active": true, + "expires_at": null + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a92", "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", "granted_by_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", "context_type": "committee", - "context_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a32", - "permission": "scan_attendance" + "context_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a31", + "permission": "manage_context", + "active": true, + "expires_at": null + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a93", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", + "granted_by_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "context_type": "project", + "context_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a52", + "permission": "manage_context", + "active": true, + "expires_at": null + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a94", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a15", + "granted_by_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "context_type": "global", + "context_id": null, + "permission": "view_statistics", + "active": true, + "expires_at": null } ] diff --git a/src/testing/db/data/members.json b/src/testing/db/data/members.json index 7ce6596..488516b 100644 --- a/src/testing/db/data/members.json +++ b/src/testing/db/data/members.json @@ -1,50 +1,210 @@ [ { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a11", "first_name": "John", + "middle_name": null, "last_name": "Doe", - "officer_role": "executive_chair", + "officer_role": "Executive Chair", "administrator": true, - "biography": "A natural leader.", + "officer_status": true, + "biography": "A natural leader with a passion for technology and innovation. Leading IEEE UCF to new heights.", "dues_paid": true, - "discord_id": "johndoe#1234", - "date_of_birth": "1998-05-10", - "email": "john.doe@ieee.org", + "discordID": "123456789012345678", + "date_of_birth": "2002-05-10", + "personal_email": "john.doe@example.com", + "ucf_email": "john.doe@ucf.edu", "phone_number": "123-456-7890", "major": "Computer Science", "gender": "M", - "graduation_year": 2025 + "graduation_year": 2025, + "portrait_url": "https://example.com/portraits/john.jpg", + "resume_url": "https://example.com/resumes/john.pdf", + "linkedin_url": "https://linkedin.com/in/johndoe", + "github_url": "https://github.com/johndoe", + "website_url": "https://johndoe.dev", + "active": true }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a12", "first_name": "Jane", + "middle_name": "Marie", "last_name": "Smith", - "officer_role": "committee_lead", + "officer_role": "Software Chair", "administrator": false, - "biography": "Loves to build things.", + "officer_status": true, + "biography": "Loves to build things and solve complex problems through code.", "dues_paid": true, - "discord_id": "janesmith#5678", - "date_of_birth": "2000-08-22", - "email": "jane.smith@ieee.org", + "discordID": "234567890123456789", + "date_of_birth": "2003-08-22", + "personal_email": "jane.smith@example.com", + "ucf_email": "jane.smith@ucf.edu", "phone_number": "098-765-4321", - "major": "Electrical Engineering", + "major": "Computer Engineering", "gender": "F", - "graduation_year": 2026 + "graduation_year": 2026, + "portrait_url": "https://example.com/portraits/jane.jpg", + "resume_url": "https://example.com/resumes/jane.pdf", + "linkedin_url": "https://linkedin.com/in/janesmith", + "github_url": "https://github.com/janesmith", + "website_url": null, + "active": true }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a13", "first_name": "Peter", + "middle_name": null, "last_name": "Jones", "officer_role": null, "administrator": false, - "biography": "New member, eager to learn.", + "officer_status": false, + "biography": "New member, eager to learn and contribute to projects.", "dues_paid": false, - "discord_id": "peterjones#9101", - "date_of_birth": "2002-01-15", - "email": "peter.jones@ieee.org", + "discordID": "345678901234567890", + "date_of_birth": "2004-01-15", + "personal_email": "peter.jones@example.com", + "ucf_email": "peter.jones@ucf.edu", "phone_number": "555-555-5555", "major": "Computer Engineering", "gender": "NB", - "graduation_year": 2027 + "graduation_year": 2027, + "portrait_url": null, + "resume_url": null, + "linkedin_url": null, + "github_url": "https://github.com/peterjones", + "website_url": null, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a14", + "first_name": "Emily", + "middle_name": "Grace", + "last_name": "Davis", + "officer_role": "Workshop Chair", + "administrator": false, + "officer_status": true, + "biography": "Passionate about teaching and organizing educational workshops for members.", + "dues_paid": true, + "discordID": "456789012345678901", + "date_of_birth": "2002-11-30", + "personal_email": "emily.davis@example.com", + "ucf_email": "emily.davis@ucf.edu", + "phone_number": "321-654-9870", + "major": "Electrical Engineering", + "gender": "F", + "graduation_year": 2025, + "portrait_url": "https://example.com/portraits/emily.jpg", + "resume_url": "https://example.com/resumes/emily.pdf", + "linkedin_url": "https://linkedin.com/in/emilydavis", + "github_url": null, + "website_url": null, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a15", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a15", + "first_name": "Michael", + "middle_name": "James", + "last_name": "Brown", + "officer_role": "Treasurer", + "administrator": false, + "officer_status": true, + "biography": "Managing IEEE finances and ensuring sustainable growth.", + "dues_paid": true, + "discordID": "567890123456789012", + "date_of_birth": "2003-03-18", + "personal_email": "michael.brown@example.com", + "ucf_email": "michael.brown@ucf.edu", + "phone_number": "407-123-4567", + "major": "Computer Science", + "gender": "M", + "graduation_year": 2026, + "portrait_url": "https://example.com/portraits/michael.jpg", + "resume_url": null, + "linkedin_url": "https://linkedin.com/in/michaelbrown", + "github_url": null, + "website_url": null, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a16", + "first_name": "Sarah", + "middle_name": null, + "last_name": "Wilson", + "officer_role": null, + "administrator": false, + "officer_status": false, + "biography": "Interested in robotics and AI. Active member of multiple projects.", + "dues_paid": true, + "discordID": "678901234567890123", + "date_of_birth": "2004-07-25", + "personal_email": "sarah.wilson@example.com", + "ucf_email": "sarah.wilson@ucf.edu", + "phone_number": "407-987-6543", + "major": "Computer Engineering", + "gender": "F", + "graduation_year": 2027, + "portrait_url": null, + "resume_url": "https://example.com/resumes/sarah.pdf", + "linkedin_url": "https://linkedin.com/in/sarahwilson", + "github_url": "https://github.com/sarahwilson", + "website_url": null, + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a17", + "first_name": "David", + "middle_name": null, + "last_name": "Lee", + "officer_role": "Project Chair", + "administrator": false, + "officer_status": true, + "biography": "Coordinating technical projects and leading development teams.", + "dues_paid": true, + "discordID": "789012345678901234", + "date_of_birth": "2002-09-12", + "personal_email": "david.lee@example.com", + "ucf_email": "david.lee@ucf.edu", + "phone_number": "407-222-3333", + "major": "Computer Science", + "gender": "M", + "graduation_year": 2025, + "portrait_url": "https://example.com/portraits/david.jpg", + "resume_url": "https://example.com/resumes/david.pdf", + "linkedin_url": "https://linkedin.com/in/davidlee", + "github_url": "https://github.com/davidlee", + "website_url": "https://davidlee.io", + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18", + "user_id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a18", + "first_name": "Jessica", + "middle_name": "Ann", + "last_name": "Garcia", + "officer_role": "Marketing Chair", + "administrator": false, + "officer_status": true, + "biography": "Creating engaging content and promoting IEEE events across campus.", + "dues_paid": true, + "discordID": "890123456789012345", + "date_of_birth": "2003-12-05", + "personal_email": "jessica.garcia@example.com", + "ucf_email": "jessica.garcia@ucf.edu", + "phone_number": "407-444-5555", + "major": "Computer Science", + "gender": "F", + "graduation_year": 2026, + "portrait_url": "https://example.com/portraits/jessica.jpg", + "resume_url": null, + "linkedin_url": "https://linkedin.com/in/jessicagarcia", + "github_url": null, + "website_url": null, + "active": true } ] diff --git a/src/testing/db/data/project_members.json b/src/testing/db/data/project_members.json index dffac50..330e0da 100644 --- a/src/testing/db/data/project_members.json +++ b/src/testing/db/data/project_members.json @@ -1,14 +1,62 @@ [ { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a61", - "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a41", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a51", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", "is_lead": true }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a62", - "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a42", - "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a51", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + "is_lead": false + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a63", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a51", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13", + "is_lead": false + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a64", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a52", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", + "is_lead": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a65", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a52", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16", + "is_lead": false + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a66", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a53", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13", "is_lead": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a67", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a53", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16", + "is_lead": false + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a68", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a54", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", + "is_lead": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a69", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a54", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a15", + "is_lead": false + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a70", + "project_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a54", + "member_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", + "is_lead": false } ] diff --git a/src/testing/db/data/projects.json b/src/testing/db/data/projects.json index 11f4939..6d22c6e 100644 --- a/src/testing/db/data/projects.json +++ b/src/testing/db/data/projects.json @@ -1,14 +1,50 @@ [ { - "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a41", + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a51", "title": "IEEE Website", "slug": "ieee-website", - "overview": "The official website for the IEEE student chapter." + "overview": "The official website for the IEEE UCF student chapter. Built with Next.js, React, and PostgreSQL.", + "hardware_info": null, + "software_info": "Tech Stack: Next.js 15, React 19, TypeScript, Drizzle ORM, PostgreSQL, TailwindCSS, NextAuth.js", + "skills": "TypeScript, React, Next.js, PostgreSQL, Web Development, UI/UX Design", + "photo_urls": null, + "discord_role_id": "5678901234567890123", + "active": true }, { - "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a42", + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a52", "title": "Micromouse Competition", "slug": "micromouse", - "overview": "Build a robot to solve a maze." + "overview": "Build an autonomous robot to solve a 16x16 maze as quickly as possible. Competing in IEEE Region 3 Micromouse competition.", + "hardware_info": "STM32 microcontroller, custom PCB, IR sensors, stepper motors, LiPo battery", + "software_info": "Embedded C, flood-fill algorithm, PID control, sensor fusion", + "skills": "Embedded Systems, PCB Design, Robotics, C Programming, Algorithm Design", + "photo_urls": null, + "discord_role_id": "6789012345678901234", + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a53", + "title": "Smart Garden System", + "slug": "smart-garden", + "overview": "IoT-based automated garden monitoring and watering system with mobile app integration.", + "hardware_info": "ESP32, soil moisture sensors, water pumps, relay modules, solar panel", + "software_info": "Arduino, MQTT protocol, React Native mobile app, Firebase backend", + "skills": "IoT, Mobile Development, Embedded Systems, React Native, Firebase", + "photo_urls": null, + "discord_role_id": "7890123456789012345", + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a54", + "title": "Solar Car", + "slug": "solar-car", + "overview": "Design and build a solar-powered vehicle for the American Solar Challenge competition.", + "hardware_info": "Solar panels, battery management system, custom chassis, brushless DC motors, telemetry system", + "software_info": "Real-time telemetry dashboard, battery optimization algorithms, CAN bus communication", + "skills": "Electrical Engineering, Mechanical Engineering, CAD, Embedded Systems, Team Collaboration", + "photo_urls": null, + "discord_role_id": "8901234567890123456", + "active": true } ] diff --git a/src/testing/db/data/sponsorships.json b/src/testing/db/data/sponsorships.json index 7d27522..8d06c53 100644 --- a/src/testing/db/data/sponsorships.json +++ b/src/testing/db/data/sponsorships.json @@ -1,18 +1,57 @@ [ { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21", - "company_name": "TechCorp", - "money_donated": 5000, - "description": "Leading provider of tech solutions.", - "tier": "gold", - "contact_email": "techcorp@email.com" + "company_name": "Lockheed Martin", + "money_donated": 10000, + "description": "Global security and aerospace company supporting engineering education.", + "tier": "Gold", + "company_logo_url": "https://example.com/logos/lockheed.png", + "website_url": "https://lockheedmartin.com", + "contact_email": "university.relations@lmco.com", + "active": true }, { "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22", - "company_name": "Innovate LLC", - "money_donated": 2500, - "description": "Startup focused on innovation.", - "tier": "silver", - "contact_email": "innovate@email.com" + "company_name": "Northrop Grumman", + "money_donated": 7500, + "description": "Leading global security company providing innovative systems and solutions.", + "tier": "Silver", + "company_logo_url": "https://example.com/logos/northrop.png", + "website_url": "https://northropgrumman.com", + "contact_email": "careers@ngc.com", + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a23", + "company_name": "Amazon Web Services", + "money_donated": 5000, + "description": "Cloud computing platform supporting tech education and innovation.", + "tier": "Silver", + "company_logo_url": "https://example.com/logos/aws.png", + "website_url": "https://aws.amazon.com", + "contact_email": "university-programs@amazon.com", + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a24", + "company_name": "Intel Corporation", + "money_donated": 3000, + "description": "Technology leader in semiconductor chip innovation.", + "tier": "Bronze", + "company_logo_url": "https://example.com/logos/intel.png", + "website_url": "https://intel.com", + "contact_email": "university@intel.com", + "active": true + }, + { + "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a25", + "company_name": "SpaceX", + "money_donated": 8000, + "description": "Advancing space exploration and satellite technology.", + "tier": "Silver", + "company_logo_url": "https://example.com/logos/spacex.png", + "website_url": "https://spacex.com", + "contact_email": "university@spacex.com", + "active": true } ] diff --git a/src/testing/db/data/users.json b/src/testing/db/data/users.json new file mode 100644 index 0000000..3903c4b --- /dev/null +++ b/src/testing/db/data/users.json @@ -0,0 +1,66 @@ +[ + { + "id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a11", + "name": "johndoe", + "email": "john.doe@ucf.edu", + "email_verified": null, + "image": "https://cdn.discordapp.com/avatars/123456789/avatar1.png", + "discordID": "123456789012345678" + }, + { + "id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a12", + "name": "janesmith", + "email": "jane.smith@ucf.edu", + "email_verified": null, + "image": "https://cdn.discordapp.com/avatars/234567890/avatar2.png", + "discordID": "234567890123456789" + }, + { + "id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a13", + "name": "peterjones", + "email": "peter.jones@ucf.edu", + "email_verified": null, + "image": "https://cdn.discordapp.com/avatars/345678901/avatar3.png", + "discordID": "345678901234567890" + }, + { + "id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a14", + "name": "emilydavis", + "email": "emily.davis@ucf.edu", + "email_verified": null, + "image": "https://cdn.discordapp.com/avatars/456789012/avatar4.png", + "discordID": "456789012345678901" + }, + { + "id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a15", + "name": "michaelbrown", + "email": "michael.brown@ucf.edu", + "email_verified": null, + "image": "https://cdn.discordapp.com/avatars/567890123/avatar5.png", + "discordID": "567890123456789012" + }, + { + "id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a16", + "name": "sarahwilson", + "email": "sarah.wilson@ucf.edu", + "email_verified": null, + "image": "https://cdn.discordapp.com/avatars/678901234/avatar6.png", + "discordID": "678901234567890123" + }, + { + "id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a17", + "name": "davidlee", + "email": "david.lee@ucf.edu", + "email_verified": null, + "image": "https://cdn.discordapp.com/avatars/789012345/avatar7.png", + "discordID": "789012345678901234" + }, + { + "id": "b1aabc99-1a1b-4ef8-aa1d-1aa1ad111a18", + "name": "jessicagarcia", + "email": "jessica.garcia@ucf.edu", + "email_verified": null, + "image": "https://cdn.discordapp.com/avatars/890123456/avatar8.png", + "discordID": "890123456789012345" + } +] diff --git a/src/testing/db/drizzle.config.ts b/src/testing/db/drizzle.config.ts index a912f8d..c6591b6 100644 --- a/src/testing/db/drizzle.config.ts +++ b/src/testing/db/drizzle.config.ts @@ -2,9 +2,9 @@ import { defineConfig } from "drizzle-kit"; export default defineConfig({ dialect: "postgresql", - schema: "../../../src/lib/database/schema.ts", + schema: "../../lib/database/schema.ts", out: "../../../drizzle", dbCredentials: { - url: "postgres://postgres:postgres@localhost:5432/ieee-website", + url: process.env.DATABASE_URL || "postgres://postgres:postgres@localhost:5432/ieee-website", }, }); \ No newline at end of file diff --git a/src/testing/db/seed.ts b/src/testing/db/seed.ts index 317f4a1..4fd4a13 100644 --- a/src/testing/db/seed.ts +++ b/src/testing/db/seed.ts @@ -3,11 +3,17 @@ import fs from 'fs'; import { Client } from 'pg'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; +import dotenv from 'dotenv'; -const DEFAULT_DB_URL = 'postgres://postgres:postgres@localhost:5432/ieee-website'; +// Load environment variables from root .env file +dotenv.config({ path: path.join(__dirname, '../../../.env') }); + +const DEFAULT_DB_URL = process.env.DATABASE_URL || 'postgres://postgres:postgres@localhost:5432/ieee-website'; const DATA_DIR = path.join(__dirname, 'data'); const SEED_ORDER = [ + 'users', + 'accounts', 'members', 'sponsorships', 'committees', @@ -43,7 +49,8 @@ async function seedDatabase(client: Client, tablesToSeed: string[]) { } const columns = Object.keys(data[0]); const placeholders = columns.map((_, i) => `$${i + 1}`).join(', '); - const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`; + const quotedColumns = columns.map(col => `"${col}"`).join(', '); + const sql = `INSERT INTO ${tableName} (${quotedColumns}) VALUES (${placeholders})`; for (const row of data) { await client.query(sql, columns.map(col => row[col])); } @@ -55,11 +62,10 @@ if (require.main === module) { const parser = yargs(hideBin(process.argv)) .option('wipe', { type: 'boolean', default: false, describe: 'Wipe all tables before seeding.' }) .option('seed', { type: 'string', describe: 'Seed all tables (default) or only specified tables (comma-separated).' }) - .option('dburl', { type: 'string', describe: 'Database URL to use.' }) - .positional('dburl', { type: 'string', describe: 'Database URL to use.' }); + .option('dburl', { type: 'string', describe: 'Database URL to use.' }); const argv = parser.parseSync(); - - const dbUrl = argv.dburl || String(argv._[0]) || DEFAULT_DB_URL; + + const dbUrl = argv.dburl || (argv._[0] ? String(argv._[0]) : DEFAULT_DB_URL); const client = new Client({ connectionString: dbUrl }); client.connect().then(async () => { if (argv.wipe) { diff --git a/src/testing/db/test-db.sh b/src/testing/db/test-db.sh new file mode 100755 index 0000000..f3e5cb0 --- /dev/null +++ b/src/testing/db/test-db.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +# IEEE Website Test Database Management Script + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Get the script's directory and project root +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" + +echo -e "${GREEN}IEEE Test Database Manager${NC}\n" + +case "$1" in + start) + echo "Starting PostgreSQL container..." + (cd "$SCRIPT_DIR" && docker compose up -d) + echo -e "${GREEN}✓ Database container started${NC}" + ;; + + stop) + echo "Stopping PostgreSQL container..." + (cd "$SCRIPT_DIR" && docker compose down) + echo -e "${GREEN}✓ Database container stopped${NC}" + ;; + + restart) + echo "Restarting PostgreSQL container..." + (cd "$SCRIPT_DIR" && docker compose restart) + echo -e "${GREEN}✓ Database container restarted${NC}" + ;; + + logs) + echo "Showing database logs..." + (cd "$SCRIPT_DIR" && docker compose logs -f) + ;; + + setup) + echo "Setting up database..." + (cd "$SCRIPT_DIR" && docker compose up -d) + sleep 2 + echo "Pushing schema..." + (cd "$PROJECT_ROOT" && pnpm db:push) + echo "Seeding database..." + (cd "$SCRIPT_DIR" && npx tsx seed.ts) + echo -e "${GREEN}✓ Database setup complete!${NC}" + ;; + + reset) + echo -e "${YELLOW}Resetting database (this will delete all data)...${NC}" + (cd "$SCRIPT_DIR" && npx tsx seed.ts --wipe) + sleep 1 + (cd "$PROJECT_ROOT" && pnpm db:push) + (cd "$SCRIPT_DIR" && npx tsx seed.ts) + echo -e "${GREEN}✓ Database reset complete!${NC}" + ;; + + seed) + echo "Seeding database..." + (cd "$SCRIPT_DIR" && npx tsx seed.ts) + echo -e "${GREEN}✓ Database seeded${NC}" + ;; + + shell) + echo "Connecting to PostgreSQL shell..." + docker exec -it db-db-1 psql -U postgres -d ieee-website + ;; + + status) + echo "Checking database status..." + docker ps | grep -E "CONTAINER|postgres" || echo -e "${RED}No database container running${NC}" + ;; + + *) + echo "Usage: $0 {start|stop|restart|logs|setup|reset|seed|shell|status}" + echo "" + echo "Commands:" + echo " start - Start the database container" + echo " stop - Stop the database container" + echo " restart - Restart the database container" + echo " logs - Show database logs" + echo " setup - Full setup (start + push schema + seed)" + echo " reset - Reset database (wipe + push + seed)" + echo " seed - Seed database with test data" + echo " shell - Open PostgreSQL shell" + echo " status - Check database container status" + exit 1 + ;; +esac