diff --git a/README.md b/README.md index 62aad77..4d9c5d5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/fE-a_qEp) The individual and team project for this class are designed to mirror the experiences of a software engineer joining a new development team: you will be “onboarded” to our codebase, make several individual contributions, and then form a team to propose, develop and implement new features. The codebase that we’ll be developing on is a Fake Stack Overflow project (let’s call it HuskyFlow). You will get an opportunity to work with the starter code which provides basic skeleton for the app and then additional features will be proposed and implemented by you! All implementation will take place in the TypeScript programming language, using React for the user interface. ## Getting Started +Visit our deployed project here: https://cs4530-s25-607.onrender.com/ + +To run this project locally: Run `npm install` in the root directory to install all dependencies for the `client`, `server`, and `shared` folders. { : .note } Refer to [IP1](https://neu-se.github.io/CS4530-Spring-2025/assignments/ip1) and [IP2](https://neu-se.github.io/CS4530-Spring-2025/assignments/ip2) for further instructions related to setting up MongoDB, setting environment variables, and running the client and server. @@ -17,7 +21,7 @@ Run `npm install` in the root directory to install all dependencies for the `cli The schemas for the database are documented in the directory `server/models/schema`. A class diagram for the schema definition is shown below: -![Class Diagram](class-diagram.png) +![Class Diagram](new-class-diagram.png) ## API Routes @@ -42,13 +46,14 @@ A class diagram for the schema definition is shown below: ### `/question` -| Endpoint | Method | Description | -| ----------------- | ------ | ------------------------------- | -| /getQuestion | GET | Fetch questions by filter | -| /getQuestionById/ | GET | Fetch a specific question by ID | -| /addQuestion | POST | Add a new question | -| /upvoteQuestion | POST | Upvote a question | -| /downvoteQuestion | POST | Downvote a question | +| Endpoint | Method | Description | +| ----------------------- | ------ | --------------------------------- | +| /getQuestion | GET | Fetch questions by filter | +| /getQuestionById/ | GET | Fetch a specific question by ID | +| /addQuestion | POST | Add a new question | +| /upvoteQuestion | POST | Upvote a question | +| /downvoteQuestion | POST | Downvote a question | +| /getCommunityQuestions/ | GET | Fetchs questions from a community | ### `/tag` @@ -88,6 +93,57 @@ A class diagram for the schema definition is shown below: | /leave | POST | Leave a game | | /games | GET | Retrieve all games | +### `/contributions` + +| Endpoint | Method | Description | +| ------------------ | ------ | -------------------------------- | +| /getContributions | GET | Fetch a user's contributions | + +### `/community` + +| Endpoint | Method | Description | +| ---------------------- | ------ | ------------------------------------ | +| /addCommunity | POST | Create a new community | +| /getCommunity/ | GET | Fetch community by communityId | +| /getAllCommunities | GET | Fetch all communities | +| /getCommunitiesByUser/ | GET | Fetch all community by userId | +| /addMember/ | POST | Add a member to community | +| /addModerator/ | POST | Add a moderator to community | +| /addMemberRequest/ | POST | Add a member request to community | +| /removeMember/ | DELETE | Remove a member to community | +| /removeModerator/ | DELETE | Remove a moderator to community | +| /rejectMemberRequest/ | DELETE | Reject a member request to community | +| /updateName/ | PATCH | Update community name | +| /updateDescription/ | PATCH | Update community description | + +### `/notifications` + +| Endpoint | Method | Description | +| ---------------------- | ------ | --------------------------------- | +| /getNotifications | GET | Gets a users notifications | +| /viewNotification | PUT | Marks a notification as viewed | +| /clearAllNotifications | PUT | Marks all notifications as viewed | + +### `/gamer` + +| Endpoint | Method | Description | +| ----------------- | ------ | ------------------------------ | +| /createGamer | POST | Create a new gamer | +| /getGamer/ | GET | Gets a gamer by username | +| /getGamers | GET | Gets all gamers | +| /deleteGamer/ | DELETE | Deletes a gamer | +| /updateGamerInfo | PATCH | Updates gamer information | +| /getLeaderboard | GET | Gets gamer leaderboard | + +### `/star` + +| Endpoint | Method | Description | +| ----------- | ------- | ----------------------------- | +| /getStars | GET | Get a user's stars | +| /addStar | POST | Add a star to a question | +| /removeStar | DELETE | Remove a star from a question | + + ## Running Stryker Mutation Testing Mutation testing helps you measure the effectiveness of your tests by introducing small changes (mutations) to your code and checking if your tests catch them. To run mutation testing with Stryker, use the following command in `server/`: diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 90b80ab..db4a5b8 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -39,7 +39,13 @@ module.exports = { '@typescript-eslint/no-throw-literal': 0, '@typescript-eslint/lines-between-class-members': 0, '@typescript-eslint/no-unused-vars': [1, { args: 'none' }], - 'import/no-extraneous-dependencies': ['error', { devDependencies: ['**/*.spec.ts'] }], + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: ['**/*.spec.ts'], + packageDir: [__dirname, './client'], + }, + ], '@typescript-eslint/naming-convention': [ 'error', { diff --git a/client/package.json b/client/package.json index 4180263..d6d9b89 100644 --- a/client/package.json +++ b/client/package.json @@ -6,11 +6,24 @@ "@cypress/instrument-cra": "^1.4.0", "@fake-stack-overflow/shared": "^1.0.0", "axios": "^1.7.9", + "highlight.js": "^11.11.1", + "katex": "^0.16.21", + "lucide-react": "^0.487.0", "mongodb": "6.12.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-hotkeys-hook": "^4.6.1", + "react-icons": "^5.5.0", + "react-markdown": "^10.1.0", "react-router-dom": "^6.28.1", "react-scripts": "5.0.1", + "react-speech-recognition": "^4.0.0", + "rehype-highlight": "^7.0.2", + "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0", "socket.io-client": "^4.8.1" }, "scripts": { diff --git a/client/public/stackOverflowLogo.png b/client/public/stackOverflowLogo.png new file mode 100644 index 0000000..93505e8 Binary files /dev/null and b/client/public/stackOverflowLogo.png differ diff --git a/client/src/components/auth/login/index.css b/client/src/components/auth/login/index.css index 9940e9b..89b4505 100644 --- a/client/src/components/auth/login/index.css +++ b/client/src/components/auth/login/index.css @@ -9,6 +9,12 @@ padding: 20px; } +.login-logo { + width: 100px; + height: auto; + margin-bottom: 20px; +} + /* Heading styles */ .container h2 { font-size: 2rem; @@ -102,3 +108,7 @@ form { margin-top: 20px; text-align: center; } + +.show-password label { + color: black; +} diff --git a/client/src/components/auth/login/index.tsx b/client/src/components/auth/login/index.tsx index f64aac4..875875b 100644 --- a/client/src/components/auth/login/index.tsx +++ b/client/src/components/auth/login/index.tsx @@ -20,7 +20,8 @@ const Login = () => { return (
-

Welcome to FakeStackOverflow!

+ StackViolet Logo +

Welcome to StackViolet!

Please login to continue.

Please enter your username.

diff --git a/client/src/components/auth/signup/index.css b/client/src/components/auth/signup/index.css index ca48bde..e73b518 100644 --- a/client/src/components/auth/signup/index.css +++ b/client/src/components/auth/signup/index.css @@ -9,6 +9,12 @@ padding: 20px; } +.signup-logo { + width: 100px; + height: auto; + margin-bottom: 20px; +} + /* Heading styles */ .container h2 { font-size: 2rem; diff --git a/client/src/components/auth/signup/index.tsx b/client/src/components/auth/signup/index.tsx index 3f7b036..e90e80e 100644 --- a/client/src/components/auth/signup/index.tsx +++ b/client/src/components/auth/signup/index.tsx @@ -21,7 +21,8 @@ const Signup = () => { return (
-

Sign up for FakeStackOverflow!

+ StackViolet Logo +

Sign up for StackViolet!

Please enter your username.

{ const [user, setUser] = useState(null); + useKeyPressDetection(user?.username); + + // apply theme + useEffect(() => { + keepTheme(); + }, []); + return ( @@ -55,8 +68,9 @@ const FakeStackOverflow = ({ socket }: { socket: FakeSOSocket | null }) => { }> - } /> - } /> + } /> + } /> + } /> } /> } /> } /> @@ -66,6 +80,9 @@ const FakeStackOverflow = ({ socket }: { socket: FakeSOSocket | null }) => { } /> } /> } /> + } /> + } /> + } /> } diff --git a/client/src/components/header/index.css b/client/src/components/header/index.css index fbacacb..41efe33 100644 --- a/client/src/components/header/index.css +++ b/client/src/components/header/index.css @@ -1,21 +1,61 @@ .header { display: flex; align-items: center; - justify-content: space-around; + justify-content: space-between; height: 10%; width: 100%; - background: #dddddd; + background: var(--header); + color: var(--text); + padding: 0 20px; +} + +.header-left { + display: flex; + align-items: center; } .title { - font-size: 32px; + font-size: 50px; font-weight: 800; + margin-left: 5px; + font-family:'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; +} + +.logo { + height: 80px; + width: auto; + margin-right: 15px; } +.search-container { + flex-grow: 1; + max-width: 500px; /* You can adjust this based on your needs */ + display: flex; + justify-content: center; + padding: 10px; +} + +.search-input { + width: 100%; + padding: 12px 20px; + font-size: 16px; + border-radius: 25px; + border: 1px solid #ccc; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + transition: border-color 0.3s ease, box-shadow 0.3s ease; +} + +.search-input:focus { + border-color: var(--button); + outline: none; + box-shadow: 0 0 5px rgba(40, 167, 69, 0.5); +} + + .logout-button { padding: 12px; font-size: 16px; - background-color: #007bff; + background-color: var(--button); color: #fff; border: none; border-radius: 5px; @@ -23,8 +63,12 @@ transition: background-color 0.3s; } +.logout-button:hover { + background-color: var(--button-hover); +} + .view-profile-button { - background-color: #007bff; + background-color: var(--button); color: white; border: none; padding: 10px 15px; @@ -34,9 +78,9 @@ } .view-profile-button:hover { - background-color: #0056b3; + background-color: var(--button-hover); } .view-profile-button:active { - background-color: #003f7f; -} \ No newline at end of file + background-color: var(--button-hover); +} diff --git a/client/src/components/header/index.tsx b/client/src/components/header/index.tsx index 0e9c4d6..01340fd 100644 --- a/client/src/components/header/index.tsx +++ b/client/src/components/header/index.tsx @@ -10,21 +10,29 @@ import useUserContext from '../../hooks/useUserContext'; * when they press Enter. */ const Header = () => { - const { val, handleInputChange, handleKeyDown, handleSignOut } = useHeader(); + const { val, handleInputChange, handleSignOut } = useHeader(); const { user: currentUser } = useUserContext(); const navigate = useNavigate(); + return (