Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(portal): create initial portal database schemas #134

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/docs/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import process from 'node:process'
import { composePlugins, withNx } from '@nx/next'
import createMDX from 'fumadocs-mdx/config'
import { fileGenerator, remarkDocGen, remarkInstall } from 'fumadocs-docgen'
Expand All @@ -16,6 +17,14 @@ const nextConfig = {
reactStrictMode: true,
output: 'standalone',
},
images: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth adding a comment here explaining why we need this!

remotePatterns: [
{
protocol: 'https',
hostname: process.env.ALLOWED_IMAGE_HOSTNAME || 'avatars.githubusercontent.com',
},
],
},
}

const withMDX = createMDX({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"title": "cuHacking 2020 wiki",
"icon": "Bird",
"pages": [
"index",
"development-timeline",
"admin-console",
"application-server",
Expand Down
109 changes: 107 additions & 2 deletions apps/docs/src/content/docs/libraries/db.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,111 @@ description: Database interactor with DrizzleORM.
This library holds the schemas and commands for making and using the database using DrizzleORM and Drizzle-Kit.
It puts together the schemas in a configuration, and provides one database object to interact with databases with type-safety.

### Mermaid Entity-Relationship Diagram

Here is the Mermaid ERD for the portal database schema:

```mermaid
erDiagram
User {
serial id "Primary key, unique"
varchar(32) firstName "First name of the user"
varchar(32) middleName "Middle name of the user, optional"
varchar(32) lastName "Last name of the user"
varchar(32) preferredName "Preferred name of the user, optional"
varchar(64) email "Email of the user, must be unique, verification required"
bool isEmailVerified "Whether the email is verified, default is 'no'"
varchar(128) avatarUrl "URL to the user's profile picture, optional"
text profileDescription "Profile description, optional"
date dateOfBirth "Date of birth of the user, verification required"
enum gender "Gender of the user, e.g., male, female, other, optional"
enum phoneNumberCountryCode "Country code of phone number, e.g., +1"
varchar(16) phoneNumber "Phone number of the user, must be unique, verification required"
bool isPhoneNumberVerified "Whether the phone number is verified, default is 'no'"
smallint numHackathonsAttended "Number of hackathons attended by the user"
text anyOtherComments "Any additional comments from the user, optional"
bool isDomestic "Whether the user is international or domestic, optional"
enum ethnicity "Ethnicity of the user, optional, black, white, asian, hispanic, middle-eastern,"
int estimatedGradYear "Estimated graduation year of the user, optional"
bool agreeToMlhReqs "MLH requirements, default is 'No'"
int resumeId "References Resume.id, mandatory relationship"
int schoolId "References School.id, mandatory relationship"
int emergencyContactId "References EmergencyContact.id, mandatory relationship"
int teamId "References Team.id, mandatory relationship"
}

EmergencyContact {
serial id "Primary key, unique"
varchar(32) name "Name of the emergency contact"
enum relationship "Relationship to the user, e.g., mother, father, sibling, friend, relative, other"
varchar(16) phoneNumber "Phone number of the contact, must be unique, verification required"
bool isPhoneNumberVerified "Whether the phone number is verified, default is 'no'"
}

Team {
serial id "Primary key, unique"
varchar(64) name "Name of the team"
varchar(128) profileImageUrl "URL to the user's profile picture, optional"
varchar(256) projectLinkUrl "URL to the project, verification required"
int teamOwnerId "References User.id, one-to-one relationship"
bool hasSubmitted "Whether the team has submitted a project, default is 'No'"
varchar(128) teamProfileImageUrl "URL to the user's profile picture, optional"
}

School {
serial id "Primary key, unique"
varchar(128) name "Name of the school, verification required"
enum levelOfStudy "Level of study, e.g., graduate school, high school, etc."
}

Program {
serial id "Primary key, unique"
varchar(128) name "Name of the program"
int schoolId "References School.id"
enum programType "Type of program, e.g., bachelor, master, diploma, certificate, etc."
}

Resume {
serial id "Primary key, unique"
varchar(128) fileLink "URL to the resume file, verification required"
bool hasPrivacyToggle "Privacy setting for the resume, default is false"
timestamp uploadedAt "Timestamp when the resume was uploaded, default is CURRENT_TIMESTAMP"
}

SocialMedia {
serial id "Primary key, unique"
int userId "References User.id, optional relationship"
varchar(16) platformName "Name of the social media platform, e.g., LinkedIn, GitHub, etc."
varchar(128) profileUrl "URL to the social media profile, verification required"
}

UserPreferences {
serial id "Primary key, unique"
int userId "References User.id, mandatory relationship"
enum preferredLanguage "Preferred language of the user, default is 'EN'"
enum eventPreferences "Event preferences, e.g., hardware, software, etc."
%% bool darkMode "Whether the user prefers dark mode, default is false"
bool privacyMode "Privacy mode, to be defined, default is 'No'"
bool isSubscribedToNewsletter "Whether the user is subscribed newsletter, default is 'No'"
enum shirtSize "Shirt size of the user"
enum pronouns "Preferred pronouns of the user, e.g., he/him, she/her, they/them, other"
enum[] dietRestrictions "Dietary restrictions, e.g., allergies, vegan, none, etc."
enum[] trackPreferences "Track preferences, e.g., hardware, software, etc., optional"
enum[] interests "Interests of the user, e.g., languages, etc., optional"
enum[] disabilities "Disabilities, if any"
enum[] applicableSkills "Applicable skills of the user"
}

%% Relationships
User ||--|| Resume : "References"
User ||--|| School : "References"
User ||--o{ EmergencyContact : "References"
User }o--|| Team : "References"
Program ||--o{ School : "References"
SocialMedia }o--|| User : "References"
UserPreferences ||--|| User : "References"
```

### Key Features

- One source of truth
Expand Down Expand Up @@ -68,7 +173,7 @@ pnpm nx run db:studio

### Other Drizzle-Kit commands

All other `drizzle-kit` [https://orm.drizzle.team/kit-docs/commands](commands) are supported, such as `generate` and `migrate`.
All other `drizzle-kit` [commands](https://orm.drizzle.team/kit-docs/commands) are supported, such as `generate` and `migrate`.
See the project graph to view them. Here is an example of running a migration:
```sh title="Terminal"
pnpm nx run db:migrate
Expand Down Expand Up @@ -121,4 +226,4 @@ Make sure that it is valid to make it optional.
If this happens, a last resort solution would be to delete the docker container & its volume,
and attempt a migration once again.

For more troubleshooting, see DrizzleORM's [https://orm.drizzle.team/kit-docs/faq](troubleshooting page)
For more troubleshooting, see DrizzleORM's [troubleshooting page](https://orm.drizzle.team/kit-docs/faq).
9 changes: 8 additions & 1 deletion libs/db/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,12 @@ export const db = drizzle(sql, {
logger: envWebsiteDb.NODE_ENV === 'development',
})

export * from './schema/user'
export * from './schema/emergencyContact'
export * from './schema/program'
export * from './schema/resume'
export * from './schema/school'
export * from './schema/session'
export * from './schema/socialMedia'
export * from './schema/team'
export * from './schema/user'
export * from './schema/userPreferences'
11 changes: 11 additions & 0 deletions libs/db/src/schema/emergencyContact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { boolean, pgEnum, pgTable, serial, varchar } from 'drizzle-orm/pg-core'

export const relationshipEnum = pgEnum('relationship', ['mother', 'father', 'sibling', 'friend', 'relative', 'other'])

export const emergencyContact = pgTable('emergencyContact', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 32 }),
relationship: relationshipEnum('relationship'),
phoneNumber: varchar('phoneNumber', { length: 16 }).unique(),
isPhoneNumberVerified: boolean('isPhoneNumberVerified').default(false),
})
9 changes: 8 additions & 1 deletion libs/db/src/schema/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
export * from './user'
export * from './emergencyContact'
export * from './program'
export * from './resume'
export * from './school'
export * from './session'
export * from './socialMedia'
export * from './team'
export * from './user'
export * from './userPreferences'
11 changes: 11 additions & 0 deletions libs/db/src/schema/program.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { integer, pgEnum, pgTable, serial, varchar } from 'drizzle-orm/pg-core'
import { school } from './school'

export const programTypeEnum = pgEnum('programType', ['bachelor', 'master', 'diploma', 'certificate'])

export const program = pgTable('program', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 128 }),
schoolId: integer('schoolId').references(() => school.id),
programType: programTypeEnum('programType'),
})
8 changes: 8 additions & 0 deletions libs/db/src/schema/resume.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { boolean, pgTable, serial, timestamp, varchar } from 'drizzle-orm/pg-core'

export const resume = pgTable('resume', {
id: serial('id').primaryKey(),
fileLink: varchar('fileLink', { length: 128 }),
hasPrivacyToggle: boolean('hasPrivacyToggle').default(false),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good touch πŸ‘

uploadedAt: timestamp('uploadedAt').defaultNow(),
})
Comment on lines +3 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider enhancing the resume table structure.

The current schema is a good start, but consider the following improvements:

  1. Add a foreign key to link resumes with users:
userId: integer('userId').notNull().references(() => users.id),
  1. Make the fileLink field required:
fileLink: varchar('fileLink', { length: 128 }).notNull(),
  1. Add an index on the fileLink field for better query performance:
{ name: 'fileLink_idx', columns: [resume.fileLink] }
  1. Consider adding a 'name' field for the resume, which could be useful for display purposes:
name: varchar('name', { length: 255 }).notNull(),

These changes would enhance data integrity and query performance.

9 changes: 9 additions & 0 deletions libs/db/src/schema/school.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { pgEnum, pgTable, serial, varchar } from 'drizzle-orm/pg-core'

export const levelOfStudyEnum = pgEnum('levelOfStudy', ['graduateSchool', 'highSchool'])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider expanding the levelOfStudyEnum.

The current enum covers graduate and high school levels, which might not be comprehensive enough for all potential use cases. Consider including additional levels such as 'undergraduate', 'middleSchool', or 'elementary' to make the schema more flexible for future requirements.

Here's a suggested expansion of the enum:

export const levelOfStudyEnum = pgEnum('levelOfStudy', [
  'elementary',
  'middleSchool',
  'highSchool',
  'undergraduate',
  'graduateSchool'
])

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for highschool right now, should be added on later imo


export const school = pgTable('school', {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea to add a location to this as well

id: serial('id').primaryKey(),
name: varchar('name', { length: 128 }),
levelOfStudy: levelOfStudyEnum('levelOfStudy'),
})
4 changes: 2 additions & 2 deletions libs/db/src/schema/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { user } from './user'

export const session = pgTable('session', {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we good to make this table before finalizing the auth provider?

id: text('id').primaryKey(),
userId: text('user_id')
userId: text('userId')
.notNull()
.references(() => user.id),
expiresAt: timestamp('expires_at', {
expiresAt: timestamp('expiresAt', {
withTimezone: true,
mode: 'date',
}).notNull(),
Expand Down
11 changes: 11 additions & 0 deletions libs/db/src/schema/socialMedia.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { integer, pgTable, serial, varchar } from 'drizzle-orm/pg-core'

import { user } from './user'

export const socialMedia = pgTable('socialMedia', {
id: serial('id').primaryKey(),
userId: integer('userId')
.references(() => user.id),
platformName: varchar('platformName', { length: 16 }),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should reference another Platform table so that we can regulate the social medias that we accept

profileUrl: varchar('profileUrl', { length: 128 }),
})
Comment on lines +5 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider using snake_case for table and column names.

While the current naming convention is consistent, it's generally recommended to use snake_case for table and column names in PostgreSQL. This aligns better with PostgreSQL conventions and avoids potential issues with case-sensitivity in queries.

Here's how you could modify the schema to use snake_case:

export const socialMedia = pgTable('social_media', {
  id: serial('id').primaryKey(),
  user_id: integer('user_id').notNull()
    .references(() => user.id),
  platform_name: varchar('platform_name', { length: 16 }).notNull(),
  profile_url: varchar('profile_url', { length: 128 }).notNull(),
}, (table) => ({
  userPlatformUnique: unique().on(table.user_id, table.platform_name),
}));

Remember to update any references to these fields in other parts of your codebase if you make this change.


πŸ› οΈ Refactor suggestion

Consider adding additional useful fields to the schema.

The current schema covers the basic needs for a social media link table. However, consider adding the following fields to enhance functionality and data tracking:

  1. created_at: A timestamp to track when the social media link was added.
  2. updated_at: A timestamp to track when the link was last modified.
  3. visibility: An enum field to allow users to control the visibility of their social media links (e.g., 'public', 'private', 'friends_only').

Here's an example of how you might implement these additions:

import { integer, pgTable, serial, varchar, timestamp, pgEnum } from 'drizzle-orm/pg-core'

export const visibilityEnum = pgEnum('visibility', ['public', 'private', 'friends_only']);

export const socialMedia = pgTable('social_media', {
  id: serial('id').primaryKey(),
  user_id: integer('user_id').notNull()
    .references(() => user.id),
  platform_name: varchar('platform_name', { length: 16 }).notNull(),
  profile_url: varchar('profile_url', { length: 128 }).notNull(),
  visibility: visibilityEnum('visibility').default('public').notNull(),
  created_at: timestamp('created_at').defaultNow().notNull(),
  updated_at: timestamp('updated_at').defaultNow().notNull(),
}, (table) => ({
  userPlatformUnique: unique().on(table.user_id, table.platform_name),
}));

These additions would provide more comprehensive data management and user control over their social media links.

13 changes: 13 additions & 0 deletions libs/db/src/schema/team.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { boolean, integer, pgTable, serial, varchar } from 'drizzle-orm/pg-core'

export const team = pgTable('team', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 64 }),
profileImageUrl: varchar('profileImageUrl', { length: 128 }),
projectLinkUrl: varchar('projectLinkUrl', { length: 256 }),
teamOwnerId: integer('teamOwnerId'),
hasSubmitted: boolean('hasSubmitted').default(false),
teamProfileImageUrl: varchar('teamProfileImageUrl', {
length: 128,
}),
})
38 changes: 33 additions & 5 deletions libs/db/src/schema/user.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
import { pgTable, text } from 'drizzle-orm/pg-core'
import { boolean, date, integer, pgEnum, pgTable, serial, smallint, text, varchar } from 'drizzle-orm/pg-core'
import { resume } from './resume'
import { school } from './school'
import { emergencyContact } from './emergencyContact'
import { team } from './team'

export const genderEnum = pgEnum('gender', ['male', 'female', 'other'])
export const phoneNumberCountryCodeEnum = pgEnum('phoneNumberCountryCode', ['+1'])
Copy link

@coderabbitai coderabbitai bot Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider expanding genderEnum for inclusivity

Currently, genderEnum includes 'male', 'female', and 'other'. To be more inclusive of all gender identities, consider expanding this enum or allowing users to self-identify their gender.

Possible code change:

-export const genderEnum = pgEnum('gender', ['male', 'female', 'other'])
+export const genderEnum = pgEnum('gender', ['male', 'female', 'non-binary', 'prefer not to say', 'other'])

Alternatively, you might use a varchar field with validation to allow for more flexibility.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const genderEnum = pgEnum('gender', ['male', 'female', 'other'])
export const genderEnum = pgEnum('gender', ['male', 'female', 'non-binary', 'prefer not to say', 'other'])

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well do this now, agree with this πŸ‘

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HasithDeAlwis, great! Happy to hear that you're making this update. 😊

(\_/)
(β€’γ……β€’)
/ γ€€ γ₯

If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

export const ethnicityEnum = pgEnum('ethnicity', ['black', 'white', 'asian', 'hispanic', 'middle-eastern'])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Re-evaluate phoneNumberCountryCodeEnum for international support

The phoneNumberCountryCodeEnum currently only includes '+'1', limiting users to those with North American phone numbers. If international users are expected, consider expanding this enum or using a varchar field.

Possible code change:

-export const phoneNumberCountryCodeEnum = pgEnum('phoneNumberCountryCode', ['+1'])
+export const phoneNumberCountryCodeEnum = varchar('phoneNumberCountryCode', { length: 5 }).notNull()

This change allows for a wider range of country codes while ensuring the field is not null.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const phoneNumberCountryCodeEnum = pgEnum('phoneNumberCountryCode', ['+1'])
export const phoneNumberCountryCodeEnum = varchar('phoneNumberCountryCode', { length: 5 }).notNull()


Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider further improvements to ethnicityEnum

While the previous comment about ethnicityEnum is still valid, consider these additional suggestions:

  1. Add an 'other' option to allow for ethnicities not listed.
  2. Consider using broader categories or allowing multiple selections.
  3. Consult with diversity and inclusion experts to ensure the categories are appropriate and inclusive.

Example improvement:

-export const ethnicityEnum = pgEnum('ethnicity', ['black', 'white', 'asian', 'hispanic', 'middle-eastern'])
+export const ethnicityEnum = pgEnum('ethnicity', ['black', 'white', 'asian', 'hispanic', 'middle_eastern', 'indigenous', 'pacific_islander', 'multiracial', 'other', 'prefer_not_to_say'])
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const ethnicityEnum = pgEnum('ethnicity', ['black', 'white', 'asian', 'hispanic', 'middle-eastern'])
export const ethnicityEnum = pgEnum('ethnicity', ['black', 'white', 'asian', 'hispanic', 'middle_eastern', 'indigenous', 'pacific_islander', 'multiracial', 'other', 'prefer_not_to_say'])

export const user = pgTable('user', {
id: text('id').primaryKey(),
name: text('name'),
email: text('email').notNull(),
avatarUrl: text('avatar_url'),
id: serial('id').primaryKey(),
firstName: varchar('firstName', { length: 32 }).notNull(),
middleName: varchar('middleName', { length: 32 }),
lastName: varchar('lastName', { length: 32 }).notNull(),
preferredName: varchar('preferredName', { length: 32 }),
email: varchar('email', { length: 64 }).unique().notNull(),
Comment on lines +13 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider adding length validation for name fields

While the varchar length is set, consider adding additional validation to ensure minimum lengths and character types for name fields. This can help prevent empty or invalid inputs.

Example:

firstName: varchar('firstName', { length: 32 }).notNull().check(sql`length(firstName) >= 2`),

isEmailVerified: boolean('isEmailVerified').default(false),
avatarUrl: varchar('avatarUrl', { length: 128 }),
profileDescription: text('profileDescription'),
dateOfBirth: date('dateOfBirth'),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Add length constraint to profileDescription

Consider adding a maximum length constraint to the profileDescription field to prevent excessively long inputs:

-profileDescription: text('profileDescription'),
+profileDescription: varchar('profileDescription', { length: 500 }),

This change limits the description to 500 characters, which should be sufficient for most use cases while preventing potential abuse.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
profileDescription: text('profileDescription'),
profileDescription: varchar('profileDescription', { length: 500 }),

gender: genderEnum('gender'),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Add age validation for dateOfBirth

Consider adding a check constraint to ensure the user is of appropriate age:

dateOfBirth: date('dateOfBirth').check(sql`dateOfBirth <= current_date - interval '13 years'`),

This example ensures users are at least 13 years old, but adjust the age limit according to your application's requirements.

phoneNumberCountryCode: phoneNumberCountryCodeEnum('phoneNumberCountryCode'),
phoneNumber: varchar('phoneNumber', { length: 16 }).unique(),
isPhoneNumberVerified: boolean('isPhoneNumberVerified').default(false),
numHackathonsAttended: smallint('numHackathonsAttended').default(0),
anyOtherComments: text('anyOtherComments'),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Add upper limit to numHackathonsAttended

Consider adding an upper limit to numHackathonsAttended to prevent unrealistic values:

-numHackathonsAttended: smallint('numHackathonsAttended').default(0),
+numHackathonsAttended: smallint('numHackathonsAttended').default(0).check(sql`numHackathonsAttended >= 0 AND numHackathonsAttended <= 100`),

This change ensures the value is non-negative and sets a reasonable upper limit of 100 hackathons.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
numHackathonsAttended: smallint('numHackathonsAttended').default(0),
numHackathonsAttended: smallint('numHackathonsAttended').default(0).check(sql`numHackathonsAttended >= 0 AND numHackathonsAttended <= 100`),

isDomestic: boolean('isDomestic'),
ethnicity: ethnicityEnum('ethnicity'),
estimatedGradYear: integer('estimatedGradYear'),
agreeToMlhReqs: boolean('agreeToMlhReqs').default(false),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Validate estimatedGradYear

Add a check constraint to ensure estimatedGradYear is within a reasonable range:

estimatedGradYear: integer('estimatedGradYear').check(sql`estimatedGradYear BETWEEN EXTRACT(YEAR FROM CURRENT_DATE) AND EXTRACT(YEAR FROM CURRENT_DATE) + 10`),

This ensures the graduation year is between the current year and 10 years in the future.

resumeId: integer('resumeId').references(() => resume.id),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Reconsider default value for agreeToMlhReqs

The agreeToMlhReqs field defaults to false, which might not be the desired behavior if agreeing is mandatory for participation.

Possible code change:

-agreeToMlhReqs: boolean('agreeToMlhReqs').default(false),
+agreeToMlhReqs: boolean('agreeToMlhReqs').notNull(),

By removing the default and adding .notNull(), you ensure that the user must explicitly agree.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
agreeToMlhReqs: boolean('agreeToMlhReqs').default(false),
agreeToMlhReqs: boolean('agreeToMlhReqs').notNull(),

schoolId: integer('schoolId').references(() => school.id),
emergencyContactId: integer('emergencyContactId').references(() => emergencyContact.id),
teamId: integer('teamId').references(() => team.id),
})
Comment on lines +12 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider adding created_at and updated_at fields

To improve record tracking and auditing capabilities, consider adding created_at and updated_at timestamp fields to the user table:

created_at: timestamp('created_at').defaultNow().notNull(),
updated_at: timestamp('updated_at').defaultNow().notNull(),

These fields can be very useful for debugging, auditing, and analyzing user data over time.

28 changes: 28 additions & 0 deletions libs/db/src/schema/userPreferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { boolean, integer, pgEnum, pgTable, serial } from 'drizzle-orm/pg-core'
import { user } from './user'

export const preferredLanguageEnum = pgEnum('preferredLanguage', ['EN'])
export const eventPreferencesEnum = pgEnum('eventPreferences', ['hardware', 'software'])
export const shirtSizeEnum = pgEnum('shirtSize', ['XS', 'S', 'M', 'L', 'XL', 'XXL'])
export const pronounsEnum = pgEnum('pronouns', ['he/him', 'she/her', 'they/them', 'other'])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Add a 'Prefer not to say' option to pronounsEnum.

Including a 'Prefer not to say' option allows users who do not wish to disclose their pronouns to have an appropriate selection.

export const dietRestrictionsEnum = pgEnum('dietRestrictions', ['allergies', 'vegan', 'none'])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Expand dietRestrictionsEnum to include more dietary options.

Currently, dietRestrictionsEnum includes ['allergies', 'vegan', 'none']. To accommodate a wider range of dietary needs, consider adding options such as 'vegetarian', 'gluten-free', 'halal', 'kosher', 'nut-free', etc.

export const trackPreferencesEnum = pgEnum('trackPreferences', ['hardware', 'software'])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Broaden the options in trackPreferencesEnum.

The trackPreferencesEnum currently contains ['hardware', 'software']. If the event offers additional tracks, consider expanding this enum to include them, providing users with more accurate preference options.

export const interestsEnum = pgEnum('interests', ['languages'])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Enhance interestsEnum with more categories.

The current interestsEnum may be too narrow to capture users' varied interests.

-export const interestsEnum = pgEnum('interests', ['languages'])
+export const interestsEnum = pgEnum('interests', ['languages', 'AI/ML', 'web_development', 'data_science', 'cybersecurity', 'mobile_development', 'game_development', 'iot', 'blockchain', 'other'])
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const interestsEnum = pgEnum('interests', ['languages'])
export const interestsEnum = pgEnum('interests', ['languages', 'AI/ML', 'web_development', 'data_science', 'cybersecurity', 'mobile_development', 'game_development', 'iot', 'blockchain', 'other'])

export const disabilitiesEnum = pgEnum('disabilities', ['mobility', 'visual', 'hearing', 'cognitive', 'mental'])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider adding 'Other' to disabilitiesEnum.

Including an 'Other' option allows users with disabilities not listed to accurately represent their needs.

export const applicableSkillsEnum = pgEnum('applicableSkills', ['JavaScript', 'TypeScript', 'Python', 'Java'])

export const userPreferences = pgTable('userPreferences', {
id: serial('id').primaryKey(),
userId: integer('userId').references(() => user.id),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Specify onDelete and onUpdate actions for the foreign key userId.

To maintain referential integrity, it's important to define what happens to userPreferences entries when a referenced user is deleted or updated. Consider specifying onDelete and onUpdate behaviors.

Apply the following change:

- userId: integer('userId').references(() => user.id),
+ userId: integer('userId').references(() => user.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
userId: integer('userId').references(() => user.id),
userId: integer('userId').references(() => user.id, { onDelete: 'cascade', onUpdate: 'cascade' }),

preferredLanguage: preferredLanguageEnum('preferredLanguage').default('EN'),
eventPreferences: eventPreferencesEnum('eventPreferences'),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Determine if eventPreferences should allow multiple selections.

If users can select multiple event preferences, you should define eventPreferences as an array type.

Update the field as follows:

- eventPreferences: eventPreferencesEnum('eventPreferences'),
+ eventPreferences: eventPreferencesEnum('eventPreferences').array(),

Committable suggestion was skipped due to low confidence.

privacyMode: boolean('privacyMode').default(false),
isSubscribedToNewsletter: boolean('isSubscribedToNewsletter').default(false),
shirtSize: shirtSizeEnum('shirtSize'),
pronouns: pronounsEnum('pronouns'),
dietRestrictions: dietRestrictionsEnum('dietRestrictions').array(),
trackPreferences: trackPreferencesEnum('trackPreferences').array(),
interests: interestsEnum('interests').array(),
disabilities: disabilitiesEnum('disabilities').array(),
applicableSkills: applicableSkillsEnum('applicableSkills').array(),
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Add timestamps for record tracking.

Including createdAt and updatedAt timestamps can be beneficial for tracking when records are created and modified.

Add the following fields to your table definition:

+ createdAt: timestamp('createdAt').defaultNow(),
+ updatedAt: timestamp('updatedAt').defaultNow().onUpdateNow(),

Ensure you import timestamp from drizzle-orm/pg-core:

import { boolean, integer, pgEnum, pgTable, serial, timestamp } from 'drizzle-orm/pg-core'

Committable suggestion was skipped due to low confidence.

Comment on lines +14 to +28
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Consider adding a unique constraint on userId.

To ensure that each user has only one set of preferences, you might want to add a unique constraint on the userId field.

Add the following after the table definition:

export const userPreferencesConstraints = pgTable('userPreferences', {
  userIdUnique: unique('userIdUnique').on(userPreferences.userId),
})

Don't forget to import unique at the top of the file:

-import { boolean, integer, pgEnum, pgTable, serial, timestamp } from 'drizzle-orm/pg-core'
+import { boolean, integer, pgEnum, pgTable, serial, timestamp, unique } from 'drizzle-orm/pg-core'

Loading