From 7fae7bff0b7091a114fba942f8e3b734a6fb9d54 Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Fri, 1 Mar 2024 21:42:52 +0800 Subject: [PATCH 1/4] setup tanstack react query, change theme to non-dark --- frontend/package.json | 5 + frontend/src/app/layout.tsx | 2 +- frontend/src/components/common/Providers.tsx | 9 +- frontend/tailwind.config.ts | 8 +- frontend/yarn.lock | 166 +++++++++++++++++++ 5 files changed, 184 insertions(+), 6 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 50f7e2e2..4af4c033 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,12 @@ }, "dependencies": { "@nextui-org/react": "^2.2.9", + "@tanstack/react-query": "^5.24.1", + "axios": "^1.6.7", + "date-fns": "^3.3.1", + "date-fns-tz": "^2.0.0", "framer-motion": "^11.0.5", + "html-react-parser": "^5.1.8", "next": "14.1.0", "react": "^18", "react-dom": "^18" diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 3309e7ed..c36c2d1c 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -16,7 +16,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + {children} diff --git a/frontend/src/components/common/Providers.tsx b/frontend/src/components/common/Providers.tsx index fa0a272f..30c9fd95 100644 --- a/frontend/src/components/common/Providers.tsx +++ b/frontend/src/components/common/Providers.tsx @@ -1,10 +1,17 @@ "use client"; import { NextUIProvider } from "@nextui-org/react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactNode } from "react"; const Providers = ({ children }: { children: ReactNode }) => { - return {children}; + const queryClient = new QueryClient(); + + return ( + + {children} + + ); }; export default Providers; diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index ce8286a8..15485029 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -10,10 +10,10 @@ const config: Config = { ], theme: { extend: { - backgroundImage: { - "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", - "gradient-conic": - "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + colors: { + background: "#032539", + white: "#ffffff", + logo: "#07c1ff", }, }, }, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 63ab2cba..f5a5935f 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1881,6 +1881,18 @@ dependencies: tslib "^2.4.0" +"@tanstack/query-core@5.24.1": + version "5.24.1" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.24.1.tgz#d40928dec22b47df97fb2648e8c499772e8d7eb2" + integrity sha512-DZ6Nx9p7BhjkG50ayJ+MKPgff+lMeol7QYXkvuU5jr2ryW/4ok5eanaS9W5eooA4xN0A/GPHdLGOZGzArgf5Cg== + +"@tanstack/react-query@^5.24.1": + version "5.24.1" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.24.1.tgz#bcb913febe0d813cec1fda7783298d07aa998b20" + integrity sha512-4+09JEdO4d6+Gc8Y/g2M/MuxDK5IY0QV8+2wL2304wPKJgJ54cBbULd3nciJ5uvh/as8rrxx6s0mtIwpRuGd1g== + dependencies: + "@tanstack/query-core" "5.24.1" + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -2145,6 +2157,11 @@ asynciterator.prototype@^1.0.0: dependencies: has-symbols "^1.0.3" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@^10.0.1: version "10.4.17" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.17.tgz#35cd5695cbbe82f536a50fa025d561b01fdec8be" @@ -2167,6 +2184,15 @@ axe-core@=4.7.0: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf" integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ== +axios@^1.6.7: + version "1.6.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" + integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== + dependencies: + follow-redirects "^1.15.4" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" @@ -2320,6 +2346,13 @@ color@^4.2.3: color-convert "^2.0.1" color-string "^1.9.0" +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -2359,6 +2392,16 @@ damerau-levenshtein@^1.0.8: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== +date-fns-tz@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-2.0.0.tgz#1b14c386cb8bc16fc56fe333d4fc34ae1d1099d5" + integrity sha512-OAtcLdB9vxSXTWHdT8b398ARImVwQMyjfYGkKD2zaGpHseG2UPHbHjXELReErZFxWdSLph3c2zOaaTyHfOhERQ== + +date-fns@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.3.1.tgz#7581daca0892d139736697717a168afbb908cfed" + integrity sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw== + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -2401,6 +2444,11 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -2442,6 +2490,36 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@5.0.3, domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -2470,6 +2548,11 @@ enhanced-resolve@^5.12.0: graceful-fs "^4.2.4" tapable "^2.2.0" +entities@^4.2.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.22.4: version "1.22.4" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.4.tgz#26eb2e7538c3271141f5754d31aabfdb215f27bf" @@ -2870,6 +2953,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== +follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -2885,6 +2973,15 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fraction.js@^4.3.7: version "4.3.7" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" @@ -3082,6 +3179,34 @@ hasown@^2.0.0, hasown@^2.0.1: dependencies: function-bind "^1.1.2" +html-dom-parser@5.0.8: + version "5.0.8" + resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-5.0.8.tgz#540057d8eb7ff28c9fd45fa9eead374c34dae20c" + integrity sha512-vuWiX9EXgu8CJ5m9EP5c7bvBmNSuQVnrY8tl0z0ZX96Uth1IPlYH/8W8VZ/hBajFf18EN+j2pukbCNd01HEd1w== + dependencies: + domhandler "5.0.3" + htmlparser2 "9.1.0" + +html-react-parser@^5.1.8: + version "5.1.8" + resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-5.1.8.tgz#b8a294854845bce96627aa4f2ba738362c0ebcf8" + integrity sha512-oAXgUB4JYHFg4le3RQZtoge1TGMkwXSZPiWiexwdx3AuldgG+QEvbwMrscSViu90JNje3V4Zq5gCUSoTxa0W0A== + dependencies: + domhandler "5.0.3" + html-dom-parser "5.0.8" + react-property "2.0.2" + style-to-js "1.1.10" + +htmlparser2@9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.1.0" + entities "^4.5.0" + ignore@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" @@ -3113,6 +3238,11 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +inline-style-parser@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.2.tgz#d498b4e6de0373458fc610ff793f6b14ebf45633" + integrity sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ== + internal-slot@^1.0.5, internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" @@ -3516,6 +3646,18 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + minimatch@9.0.3, minimatch@^9.0.1: version "9.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" @@ -3852,6 +3994,11 @@ prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -3875,6 +4022,11 @@ react-is@^16.13.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-property@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.2.tgz#d5ac9e244cef564880a610bc8d868bd6f60fdda6" + integrity sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug== + react-remove-scroll-bar@^2.3.4: version "2.3.4" resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9" @@ -4209,6 +4361,20 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +style-to-js@1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.10.tgz#ec20e1264ba11dc7f71b94b3a3a05566ed856e54" + integrity sha512-VC7MBJa+y0RZhpnLKDPmVRLRswsASLmixkiZ5R8xZpNT9VyjeRzwnXd2pBzAWdgSGv/pCNNH01gPCCUsB9exYg== + dependencies: + style-to-object "1.0.5" + +style-to-object@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.5.tgz#5e918349bc3a39eee3a804497d97fcbbf2f0d7c0" + integrity sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ== + dependencies: + inline-style-parser "0.2.2" + styled-jsx@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" From 7224a29942b88dbfc140bfcffcd4b7b0d673654a Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Fri, 1 Mar 2024 21:43:18 +0800 Subject: [PATCH 2/4] create loading component with animation --- frontend/src/components/common/ITSLogo.tsx | 19 ++++++++++++++ .../src/components/common/LogoLoading.tsx | 25 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 frontend/src/components/common/ITSLogo.tsx create mode 100644 frontend/src/components/common/LogoLoading.tsx diff --git a/frontend/src/components/common/ITSLogo.tsx b/frontend/src/components/common/ITSLogo.tsx new file mode 100644 index 00000000..ba68265c --- /dev/null +++ b/frontend/src/components/common/ITSLogo.tsx @@ -0,0 +1,19 @@ +import { Image } from "@nextui-org/react"; + +interface Props { + width?: string; + height?: string; +} + +const ITSLogo = ({ width = "100%", height = "100%" }: Props) => { + return ( + + ); +}; + +export default ITSLogo; diff --git a/frontend/src/components/common/LogoLoading.tsx b/frontend/src/components/common/LogoLoading.tsx new file mode 100644 index 00000000..770686e7 --- /dev/null +++ b/frontend/src/components/common/LogoLoading.tsx @@ -0,0 +1,25 @@ +import ITSLogo from "./ITSLogo"; + +interface Props { + minHeight?: number; +} + +const LogoLoading = ({ minHeight }: Props) => { + return ( +
+
+ +
+ +
+
Loading...
+
+
+
+ ); +}; + +export default LogoLoading; From d303240c6ef4ce7a6ba1d8e13593a212728716ee Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Fri, 1 Mar 2024 21:43:41 +0800 Subject: [PATCH 3/4] create assignment page --- .gitignore | 3 + frontend/public/logo.svg | 33 ++ frontend/public/next.svg | 1 - frontend/public/vercel.svg | 1 - .../app/(pages)/assignments/[id]/layout.tsx | 29 ++ .../src/app/(pages)/assignments/[id]/page.tsx | 40 ++ .../components/assignment/AssignmentPage.tsx | 54 +++ .../assignment/AssignmentQuestion.tsx | 32 ++ .../helpers/assignment-service/api-wrapper.ts | 98 +++++ frontend/src/types/HttpStatusCode.ts | 384 ++++++++++++++++++ frontend/src/types/assignment-service.d.ts | 37 ++ frontend/src/utils/dateUtils.ts | 36 ++ 12 files changed, 746 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 frontend/public/logo.svg delete mode 100644 frontend/public/next.svg delete mode 100644 frontend/public/vercel.svg create mode 100644 frontend/src/app/(pages)/assignments/[id]/layout.tsx create mode 100644 frontend/src/app/(pages)/assignments/[id]/page.tsx create mode 100644 frontend/src/components/assignment/AssignmentPage.tsx create mode 100644 frontend/src/components/assignment/AssignmentQuestion.tsx create mode 100644 frontend/src/helpers/assignment-service/api-wrapper.ts create mode 100644 frontend/src/types/HttpStatusCode.ts create mode 100644 frontend/src/types/assignment-service.d.ts create mode 100644 frontend/src/utils/dateUtils.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..109a1c99 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# ignoring any env files and node_modules in each microservice, will be helpful for local development +**/.env* +**/node_modules/** \ No newline at end of file diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg new file mode 100644 index 00000000..f015e3a5 --- /dev/null +++ b/frontend/public/logo.svg @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/frontend/public/next.svg b/frontend/public/next.svg deleted file mode 100644 index 5174b28c..00000000 --- a/frontend/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/public/vercel.svg b/frontend/public/vercel.svg deleted file mode 100644 index d2f84222..00000000 --- a/frontend/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/app/(pages)/assignments/[id]/layout.tsx b/frontend/src/app/(pages)/assignments/[id]/layout.tsx new file mode 100644 index 00000000..2f7e648d --- /dev/null +++ b/frontend/src/app/(pages)/assignments/[id]/layout.tsx @@ -0,0 +1,29 @@ +import AssignmentService from "@/helpers/assignment-service/api-wrapper"; +import { Metadata } from "next"; + +type Props = { + params: { + id: string; + }; +}; + +export default function AssignmentPageLayout({ + children, +}: Readonly<{ children: React.ReactNode }>) { + return
{children}
; +} + +export async function generateMetadata({ params }: Props) { + const assignmentTitle = ( + await AssignmentService.getAssignmentById({ + assignmentId: params.id, + }) + ).title; + + const metadata: Metadata = { + title: assignmentTitle, + description: `Assignment page for ${assignmentTitle}`, + }; + + return metadata; +} diff --git a/frontend/src/app/(pages)/assignments/[id]/page.tsx b/frontend/src/app/(pages)/assignments/[id]/page.tsx new file mode 100644 index 00000000..9c943523 --- /dev/null +++ b/frontend/src/app/(pages)/assignments/[id]/page.tsx @@ -0,0 +1,40 @@ +"use client"; + +import AssignmentPage from "@/components/assignment/AssignmentPage"; +import LogoLoading from "@/components/common/LogoLoading"; +import AssignmentService from "@/helpers/assignment-service/api-wrapper"; +import { useQuery } from "@tanstack/react-query"; +import { notFound } from "next/navigation"; + +interface Props { + id: string; +} + +const page = ({ id }: Props) => { + const { + data: assignment, + isLoading, + isError, + } = useQuery({ + queryKey: ["get-assignment", id], + queryFn: async () => { + const assignment = await AssignmentService.getAssignmentById({ + assignmentId: id, + }); + + return assignment; + }, + }); + + if (isError) { + return notFound(); + } + + return ( +
+ {isLoading ? : } +
+ ); +}; + +export default page; diff --git a/frontend/src/components/assignment/AssignmentPage.tsx b/frontend/src/components/assignment/AssignmentPage.tsx new file mode 100644 index 00000000..ded87d94 --- /dev/null +++ b/frontend/src/components/assignment/AssignmentPage.tsx @@ -0,0 +1,54 @@ +import { notFound } from "next/navigation"; +import AssignmentQuestion from "./AssignmentQuestion"; +import DateUtils from "../../utils/dateUtils"; +import { Button } from "@nextui-org/react"; + +interface Props { + assignment?: Assignment; +} + +const AssignmentPage = ({ assignment }: Props) => { + if (!assignment) { + return notFound(); + } + + return ( +
+ {/* Assignment Header */} +
+
+

{assignment.title}

+ +
+

+ Due on:{" "} + + {DateUtils.parseTimestampToDate(assignment.deadline)} + +

+

+ Number of questions:{" "} + + {assignment.numberOfQuestions} + +

+
+
+ + {/* Button for submission */} +
+ +
+
+ + {/* Assignment questions */} + {assignment.questions.map((question) => { + return ; + })} +
+ ); +}; + +export default AssignmentPage; diff --git a/frontend/src/components/assignment/AssignmentQuestion.tsx b/frontend/src/components/assignment/AssignmentQuestion.tsx new file mode 100644 index 00000000..b95249b5 --- /dev/null +++ b/frontend/src/components/assignment/AssignmentQuestion.tsx @@ -0,0 +1,32 @@ +import { Divider } from "@nextui-org/react"; +import parse from "html-react-parser"; + +interface Props { + question: Question; +} + +const AssignmentQuestion = ({ question }: Props) => { + return ( +
+
+ {/* Question title */} +
+
+ {question.title} +
+
+ + + + {/* Question description */} +
+
+ {parse(question.description)} +
+
+
+
+ ); +}; + +export default AssignmentQuestion; diff --git a/frontend/src/helpers/assignment-service/api-wrapper.ts b/frontend/src/helpers/assignment-service/api-wrapper.ts new file mode 100644 index 00000000..864d1fde --- /dev/null +++ b/frontend/src/helpers/assignment-service/api-wrapper.ts @@ -0,0 +1,98 @@ +import HttpStatusCode from "@/types/HttpStatusCode"; +import axios from "axios"; + +const api = axios.create({ + baseURL: "http://localhost:8080/assignment/api", + timeout: 5000, + headers: { + "Content-type": "application/json", + }, +}); + +const getAssignmentById = async ({ + assignmentId, +}: { + assignmentId: string; +}) => { + // const response = await api.get(`/assignment/${assignmentId}`); + + // if (response.status !== HttpStatusCode.OK) { + // throw new Error("Failed to fetch assignment"); + // } + + // const assignment: Assignment = response.data; + + const assignment: Assignment = { + id: assignmentId, + title: "Assignment 1: Introduction to programming", + deadline: 1711900799999, + isPublished: true, + numberOfQuestions: 2, + questions: [ + { + id: "1", + title: "Question 1: Two Sum", + description: ` +

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

+

You may assume that each input would have exactly one solution, and you may not use the same element twice.

+

You can return the answer in any order.

+

 

+

Example 1:

+
Input: nums = [2,7,11,15], target = 9
+          Output: [0,1]
+          Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].
+          
+

Example 2:

+
Input: nums = [3,2,4], target = 6
+          Output: [1,2]
+          
+

 

+

Constraints:

+
    +
  • 2 <= nums.length <= 104
  • +
  • -109 <= nums[i] <= 109
  • +
  • -109 <= target <= 109
  • +
  • Only one valid answer exists.
  • +
+

 

+ Follow-up:  + Can you come up with an algorithm that is less than O(n2) time complexity? + `, + numberOfTestCases: 1, + testCases: [ + { + input: "1", + output: "1", + isPublic: true, + }, + ], + referenceSolutionId: "referenceSolutionId", + }, + { + id: "2", + title: "Question 2", + description: "This is another question", + numberOfTestCases: 1, + testCases: [ + { + input: "1", + output: "1", + isPublic: true, + }, + ], + referenceSolutionId: "referenceSolutionId", + }, + ], + authors: ["1"], + createdOn: Date.now(), + updatedOn: Date.now(), + }; + + return assignment; +}; + +const AssignmentService = { + getAssignmentById, +}; + +export default AssignmentService; diff --git a/frontend/src/types/HttpStatusCode.ts b/frontend/src/types/HttpStatusCode.ts new file mode 100644 index 00000000..134bb669 --- /dev/null +++ b/frontend/src/types/HttpStatusCode.ts @@ -0,0 +1,384 @@ +"use strict"; + +/** + * Hypertext Transfer Protocol (HTTP) response status codes. + * Copied from {@link https://gist.github.com/scokmen/f813c904ef79022e84ab2409574d1b45}. + * + * @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes} + */ +enum HttpStatusCode { + /** + * The server has received the request headers and the client should proceed to send the request body + * (in the case of a request for which a body needs to be sent; for example, a POST request). + * Sending a large request body to a server after a request has been rejected for inappropriate headers would be inefficient. + * To have a server check the request's headers, a client must send Expect: 100-continue as a header in its initial request + * and receive a 100 Continue status code in response before sending the body. The response 417 Expectation Failed indicates the request should not be continued. + */ + CONTINUE = 100, + + /** + * The requester has asked the server to switch protocols and the server has agreed to do so. + */ + SWITCHING_PROTOCOLS = 101, + + /** + * A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request. + * This code indicates that the server has received and is processing the request, but no response is available yet. + * This prevents the client from timing out and assuming the request was lost. + */ + PROCESSING = 102, + + /** + * Standard response for successful HTTP requests. + * The actual response will depend on the request method used. + * In a GET request, the response will contain an entity corresponding to the requested resource. + * In a POST request, the response will contain an entity describing or containing the result of the action. + */ + OK = 200, + + /** + * The request has been fulfilled, resulting in the creation of a new resource. + */ + CREATED = 201, + + /** + * The request has been accepted for processing, but the processing has not been completed. + * The request might or might not be eventually acted upon, and may be disallowed when processing occurs. + */ + ACCEPTED = 202, + + /** + * SINCE HTTP/1.1 + * The server is a transforming proxy that received a 200 OK from its origin, + * but is returning a modified version of the origin's response. + */ + NON_AUTHORITATIVE_INFORMATION = 203, + + /** + * The server successfully processed the request and is not returning any content. + */ + NO_CONTENT = 204, + + /** + * The server successfully processed the request, but is not returning any content. + * Unlike a 204 response, this response requires that the requester reset the document view. + */ + RESET_CONTENT = 205, + + /** + * The server is delivering only part of the resource (byte serving) due to a range header sent by the client. + * The range header is used by HTTP clients to enable resuming of interrupted downloads, + * or split a download into multiple simultaneous streams. + */ + PARTIAL_CONTENT = 206, + + /** + * The message body that follows is an XML message and can contain a number of separate response codes, + * depending on how many sub-requests were made. + */ + MULTI_STATUS = 207, + + /** + * The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response, + * and are not being included again. + */ + ALREADY_REPORTED = 208, + + /** + * The server has fulfilled a request for the resource, + * and the response is a representation of the result of one or more instance-manipulations applied to the current instance. + */ + IM_USED = 226, + + /** + * Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation). + * For example, this code could be used to present multiple video format options, + * to list files with different filename extensions, or to suggest word-sense disambiguation. + */ + MULTIPLE_CHOICES = 300, + + /** + * This and all future requests should be directed to the given URI. + */ + MOVED_PERMANENTLY = 301, + + /** + * This is an example of industry practice contradicting the standard. + * The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect + * (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 + * with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 + * to distinguish between the two behaviours. However, some Web applications and frameworks + * use the 302 status code as if it were the 303. + */ + FOUND = 302, + + /** + * SINCE HTTP/1.1 + * The response to the request can be found under another URI using a GET method. + * When received in response to a POST (or PUT/DELETE), the client should presume that + * the server has received the data and should issue a redirect with a separate GET message. + */ + SEE_OTHER = 303, + + /** + * Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match. + * In such case, there is no need to retransmit the resource since the client still has a previously-downloaded copy. + */ + NOT_MODIFIED = 304, + + /** + * SINCE HTTP/1.1 + * The requested resource is available only through a proxy, the address for which is provided in the response. + * Many HTTP clients (such as Mozilla and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons. + */ + USE_PROXY = 305, + + /** + * No longer used. Originally meant "Subsequent requests should use the specified proxy." + */ + SWITCH_PROXY = 306, + + /** + * SINCE HTTP/1.1 + * In this case, the request should be repeated with another URI; however, future requests should still use the original URI. + * In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. + * For example, a POST request should be repeated using another POST request. + */ + TEMPORARY_REDIRECT = 307, + + /** + * The request and all future requests should be repeated using another URI. + * 307 and 308 parallel the behaviors of 302 and 301, but do not allow the HTTP method to change. + * So, for example, submitting a form to a permanently redirected resource may continue smoothly. + */ + PERMANENT_REDIRECT = 308, + + /** + * The server cannot or will not process the request due to an apparent client error + * (e.g., malformed request syntax, too large size, invalid request message framing, or deceptive request routing). + */ + BAD_REQUEST = 400, + + /** + * Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet + * been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the + * requested resource. See Basic access authentication and Digest access authentication. 401 semantically means + * "unauthenticated",i.e. the user does not have the necessary credentials. + */ + UNAUTHORIZED = 401, + + /** + * Reserved for future use. The original intention was that this code might be used as part of some form of digital + * cash or micro payment scheme, but that has not happened, and this code is not usually used. + * Google Developers API uses this status if a particular developer has exceeded the daily limit on requests. + */ + PAYMENT_REQUIRED = 402, + + /** + * The request was valid, but the server is refusing action. + * The user might not have the necessary permissions for a resource. + */ + FORBIDDEN = 403, + + /** + * The requested resource could not be found but may be available in the future. + * Subsequent requests by the client are permissible. + */ + NOT_FOUND = 404, + + /** + * A request method is not supported for the requested resource; + * for example, a GET request on a form that requires data to be presented via POST, or a PUT request on a read-only resource. + */ + METHOD_NOT_ALLOWED = 405, + + /** + * The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request. + */ + NOT_ACCEPTABLE = 406, + + /** + * The client must first authenticate itself with the proxy. + */ + PROXY_AUTHENTICATION_REQUIRED = 407, + + /** + * The server timed out waiting for the request. + * According to HTTP specifications: + * "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time." + */ + REQUEST_TIMEOUT = 408, + + /** + * Indicates that the request could not be processed because of conflict in the request, + * such as an edit conflict between multiple simultaneous updates. + */ + CONFLICT = 409, + + /** + * Indicates that the resource requested is no longer available and will not be available again. + * This should be used when a resource has been intentionally removed and the resource should be purged. + * Upon receiving a 410 status code, the client should not request the resource in the future. + * Clients such as search engines should remove the resource from their indices. + * Most use cases do not require clients and search engines to purge the resource, and a "404 Not Found" may be used instead. + */ + GONE = 410, + + /** + * The request did not specify the length of its content, which is required by the requested resource. + */ + LENGTH_REQUIRED = 411, + + /** + * The server does not meet one of the preconditions that the requester put on the request. + */ + PRECONDITION_FAILED = 412, + + /** + * The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large". + */ + PAYLOAD_TOO_LARGE = 413, + + /** + * The URI provided was too long for the server to process. Often the result of too much data being encoded as a query-string of a GET request, + * in which case it should be converted to a POST request. + * Called "Request-URI Too Long" previously. + */ + URI_TOO_LONG = 414, + + /** + * The request entity has a media type which the server or resource does not support. + * For example, the client uploads an image as image/svg+xml, but the server requires that images use a different format. + */ + UNSUPPORTED_MEDIA_TYPE = 415, + + /** + * The client has asked for a portion of the file (byte serving), but the server cannot supply that portion. + * For example, if the client asked for a part of the file that lies beyond the end of the file. + * Called "Requested Range Not Satisfiable" previously. + */ + RANGE_NOT_SATISFIABLE = 416, + + /** + * The server cannot meet the requirements of the Expect request-header field. + */ + EXPECTATION_FAILED = 417, + + /** + * This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, + * and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by + * teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including Google.com. + */ + I_AM_A_TEAPOT = 418, + + /** + * The request was directed at a server that is not able to produce a response (for example because a connection reuse). + */ + MISDIRECTED_REQUEST = 421, + + /** + * The request was well-formed but was unable to be followed due to semantic errors. + */ + UNPROCESSABLE_ENTITY = 422, + + /** + * The resource that is being accessed is locked. + */ + LOCKED = 423, + + /** + * The request failed due to failure of a previous request (e.g., a PROPPATCH). + */ + FAILED_DEPENDENCY = 424, + + /** + * The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field. + */ + UPGRADE_REQUIRED = 426, + + /** + * The origin server requires the request to be conditional. + * Intended to prevent "the 'lost update' problem, where a client + * GETs a resource's state, modifies it, and PUTs it back to the server, + * when meanwhile a third party has modified the state on the server, leading to a conflict." + */ + PRECONDITION_REQUIRED = 428, + + /** + * The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes. + */ + TOO_MANY_REQUESTS = 429, + + /** + * The server is unwilling to process the request because either an individual header field, + * or all the header fields collectively, are too large. + */ + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + + /** + * A server operator has received a legal demand to deny access to a resource or to a set of resources + * that includes the requested resource. The code 451 was chosen as a reference to the novel Fahrenheit 451. + */ + UNAVAILABLE_FOR_LEGAL_REASONS = 451, + + /** + * A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. + */ + INTERNAL_SERVER_ERROR = 500, + + /** + * The server either does not recognize the request method, or it lacks the ability to fulfill the request. + * Usually this implies future availability (e.g., a new feature of a web-service API). + */ + NOT_IMPLEMENTED = 501, + + /** + * The server was acting as a gateway or proxy and received an invalid response from the upstream server. + */ + BAD_GATEWAY = 502, + + /** + * The server is currently unavailable (because it is overloaded or down for maintenance). + * Generally, this is a temporary state. + */ + SERVICE_UNAVAILABLE = 503, + + /** + * The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. + */ + GATEWAY_TIMEOUT = 504, + + /** + * The server does not support the HTTP protocol version used in the request + */ + HTTP_VERSION_NOT_SUPPORTED = 505, + + /** + * Transparent content negotiation for the request results in a circular reference. + */ + VARIANT_ALSO_NEGOTIATES = 506, + + /** + * The server is unable to store the representation needed to complete the request. + */ + INSUFFICIENT_STORAGE = 507, + + /** + * The server detected an infinite loop while processing the request. + */ + LOOP_DETECTED = 508, + + /** + * Further extensions to the request are required for the server to fulfill it. + */ + NOT_EXTENDED = 510, + + /** + * The client needs to authenticate to gain network access. + * Intended for use by intercepting proxies used to control access to the network (e.g., "captive portals" used + * to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot). + */ + NETWORK_AUTHENTICATION_REQUIRED = 511, +} + +export default HttpStatusCode; diff --git a/frontend/src/types/assignment-service.d.ts b/frontend/src/types/assignment-service.d.ts new file mode 100644 index 00000000..44fec4db --- /dev/null +++ b/frontend/src/types/assignment-service.d.ts @@ -0,0 +1,37 @@ +type Assignment = { + id: string; + title: string; + deadline: number; + isPublished: boolean; + numberOfQuestions: number; + questions: Question[]; + authors: string[]; + createdOn: number; + updatedOn: number; +}; + +type Question = { + id: string; + title: string; + description: string; + deadline?: number; + numberOfTestCases: number; + testCases?: TestCase[]; + referenceSolutionId?: string; + referenceSolution?: ReferenceSolution; + assignmentId?: string; +}; + +type TestCase = { + id?: string; + input: string; + output: string; + isPublic: boolean = true; +}; + +type ReferenceSolution = { + id?: string; + language: string; + code: string; + questionId?: string; +}; diff --git a/frontend/src/utils/dateUtils.ts b/frontend/src/utils/dateUtils.ts new file mode 100644 index 00000000..8398d1d9 --- /dev/null +++ b/frontend/src/utils/dateUtils.ts @@ -0,0 +1,36 @@ +import { format } from "date-fns"; + +const parseTimestampToDate = (timestamp: number) => { + const date = new Date(timestamp).toLocaleDateString("en-US", { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + timeZone: "Asia/Singapore", + }); + + return date; +}; + +const convertDateToTimestamp = (date: string | Date) => { + if (typeof date === "string") { + date = new Date(date); + + // adjust the time zone to Singapore time + date.setHours(date.getHours() - 8); + } + + const timestamp = date.getTime(); + return timestamp; +}; + +console.log(convertDateToTimestamp("2024-03-31T23:59:59.999Z")); + +const DateUtils = { + parseTimestampToDate, + convertDateToTimestamp, +}; + +export default DateUtils; From 4f43b5c333ec62ce7a254d024612d86e2f6aefec Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Fri, 1 Mar 2024 23:42:46 +0800 Subject: [PATCH 4/4] fix sidebar that doesn't render to the full page --- frontend/src/app/layout.tsx | 2 +- frontend/src/components/assignment/AssignmentPage.tsx | 2 +- frontend/src/components/common/SideBar.tsx | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index e262cea9..e6e2094d 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -20,7 +20,7 @@ export default function RootLayout({ -
+
{children} diff --git a/frontend/src/components/assignment/AssignmentPage.tsx b/frontend/src/components/assignment/AssignmentPage.tsx index ded87d94..900eff81 100644 --- a/frontend/src/components/assignment/AssignmentPage.tsx +++ b/frontend/src/components/assignment/AssignmentPage.tsx @@ -13,7 +13,7 @@ const AssignmentPage = ({ assignment }: Props) => { } return ( -
+
{/* Assignment Header */}
diff --git a/frontend/src/components/common/SideBar.tsx b/frontend/src/components/common/SideBar.tsx index 1577e670..cbc8706c 100644 --- a/frontend/src/components/common/SideBar.tsx +++ b/frontend/src/components/common/SideBar.tsx @@ -2,7 +2,6 @@ import { useState } from "react"; import { Avatar, Button, User } from "@nextui-org/react"; -import { TbLayoutSidebarLeftCollapse } from "react-icons/tb"; import { HiOutlineChevronDoubleLeft, HiMenu } from "react-icons/hi"; import classNames from "classnames"; @@ -13,7 +12,7 @@ const SideBar = () => { const [isCollapsible, setIsCollapsible] = useState(false); const wrapperClasses = classNames( - "h-screen px-4 pt-8 pb-4 bg-gray-200 text-black flex justify-between flex-col border border-dashed", + "px-4 pt-8 pb-4 bg-gray-200 text-black flex justify-between flex-col border border-dashed", { ["w-60"]: !isCollapsed, ["w-20"]: isCollapsed,