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

Conversation

JeremyFriesenGitHub
Copy link
Contributor

@JeremyFriesenGitHub JeremyFriesenGitHub commented Oct 6, 2024

🚩 Linked issue

Here is the linked issue.

🛠 How does this PR address the issue(s)? How does it solve the problem?

This PR implements a Drizzle PSQL DB for the Portal User Profile creation. For now, this is a temporary, rough-draft, tentative schema, that can be changed at any time.

🧠 Describe any alternatives you've considered

Drizzle is type safe, we can get direct access to attributes, we can make queries easily and we are able to migrate easily and efficiently towards other databases or make/incorporate changes easily into the db if needed. This is why setting up an ORM is extremely critical for our backend infrastructure and will save a lot of time.

✨ Dependency Changes (if applicable)

N/A

📸 Before and After Screenshots (if applicable)

Before After
cuHacking Database Design-2024-09-28-192630 cuHacking Database Design-2024-10-06-034159

✍️ Additional comments (if applicable)

We updated a bit of the database design. There are more changes to come for that design (for instance, UserPreferances table should be merged with the User table). But for now, we are going to set everything up and we will be able to run migrations later thanks to DrizzleORM.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced multiple new schemas: emergencyContact, program, resume, school, socialMedia, team, and userPreferences, enhancing data management capabilities.
    • Added comprehensive user preferences management with various enumerated types for better customization.
  • Improvements

    • Expanded the existing user schema with new fields for better user data handling.
    • Enhanced the session schema with updated field names for clarity.
    • Implemented support for remote images in the Next.js application, allowing for better media integration.
    • Added a new section in documentation featuring a Mermaid Entity-Relationship Diagram for the portal database schema, improving clarity on entity relationships.

These updates improve the overall functionality and user experience within the application.

Copy link

coderabbitai bot commented Oct 6, 2024

📝 Walkthrough

Walkthrough

The changes in this pull request involve the introduction of new database schemas and modifications to existing schemas using the Drizzle ORM for PostgreSQL. New files define schemas for emergencyContact, program, resume, school, socialMedia, team, and userPreferences, each with specified fields and enumerations. Additionally, the index.ts files have been updated to export these new schemas, enhancing the overall module interface. Existing schemas, such as user and session, have also been modified to improve their structure and data types.

Changes

File Path Change Summary
libs/db/src/index.ts Added exports for emergencyContact, program, resume, school, socialMedia, team, userPreferences. Retained export for user.
libs/db/src/schema/emergencyContact.ts Introduced schema for emergencyContact with fields: id, name, relationship, phoneNumber, isPhoneNumberVerified. Added relationshipEnum.
libs/db/src/schema/index.ts Added exports for emergencyContact, program, resume, school, socialMedia, team, userPreferences. Retained export for user.
libs/db/src/schema/program.ts Introduced schema for program with fields: id, name, schoolId, programType. Added programTypeEnum.
libs/db/src/schema/resume.ts Introduced schema for resume with fields: id, fileLink, hasPrivacyToggle, uploadedAt.
libs/db/src/schema/school.ts Introduced schema for school with fields: id, name, levelOfStudy. Added levelOfStudyEnum.
libs/db/src/schema/session.ts Renamed fields in session: user_id to userId, expires_at to expiresAt.
libs/db/src/schema/socialMedia.ts Introduced schema for socialMedia with fields: id, userId, platformName, profileUrl.
libs/db/src/schema/team.ts Introduced schema for team with fields: id, name, profileImageUrl, projectLinkUrl, teamOwnerId, hasSubmitted, teamProfileImageUrl.
libs/db/src/schema/user.ts Updated user schema with new fields and types, including firstName, middleName, lastName, and several new foreign keys. Added enums for gender, phoneNumberCountryCode, and ethnicity.
libs/db/src/schema/userPreferences.ts Introduced schema for userPreferences with various fields and enumerations for user preference categories.
apps/docs/next.config.mjs Added configuration for handling remote images from https://avatars.githubusercontent.com.

Possibly related PRs

  • feat(api): create modularized libraries #113: The changes in this PR involve the creation of a modularized database library (@cuhacking/db) built with DrizzleORM, which is directly related to the modifications in the main PR that expand the database schemas and exports in the libs/db/src/index.ts file.
  • docs(knowledge-base): migrate 2020 cuhacking docs #132: This PR includes documentation updates for various database schemas, including User, EmergencyContact, Team, School, Program, Resume, SocialMedia, and UserPreferences, which are all directly related to the new exports added in the main PR.

Suggested labels

released

Suggested reviewers

  • MFarabi619

🐰 In the meadow, schemas bloom,
New tables rise, dispelling gloom.
With enums bright and fields so clear,
Our data sings, let’s give a cheer!
From user prefs to teams so grand,
A rabbit's hop through code so planned! 🥕✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

🧹 Outside diff range and nitpick comments (17)
libs/db/src/schema/session.ts (1)

Line range hint 1-14: Overall schema changes align well with PR objectives.

The renaming of fields from snake_case to camelCase improves consistency with JavaScript naming conventions, which is beneficial for a TypeScript project. These changes, while minor, contribute to the creation of initial portal database schemas as outlined in the PR objectives.

The session table structure appears well-suited for managing user sessions, which is crucial for the portal user profiles. The relationship between the session and user tables is maintained, ensuring data integrity.

As you continue to develop the schema, consider the following:

  1. Ensure that all related tables follow the same naming convention for consistency.
  2. If you plan to merge the UserPreferences table with the User table as mentioned in the PR description, make sure to update any references to UserPreferences in other parts of the codebase.
  3. As you're using Drizzle ORM, take advantage of its migration capabilities when making future schema changes to ensure smooth updates and backwards compatibility.
libs/db/src/schema/resume.ts (1)

1-8: Overall, good initial schema implementation with room for improvement.

This schema provides a solid foundation for storing resume information using Drizzle ORM, aligning well with the PR objectives. It offers type safety and ease of querying as mentioned in the PR description. However, consider the suggested enhancements to improve data integrity, query performance, and user association.

As this is described as a temporary, rough draft in the PR objectives, these improvements can be incorporated in future iterations. Remember to update the database design screenshots when making these changes.

libs/db/src/schema/school.ts (1)

5-9: LGTM: Table structure is appropriate. Consider additional fields.

The 'school' table structure is well-defined with a primary key, name, and level of study. However, consider adding more fields to enrich the school information:

  1. location: To store the school's address or city.
  2. establishedDate: To record when the school was founded.
  3. website: To store the school's official website URL.

These additional fields could provide more comprehensive information about each school.

Here's an example of how you might expand the table definition:

export const school = pgTable('school', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 128 }),
  levelOfStudy: levelOfStudyEnum('levelOfStudy'),
  location: varchar('location', { length: 255 }),
  establishedDate: date('established_date'),
  website: varchar('website', { length: 255 })
})
libs/db/src/schema/socialMedia.ts (2)

3-3: Consider using PascalCase for imported table names.

The import of the 'user' table is correct and follows good practices by separating table schemas into different files. However, to align with common TypeScript conventions, consider using PascalCase for imported table names.

You could update the import statement as follows:

-import { user } from './user'
+import { user as User } from './user'

Then use User instead of user in the foreign key reference.


5-11: Approve schema with suggestions for improvement.

The overall structure of the socialMedia table schema looks good. Here are some suggestions for improvement:

  1. Consider adding NOT NULL constraints to required fields.
  2. It might be beneficial to add a unique constraint on the combination of userId and platformName to prevent duplicate entries for the same user and platform.
  3. Verify if the varchar length limits are sufficient for your use case.

Here's a suggested improvement to the schema:

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

This adds NOT NULL constraints and a unique constraint on userId and platformName.

libs/db/src/schema/program.ts (2)

4-4: LGTM: Enum definition is clear and covers common program types.

The 'programTypeEnum' is well-defined and exported for use throughout the application. It covers the most common program types: bachelor, master, diploma, and certificate.

Consider if there are any other program types that might need to be included in the future, such as 'associate' or 'doctorate'. If not immediately necessary, this can be addressed in future iterations.


6-11: LGTM: Table structure is well-defined and establishes proper relationships.

The 'program' table is well-structured with appropriate field types:

  • 'id' as a serial primary key
  • 'name' as a varchar with a reasonable length limit
  • 'schoolId' establishing a foreign key relationship with the 'school' table
  • 'programType' utilizing the defined enum

Consider the following suggestions for potential improvements:

  1. Add a unique constraint to the 'name' field if program names are expected to be unique.
  2. Consider adding a 'description' field for more detailed program information.
  3. If applicable, add a 'createdAt' and 'updatedAt' timestamp fields for tracking record changes.

Example implementation:

export const program = pgTable('program', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 128 }).unique(),
  description: varchar('description', { length: 500 }),
  schoolId: integer('schoolId').references(() => school.id),
  programType: programTypeEnum('programType'),
  createdAt: timestamp('created_at').defaultNow(),
  updatedAt: timestamp('updated_at').defaultNow(),
})

These suggestions can be implemented based on the specific requirements of your application.

libs/db/src/schema/emergencyContact.ts (3)

3-3: LGTM: Comprehensive relationship enum, consider additional options.

The relationshipEnum covers common relationship types and includes an 'other' option for flexibility.

Consider if there are any other common relationships that should be explicitly included, such as 'guardian' or 'spouse'. This could improve data consistency and make it easier to filter or group by relationship type in the future.


5-11: LGTM: Well-structured schema, consider some enhancements.

The emergencyContact table schema is well-designed for storing essential emergency contact information. Here are some suggestions for potential improvements:

  1. The name field length (32 characters) might be too short for some names, especially those with middle names or from cultures with longer name formats. Consider increasing this to 64 or 128 characters.

  2. For the phoneNumber field, ensure that the length of 16 characters is sufficient for international number formats, including the '+' sign and country code.

  3. Consider adding an email address field, as it can be a useful alternative contact method in emergencies.

  4. There's currently no field linking this contact to a user. You might want to add a foreign key reference to the user table, unless this relationship is managed elsewhere.

Here's a suggested enhancement to the schema:

export const emergencyContact = pgTable('emergencyContact', {
  id: serial('id').primaryKey(),
  userId: integer('userId').notNull().references(() => user.id), // Add this line
  name: varchar('name', { length: 64 }), // Increased length
  relationship: relationshipEnum('relationship'),
  phoneNumber: varchar('phoneNumber', { length: 20 }).unique(), // Increased length
  isPhoneNumberVerified: boolean('isPhoneNumberVerified').default(false),
  email: varchar('email', { length: 255 }), // Add this line
})

Note: Ensure to import the integer type and reference the user table correctly if you implement these changes.


1-11: Overall assessment: Good initial implementation with room for enhancements.

This file successfully implements a basic schema for emergency contacts using Drizzle ORM, aligning well with the PR objectives. The code is well-structured and achieves its primary goal of setting up the initial schema for Portal User Profiles.

The suggestions provided earlier aim to enhance the robustness and flexibility of the schema, particularly in terms of accommodating various name formats, international phone numbers, and additional useful information like email addresses.

As mentioned in the PR description, this is a temporary, rough draft subject to change. The current implementation provides a solid foundation that can be easily modified or expanded using Drizzle ORM's migration capabilities.

As you continue to develop this schema, consider the following architectural points:

  1. Data normalization: Evaluate whether the emergency contact information should be in a separate table or part of the user table, based on your application's requirements and expected query patterns.

  2. Indexing: As the database grows, you might need to add indexes to frequently queried fields for better performance.

  3. Validation: Implement server-side validation for fields like phone numbers and email addresses to ensure data integrity.

  4. Privacy and security: Ensure that access to this sensitive information is properly restricted and consider encryption for highly sensitive data.

libs/db/src/index.ts (2)

13-21: LGTM! Consider grouping related schemas.

The new schema exports align well with the PR objectives of creating initial portal database schemas. The alphabetical organization improves readability. Great job on implementing a comprehensive set of schemas for the Portal User Profiles!

As a minor suggestion for improved organization, consider grouping related schemas together. For example, you could group user-related schemas (user, userPreferences) and education-related schemas (school, program) next to each other.

Here's a suggested regrouping:

export * from './schema/user'
export * from './schema/userPreferences'
export * from './schema/emergencyContact'
export * from './schema/socialMedia'
export * from './schema/resume'
export * from './schema/school'
export * from './schema/program'
export * from './schema/team'
export * from './schema/session'

This grouping might make it easier to understand the relationships between different schemas at a glance.


13-21: Consider adding documentation and planning for future changes.

The new schema exports provide a solid foundation for the Portal User Profiles, aligning well with the PR objectives. To further improve the implementation:

  1. Consider adding comments or documentation for each schema export, briefly describing its purpose and any important relationships with other schemas. This will be helpful for other developers and for future maintenance.

  2. Given the plan to merge the UserPreferences table with the User table in the future, it might be beneficial to add a TODO comment near these exports to remind about this future change.

  3. Since this is described as a temporary, rough draft of the schema, consider adding a comment at the top of the file indicating its draft status and that it's subject to change.

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

// TODO: This is a draft schema and is subject to change

// User-related schemas
export * from './schema/user'
export * from './schema/userPreferences' // TODO: Plan to merge with User schema
export * from './schema/emergencyContact'
export * from './schema/socialMedia'

// Education-related schemas
export * from './schema/school'
export * from './schema/program'

// Other schemas
export * from './schema/resume'
export * from './schema/team'
export * from './schema/session'

These additions will help maintain clarity as the schema evolves and make it easier for the team to track planned changes.

libs/db/src/schema/userPreferences.ts (2)

14-14: Align table naming conventions to snake_case.

The table name userPreferences is in camelCase. For consistency and adherence to SQL best practices, consider renaming it to user_preferences.


21-21: Consider setting a default value or making shirtSize optional.

To handle cases where users may not wish to provide their shirt size, consider providing a default value or allowing nulls.

libs/db/src/schema/user.ts (3)

24-24: Validate and format the phoneNumber field

While the phoneNumber field is defined with a length limit and uniqueness, adding validation to ensure numbers are correctly formatted can prevent errors and improve data quality.

Consider integrating a phone number validation library or implementing a regex pattern to validate the format.


19-20: Implement content guidelines for profileDescription

Allowing free-form text in profileDescription can lead to the entry of sensitive or inappropriate information.

Consider:

  • Adding content moderation.
  • Providing users with guidance on acceptable content.
  • Implementing length limits to prevent excessive input.

28-28: Clarify the purpose of isDomestic

The isDomestic boolean may need additional context or documentation to ensure consistent usage across the application.

Provide a comment or documentation explaining what qualifies as "domestic" in your context.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 18dacda and 3da1278.

📒 Files selected for processing (11)
  • libs/db/src/index.ts (1 hunks)
  • libs/db/src/schema/emergencyContact.ts (1 hunks)
  • libs/db/src/schema/index.ts (1 hunks)
  • libs/db/src/schema/program.ts (1 hunks)
  • libs/db/src/schema/resume.ts (1 hunks)
  • libs/db/src/schema/school.ts (1 hunks)
  • libs/db/src/schema/session.ts (1 hunks)
  • libs/db/src/schema/socialMedia.ts (1 hunks)
  • libs/db/src/schema/team.ts (1 hunks)
  • libs/db/src/schema/user.ts (1 hunks)
  • libs/db/src/schema/userPreferences.ts (1 hunks)
🔇 Additional comments (18)
libs/db/src/schema/index.ts (1)

1-9: LGTM! Consider organizing exports alphabetically.

The new exports align well with the PR objective of creating initial portal database schemas for user profiles. The inclusion of various aspects like emergency contacts, programs, resumes, etc., provides a comprehensive structure for user data.

To improve readability and maintainability, consider organizing the exports alphabetically. This would make it easier to locate specific modules in the future, especially as the number of exports grows.

Here's a suggested reorganization:

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'

Let's verify the existence of the newly exported modules:

This script will help ensure that all the exported schema modules have corresponding files in the expected location.

✅ Verification successful

All exported schema modules verified successfully.

All the exported schema modules have been confirmed to exist as expected.

To improve readability and maintainability, consider organizing the exports alphabetically.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the newly exported schema modules

# Test: Check if all the exported schema files exist
for schema in emergencyContact program resume school session socialMedia team user userPreferences; do
  if [ ! -f "libs/db/src/schema/${schema}.ts" ]; then
    echo "Error: ${schema}.ts does not exist"
    exit 1
  fi
done

echo "All schema files exist"

Length of output: 1399

libs/db/src/schema/session.ts (2)

Line range hint 10-14: Approve the renaming of expires_at to expiresAt and request clarification on timestamp mode.

The change to camelCase is consistent with JavaScript conventions and the previous field change. The use of a timestamp with timezone is appropriate for session expiration.

Please run the following script to check for any remaining references to expires_at:

#!/bin/bash
# Search for any remaining references to 'expires_at' in TypeScript files
rg --type typescript 'expires_at' -g '!libs/db/src/schema/session.ts'

Could you please clarify the purpose of setting mode: 'date' for the timestamp? It's not immediately clear what this setting does in the context of Drizzle ORM and how it affects the stored expiration time.


7-9: Approve the renaming of user_id to userId.

The change from snake_case to camelCase improves consistency with JavaScript naming conventions while maintaining the field's properties and relationships.

To ensure this change doesn't break existing code, please run the following script to check for any remaining references to user_id:

✅ Verification successful

Verified the renaming of user_id to userId.

The remaining references to 'user_id' in drizzle/schema.ts are part of the schema definitions and correctly map the ORM field userId to the database column user_id. This change maintains consistency in the codebase without introducing issues.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to 'user_id' in TypeScript files
rg --type typescript 'user_id' -g '!libs/db/src/schema/session.ts'

Length of output: 104


Script:

#!/bin/bash
# Search for any remaining references to 'user_id' in TypeScript files
rg --type ts 'user_id' -g '!libs/db/src/schema/session.ts'

Length of output: 173

libs/db/src/schema/resume.ts (1)

1-1: LGTM: Imports are appropriate and fully utilized.

The imports from 'drizzle-orm/pg-core' are correct and all imported types (boolean, pgTable, serial, timestamp, varchar) are used in the schema definition.

libs/db/src/schema/school.ts (2)

1-1: LGTM: Imports are correct and necessary.

The imports from 'drizzle-orm/pg-core' are appropriate for defining PostgreSQL tables and enums using Drizzle ORM.


1-9: Overall implementation aligns well with PR objectives.

This schema definition for the 'school' table is a solid starting point for the portal database. It successfully uses Drizzle ORM, aligning with the PR objectives of leveraging an ORM for type safety and efficient migrations. The basic structure is in place, allowing for easy expansion as requirements evolve.

Consider the suggestions for expanding the level of study enum and adding more fields to the school table to make the schema more comprehensive. These changes can be implemented now or in future iterations, depending on your immediate needs and timeline.

Remember that this is part of the initial schema setup, and as mentioned in the PR description, further updates and refinements are anticipated. The current implementation provides a good foundation for these future changes.

libs/db/src/schema/socialMedia.ts (1)

1-1: LGTM: Appropriate imports for Drizzle ORM schema definition.

The imports from 'drizzle-orm/pg-core' are correct and all imported items are utilized in the schema definition.

libs/db/src/schema/program.ts (2)

1-2: LGTM: Imports are appropriate for the schema definition.

The imports from 'drizzle-orm/pg-core' provide the necessary tools for defining the PostgreSQL table schema. The import of 'school' indicates a relationship between the 'program' and 'school' tables, which aligns with the expected database structure.


1-11: Overall: Well-structured schema implementation for the 'program' table.

This file successfully implements the initial schema for the 'program' table using Drizzle ORM, aligning with the PR objectives. The structure is clear, establishes necessary relationships, and provides a solid foundation for the portal database.

Key points:

  1. Appropriate use of Drizzle ORM for type-safe schema definition.
  2. Clear enum definition for program types.
  3. Well-structured table with primary key, foreign key relationship, and use of custom enum.

The implementation serves as a good starting point for the portal database schemas. As mentioned in the PR description, this is a temporary draft and subject to change, which allows for future refinements based on evolving requirements.

To ensure consistency with other parts of the schema, let's verify the 'school' table structure:

This will help confirm that the 'school' table exists and has a compatible structure for the foreign key relationship.

libs/db/src/schema/team.ts (3)

1-1: LGTM: Imports are appropriate and concise.

The imports from 'drizzle-orm/pg-core' are correctly selected for defining a PostgreSQL table schema. All imported types are utilized in the schema definition, maintaining a clean and efficient import statement.


1-13: Overall, good initial implementation of the team schema.

This file successfully introduces the 'team' table schema using Drizzle ORM, aligning with the PR objectives. The implementation provides a solid foundation for the portal database, allowing for easy future refinements as mentioned in the PR description.

Key points:

  1. The schema covers essential team attributes.
  2. Use of Drizzle ORM facilitates type safety and efficient migrations.
  3. The structure allows for future updates, such as merging with a potential UserPreferences table.

Consider the suggested improvements in the previous comment to enhance data integrity and reduce redundancy. As this is described as a temporary, rough draft, these changes can be incorporated in future iterations of the schema.


3-13: 🛠️ Refactor suggestion

Consider additional constraints and potential redundancy in the schema.

The schema is well-structured, but consider the following improvements:

  1. Add a 'not null' constraint to the 'name' field, as every team should have a name.
  2. There seems to be redundancy between 'profileImageUrl' and 'teamProfileImageUrl'. Consider consolidating these into a single field.
  3. If there's a corresponding 'user' table, add a foreign key constraint to 'teamOwnerId'.
  4. Consider adding unique constraints where appropriate (e.g., 'name' field).

Here's a suggested revision:

export const team = pgTable('team', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 64 }).notNull().unique(),
  profileImageUrl: varchar('profileImageUrl', { length: 128 }),
  projectLinkUrl: varchar('projectLinkUrl', { length: 256 }),
  teamOwnerId: integer('teamOwnerId').references(() => user.id), // Assuming a 'user' table exists
  hasSubmitted: boolean('hasSubmitted').default(false),
});

This revision addresses the mentioned points and aligns with the PR objectives of establishing an initial, changeable schema.

To verify the existence of a 'user' table and its structure, run:

✅ Verification successful

Verification Successful: Foreign key constraint is valid.

The 'user' table exists with an 'id' field defined as a primary key, confirming that adding a foreign key constraint to 'teamOwnerId' is appropriate.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for 'user' table definition
# Expected result: A 'user' table definition with an 'id' field

rg --type typescript 'export const user = pgTable\(' libs/db/src/schema

Length of output: 108


Script:

#!/bin/bash
# Description: Check for 'user' table definition using the correct type identifier
# Expected result: A 'user' table definition with an 'id' field

rg --type ts 'export const user = pgTable\(' libs/db/src/schema

Length of output: 130


Script:

#!/bin/bash
# Description: Verify the 'id' field in the 'user' table definition
# Expected result: The 'id' field is defined as a primary key

rg "id\s*:\s*serial\('id'\)\.primaryKey\(\)" libs/db/src/schema/user.ts

Length of output: 112

libs/db/src/schema/emergencyContact.ts (1)

1-1: LGTM: Imports are appropriate and used correctly.

The necessary functions from 'drizzle-orm/pg-core' are imported for defining the PostgreSQL table schema using Drizzle ORM.

libs/db/src/schema/userPreferences.ts (2)

17-17: Ensure default value for preferredLanguage accommodates all users.

While 'EN' is set as the default, consider if this is appropriate for your user base. If the application supports multiple languages, users might prefer to set their language during registration.


23-27: Verify handling of empty arrays for preference fields.

Ensure that the application and database correctly handle scenarios where users have not selected any options for dietRestrictions, trackPreferences, interests, disabilities, and applicableSkills. This might involve setting default empty arrays or allowing null values.

Consider adding default empty arrays:

- dietRestrictions: dietRestrictionsEnum('dietRestrictions').array(),
+ dietRestrictions: dietRestrictionsEnum('dietRestrictions').array().default([]),

Apply similar changes to the other array fields if appropriate.

libs/db/src/schema/user.ts (3)

32-35: Ensure foreign key constraints are correctly enforced

The foreign keys resumeId, schoolId, emergencyContactId, and teamId may require .notNull() if these relationships are mandatory.

Please verify if these associations are optional. If they are required, consider:

-resumeId: integer('resumeId').references(() => resume.id),
+resumeId: integer('resumeId').references(() => resume.id).notNull(),

Repeat for the other foreign keys as needed.


21-21: Handle dateOfBirth with care for compliance

Collecting dateOfBirth may have implications under data protection regulations like GDPR or COPPA, especially for users under certain ages.

Ensure that:

  • Appropriate consent is obtained.
  • Data is stored and processed in compliance with regulations.
  • Age-related access controls are implemented if necessary.

26-26: Check the datatype for numHackathonsAttended

Using smallint limits the maximum value to 32,767. If users might attend more hackathons, or if this field could be misused, consider using a larger integer type.

Comment on lines +3 to +8
export const resume = pgTable('resume', {
id: serial('id').primaryKey(),
fileLink: varchar('fileLink', { length: 128 }),
hasPrivacyToggle: boolean('hasPrivacyToggle').default(false),
uploadedAt: timestamp('uploadedAt').defaultNow(),
})
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.

@@ -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'
])

Comment on lines +5 to +11
export const socialMedia = pgTable('socialMedia', {
id: serial('id').primaryKey(),
userId: integer('userId')
.references(() => user.id),
platformName: varchar('platformName', { length: 16 }),
profileUrl: varchar('profileUrl', { length: 128 }),
})
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.

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 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'])
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 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' }),

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.

import { emergencyContact } from './emergencyContact'
import { team } from './team'

export const genderEnum = pgEnum('gender', ['male', 'female', 'other'])
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!

import { team } from './team'

export const genderEnum = pgEnum('gender', ['male', 'female', 'other'])
export const phoneNumberCountryCodeEnum = pgEnum('phoneNumberCountryCode', ['+1'])
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()

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.

⚠️ 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(),

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
apps/docs/next.config.mjs (1)

19-26: LGTM! Consider future-proofing the configuration.

The addition of the images configuration is correct and well-structured. It properly allows remote images from GitHub avatars, which is suitable for a documentation site.

For future-proofing, consider using an environment variable for the hostname. This would make it easier to change or add additional hostnames in different environments. For example:

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

This change is optional and can be implemented in a future update if needed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3da1278 and b4dfeae.

📒 Files selected for processing (1)
  • apps/docs/next.config.mjs (1 hunks)
🧰 Additional context used

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
apps/docs/src/content/docs/libraries/db.mdx (1)

13-116: Excellent addition of the Mermaid ERD!

The Entity-Relationship Diagram provides a clear and comprehensive visual representation of the database schema, which is crucial for understanding the system's structure. This aligns well with the PR objectives of creating initial portal database schemas.

However, I have a few suggestions for improvement:

  1. Consider merging UserPreferences into the User table as mentioned in the PR objectives. This could simplify the schema and reduce the number of joins needed in queries.

  2. The Team entity has both profileImageUrl and teamProfileImageUrl. This seems redundant and could be consolidated.

  3. In the User entity, resumeId, schoolId, emergencyContactId, and teamId are marked as mandatory relationships. Consider if all of these should be required upon user creation or if some could be optional.

  4. The SocialMedia entity has a many-to-one relationship with User, but the userId is marked as optional. Consider if this should be mandatory or if there's a use case for social media entries not associated with a user.

  5. In the relationships section, User has a one-to-many relationship with EmergencyContact, but typically a user would have only one emergency contact. Consider if this should be a one-to-one relationship.

Would you like assistance in implementing any of these suggestions?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 417207a and 3889133.

📒 Files selected for processing (1)
  • apps/docs/src/content/docs/libraries/db.mdx (3 hunks)
🧰 Additional context used
🔇 Additional comments (2)
apps/docs/src/content/docs/libraries/db.mdx (2)

176-179: Improved clarity in Drizzle-Kit commands section

The changes in this section enhance the documentation by providing more specific information about Drizzle-Kit commands and including an example of how to run a migration. This improvement in clarity will be helpful for developers using the library.


229-229: Helpful addition of troubleshooting resource

The inclusion of a link to DrizzleORM's troubleshooting page is a valuable addition to the documentation. It provides users with an official resource for resolving issues they may encounter while working with DrizzleORM.

@JeremyFriesenGitHub JeremyFriesenGitHub force-pushed the jowi/feat/133-create-initial-portal-database-schemas branch from 3889133 to 9c3fae1 Compare October 6, 2024 18:52
@JeremyFriesenGitHub JeremyFriesenGitHub linked an issue Oct 6, 2024 that may be closed by this pull request
@JeremyFriesenGitHub JeremyFriesenGitHub force-pushed the jowi/feat/133-create-initial-portal-database-schemas branch from 9c3fae1 to 33a5186 Compare October 6, 2024 19:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (3)
apps/docs/next.config.mjs (2)

20-27: LGTM: Image optimization configuration added.

The images configuration is well-structured and allows for flexible remote image optimization. Using an environment variable for the hostname enhances security and configurability.

Consider adding validation for the ALLOWED_IMAGE_HOSTNAME environment variable to ensure it's a valid hostname. This can prevent potential security issues if the environment variable is misconfigured. Here's a suggested implementation:

const validateHostname = (hostname) => {
  const hostnameRegex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
  return hostnameRegex.test(hostname);
};

const allowedImageHostname = process.env.ALLOWED_IMAGE_HOSTNAME || 'avatars.githubusercontent.com';

if (!validateHostname(allowedImageHostname)) {
  throw new Error(`Invalid ALLOWED_IMAGE_HOSTNAME: ${allowedImageHostname}`);
}

// ... rest of the config

images: {
  remotePatterns: [
    {
      protocol: 'https',
      hostname: allowedImageHostname,
    },
  ],
},

This addition will validate the hostname before using it in the configuration, providing an extra layer of security.


Line range hint 1-27: Summary: Image optimization configuration enhances flexibility and security.

The changes to this file are minimal but impactful. They introduce image optimization configuration that aligns with Next.js best practices. The use of environment variables for the hostname adds flexibility and enhances security. These changes support the PR's objective of setting up initial configurations for the portal.

As the project evolves, consider the following:

  1. Implement a centralized configuration management system if more environment-based configs are added in the future.
  2. Document the ALLOWED_IMAGE_HOSTNAME environment variable in your project's documentation or README to ensure other developers are aware of its usage and importance.
apps/docs/src/content/docs/libraries/db.mdx (1)

13-116: Great addition of the Mermaid ERD! A few suggestions for improvement:

The Entity-Relationship Diagram provides a clear overview of the database schema. Here are some suggestions to enhance it further:

  1. Consider adding more specific enumerations for fields like trackPreferences and applicableSkills in the UserPreferences entity. This will help maintain consistency and facilitate easier querying.

  2. The agreeToMlhReqs field in the User entity might benefit from a more descriptive name or additional documentation to clarify its purpose.

  3. Review the cardinality of relationships:

    • The User to EmergencyContact relationship is currently many-to-one. Consider if a user should have multiple emergency contacts (one-to-many).
    • The User to Team relationship is currently optional many-to-one. Clarify if users can be part of multiple teams or if teams can have multiple users.
  4. Consider adding created_at and updated_at timestamp fields to entities for audit purposes.

Would you like me to propose specific changes to address these points?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3889133 and 33a5186.

📒 Files selected for processing (3)
  • apps/docs/next.config.mjs (2 hunks)
  • apps/docs/src/content/docs/knowledge-base/cuHacking-2020-wiki/meta.json (0 hunks)
  • apps/docs/src/content/docs/libraries/db.mdx (3 hunks)
💤 Files with no reviewable changes (1)
  • apps/docs/src/content/docs/knowledge-base/cuHacking-2020-wiki/meta.json
🧰 Additional context used
🔇 Additional comments (3)
apps/docs/next.config.mjs (1)

1-1: LGTM: Explicit import of process module.

The explicit import of the process module is correct and follows best practices for Next.js configurations, especially in newer versions where Node.js APIs might need to be imported explicitly.

apps/docs/src/content/docs/libraries/db.mdx (2)

176-177: Improved clarity and added helpful link.

The text adjustments enhance readability and provide more comprehensive information about available Drizzle-Kit commands. The addition of the link to the official documentation is particularly useful for users who need more detailed information.


229-229: Helpful addition of troubleshooting resource.

The inclusion of a link to DrizzleORM's official troubleshooting page is a valuable addition. It provides users with an authoritative source for resolving issues they might encounter while working with the library.

@MFarabi619
Copy link
Member

@JeremyFriesenGitHub Is it possible to link a publicly visible live link to the actual mermaid code?

@JeremyFriesenGitHub
Copy link
Contributor Author

@JeremyFriesenGitHub Is it possible to link a publicly visible live link to the actual mermaid code?

i have no idea atp, im not sure.

@JowiAoun JowiAoun marked this pull request as ready for review October 13, 2024 00:46
@JowiAoun JowiAoun force-pushed the jowi/feat/133-create-initial-portal-database-schemas branch from 33a5186 to 4dc2a04 Compare October 13, 2024 00:48
Copy link

netlify bot commented Oct 13, 2024

Deploy Preview for website-cuhacking ready!

Name Link
🔨 Latest commit 479b8a2
🔍 Latest deploy log https://app.netlify.com/sites/website-cuhacking/deploys/67113d03c6072100084bd6c9
😎 Deploy Preview https://deploy-preview-134--website-cuhacking.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 33a5186 and 4dc2a04.

📒 Files selected for processing (14)
  • apps/docs/next.config.mjs (2 hunks)
  • apps/docs/src/content/docs/knowledge-base/cuHacking-2020-wiki/meta.json (0 hunks)
  • apps/docs/src/content/docs/libraries/db.mdx (3 hunks)
  • libs/db/src/index.ts (1 hunks)
  • libs/db/src/schema/emergencyContact.ts (1 hunks)
  • libs/db/src/schema/index.ts (1 hunks)
  • libs/db/src/schema/program.ts (1 hunks)
  • libs/db/src/schema/resume.ts (1 hunks)
  • libs/db/src/schema/school.ts (1 hunks)
  • libs/db/src/schema/session.ts (1 hunks)
  • libs/db/src/schema/socialMedia.ts (1 hunks)
  • libs/db/src/schema/team.ts (1 hunks)
  • libs/db/src/schema/user.ts (1 hunks)
  • libs/db/src/schema/userPreferences.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/docs/src/content/docs/knowledge-base/cuHacking-2020-wiki/meta.json
🚧 Files skipped from review as they are similar to previous changes (11)
  • apps/docs/next.config.mjs
  • apps/docs/src/content/docs/libraries/db.mdx
  • libs/db/src/index.ts
  • libs/db/src/schema/emergencyContact.ts
  • libs/db/src/schema/index.ts
  • libs/db/src/schema/program.ts
  • libs/db/src/schema/resume.ts
  • libs/db/src/schema/school.ts
  • libs/db/src/schema/session.ts
  • libs/db/src/schema/socialMedia.ts
  • libs/db/src/schema/team.ts
🧰 Additional context used
🔇 Additional comments (10)
libs/db/src/schema/userPreferences.ts (9)

1-2: Imports look good!

The necessary functions from 'drizzle-orm/pg-core' are imported, and the 'user' import suggests proper modularization of the schema.


1-28: Overall assessment: Good foundation with room for improvement

The userPreferences schema provides a solid foundation for storing user preferences. The suggested improvements will enhance the schema's inclusivity, flexibility, and data integrity. Key areas for improvement include:

  1. Expanding enum options to be more inclusive and comprehensive.
  2. Adding referential integrity constraints to the foreign key.
  3. Considering array types for multi-select preferences.
  4. Adding timestamps for better record tracking.
  5. Implementing a unique constraint on userId.

These changes will result in a more robust and user-friendly schema. Great work on setting up this initial structure!


28-28: 🛠️ Refactor suggestion

Add timestamps for record tracking.

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

   applicableSkills: applicableSkillsEnum('applicableSkills').array(),
+  createdAt: timestamp('createdAt').defaultNow(),
+  updatedAt: timestamp('updatedAt').defaultNow().onUpdateNow(),
 })

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

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

Likely invalid or redundant comment.


16-16: 🛠️ 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.

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

Likely invalid or redundant comment.


7-7: 🛠️ 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 pronounsEnum = pgEnum('pronouns', ['he/him', 'she/her', 'they/them', 'other'])
+export const pronounsEnum = pgEnum('pronouns', ['he/him', 'she/her', 'they/them', 'other', 'prefer not to say'])

Likely invalid or redundant comment.


18-18: 🛠️ Refactor suggestion

Consider allowing multiple selections for eventPreferences.

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

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

Likely invalid or redundant comment.


11-11: 🛠️ Refactor suggestion

Consider adding 'Other' to disabilitiesEnum.

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

-export const disabilitiesEnum = pgEnum('disabilities', ['mobility', 'visual', 'hearing', 'cognitive', 'mental'])
+export const disabilitiesEnum = pgEnum('disabilities', ['mobility', 'visual', 'hearing', 'cognitive', 'mental', 'other'])

Likely invalid or redundant comment.


8-8: 🛠️ Refactor suggestion

Expand dietRestrictionsEnum to include more dietary options.

To accommodate a wider range of dietary needs, consider adding more options.

-export const dietRestrictionsEnum = pgEnum('dietRestrictions', ['allergies', 'vegan', 'none'])
+export const dietRestrictionsEnum = pgEnum('dietRestrictions', ['allergies', 'vegan', 'vegetarian', 'gluten-free', 'halal', 'kosher', 'nut-free', 'none', 'other'])

Likely invalid or redundant comment.


9-9: 🛠️ Refactor suggestion

Broaden the options in trackPreferencesEnum.

If the event offers additional tracks, consider expanding this enum to provide users with more accurate preference options.

-export const trackPreferencesEnum = pgEnum('trackPreferences', ['hardware', 'software'])
+export const trackPreferencesEnum = pgEnum('trackPreferences', ['hardware', 'software', 'design', 'data_science', 'cybersecurity', 'other'])

Likely invalid or redundant comment.

libs/db/src/schema/user.ts (1)

1-5: LGTM: Imports are appropriate and used correctly

The imports from 'drizzle-orm/pg-core' and other schema files are correctly utilized in the schema definition, providing necessary types and references for foreign keys.

Comment on lines +14 to +28
export const userPreferences = pgTable('userPreferences', {
id: serial('id').primaryKey(),
userId: integer('userId').references(() => user.id),
preferredLanguage: preferredLanguageEnum('preferredLanguage').default('EN'),
eventPreferences: eventPreferencesEnum('eventPreferences'),
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

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'

export const pronounsEnum = pgEnum('pronouns', ['he/him', 'she/her', 'they/them', 'other'])
export const dietRestrictionsEnum = pgEnum('dietRestrictions', ['allergies', 'vegan', 'none'])
export const trackPreferencesEnum = pgEnum('trackPreferences', ['hardware', 'software'])
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'])

Comment on lines +13 to +16
firstName: varchar('firstName', { length: 32 }).notNull(),
middleName: varchar('middleName', { length: 32 }),
lastName: varchar('lastName', { length: 32 }).notNull(),
preferredName: varchar('preferredName', { length: 32 }),
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 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.

anyOtherComments: text('anyOtherComments'),
isDomestic: boolean('isDomestic'),
ethnicity: ethnicityEnum('ethnicity'),
estimatedGradYear: integer('estimatedGradYear'),
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.

Comment on lines +12 to +35
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(),
isEmailVerified: boolean('isEmailVerified').default(false),
avatarUrl: varchar('avatarUrl', { length: 128 }),
profileDescription: text('profileDescription'),
dateOfBirth: date('dateOfBirth'),
gender: genderEnum('gender'),
phoneNumberCountryCode: phoneNumberCountryCodeEnum('phoneNumberCountryCode'),
phoneNumber: varchar('phoneNumber', { length: 16 }).unique(),
isPhoneNumberVerified: boolean('isPhoneNumberVerified').default(false),
numHackathonsAttended: smallint('numHackathonsAttended').default(0),
anyOtherComments: text('anyOtherComments'),
isDomestic: boolean('isDomestic'),
ethnicity: ethnicityEnum('ethnicity'),
estimatedGradYear: integer('estimatedGradYear'),
agreeToMlhReqs: boolean('agreeToMlhReqs').default(false),
resumeId: integer('resumeId').references(() => resume.id),
schoolId: integer('schoolId').references(() => school.id),
emergencyContactId: integer('emergencyContactId').references(() => emergencyContact.id),
teamId: integer('teamId').references(() => team.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

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.

email: varchar('email', { length: 64 }).unique().notNull(),
isEmailVerified: boolean('isEmailVerified').default(false),
avatarUrl: varchar('avatarUrl', { length: 128 }),
profileDescription: text('profileDescription'),
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 }),

phoneNumberCountryCode: phoneNumberCountryCodeEnum('phoneNumberCountryCode'),
phoneNumber: varchar('phoneNumber', { length: 16 }).unique(),
isPhoneNumberVerified: boolean('isPhoneNumberVerified').default(false),
numHackathonsAttended: smallint('numHackathonsAttended').default(0),
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`),


export const genderEnum = pgEnum('gender', ['male', 'female', 'other'])
export const phoneNumberCountryCodeEnum = pgEnum('phoneNumberCountryCode', ['+1'])
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

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'])

@JeremyFriesenGitHub
Copy link
Contributor Author

@JeremyFriesenGitHub Is it possible to link a publicly visible live link to the actual mermaid code?

After looking at this for a while, I have no idea what u mean by this. Could you clarify @MFarabi619

@MFarabi619
Copy link
Member

@HasithDeAlwis

@MFarabi619
Copy link
Member

MFarabi619 commented Oct 15, 2024

chore(blocking): elaborate on behaviour for fix commits

I know it's strange that I nitpick on commit messages, it's because I review code very differently from most others; and in extremely close adherence to how I expect our code to change for trunk-based dev.

When I review code, I go by scanning commit history.

  • Provides me with more context
  • Allows me to understand the contributor's thought process better because I go through it linearly/chronologically
    • In turn that allows me to understand your business logic much faster without asking you tons of questions
    • This requires you to commit changes in an order that makes sense

If the commit message is redundant/misleading, that makes it very difficult to review code.

  • We're doing trunk-based development, so I'm assuming that the order in which your commits appear is the order in which I'm expected to review them.
    • Very much like reading a book. You go from one page/chapter to the next
    • You don't jump back and forth randomly. The book wouldn't make any sense when read out of order
    • When I code I do indeed make changes in a haphazard order. When I commit them, I do them in hunks, re-order, cherry-pick etc.
    • The order in which you see my commits ends up looking as if I did them perfectly the first time. A little bit of git-foo easily makes this possible.
  • My personal preference it to have longer commit message. An extra word or two can save an enormous amount of time
  • I try to use the type and scopes to indicate location

image

For fix commits, it's implied that it was a bug by the type, and the behaviour wasn't intended. Explain what was happening that wasn't desirable, that way if/when we downversion, we can quickly parse the commit history and be aware of the known issues.

You also want to avoid mentioning the types and scopes in the description of the message

  • The type and scope already indicate the location of the change
  • This allows me to prioritize things
  • I can also debug extremely fast by concluding whether a change in a certain area introduced a bug or not, thereby eliminating options

When you're scanning the commit history, which one is more helpful?

fix(docs): bug fix for tools-overview page
fix(wiki): bug fix for next button for wiki home page

Or

fix(docs/tools-overview): page displaying 404 error due to missing image loader in nextjs config
fix(docs/knowledge-base/wiki): clicking next button goes to _____ instead of _____

The tools-overview page was completely crashed and non functional for a bit due to the missing image loader, where was the breaking change introduced and how'd that make it past tests? :/

Avoid making bugfixes from unmerged features on the PR unless in dire emergencies that require velocity. Just squash/amend those fixes to your feature. A fix implies that this bug already existed on main. A high volume of fixes is a strong sign for us to improve testing and/or review processes.

Essentially answering who, what, where, when, why as much as possible. The commit message metadata already has the who, and when. The Issue+PR covers most of the 'why'. With your commit message you want to answer the what, and where, and it's EXTREMELY important for this to be laser-precise. This is the final level at which I decide whether to jump into code or not and find out. Commit messages are like road signs. They simply cannot be misleading/unhelpful. If you tell me to turn left/right, then I will turn left/right.

fix(libraries/db): bug fix for links on db page

  • What link was it?
  • What was not working as intended?
  • If this was a content change, it can be docs(libraries/db): fix broken link.., if this was a framework/layout level change then it should be fix(docs)...

feat(libs/db): initialize database schemas

  • Are we not using the api scope to signify back-end changes as mentioned in the contribution guidelines?
  • For example, I wouldn't do feat(apps/portal).., I'd just do feat(ui/portal) for a pure front-end change.

docs(libraries/db): add mermaid erd diagram to docs

  • The type already indicates that it's the docs project, you don't need to mention it again in the description

Codeowners are expected to look after the conventions and best practices of their respective areas to ensure they scale manageably and are consistent with the rest of the ecosystem. Conventions aren't set in stone and you can propose to change them.

Haven't completed my review, will do another pass later. Let me know what you guys think.

Copy link
Collaborator

@HasithDeAlwis HasithDeAlwis left a comment

Choose a reason for hiding this comment

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

Can we add TODO comments to add checks. Read more here.
I think all URLs need this.

Other than that good work on this, let's clean up a tiny bit of things then get this merged in.

@@ -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!

import { emergencyContact } from './emergencyContact'
import { team } from './team'

export const genderEnum = pgEnum('gender', ['male', 'female', '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 👍

@@ -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?

@@ -0,0 +1,9 @@
import { pgEnum, pgTable, serial, varchar } from 'drizzle-orm/pg-core'

export const levelOfStudyEnum = pgEnum('levelOfStudy', ['graduateSchool', 'highSchool'])
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 levelOfStudyEnum = pgEnum('levelOfStudy', ['graduateSchool', 'highSchool'])

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(),
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

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 👍

@MFarabi619 MFarabi619 force-pushed the jowi/feat/133-create-initial-portal-database-schemas branch from 4dc2a04 to 479b8a2 Compare October 17, 2024 16:36
Copy link

netlify bot commented Oct 17, 2024

Deploy Preview for docs-cuhacking ready!

Name Link
🔨 Latest commit 479b8a2
🔍 Latest deploy log https://app.netlify.com/sites/docs-cuhacking/deploys/67113d0353fc0900086d28fe
😎 Deploy Preview https://deploy-preview-134--docs-cuhacking.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@MFarabi619
Copy link
Member

Closing as stale

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants