From 681ae89855e531b6f1fd469e89a458db4c735bb8 Mon Sep 17 00:00:00 2001 From: Krit Kasikpan <113231007+KhunKrit46@users.noreply.github.com> Date: Mon, 25 Nov 2024 21:31:08 -0500 Subject: [PATCH] Hotfix submission number (#119) * hotfix iter 1 * routes fix + ui --------- Co-authored-by: Eren --- README.md | 35 ++- client/src/components/ChallengeCard.tsx | 96 +++++-- client/src/pages/Challenges.tsx | 325 +++++++++++++--------- client/src/types/ChallengeDetailsShort.ts | 3 +- package-lock.json | 11 +- server/controllers/SolutionController.js | 68 +++-- server/index.js | 94 +++---- server/routes/SolutionRoutes.js | 9 +- 8 files changed, 395 insertions(+), 246 deletions(-) diff --git a/README.md b/README.md index 6056cb5..1f1f7f1 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,47 @@ # UML_Mentor - ### Development guide (OUTDATED) -Clone the repository + +Clone the repository + ``` git clone https://github.com/utmgdsc/UML_Mentor.git cd UML_Mentor ``` -Install the dependencies + +Install the dependencies + ``` npm install -cd client +cd client npm install ``` + Run the client in dev mode + ``` npm run dev ``` -Go to `server/index.js` and uncomment line `18` + +Go to `server/index.js` and uncomment line `18` + ```js await importChallenges(); ``` + Open a new terminal and run the server in dev mode (execute from root directory). + ``` npm run devStart ``` -The challenges are now imported from the `challenges.json` into the SQLite database. You should now uncomment line 18 to avoid import errors in the future runs. Alternatively, you can pass the `true` parameter to tell the function to reimport challenges on every run (not recommended). + +The challenges are now imported from the `challenges.json` into the SQLite database. You should now uncomment line 18 to avoid import errors in the future runs. Alternatively, you can pass the `true` parameter to tell the function to reimport challenges on every run (not recommended). + ```js // await importChallenges(); ``` -Access the app via the client port `3000`. All of the client requests starting with `/api` are redirected to the server `8080`. + +Access the app via the client port `3000`. All of the client requests starting with `/api` are redirected to the server `8080`. ## Developer's Documentation @@ -60,7 +72,8 @@ Access the app via the client port `3000`. All of the client requests starting w #### Middleware #### AI -For Eren to do. + +For Eren to do. ### Authentication @@ -72,25 +85,25 @@ For Eren to do. #### Production - ## Basic user documentation ### Solving challenges. -### Posting solutions. +### Posting solutions. ### Viewing solutions. ## Admin documentation ### Managing challenges + - Add - Delete - Hide - Edit (todo) ### Managing users + - Delete Solutions - Delete comments - Admin dashboard - diff --git a/client/src/components/ChallengeCard.tsx b/client/src/components/ChallengeCard.tsx index 7e5ce39..de30b53 100644 --- a/client/src/components/ChallengeCard.tsx +++ b/client/src/components/ChallengeCard.tsx @@ -14,6 +14,7 @@ function ChallengeCard({ isAdmin, hidden, keyPatterns, + solutionCount, }: ChallengeDetailsShort) { const [deleted, setDeleted] = useState(false); @@ -26,22 +27,28 @@ function ChallengeCard({ const backgroundColor = completed ? "bg-success-subtle" : ""; function handleDelete() { - if (!window.confirm("Are you sure you want to delete the challenge '" + title + "'?")) { + if ( + !window.confirm( + "Are you sure you want to delete the challenge '" + title + "'?", + ) + ) { return; } fetch("/api/challenges/" + id, { method: "DELETE", - }).then((response) => { - if (response.ok) { - console.log("Challenge deleted"); - setDeleted(true); - } else { - console.log("Failed to delete challenge"); - } - }).catch((err) => { - console.error(err); - }); + }) + .then((response) => { + if (response.ok) { + console.log("Challenge deleted"); + setDeleted(true); + } else { + console.log("Failed to delete challenge"); + } + }) + .catch((err) => { + console.error(err); + }); } function handleHide(hidden: boolean) { @@ -51,17 +58,19 @@ function ChallengeCard({ headers: { "Content-Type": "application/json", }, - }).then((response) => { - if (response.ok) { - console.log("Challenge hidden"); - setDeleted(true); - } else { - console.log("Failed to hide challenge"); - } - return response.json(); - }).catch((err) => { - console.error(err); - }); + }) + .then((response) => { + if (response.ok) { + console.log("Challenge hidden"); + setDeleted(true); + } else { + console.log("Failed to hide challenge"); + } + return response.json(); + }) + .catch((err) => { + console.error(err); + }); } if (deleted) { @@ -69,7 +78,7 @@ function ChallengeCard({ } // Split pattern names from keyPatterns and render them as tags - const patternTags = keyPatterns?.map((pattern, index) => { + const patternTags = keyPatterns.map((pattern, index) => { const patternName = pattern.split(" ")[0]; // Get only the first word (e.g., "Strategy", "Factory", etc.) return ( - - {difficultyStars} - + {difficultyStars} {title} {/* Render tags in a flexbox container */} @@ -105,23 +112,54 @@ function ChallengeCard({ }} > {patternTags} + + {`${solutionCount || 0}`} + {generalDescription}
{isAdmin && hidden && ( - )} {isAdmin && !hidden && ( - )} {isAdmin && ( - )} diff --git a/client/src/pages/Challenges.tsx b/client/src/pages/Challenges.tsx index a56c275..f411950 100644 --- a/client/src/pages/Challenges.tsx +++ b/client/src/pages/Challenges.tsx @@ -21,7 +21,9 @@ function Challenges() { const [sortByDifficulty, setSortByDifficulty] = useState(false); const [filter, setFilter] = useState([] as ChallengeDifficulties[]); const [hideComplete, setHideComplete] = useState(false); - const [selectedChallengeTypes, setSelectedChallengeTypes] = useState([]); + const [selectedChallengeTypes, setSelectedChallengeTypes] = useState< + string[] + >([]); const { isAdmin } = useCheckRole(); const query = useQuery(); @@ -48,9 +50,29 @@ function Challenges() { } return response.json() as Promise; }) - .then((data) => { - setChallengesData(data); - setIsLoading(false); + .then(async (data) => { + // Fetch the number of solutions for each challenge + fetch("/api/solutions/counts") + .then((response) => { + if (!response.ok) { + console.error("Response status:", response.status); // Log the status + throw new Error("Failed to fetch solution counts"); + } + return response.json(); + }) + .then((solutionCounts) => { + const challengesWithSolutionCount = data.map((challenge) => { + const count = + solutionCounts.find((sc) => sc.challengeId === challenge.id) + ?.solutionCount || 0; // Default to 0 if not found + return { ...challenge, solutionCount: count }; + }); + setChallengesData(challengesWithSolutionCount); + setIsLoading(false); + }) + .catch((err) => { + console.error("Failed fetching the challenges.", err.message); + }); // Extract challenge types from keyPatterns and categorize them const patternTypesSet = new Set(); @@ -62,11 +84,14 @@ function Challenges() { const patternIndex = words.findIndex( (word) => word.toLowerCase() === "pattern" ); - const patternType = patternIndex !== -1 - ? words.slice(0, patternIndex).join(" ") - : patternList; + const patternType = + patternIndex !== -1 + ? words.slice(0, patternIndex).join(" ") + : patternList; - if (!Object.values(patternCategories).flat().includes(patternType)) { + if ( + !Object.values(patternCategories).flat().includes(patternType) + ) { detectedExtraPatterns.add(patternType); // Add to extra if not in known categories } patternTypesSet.add(patternType); @@ -92,7 +117,11 @@ function Challenges() { const makeGrid = useCallback((): JSX.Element[] => { if (challengesData?.length === 0) - return [

No challenges found

]; + return [ +

+ No challenges found +

, + ]; if (isLoading || challengesData === undefined) { return [ @@ -141,7 +170,19 @@ function Challenges() { row.push( - +