diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9815d4fd..84860daa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,9 +46,9 @@ To get started with Code Racer locally, follow these steps 1. Make sure you have installed Docker locally (See above Prerequisites) -2. Fork the repo +2. Fork the repository as your own repo -3. Clone your fork +3. Clone forked repo to your local machine ```sh git clone https://github.com//code-racer.git @@ -69,25 +69,25 @@ To get started with Code Racer locally, follow these steps ```sh npm i ``` - + 8. Start the Database + If you are using Docker don't forget to run `docker-compose up` to start the database. + (Optional if using Docker for managing the database): Start the Database. + ```sh npm run dev:db ``` -If you are using Docker don't forget to run `docker-compose up` to start the database. 9. Start the app dev server ```sh npm run dev:app ``` - 10. Start the web socket server -```sh -npm run dev:wss -``` - + ```sh + npm run dev:wss + ``` Open your browser and visit to see the application running. ## Working on New Features @@ -101,7 +101,7 @@ There is also a new video explaining how you can contribute to this project: If you want to work on a new feature, follow these steps. -1. Fork the repo +1. Fork the repository 2. Clone your fork 3. Checkout a new branch 4. Do your work diff --git a/package-lock.json b/package-lock.json index 09ace0fd..0af40621 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,14 @@ "workspaces": [ "packages/*" ], + "dependencies": { + "@prisma/client": "^5.12.0" + }, "devDependencies": { "@flydotio/dockerfile": "^0.4.0", "cross-env": "^7.0.3", - "husky": "^8.0.3" + "husky": "^8.0.3", + "prisma": "^5.12.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -601,13 +605,10 @@ } }, "node_modules/@prisma/client": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.0.0.tgz", - "integrity": "sha512-XlO5ELNAQ7rV4cXIDJUNBEgdLwX3pjtt9Q/RHqDpGf43szpNJx2hJnggfFs7TKNx0cOFsl6KJCSfqr5duEU/bQ==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.12.0.tgz", + "integrity": "sha512-bk/+KPpRm0+IzqFCtAxrj+/TNiHzulspnO+OkysaYY/atc/eX0Gx8V3tTLxbHKVX0LKD4Hi8KKCcSbU1U72n7Q==", "hasInstallScript": true, - "dependencies": { - "@prisma/engines-version": "4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584" - }, "engines": { "node": ">=16.13" }, @@ -620,17 +621,56 @@ } } }, + "node_modules/@prisma/debug": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.12.0.tgz", + "integrity": "sha512-wK3fQLxPLMqf5riT5ZIhl8NffPSzFUwtzFX5CH7z/oI9Swmo9UhQlUgZABIVgdXSJ5OAlmRcDZtDKaMApIl8sg==", + "devOptional": true + }, "node_modules/@prisma/engines": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.0.0.tgz", - "integrity": "sha512-kyT/8fd0OpWmhAU5YnY7eP31brW1q1YrTGoblWrhQJDiN/1K+Z8S1kylcmtjqx5wsUGcP1HBWutayA/jtyt+sg==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.12.0.tgz", + "integrity": "sha512-rFNRul9JGu0d3tf8etBgmDQ4NVoDwgGrRguvQOc8i+c6g7xPjRuu4aKzMMvHWUuccvRx5+fs1KMBxQ0x2THt+Q==", "devOptional": true, - "hasInstallScript": true + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "5.12.0", + "@prisma/engines-version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab", + "@prisma/fetch-engine": "5.12.0", + "@prisma/get-platform": "5.12.0" + } }, - "node_modules/@prisma/engines-version": { - "version": "4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584.tgz", - "integrity": "sha512-HHiUF6NixsldsP3JROq07TYBLEjXFKr6PdH8H4gK/XAoTmIplOJBCgrIUMrsRAnEuGyRoRLXKXWUb943+PFoKQ==" + "node_modules/@prisma/engines/node_modules/@prisma/engines-version": { + "version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab.tgz", + "integrity": "sha512-6yvO8s80Tym61aB4QNtYZfWVmE3pwqe807jEtzm8C5VDe7nw8O1FGX3TXUaXmWV0fQTIAfRbeL2Gwrndabp/0g==", + "devOptional": true + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.12.0.tgz", + "integrity": "sha512-qkHQbZ1hspvOwcImvqY4yj7+FUlw0+uP+6tu3g24V4ULHOXLLkvr5ZZc6vy26OF0hkbD3kcDJCeutFis3poKgg==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.12.0", + "@prisma/engines-version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab", + "@prisma/get-platform": "5.12.0" + } + }, + "node_modules/@prisma/fetch-engine/node_modules/@prisma/engines-version": { + "version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab.tgz", + "integrity": "sha512-6yvO8s80Tym61aB4QNtYZfWVmE3pwqe807jEtzm8C5VDe7nw8O1FGX3TXUaXmWV0fQTIAfRbeL2Gwrndabp/0g==", + "devOptional": true + }, + "node_modules/@prisma/get-platform": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.12.0.tgz", + "integrity": "sha512-81Ptv9YJnwTArEBPQ2Lvu58sZPxy4OixKxVVgysFan6A3bFP7q8gIg15WTjsRuH4WXh6B667EM9sqoMTNu0fLQ==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.12.0" + } }, "node_modules/@radix-ui/number": { "version": "1.0.1", @@ -8051,13 +8091,13 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prisma": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.0.0.tgz", - "integrity": "sha512-KYWk83Fhi1FH59jSpavAYTt2eoMVW9YKgu8ci0kuUnt6Dup5Qy47pcB4/TLmiPAbhGrxxSz7gsSnJcCmkyPANA==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.12.0.tgz", + "integrity": "sha512-zxw4WSIvpsyNbpv8r7Fxgm7nwTFVmD6wbN6VuH13lClOceSANDOMl4jO3oxE6VzhjxmnEJqOGZjON2T2UpmLag==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.0.0" + "@prisma/engines": "5.12.0" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index b7cf00b1..b47b315e 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,10 @@ "devDependencies": { "@flydotio/dockerfile": "^0.4.0", "cross-env": "^7.0.3", - "husky": "^8.0.3" + "husky": "^8.0.3", + "prisma": "^5.12.0" + }, + "dependencies": { + "@prisma/client": "^5.12.0" } } diff --git a/packages/app/prisma/schema.prisma b/packages/app/prisma/schema.prisma index 711bf35c..143b37fe 100644 --- a/packages/app/prisma/schema.prisma +++ b/packages/app/prisma/schema.prisma @@ -1,195 +1,178 @@ generator client { - provider = "prisma-client-js" - binaryTargets = ["native", "rhel-openssl-1.0.x"] + provider = "prisma-client-js" + binaryTargets = ["native", "rhel-openssl-1.0.x"] } datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -enum UserRole { - ADMIN - USER -} - -enum AchievementType { - FIRST_RACE - FIRST_SNIPPET - FIFTH_RACE + provider = "postgresql" + url = env("DATABASE_URL") } model User { - id String @id @default(cuid()) - createdAt DateTime @default(now()) @map("created_at") - name String? - bio String? - email String? @unique - emailVerified DateTime? @map("email_verified") - image String? - accounts Account[] - sessions Session[] - results Result[] - achievements Achievement[] - // followers String[] - // following String[] - - topLanguages String[] - languagesMap Json? - - averageAccuracy Decimal @default(0) @db.Decimal(5, 2) - averageCpm Decimal @default(0) @db.Decimal(6, 2) - - role UserRole @default(USER) - - snippets Snippet[] - snippetVotes SnippetVote[] - RaceParticipant RaceParticipant[] - notifications Notification[] - - @@map("users") + id String @id @default(cuid()) + createdAt DateTime @default(now()) @map("created_at") + name String? + email String? @unique + emailVerified DateTime? @map("email_verified") + image String? + averageAccuracy Decimal @default(0) @db.Decimal(5, 2) + averageCpm Decimal @default(0) @db.Decimal(6, 2) + role UserRole @default(USER) + bio String? + languagesMap Json? + topLanguages String[] + accounts Account[] + achievements Achievement[] + notifications Notification[] + RaceParticipant RaceParticipant[] + results Result[] + sessions Session[] + snippetVotes SnippetVote[] + snippets Snippet[] + + @@map("users") } -// Necessary for Next auth model Account { - id String @id @default(cuid()) - userId String @map("user_id") - type String - provider String - providerAccountId String @map("provider_account_id") - refresh_token String? @db.Text - access_token String? @db.Text - expires_at Int? - token_type String? - scope String? - id_token String? @db.Text - session_state String? - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@unique([provider, providerAccountId]) - @@map("accounts") + id String @id @default(cuid()) + userId String @map("user_id") + type String + provider String + providerAccountId String @map("provider_account_id") + refresh_token String? + access_token String? + expires_at Int? + token_type String? + scope String? + id_token String? + session_state String? + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([provider, providerAccountId]) + @@map("accounts") } model Session { - id String @id @default(cuid()) - sessionToken String @unique @map("session_token") - userId String @map("user_id") - expires DateTime - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + sessionToken String @unique @map("session_token") + userId String @map("user_id") + expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@map("sessions") + @@map("sessions") } model VerificationToken { - identifier String - token String @unique - expires DateTime + identifier String + token String @unique + expires DateTime - @@unique([identifier, token]) - @@map("verification_tokens") + @@unique([identifier, token]) + @@map("verification_tokens") } model Result { - id String @id @default(cuid()) - snippet Snippet @relation(fields: [snippetId], references: [id], onDelete: Cascade) - userId String @map("user_id") - createdAt DateTime @default(now()) @map("created_at") - accuracy Decimal @db.Decimal(5, 2) - cpm Int - takenTime String @map("taken_time") - errorCount Int? @map("error_count") - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - snippetId String - RaceParticipant RaceParticipant[] + id String @id @default(cuid()) + userId String @map("user_id") + createdAt DateTime @default(now()) @map("created_at") + accuracy Decimal @db.Decimal(5, 2) + cpm Int + takenTime String @map("taken_time") + errorCount Int? @map("error_count") + snippetId String + RaceParticipant RaceParticipant[] + snippet Snippet @relation(fields: [snippetId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@map("results") + @@map("results") } model Achievement { - userId String @map("user_id") - achievementType AchievementType @map("achievement_type") - unlockedAt DateTime @default(now()) @map("unlocked_at") - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String @map("user_id") + achievementType AchievementType @map("achievement_type") + unlockedAt DateTime @default(now()) @map("unlocked_at") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@id([userId, achievementType]) - @@map("achievements") + @@id([userId, achievementType]) + @@map("achievements") } model Snippet { - id String @id @default(cuid()) - name String? - - code String - language String - User User? @relation(fields: [userId], references: [id], onDelete: Cascade) - userId String? @map("user_id") - onReview Boolean @default(false) @map("on_review") - rating Int @default(0) - Result Result[] + id String @id @default(cuid()) + code String + language String + userId String? @map("user_id") + onReview Boolean @default(false) @map("on_review") + rating Int @default(0) + name String? + Race Race[] + Result Result[] + votes SnippetVote[] + User User? @relation(fields: [userId], references: [id], onDelete: Cascade) - votes SnippetVote[] - Race Race[] - - @@map("snippets") + @@map("snippets") } model Race { - id String @id @default(cuid()) - - snippetId String @map("snippet_id") - snippet Snippet @relation(fields: [snippetId], references: [id], onDelete: Cascade) - participants RaceParticipant[] - - startedAt DateTime? @map("started_at") - endedAt DateTime? @map("ended_at") - createdAt DateTime @default(now()) @map("created_at") + id String @id @default(cuid()) + snippetId String @map("snippet_id") + startedAt DateTime? @map("started_at") + endedAt DateTime? @map("ended_at") + createdAt DateTime @default(now()) @map("created_at") + snippet Snippet @relation(fields: [snippetId], references: [id], onDelete: Cascade) + participants RaceParticipant[] - @@map("race") + @@map("race") } model RaceParticipant { - id String @id @default(cuid()) + id String @id @default(cuid()) + raceId String + userId String? @map("user_id") + resultId String? @map("result_id") + Race Race @relation(fields: [raceId], references: [id], onDelete: Cascade) + result Result? @relation(fields: [resultId], references: [id], onDelete: Cascade) + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) - Race Race @relation(fields: [raceId], references: [id], onDelete: Cascade) - raceId String - - // a race participant could be a guest user - userId String? @map("user_id") - user User? @relation(fields: [userId], references: [id], onDelete: Cascade) - - // a user could exit the race without finishing, or is a guest user - resultId String? @map("result_id") - result Result? @relation(fields: [resultId], references: [id], onDelete: Cascade) - - @@map("race_participants") -} - -enum VoteType { - UP - DOWN + @@map("race_participants") } model SnippetVote { - snippetId String - userId String - User User @relation(fields: [userId], references: [id], onDelete: Cascade) - Snippet Snippet @relation(fields: [snippetId], references: [id], onDelete: Cascade) - type VoteType + snippetId String + userId String + type VoteType + Snippet Snippet @relation(fields: [snippetId], references: [id], onDelete: Cascade) + User User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@id([userId, snippetId]) - @@map("snippet_votes") + @@id([userId, snippetId]) + @@map("snippet_votes") } model Notification { - id String @id @default(cuid()) - title String - description String - ctaUrl String? @map("cta_url") // URL to redirect when user click on notification - read Boolean @default(false) - userId String @map("user_id") - createdAt DateTime @default(now()) @map("created_at") + id String @id @default(cuid()) + title String + description String + ctaUrl String? @map("cta_url") + read Boolean @default(false) + userId String @map("user_id") + createdAt DateTime @default(now()) @map("created_at") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("notification") +} - user User @relation(fields: [userId], references: [id], onDelete: Cascade) +enum UserRole { + ADMIN + USER +} - @@map("notification") +enum AchievementType { + FIRST_RACE + FIRST_SNIPPET + FIFTH_RACE +} + +enum VoteType { + UP + DOWN } diff --git a/packages/app/src/app/dashboard/_components/recentRaces.tsx b/packages/app/src/app/dashboard/_components/recentRaces.tsx index 58e189c4..83ef2764 100644 --- a/packages/app/src/app/dashboard/_components/recentRaces.tsx +++ b/packages/app/src/app/dashboard/_components/recentRaces.tsx @@ -1,7 +1,7 @@ "use client"; import * as React from "react"; -import type { Result, Snippet } from "@prisma/client"; +import type { Result } from "@prisma/client"; import { type ColumnDef } from "unstyled-table"; import Link from "next/link"; @@ -43,26 +43,26 @@ export function RecentRacesTable({ }, }, { - accessorKey: "name", + accessorKey: "snippet.name", header: "Name", cell: ({ cell }) => { - const snippet = cell.getValue() as Snippet; + const name = cell.getValue() as string; - return {snippet.name ?? "-"}; + return {snippet?.name ?? "-"}; }, }, { - accessorKey: "language", + accessorKey: "snippet.language", header: "Language", cell: ({ cell }) => { - const snippet = cell.getValue() as Snippet; + const snippet = cell.getValue() as string; const language = snippetLanguages.find((language) => { - if (language.value === snippet.language) { + if (language.value === snippet) { return language.label; } }); - return language?.label; + return {language ?? "-"}; }, }, { diff --git a/packages/app/src/app/terms/page.tsx b/packages/app/src/app/terms/page.tsx index 282f176a..017adf17 100644 --- a/packages/app/src/app/terms/page.tsx +++ b/packages/app/src/app/terms/page.tsx @@ -4,10 +4,8 @@ const page = () => { <>
-

CodeRacer - Terms of Service

-

Effective Date: [Date]

- -

Welcome to CodeRacer! These Terms of Service ("Terms") constitute a legal agreement between you and CodeRacer. Please read these Terms carefully before using our platform, which is accessible at https://code-racer-eight.vercel.app/. By using CodeRacer, you agree to be bound by these Terms.

+

CodeRacer - Terms of Service

+

Welcome to CodeRacer! These Terms of Service Terms constitute a legal agreement between you and CodeRacer. Please read these Terms carefully before using our platform, which is accessible at https://code-racer-eight.vercel.app/. By using CodeRacer, you agree to be bound by these Terms.

1. User Accounts

@@ -45,7 +43,7 @@ const page = () => {

6. Limitation of Liability

- 6.1. Disclaimer: CodeRacer is provided "as is," and we make no warranties or representations about the accuracy or reliability of the platform. Your use of CodeRacer is at your own risk. + 6.1. Disclaimer: CodeRacer is provided as is, and we make no warranties or representations about the accuracy or reliability of the platform. Your use of CodeRacer is at your own risk.

7. Changes to Terms

@@ -58,6 +56,7 @@ const page = () => { 8.1. Questions: If you have any questions or concerns about these Terms, please contact us at contact@coderacer.com.

+ ); };