diff --git a/backend/api-gateway/app/types/submission.ts b/backend/api-gateway/app/types/submission.ts new file mode 100644 index 000000000..5368b7ade --- /dev/null +++ b/backend/api-gateway/app/types/submission.ts @@ -0,0 +1,13 @@ +import { z } from "zod"; +import { userSchema } from "./user"; + +export const submissionSchema = z.object({ + id: z.number(), + questionId: z.string(), + users: z.array(userSchema), + code: z.string(), + lang: z.string(), + createdAt: z.string(), +}); + +export type Submission = z.infer; diff --git a/backend/api-gateway/app/types/usersService.ts b/backend/api-gateway/app/types/usersService.ts index 034116e54..2f257cc8d 100644 --- a/backend/api-gateway/app/types/usersService.ts +++ b/backend/api-gateway/app/types/usersService.ts @@ -1,12 +1,15 @@ import { z } from "zod"; import { HTTP_STATUS } from "./http"; import { userSchema } from "./user"; +import { submissionSchema } from "./submission"; export const getUserSchema = z.union([ z.object({ status: z.literal(HTTP_STATUS.SUCCESS), data: z.object({ - user: userSchema, + user: userSchema.extend({ + submissions: z.array(submissionSchema), + }), }), }), z.object({ diff --git a/backend/question-service/server/app/routers/admin.py b/backend/question-service/server/app/routers/admin.py index a97c87a87..f6613ea09 100644 --- a/backend/question-service/server/app/routers/admin.py +++ b/backend/question-service/server/app/routers/admin.py @@ -28,6 +28,12 @@ async def create_question(question: dict): if collection.find_one({"id": question["id"]}): raise HTTPException( status_code=400, detail="Question with the same id already exists.") + + # Check if question with the same title already exists + if collection.find_one({"title": question["title"]}): + raise HTTPException( + status_code=400, detail="Question with the same title already exists.") + result = collection.insert_one(question) question["_id"] = str(question["_id"]) @@ -41,6 +47,11 @@ async def update_question(question_id: int, updated_data: dict): # Check if updated_data is empty if not updated_data: raise HTTPException(status_code=400, detail="Updated data not provided.") + + # Check if question with the same title already exists + if collection.find_one({"title": updated_data["title"]}): + raise HTTPException( + status_code=400, detail="Question with the same title already exists.") result = collection.update_one({"id": question_id}, {"$set": updated_data}) diff --git a/backend/user-service/app/prisma/schema.prisma b/backend/user-service/app/prisma/schema.prisma index cbd2c8bb2..4dff76422 100644 --- a/backend/user-service/app/prisma/schema.prisma +++ b/backend/user-service/app/prisma/schema.prisma @@ -21,7 +21,7 @@ model Submission { id Int @id @default(autoincrement()) questionId String users User[] - code String + code String @db.LongText lang String createdAt DateTime @default(now()) } diff --git a/backend/user-service/app/services/user.service.ts b/backend/user-service/app/services/user.service.ts index 05e3bb26b..fc7174c5c 100644 --- a/backend/user-service/app/services/user.service.ts +++ b/backend/user-service/app/services/user.service.ts @@ -20,9 +20,15 @@ export const userService = { delete: (id: Prisma.UserDeleteArgs["where"]["id"]) => db.user.delete({ where: { id } }), findById: (id: Prisma.UserFindUniqueArgs["where"]["id"]) => - db.user.findUnique({ where: { id } }), + db.user.findUnique({ + where: { id }, + include: { submissions: { include: { users: true } } }, + }), findByEmail: (email: Prisma.UserFindUniqueArgs["where"]["email"]) => - db.user.findUnique({ where: { email } }), + db.user.findUnique({ + where: { email }, + include: { submissions: { include: { users: true } } }, + }), update: ( id: Prisma.UserUpdateArgs["where"]["id"], data: Prisma.UserUpdateInput diff --git a/docker-compose.yml b/docker-compose.yml index 4dc7a49fa..45207d13e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,9 +31,6 @@ services: volumes: - ./backend/user-service/app:/app:delegated - /app/node_modules - # depends_on: - # user-service-db: - # condition: service_healthy ports: - "8002:80" @@ -71,7 +68,7 @@ services: - /app/node_modules ports: - "8005:80" - + communication-service: container_name: communication-service build: @@ -83,34 +80,6 @@ services: ports: - "8006:80" - # user-service-db: - # container_name: user-service-db - # build: - # context: ./backend/user-service/db - # dockerfile: Dockerfile.dev - # restart: always - # environment: - # MONGO_INITDB_ROOT_USERNAME: root - # MONGO_INITDB_ROOT_PASSWORD: prisma - # MONGO_REPLICA_HOST: 127.0.0.1 - # MONGO_REPLICA_PORT: 27018 - # ports: - # - 27018:27018 - # healthcheck: - # test: - # [ - # "CMD", - # "mongo", - # "admin", - # "--port", - # "27018", - # "--eval", - # "db.adminCommand('ping')", - # ] - # interval: 5s - # timeout: 2s - # retries: 20 - question-service-db: container_name: question-service-db image: mongo:latest diff --git a/frontend/app/package-lock.json b/frontend/app/package-lock.json index 4372e8609..57f6efca2 100644 --- a/frontend/app/package-lock.json +++ b/frontend/app/package-lock.json @@ -3462,9 +3462,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -8898,9 +8898,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", diff --git a/frontend/app/src/components/Card/Card.tsx b/frontend/app/src/components/Card/Card.tsx index 0db5734d7..2b061f8f8 100644 --- a/frontend/app/src/components/Card/Card.tsx +++ b/frontend/app/src/components/Card/Card.tsx @@ -8,9 +8,9 @@ interface CardProps extends BoxProps { export const Card = ({ children, ...boxProps }: CardProps) => { return ( - + {title} {description} {!disableCancel && } - - {confirmButtonText} - + {title === "Leave Room" ? ( + + ) : ( + + {confirmButtonText} + + )} diff --git a/frontend/app/src/components/Dropdown/Dropdown.tsx b/frontend/app/src/components/Dropdown/Dropdown.tsx index cedf19c4e..303e4db8e 100644 --- a/frontend/app/src/components/Dropdown/Dropdown.tsx +++ b/frontend/app/src/components/Dropdown/Dropdown.tsx @@ -14,13 +14,19 @@ interface DropdownProps { options: Option[]; placeholder?: string; value?: any; - onChangeHandler?: ((newValue: SingleValue<{ - label: string; - value: any; - }>, actionMeta: ActionMeta<{ - label: string; - value: any; - }>) => void) | undefined + onChangeHandler?: + | (( + newValue: SingleValue<{ + label: string; + value: any; + }>, + actionMeta: ActionMeta<{ + label: string; + value: any; + }>, + ) => void) + | undefined; + width?: number; } export const Dropdown = ({ @@ -29,11 +35,14 @@ export const Dropdown = ({ options, placeholder, value, - onChangeHandler + onChangeHandler, + width, }: DropdownProps) => { return ( - - {title} + + + {title} + setInputValue(e.target.value)} - color="light.100" + color="dark.100 +" + w={72} /> diff --git a/frontend/app/src/features/settings/components/ProfilePanel.tsx b/frontend/app/src/features/settings/components/ProfilePanel.tsx index 30a50f13e..42ce50638 100644 --- a/frontend/app/src/features/settings/components/ProfilePanel.tsx +++ b/frontend/app/src/features/settings/components/ProfilePanel.tsx @@ -55,10 +55,19 @@ export const ProfilePanel = () => { - + Name - + {errors.name && errors.name.message} diff --git a/frontend/app/src/features/settings/components/SettingsSidebar.tsx b/frontend/app/src/features/settings/components/SettingsSidebar.tsx index b5f8c61fd..043a22334 100644 --- a/frontend/app/src/features/settings/components/SettingsSidebar.tsx +++ b/frontend/app/src/features/settings/components/SettingsSidebar.tsx @@ -3,25 +3,31 @@ import { TABS } from "../constants/tabs"; export const SettingsSidebar = () => { return ( - + {TABS.map(({ label }) => { - const isDelete = label === "Delete account"; + const isDelete = label === "Delete Account"; return ( {label} diff --git a/frontend/app/src/features/settings/constants/tabs.ts b/frontend/app/src/features/settings/constants/tabs.ts index 97eafbb65..364a69a18 100644 --- a/frontend/app/src/features/settings/constants/tabs.ts +++ b/frontend/app/src/features/settings/constants/tabs.ts @@ -1,8 +1,8 @@ export const TABS = [ { - label: "Profile settings", + label: "Profile Information", }, { - label: "Delete account", + label: "Delete Account", }, ]; diff --git a/frontend/app/src/features/submissions/components/SubmissionsCodeModal.tsx b/frontend/app/src/features/submissions/components/SubmissionsCodeModal.tsx new file mode 100644 index 000000000..3472d2735 --- /dev/null +++ b/frontend/app/src/features/submissions/components/SubmissionsCodeModal.tsx @@ -0,0 +1,33 @@ +import { + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from "@chakra-ui/react"; + +interface SubmissionsCodeModal { + code: string; + isOpen: boolean; + onClose: () => void; +} + +const SubmissionsCodeModal = ({ + code, + isOpen, + onClose, +}: SubmissionsCodeModal) => ( + + + + Code + + {code} + + + +); + +export default SubmissionsCodeModal; diff --git a/frontend/app/src/features/submissions/components/SubmissionsTable.tsx b/frontend/app/src/features/submissions/components/SubmissionsTable.tsx new file mode 100644 index 000000000..6bb0bdbe5 --- /dev/null +++ b/frontend/app/src/features/submissions/components/SubmissionsTable.tsx @@ -0,0 +1,251 @@ +import { useAuth } from "@/hooks"; +import { Submission } from "@/types/submission"; +import { + SortingState, + createColumnHelper, + flexRender, + getCoreRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { useMemo, useState } from "react"; +import { + Avatar, + Box, + Button, + HStack, + IconButton, + Table, + TableContainer, + Tbody, + Td, + Text, + Th, + Thead, + Tr, + VStack, + useDisclosure, +} from "@chakra-ui/react"; +import { + PiCodeBold, + PiListBold, + PiSortAscendingBold, + PiSortDescendingBold, +} from "react-icons/pi"; +import SubmissionsCodeModal from "./SubmissionsCodeModal"; +import { LANGUAGES, SUBMISSIONS_DEFAULT_SORTING_STATE } from "../constants"; + +const columnHelper = createColumnHelper(); + +interface SubmissionsTableProps { + submissions: Submission[]; +} + +const SubmissionsTable = ({ submissions }: SubmissionsTableProps) => { + const { data } = useAuth(); + const { + isOpen: isCodeModalOpen, + onOpen: onCodeModalOpen, + onClose: onCodeModalClose, + } = useDisclosure(); + const [currCode, setCurrCode] = useState(""); + const [sorting, setSorting] = useState( + SUBMISSIONS_DEFAULT_SORTING_STATE, + ); + + const columns = useMemo( + () => [ + columnHelper.accessor("questionId", { + header: "Question ID", + cell: info => {info.getValue()}, + }), + // columnHelper.accessor("title", { + // header: "Title", + // cell: info => {info.getValue()}, + // size: Number.MAX_SAFE_INTEGER, + // }), + columnHelper.accessor("createdAt", { + header: "Attempted At", + cell: info => ( + + {new Date(info.getValue()).toLocaleString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true, + })} + + ), + }), + columnHelper.accessor("users", { + header: "Attempted With", + cell: info => + info + .getValue() + ?.filter(user => user.id !== data?.user.id) + ?.map(user => ( + + {user.name && {user.name}} + {user.email && ( + + {user.email} + + )} + + )), + }), + columnHelper.accessor("lang", { + header: "Language", + cell: info => ( + {LANGUAGES[info.getValue()] || info.getValue()} + ), + }), + columnHelper.display({ + id: "actions", + cell: ({ row }) => ( + + ), + }), + ], + [], + ); + + const table = useReactTable({ + initialState: { + sorting: SUBMISSIONS_DEFAULT_SORTING_STATE, + }, + data: submissions, + columns, + enableSortingRemoval: false, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + state: { + sorting, + }, + onSortingChange: setSorting, + }); + + return ( + <> + + + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => ( + + ))} + + ))} + + + {table.getRowModel().rows.length === 0 && ( + + No submissions yet. + + )} + {table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + ))} + + ))} + +
+ {header.isPlaceholder ? null : ( + + {flexRender( + header.column.columnDef.header, + header.getContext(), + )} + {{ + asc: ( + } + variant="icon" + size="sm" + _hover={{ bg: "transparent" }} + /> + ), + desc: ( + } + variant="icon" + size="sm" + _hover={{ bg: "transparent" }} + /> + ), + }[header.column.getIsSorted() as string] ?? ( + } + variant="icon" + size="sm" + _hover={{ bg: "transparent" }} + /> + )} + + )} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+ + + ); +}; + +export default SubmissionsTable; diff --git a/frontend/app/src/features/submissions/constants/index.ts b/frontend/app/src/features/submissions/constants/index.ts new file mode 100644 index 000000000..04982d5ec --- /dev/null +++ b/frontend/app/src/features/submissions/constants/index.ts @@ -0,0 +1,16 @@ +export const SUBMISSIONS_DEFAULT_SORTING_STATE = [ + { id: "createdAt", desc: false }, +]; + +interface Languages { + [key: string]: string; +} + +export const LANGUAGES: Languages = { + javascript: "Javascript", + python: "Python", + csharp: "C#", + cpp: "C++", + typescript: "Typescript", + java: "Java", +}; diff --git a/frontend/app/src/hooks/useAuth.ts b/frontend/app/src/hooks/useAuth.ts index 80a4988a3..1fc81a934 100644 --- a/frontend/app/src/hooks/useAuth.ts +++ b/frontend/app/src/hooks/useAuth.ts @@ -1,6 +1,7 @@ import { API_ENDPOINT } from "@/constants/api"; import { makeSuccessResponseSchema } from "@/lib/api"; import { backendApi } from "@/lib/axios"; +import { submissionSchema } from "@/types/submission"; import { userSchema } from "@/types/user"; import { useQuery } from "@tanstack/react-query"; import { useNavigate } from "react-router-dom"; @@ -12,6 +13,7 @@ const getAuthResponseSchema = makeSuccessResponseSchema( z.object({ user: userSchema.extend({ roomId: z.string().optional(), + submissions: z.array(submissionSchema), }), }), ); diff --git a/frontend/app/src/lib/axios.ts b/frontend/app/src/lib/axios.ts index c30215b68..033111eec 100644 --- a/frontend/app/src/lib/axios.ts +++ b/frontend/app/src/lib/axios.ts @@ -23,6 +23,7 @@ const failResponseSchema = z.object({ error_description: z.string().optional(), }) .optional(), + message: z.string().optional(), }); export const anyApiResponseSchema = z.union([ @@ -61,6 +62,8 @@ function responseHandler(response: AxiosResponse | undefined) { errorMessage = parsedResponseData.data.data.error; } else if (parsedResponseData.data.data?.message) { errorMessage = parsedResponseData.data.data.message; + } else if (parsedResponseData.data.message) { + errorMessage = parsedResponseData.data.message; } else { errorMessage = API_ERROR.STATUS_FAIL; } diff --git a/frontend/app/src/main.tsx b/frontend/app/src/main.tsx index f2dc4f629..d701a021d 100644 --- a/frontend/app/src/main.tsx +++ b/frontend/app/src/main.tsx @@ -12,6 +12,7 @@ import { import { ChakraProvider, createStandaloneToast } from "@chakra-ui/react"; import { globalToastOptions, theme } from "@/theme"; import "@fontsource-variable/inter"; +import { Toast } from "./components/Toast/Toast.tsx"; // To show toasts outside of react components (e.g. in react-query's global onError) const { ToastContainer, toast } = createStandaloneToast({ theme }); @@ -30,13 +31,18 @@ const errorHandler = ({ } console.error(`Failed ${type}:`, error); toast({ + render: ({ onClose }) => ( + + ), ...globalToastOptions.defaultOptions, - description: - error instanceof Error - ? error.message - : "An unexpected error has occurred", - status: "error", - isClosable: true, }); }; diff --git a/frontend/app/src/pages/home/index.tsx b/frontend/app/src/pages/home/index.tsx index c6dc357e4..df5bc5263 100644 --- a/frontend/app/src/pages/home/index.tsx +++ b/frontend/app/src/pages/home/index.tsx @@ -8,6 +8,7 @@ import { AlertIcon, AlertTitle, HStack, + VStack, useDisclosure, } from "@chakra-ui/react"; import { useNavigate } from "react-router-dom"; @@ -44,7 +45,7 @@ function HomePage() { onClose={onClose} title="Session in Progress" description="Please end your current session before starting a new one." - > + /> {roomId && ( - - - You have an existing session! - - End the previous session before creating a new one. - + + + + + You have an existing session! + + + End the previous session before creating a new one. + + )} diff --git a/frontend/app/src/pages/index.tsx b/frontend/app/src/pages/index.tsx index 38e23240f..510a01288 100644 --- a/frontend/app/src/pages/index.tsx +++ b/frontend/app/src/pages/index.tsx @@ -11,12 +11,7 @@ function LandingPage() { return ( - + Let your peers help you ace technical interviews {data?.user ? ( diff --git a/frontend/app/src/pages/profile/[userId].tsx b/frontend/app/src/pages/profile/[userId].tsx index f9341c95b..2367c4fe1 100644 --- a/frontend/app/src/pages/profile/[userId].tsx +++ b/frontend/app/src/pages/profile/[userId].tsx @@ -1,21 +1,51 @@ import { Page } from "@/components"; -import { VStack } from "@chakra-ui/react"; -import { useParams } from "react-router-dom"; +import { ROUTE } from "@/constants/route"; +import SubmissionsTable from "@/features/submissions/components/SubmissionsTable"; +import { useAuth } from "@/hooks"; +import { Avatar, HStack, VStack, Text, Button, Icon } from "@chakra-ui/react"; +import { PiPencilBold } from "react-icons/pi"; +import { useNavigate } from "react-router-dom"; function ProfilePage() { - const { userId } = useParams(); + const { data } = useAuth(); + const navigate = useNavigate(); return ( - - + + Profile Information + + - {userId} - + + + + + {data?.user.name} + + + {data?.user.email} + + + + + + + Submissions + + {data?.user.submissions ? ( + + ) : ( + No submissions yet. + )} ); } diff --git a/frontend/app/src/pages/settings/index.tsx b/frontend/app/src/pages/settings/index.tsx index d5d32757c..90d8f3355 100644 --- a/frontend/app/src/pages/settings/index.tsx +++ b/frontend/app/src/pages/settings/index.tsx @@ -15,7 +15,7 @@ function SettingsPage() { > ({ ...provided, - background: "dark.950", - borderColor: "light.500", + background: "dark.900", + borderColor: "dark.700", }), control: provided => ({ ...provided, @@ -27,9 +27,8 @@ export const singleSelectStyles: ( dropdownIndicator: provided => ({ ...provided, background: "transparent", - color: "light.100", + color: "dark.100", border: "0px", - px: "0.5rem", }), indicatorSeparator: () => ({ display: "none", @@ -37,7 +36,7 @@ export const singleSelectStyles: ( valueContainer: provided => ({ ...provided, px: "0.5rem", - color: "light.100", + color: "dark.100", fontSize, py: valueContainerPy, }), @@ -45,22 +44,23 @@ export const singleSelectStyles: ( ...provided, background: "transparent", _hover: { - background: "light.500", + background: "dark.700", }, - color: "light.100", + color: "dark.100", fontSize, py: "0rem", }), - menu: provided => ({ ...provided, background: "dark.500" }), + menu: provided => ({ ...provided, background: "dark.900" }), menuList: provided => ({ ...provided, background: "transparent", }), noOptionsMessage: provided => ({ ...provided, - color: "light.100", + color: "dark.100", fontSize, }), + placeholder: provided => ({ ...provided, color: "dark.300" }), }; }; @@ -71,21 +71,21 @@ export const multiSelectStyles: ( return { container: provided => ({ ...provided, - background: "dark.950", - borderColor: "light.500", - borderRadius: "0.375rem", + background: "dark.900", + borderColor: "dark.700", + borderRadius: "md", }), clearIndicator: provided => ({ ...provided, - color: "light.100", + color: "dark.100", _hover: { - background: "light.400", + background: "dark.700", }, }), dropdownIndicator: provided => ({ ...provided, background: "transparent", - color: "light.100", + color: "dark.100", border: "0px", }), indicatorSeparator: () => ({ @@ -93,27 +93,28 @@ export const multiSelectStyles: ( }), valueContainer: provided => ({ ...provided, - px: "0.5rem", + px: 2, }), multiValue: provided => ({ ...provided, background: "transparent", - color: "light.100", + color: "dark.100", }), option: provided => ({ ...provided, background: "transparent", _hover: { - background: "light.500", + background: "dark.700", }, - color: "light.100", + color: "dark.100", }), - menu: provided => ({ ...provided, background: "dark.500" }), + menu: provided => ({ ...provided, background: "dark.900" }), menuList: provided => ({ ...provided, - background: "dark.500", - borderColor: "light.400", + background: "dark.900", + borderColor: "dark.700", }), - noOptionsMessage: provided => ({ ...provided, color: "light.100" }), + noOptionsMessage: provided => ({ ...provided, color: "dark.100" }), + placeholder: provided => ({ ...provided, color: "dark.300" }), }; }; diff --git a/frontend/app/src/theme/components/Button.tsx b/frontend/app/src/theme/components/Button.tsx index ea37beb48..a747adabd 100644 --- a/frontend/app/src/theme/components/Button.tsx +++ b/frontend/app/src/theme/components/Button.tsx @@ -5,25 +5,25 @@ const icon = defineStyle({ color: "dark.300", _hover: { color: "dark.100", - bg: "dark.800", + bg: "dark.700", }, _active: { - bg: "dark.800", + bg: "dark.700", }, }); const outline = defineStyle({ bg: "transparent", border: "1px", - borderColor: "dark.800", + borderColor: "dark.700", color: "dark.100", fontWeight: "medium", shadow: "sm", _active: { - bg: "dark.800", + bg: "dark.700", }, _hover: { - bg: "dark.800", + bg: "dark.700", }, }); @@ -31,7 +31,7 @@ const outlineWarning = defineStyle({ bg: "transparent", border: "1px", borderColor: "red.900", - color: "red.600", + color: "red.500", fontWeight: "medium", shadow: "sm", _hover: { diff --git a/frontend/app/src/theme/components/Input.tsx b/frontend/app/src/theme/components/Input.tsx index b173ad9dc..4288b02a0 100644 --- a/frontend/app/src/theme/components/Input.tsx +++ b/frontend/app/src/theme/components/Input.tsx @@ -7,7 +7,7 @@ const { definePartsStyle, defineMultiStyleConfig } = const outline = definePartsStyle({ field: { border: "1px", - borderColor: "dark.800", + borderColor: "dark.700", borderRadius: "md", color: "dark.100", _focusVisible: { @@ -32,7 +32,7 @@ const outline = definePartsStyle({ }, addon: { border: "1px solid", - borderColor: "dark.800", + borderColor: "dark.700", bg: "transparent", }, }); diff --git a/frontend/app/src/theme/components/Menu.tsx b/frontend/app/src/theme/components/Menu.tsx index 0318ad6ee..ffc15fd6b 100644 --- a/frontend/app/src/theme/components/Menu.tsx +++ b/frontend/app/src/theme/components/Menu.tsx @@ -5,9 +5,9 @@ const { defineMultiStyleConfig } = createMultiStyleConfigHelpers(parts.keys); const outline = { list: { - bg: "dark.950", + bg: "dark.900", border: "1px", - borderColor: "dark.800", + borderColor: "dark.700", p: 1, shadow: "sm", minW: 36, @@ -21,7 +21,7 @@ const outline = { py: 2, transitionProperty: "common", transitionDuration: "normal", - _hover: { bg: "dark.800" }, + _hover: { bg: "dark.700" }, }, groupTitle: { color: "dark.300", diff --git a/frontend/app/src/theme/components/Modal.tsx b/frontend/app/src/theme/components/Modal.tsx index 910e35593..119ba001e 100644 --- a/frontend/app/src/theme/components/Modal.tsx +++ b/frontend/app/src/theme/components/Modal.tsx @@ -6,9 +6,9 @@ const { definePartsStyle, defineMultiStyleConfig } = const baseStyle = definePartsStyle({ dialog: { - bg: "dark.950", + bg: "dark.900", border: "1px", - borderColor: "dark.800", + borderColor: "dark.700", }, header: { color: "dark.100", diff --git a/frontend/app/src/theme/components/Spinner.ts b/frontend/app/src/theme/components/Spinner.ts index d58d370e9..f0e8b5610 100644 --- a/frontend/app/src/theme/components/Spinner.ts +++ b/frontend/app/src/theme/components/Spinner.ts @@ -2,6 +2,6 @@ import { defineStyleConfig } from "@chakra-ui/react"; export const Spinner = defineStyleConfig({ baseStyle: { - color: "light.100", + color: "dark.100", }, }); diff --git a/frontend/app/src/theme/components/Tag.tsx b/frontend/app/src/theme/components/Tag.tsx index ba5101b28..447cb8be2 100644 --- a/frontend/app/src/theme/components/Tag.tsx +++ b/frontend/app/src/theme/components/Tag.tsx @@ -8,7 +8,7 @@ const baseStyle = definePartsStyle({ container: { bg: "transparent", border: "1px", - borderColor: "dark.800", + borderColor: "dark.700", color: "dark.300", }, }); @@ -16,21 +16,21 @@ const baseStyle = definePartsStyle({ const green = definePartsStyle({ container: { borderColor: "green.900", - color: "green.600", + color: "green.500", }, }); const yellow = definePartsStyle({ container: { borderColor: "yellow.900", - color: "yellow.600", + color: "yellow.500", }, }); const red = definePartsStyle({ container: { borderColor: "red.900", - color: "red.600", + color: "red.500", }, }); diff --git a/frontend/app/src/theme/components/Textarea.tsx b/frontend/app/src/theme/components/Textarea.tsx index 3e7c52cae..d2f513df3 100644 --- a/frontend/app/src/theme/components/Textarea.tsx +++ b/frontend/app/src/theme/components/Textarea.tsx @@ -2,7 +2,7 @@ import { defineStyle, defineStyleConfig } from "@chakra-ui/react"; const outline = defineStyle({ border: "1px", - borderColor: "dark.800", + borderColor: "dark.700", borderRadius: "md", color: "dark.100", _focusVisible: { diff --git a/frontend/app/src/theme/index.ts b/frontend/app/src/theme/index.ts index 04deceb32..1d18b9788 100644 --- a/frontend/app/src/theme/index.ts +++ b/frontend/app/src/theme/index.ts @@ -35,7 +35,7 @@ export const theme = extendTheme({ styles: { global: { body: { - background: "dark.950", + background: "dark.900", }, }, }, diff --git a/frontend/app/src/types/submission.ts b/frontend/app/src/types/submission.ts index 4e65bcf3e..5368b7ade 100644 --- a/frontend/app/src/types/submission.ts +++ b/frontend/app/src/types/submission.ts @@ -1,9 +1,10 @@ import { z } from "zod"; +import { userSchema } from "./user"; export const submissionSchema = z.object({ - id: z.string(), + id: z.number(), questionId: z.string(), - userIds: z.array(z.string()), + users: z.array(userSchema), code: z.string(), lang: z.string(), createdAt: z.string(),