From 733b4d0647813dfe1b73ec291163795bda8d9b8c Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:26:13 +0000 Subject: [PATCH 001/323] Setting up GitHub Classroom Feedback From 8b7f9d419988fa139e9b8dc325045244182e3840 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:26:16 +0000 Subject: [PATCH 002/323] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 62aad77..69871ee 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![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 From aef47e6ec280931a44830da9c41c7d3aeb4bf95a Mon Sep 17 00:00:00 2001 From: "Adeel A. Bhutta" <88048498+abhutta0@users.noreply.github.com> Date: Fri, 28 Feb 2025 11:11:32 -0500 Subject: [PATCH 003/323] Update package.json build command --- server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/package.json b/server/package.json index 5374eee..acc9c09 100644 --- a/server/package.json +++ b/server/package.json @@ -47,7 +47,7 @@ "lint:fix": "eslint . --fix", "test": "jest -w=1 --coverage --detectOpenHandles --detectLeaks", "debug-test": "jest -w=1 --coverage --detectOpenHandles", - "prebuild": "cd ..; npm run build --workspaces shared", + "prebuild": "cd ..; npm run build --workspace=shared", "build": "tsc", "start:prod": "node ./dist/server.js", "stryker": "stryker run", From c19c655e9628fb331dcea975c9353ba217564707 Mon Sep 17 00:00:00 2001 From: K-maker411 Date: Tue, 4 Mar 2025 15:33:57 -0600 Subject: [PATCH 004/323] Added preliminary community model and types, updated sideBarNav component (will add more front end changes later) --- .../src/components/main/sideBarNav/index.tsx | 6 +++ server/models/community.model.ts | 19 +++++++ server/models/schema/community.schema.ts | 53 +++++++++++++++++++ shared/types/community.d.ts | 50 +++++++++++++++++ shared/types/types.d.ts | 1 + 5 files changed, 129 insertions(+) create mode 100644 server/models/community.model.ts create mode 100644 server/models/schema/community.schema.ts create mode 100644 shared/types/community.d.ts diff --git a/client/src/components/main/sideBarNav/index.tsx b/client/src/components/main/sideBarNav/index.tsx index 4a5d1af..df44319 100644 --- a/client/src/components/main/sideBarNav/index.tsx +++ b/client/src/components/main/sideBarNav/index.tsx @@ -65,6 +65,12 @@ const SideBarNav = () => { className={({ isActive }) => `menu_button ${isActive ? 'menu_selected' : ''}`}> Games + `menu_button ${isActive ? 'menu_selected' : ''}`}> + Communities + ); }; diff --git a/server/models/community.model.ts b/server/models/community.model.ts new file mode 100644 index 0000000..9ef7bec --- /dev/null +++ b/server/models/community.model.ts @@ -0,0 +1,19 @@ +import mongoose, { Model } from 'mongoose'; +import communitySchema from './schema/community.schema'; +import { DatabaseCommunity } from '../types/types'; + +/** + * Mongoose model for the `Community` collection. + * + * This model is created using the `Community` interface and the `communitySchema`, representing the + * `Community` collection in the MongoDB database, and provides an interface for interacting with + * the stored communities. + * + * @type {Model} + */ +const CommunityModel: Model = mongoose.model( + 'Community', + communitySchema, +); + +export default CommunityModel; diff --git a/server/models/schema/community.schema.ts b/server/models/schema/community.schema.ts new file mode 100644 index 0000000..66cabe2 --- /dev/null +++ b/server/models/schema/community.schema.ts @@ -0,0 +1,53 @@ +import { Schema } from 'mongoose'; + +/** + * Mongoose schema for the Community collection. + * + * This schema defines the structure of the community used in the database. + * Each community includes the following fields: + * + * - `name`: The name of the community. + * - `about`: A brief description of the community. + * - `rules`: The rules of the community. + * - `members`: The list of members in the community. + * - `createdBy`: The username of the user who created the community. + * - `createdAt`: The date and time when the community was created. + * - `groupChatId`: The id of the community's group chat. + * TODO: NEED TO ADD: + * - admins + * - bulletin board + * - visibility + * - community stats + */ + +const communitySchema: Schema = new Schema( + { + name: { + type: String, + required: true, + }, + about: { + type: String, + required: true, + }, + rules: { + type: String, + required: true, + }, + members: { + type: [String], + required: true, + }, + createdBy: { + type: String, + required: true, + }, + groupChatId: { + type: Schema.Types.ObjectId, + required: true, + }, + }, + { collection: 'Community', timestamps: true }, +); + +export default communitySchema; diff --git a/shared/types/community.d.ts b/shared/types/community.d.ts new file mode 100644 index 0000000..7a587ae --- /dev/null +++ b/shared/types/community.d.ts @@ -0,0 +1,50 @@ +import { Request } from 'express'; +import { ObjectId } from 'mongodb'; + +/** + * Represents a community in the application. + * - `name`: The name of the community. + * - `about`: A brief description of the community. + * - `rules`: The rules of the community. + * - `members`: The list of members in the community. + * - `createdBy`: The username of the user who created the community. + * - `groupChatId`: The id of the community's group chat. + */ +export interface Community { + name: string; + about: string; + rules: string; + members: string[]; + createdBy: string; + groupChatId: ObjectId; +} + +/** + * Represents a community stored in the database. + * - `_id`: Unique identifier for the community. + * - `name`: The name of the community. + * - `about`: A brief description of the community. + * - `rules`: The rules of the community. + * - `members`: The list of members in the community. + * - `createdBy`: The username of the user who created the community. + * - `groupChatId`: The id of the community's group chat. + */ +export interface DatabaseCommunity extends Community { + _id: ObjectId; +} + +/** + * Interface extending the request body for creating a new community. + * - `community`: The community object being created. + */ +export interface CreateCommunityRequest extends Request { + body: { + community: Omit; + }; +} + +/** + * Type representing possible responses for a Community-related operation. + * - Either a `DatabaseCommunity` object or an error message. + */ +export type CommunityResponse = DatabaseCommunity | { error: string }; diff --git a/shared/types/types.d.ts b/shared/types/types.d.ts index 37de3e9..04ba6eb 100644 --- a/shared/types/types.d.ts +++ b/shared/types/types.d.ts @@ -7,3 +7,4 @@ export * from './question'; export * from './socket'; export * from './tag'; export * from './user'; +export * from './community'; From 5b36a6f513c4627fba042b7699aa004e7cb87684 Mon Sep 17 00:00:00 2001 From: K-maker411 Date: Tue, 4 Mar 2025 20:23:40 -0600 Subject: [PATCH 005/323] Basic community service and controller completed, starting frontend work --- server/controllers/community.controller.ts | 68 +++++++++++++++++++ server/services/community.service.ts | 54 +++++++++++++++ .../controllers/community.controller.spec.ts | 0 .../tests/services/community.service.spec.ts | 0 shared/types/community.d.ts | 8 +++ shared/types/socket.d.ts | 11 +++ 6 files changed, 141 insertions(+) create mode 100644 server/controllers/community.controller.ts create mode 100644 server/services/community.service.ts create mode 100644 server/tests/controllers/community.controller.spec.ts create mode 100644 server/tests/services/community.service.spec.ts diff --git a/server/controllers/community.controller.ts b/server/controllers/community.controller.ts new file mode 100644 index 0000000..5a16f3f --- /dev/null +++ b/server/controllers/community.controller.ts @@ -0,0 +1,68 @@ +import express, { Request, Response, Router } from 'express'; +import { ObjectId } from 'mongodb'; + +import { + Community, + CreateCommunityRequest, + CommunityResponse, + FakeSOSocket, + CommunitiesResponse, +} from '../types/types'; +import { saveCommunity, getCommunityById, getAllCommunities } from '../services/community.service'; + +const communityController = (socket: FakeSOSocket) => { + const router: Router = express.Router(); + + /** + * Validates that the request body contains all required fields for a community. + * @param req The incoming request containing community data. + * @returns `true` if the body contains valid community fields; otherwise, `false`. + */ + const isCommunityBodyValid = (req: CreateCommunityRequest): boolean => + req.body !== undefined && + req.body.community !== undefined && + req.body.community.name !== undefined && + req.body.community.name !== '' && + req.body.community.about !== undefined && + req.body.community.about !== '' && + req.body.community.rules !== undefined && + req.body.community.rules !== '' && + req.body.community.members !== undefined && + Array.isArray(req.body.community.members) && + req.body.community.createdBy !== undefined && + req.body.community.createdBy !== ''; + + const createCommunity = async (req: CreateCommunityRequest, res: Response): Promise => { + if (!isCommunityBodyValid(req)) { + res.status(400).send('Invalid community body'); + return; + } + + const requestCommunity = req.body.community; + + const community: Community = { + ...requestCommunity, + groupChatId: new ObjectId(), + }; + + const result: CommunityResponse = await saveCommunity(community); + + if ('error' in result) { + res.status(500).send(result.error); + return; + } + + res.status(200).json(result); + }; + + const getCommunities = async (req: Request, res: Response): Promise => { + const communities: CommunitiesResponse = await getAllCommunities(); + + if ('error' in communities) { + res.status(500).send(communities.error); + return; + } + + res.status(200).json(communities); + }; +}; diff --git a/server/services/community.service.ts b/server/services/community.service.ts new file mode 100644 index 0000000..57e2744 --- /dev/null +++ b/server/services/community.service.ts @@ -0,0 +1,54 @@ +import CommunityModel from '../models/community.model'; +import { + Community, + CommunityResponse, + DatabaseCommunity, + CommunitiesResponse, +} from '../types/types'; + +/** + * Saves a new community to the database. + * @param communityPayload - The community object to save. + * @returns {Promise} - The saved community or an error message. + */ +export const saveCommunity = async (communityPayload: Community): Promise => { + try { + const result = await CommunityModel.create(communityPayload); + if (!result) { + throw new Error('Error saving community'); + } + return result; + } catch (error) { + return { error: `Error saving community: ${error}` }; + } +}; + +/** + * Retrieves a community by its ID. + * @param communityId - The ID of the community to retrieve. + * @returns {Promise} - The retrieved community or an error message. + */ +export const getCommunityById = async (communityId: string): Promise => { + try { + const community: DatabaseCommunity | null = await CommunityModel.findById(communityId); + if (!community) { + throw new Error('Community not found'); + } + return community; + } catch (error) { + return { error: `Error retrieving community: ${error}` }; + } +}; + +/** + * Retrieves all communities from the database. + * @returns {Promise} - An array of all communities or an error message. + */ +export const getAllCommunities = async (): Promise => { + try { + const communities: DatabaseCommunity[] = await CommunityModel.find(); + return communities; + } catch (error) { + return { error: `Error retrieving communities: ${error}` }; + } +}; diff --git a/server/tests/controllers/community.controller.spec.ts b/server/tests/controllers/community.controller.spec.ts new file mode 100644 index 0000000..e69de29 diff --git a/server/tests/services/community.service.spec.ts b/server/tests/services/community.service.spec.ts new file mode 100644 index 0000000..e69de29 diff --git a/shared/types/community.d.ts b/shared/types/community.d.ts index 7a587ae..9fbef6b 100644 --- a/shared/types/community.d.ts +++ b/shared/types/community.d.ts @@ -48,3 +48,11 @@ export interface CreateCommunityRequest extends Request { * - Either a `DatabaseCommunity` object or an error message. */ export type CommunityResponse = DatabaseCommunity | { error: string }; + +/** + * Type representing the response for multiple community-related operations. + * Either: + * - `DatabaseCommunity[]`: A list of community objects if the operation is successful. + * - `error`: An error message if the operation fails. + */ +export type CommunitiesResponse = DatabaseCommunity[] | { error: string }; diff --git a/shared/types/socket.d.ts b/shared/types/socket.d.ts index a25fc84..cd9d25c 100644 --- a/shared/types/socket.d.ts +++ b/shared/types/socket.d.ts @@ -109,6 +109,16 @@ export interface ClientToServerEvents { leaveChat: (chatID: string | undefined) => void; } +/** + * Interface representing the payload for a community update event. + * - `community`: The updated community object. + * - `type`: The type of modification (`'created'`, `'deleted'`, or `'updated'`). + */ +export interface CommunityUpdatePayload { + community: DatabaseCommunity; + type: 'created' | 'deleted' | 'updated'; +} + /** * Interface representing the events the server can emit to the client. * - `questionUpdate`: Server sends updated question. @@ -133,4 +143,5 @@ export interface ServerToClientEvents { gameUpdate: (game: GameUpdatePayload) => void; gameError: (error: GameErrorPayload) => void; chatUpdate: (chat: ChatUpdatePayload) => void; + communityUpdate: (community: CommunityUpdatePayload) => void; } From ff1f9d996b014fb28c03df28e4c12a4f8027bb7f Mon Sep 17 00:00:00 2001 From: K-maker411 Date: Wed, 5 Mar 2025 16:01:09 -0600 Subject: [PATCH 006/323] Broken frontend for creating new community, trying to add ChakraUI components --- .../components/main/newCommunity/index.tsx | 59 +++ .../src/components/main/sideBarNav/index.tsx | 2 +- client/src/hooks/useNewCommunity.ts | 109 ++++ client/src/services/communityService.ts | 52 ++ package-lock.json | 478 +++++++++++++++++- package.json | 8 +- server/app.ts | 3 +- server/controllers/community.controller.ts | 7 + server/package.json | 4 +- server/services/community.service.ts | 9 +- 10 files changed, 721 insertions(+), 10 deletions(-) create mode 100644 client/src/components/main/newCommunity/index.tsx create mode 100644 client/src/hooks/useNewCommunity.ts create mode 100644 client/src/services/communityService.ts diff --git a/client/src/components/main/newCommunity/index.tsx b/client/src/components/main/newCommunity/index.tsx new file mode 100644 index 0000000..14a9e44 --- /dev/null +++ b/client/src/components/main/newCommunity/index.tsx @@ -0,0 +1,59 @@ +import useNewCommunity from '../../../hooks/useNewCommunity'; + +const NewCommunityPage = () => { + const { + name, + setName, + about, + setAbout, + rules, + setRules, + nameErr, + aboutErr, + rulesErr, + postCommunity, + err, + } = useNewCommunity(); + + const isNameErr = nameErr !== ''; + const isAboutErr = aboutErr !== ''; + const isRulesErr = rulesErr !== ''; + return ( +
+ setName(e.target.value)} + onError={isNameErr} + helperText={nameErr} + label='Enter Community Name' + /> + setAbout(e.target.value)} + error={isAboutErr} + helperText={aboutErr} + label="Enter Community 'About' Text" + /> + setRules(e.target.value)} + error={isRulesErr} + helperText={rulesErr} + label='Enter Community Rules' + /> + + + ); +}; + +export default NewCommunityPage; diff --git a/client/src/components/main/sideBarNav/index.tsx b/client/src/components/main/sideBarNav/index.tsx index df44319..f3bf5bf 100644 --- a/client/src/components/main/sideBarNav/index.tsx +++ b/client/src/components/main/sideBarNav/index.tsx @@ -66,7 +66,7 @@ const SideBarNav = () => { Games `menu_button ${isActive ? 'menu_selected' : ''}`}> Communities diff --git a/client/src/hooks/useNewCommunity.ts b/client/src/hooks/useNewCommunity.ts new file mode 100644 index 0000000..d5ecfc0 --- /dev/null +++ b/client/src/hooks/useNewCommunity.ts @@ -0,0 +1,109 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { ObjectId } from 'mongodb'; +import useUserContext from './useUserContext'; +import { createCommunity } from '../services/communityService'; + +/** + * Custom hook to handle community submission and form validation + * @returns name - The current value of the name input. + * @returns about - The current value of the about input. + * @returns rules - The current value of the rules input. + * @returns nameErr - Error message for the name field, if any. + * @returns aboutErr - Error message for the about field, if any. + * @returns rulesErr - Error message for the rules field, if any. + * @returns postCommunity - Function to validate the form and submit a new community. + */ +const useNewCommunity = () => { + const navigate = useNavigate(); + const { user } = useUserContext(); + const [name, setName] = useState(''); + const [about, setAbout] = useState(''); + const [rules, setRules] = useState(''); + + const [err, setErr] = useState(''); + const [nameErr, setNameErr] = useState(''); + const [aboutErr, setAboutErr] = useState(''); + const [rulesErr, setRulesErr] = useState(''); + + /** + * Function to validate the form before submitting the community. + * @returns boolean - True if the form is valid, false otherwise. + */ + const validateForm = (): boolean => { + let isValid = true; + + if (!name) { + setNameErr('Name cannot be empty'); + isValid = false; + } else if (name.length > 100) { + setNameErr('Name cannot be more than 100 characters'); + isValid = false; + } else { + setNameErr(''); + } + + if (!about) { + setAboutErr('About cannot be empty'); + isValid = false; + } else if (about.length > 500) { + setAboutErr('About cannot be more than 500 characters'); + isValid = false; + } else { + setAboutErr(''); + } + + if (!rules) { + setRulesErr('Rules cannot be empty'); + isValid = false; + } else if (rules.length > 500) { + setRulesErr('Rules cannot be more than 500 characters'); + isValid = false; + } else { + setRulesErr(''); + } + + return isValid; + }; + + /** + * Function to submit the new community + */ + const postCommunity = async () => { + if (!validateForm()) return; + + const community = { + name, + about, + rules, + members: [user.username], + createdBy: user.username, + groupChatId: new ObjectId(), + }; + + const response = await createCommunity(community); + + if ('error' in response) { + setErr('Could not create community'); + return; + } + + navigate(`/community/${response._id}`); + }; + + return { + name, + setName, + about, + setAbout, + rules, + setRules, + nameErr, + aboutErr, + rulesErr, + postCommunity, + err, + }; +}; + +export default useNewCommunity; diff --git a/client/src/services/communityService.ts b/client/src/services/communityService.ts new file mode 100644 index 0000000..9d4f4e5 --- /dev/null +++ b/client/src/services/communityService.ts @@ -0,0 +1,52 @@ +import axios from 'axios'; +import { DatabaseCommunity, Community } from '../types/types'; +import api from './config'; + +const COMMUNITY_API_URL = `${process.env.REACT_APP_SERVER_URL}/community`; + +/** + * Function to get communities + * + * @throws Error if there is an issue fetching communities. + */ +const getCommunities = async (): Promise => { + const res = await api.get(`${COMMUNITY_API_URL}/getCommunities`); + if (res.status !== 200) { + throw new Error('Error when fetching communities'); + } + return res.data; +}; + +/** + * Function to get a community by its ID + * @param id The ID of the community to fetch + * @throws Error if there is an issue fetching the community. + */ +const getCommunityById = async (id: string): Promise => { + const res = await api.get(`${COMMUNITY_API_URL}/getCommunity/${id}`); + if (res.status !== 200) { + throw new Error('Error when fetching community'); + } + return res.data; +}; + +/** + * Sends a POST request to create a new community. + * @param community - The community object to create. + * @returns {Promise} The newly created community object. + * @throws {Error} If an error occurs during the creation process. + */ +const createCommunity = async (community: Community): Promise => { + try { + const res = await api.post(`${COMMUNITY_API_URL}/create`, { community }); + return res.data; + } catch (error) { + if (axios.isAxiosError(error) && error.response) { + throw new Error(`Error while creating community: ${error.response.data}`); + } else { + throw new Error('Error while creating community'); + } + } +}; + +export { getCommunities, getCommunityById, createCommunity }; diff --git a/package-lock.json b/package-lock.json index 99585ab..a054575 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,13 @@ "client", "server", "shared" - ] + ], + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@fontsource/roboto": "^5.2.5", + "@mui/material": "^6.4.7" + } }, "client": { "name": "@fake-stack-overflow/client", @@ -2498,6 +2504,167 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT" }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -2583,6 +2750,15 @@ "resolved": "shared", "link": true }, + "node_modules/@fontsource/roboto": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.2.5.tgz", + "integrity": "sha512-70r2UZ0raqLn5W+sPeKhqlf8wGvUXFWlofaDlcbt/S3d06+17gXKr3VNqDODB0I1ASme3dGT5OJj9NABt7OTZQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3589,6 +3765,222 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@mui/core-downloads-tracker": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.7.tgz", + "integrity": "sha512-XjJrKFNt9zAKvcnoIIBquXyFyhfrHYuttqMsoDS7lM7VwufYG4fAPw4kINjBFg++fqXM2BNAuWR9J7XVIuKIKg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.7.tgz", + "integrity": "sha512-K65StXUeGAtFJ4ikvHKtmDCO5Ab7g0FZUu2J5VpoKD+O6Y3CjLYzRi+TMlI3kaL4CL158+FccMoOd/eaddmeRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.4.7", + "@mui/system": "^6.4.7", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.6", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^6.4.7", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "license": "MIT" + }, + "node_modules/@mui/private-theming": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.6.tgz", + "integrity": "sha512-T5FxdPzCELuOrhpA2g4Pi6241HAxRwZudzAuL9vBvniuB5YU82HCmrARw32AuCiyTfWzbrYGGpZ4zyeqqp9RvQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.4.6", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.6.tgz", + "integrity": "sha512-vSWYc9ZLX46be5gP+FCzWVn5rvDr4cXC5JBZwSIkYk9xbC7GeV+0kCvB8Q6XLFQJy+a62bbqtmdwS4Ghi9NBlQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.7.tgz", + "integrity": "sha512-7wwc4++Ak6tGIooEVA9AY7FhH2p9fvBMORT4vNLMAysH3Yus/9B9RYMbrn3ANgsOyvT3Z7nE+SP8/+3FimQmcg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.4.6", + "@mui/styled-engine": "^6.4.6", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.6", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.21", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", + "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.6.tgz", + "integrity": "sha512-43nZeE1pJF2anGafNydUcYFPtHwAqiBiauRtaMvurdrZI3YrUjHkAu43RBsxef7OFtJMXGiHFvq43kb7lig0sA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "license": "MIT" + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3726,6 +4118,16 @@ } } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@remix-run/router": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz", @@ -4716,7 +5118,6 @@ "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/q": { @@ -4741,7 +5142,6 @@ "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -4758,6 +5158,15 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -6907,6 +7316,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -7778,7 +8196,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -8259,6 +8676,16 @@ "utila": "~0.4" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -10444,6 +10871,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -11203,6 +11636,21 @@ "node": ">=8" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -22100,6 +22548,22 @@ "node": ">=10" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -23758,6 +24222,12 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", diff --git a/package.json b/package.json index 68a2152..5fe70b9 100644 --- a/package.json +++ b/package.json @@ -6,5 +6,11 @@ "client", "server", "shared" - ] + ], + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@fontsource/roboto": "^5.2.5", + "@mui/material": "^6.4.7" + } } diff --git a/server/app.ts b/server/app.ts index 573efba..d97d24a 100644 --- a/server/app.ts +++ b/server/app.ts @@ -18,7 +18,7 @@ import userController from './controllers/user.controller'; import messageController from './controllers/message.controller'; import chatController from './controllers/chat.controller'; import gameController from './controllers/game.controller'; - +import communityController from './controllers/community.controller'; dotenv.config(); const MONGO_URL = `${process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017'}/fake_so`; @@ -82,6 +82,7 @@ app.use('/messaging', messageController(socket)); app.use('/user', userController(socket)); app.use('/chat', chatController(socket)); app.use('/games', gameController(socket)); +app.use('/community', communityController(socket)); // Export the app instance export { app, server, startServer }; diff --git a/server/controllers/community.controller.ts b/server/controllers/community.controller.ts index 5a16f3f..b310d76 100644 --- a/server/controllers/community.controller.ts +++ b/server/controllers/community.controller.ts @@ -65,4 +65,11 @@ const communityController = (socket: FakeSOSocket) => { res.status(200).json(communities); }; + + router.post('/create', createCommunity); + router.get('/getAll', getCommunities); + + return router; }; + +export default communityController; diff --git a/server/package.json b/server/package.json index acc9c09..f7cff04 100644 --- a/server/package.json +++ b/server/package.json @@ -2,6 +2,7 @@ "name": "@fake-stack-overflow/server", "version": "1.0.0", "dependencies": { + "@fake-stack-overflow/shared": "^1.0.0", "cors": "^2.8.5", "express": "^4.21.2", "mongodb": "6.12.0", @@ -9,8 +10,7 @@ "nanoid": "^3.3.8", "npm": "^11.0.0", "socket.io": "^4.8.1", - "socket.io-client": "^4.8.1", - "@fake-stack-overflow/shared": "^1.0.0" + "socket.io-client": "^4.8.1" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", diff --git a/server/services/community.service.ts b/server/services/community.service.ts index 57e2744..af31f2c 100644 --- a/server/services/community.service.ts +++ b/server/services/community.service.ts @@ -1,4 +1,5 @@ import CommunityModel from '../models/community.model'; +import ChatModel from '../models/chat.model'; import { Community, CommunityResponse, @@ -7,12 +8,18 @@ import { } from '../types/types'; /** - * Saves a new community to the database. + * Saves a new community to the database (including creating the group chat). * @param communityPayload - The community object to save. * @returns {Promise} - The saved community or an error message. */ export const saveCommunity = async (communityPayload: Community): Promise => { try { + const chat = await ChatModel.create({ messages: [] }); + if (!chat) { + throw new Error('Error creating chat'); + } + + communityPayload.groupChatId = chat._id; const result = await CommunityModel.create(communityPayload); if (!result) { throw new Error('Error saving community'); From a6cfbe36242becac4089f407a05b9d140013efa1 Mon Sep 17 00:00:00 2001 From: K-maker411 Date: Wed, 5 Mar 2025 17:18:29 -0600 Subject: [PATCH 007/323] Application works fine if i comment out newCommunity index.tsx, trying to fix --- .../components/main/newCommunity/index.tsx | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/client/src/components/main/newCommunity/index.tsx b/client/src/components/main/newCommunity/index.tsx index 14a9e44..08df18f 100644 --- a/client/src/components/main/newCommunity/index.tsx +++ b/client/src/components/main/newCommunity/index.tsx @@ -1,59 +1,59 @@ -import useNewCommunity from '../../../hooks/useNewCommunity'; +// import useNewCommunity from '../../../hooks/useNewCommunity'; -const NewCommunityPage = () => { - const { - name, - setName, - about, - setAbout, - rules, - setRules, - nameErr, - aboutErr, - rulesErr, - postCommunity, - err, - } = useNewCommunity(); +// const NewCommunityPage = () => { +// const { +// name, +// setName, +// about, +// setAbout, +// rules, +// setRules, +// nameErr, +// aboutErr, +// rulesErr, +// postCommunity, +// err, +// } = useNewCommunity(); - const isNameErr = nameErr !== ''; - const isAboutErr = aboutErr !== ''; - const isRulesErr = rulesErr !== ''; - return ( -
- setName(e.target.value)} - onError={isNameErr} - helperText={nameErr} - label='Enter Community Name' - /> - setAbout(e.target.value)} - error={isAboutErr} - helperText={aboutErr} - label="Enter Community 'About' Text" - /> - setRules(e.target.value)} - error={isRulesErr} - helperText={rulesErr} - label='Enter Community Rules' - /> - - - ); -}; +// const isNameErr = nameErr !== ''; +// const isAboutErr = aboutErr !== ''; +// const isRulesErr = rulesErr !== ''; +// return ( +//
+// setName(e.target.value)} +// onError={isNameErr} +// helperText={nameErr} +// label='Enter Community Name' +// /> +// setAbout(e.target.value)} +// error={isAboutErr} +// helperText={aboutErr} +// label="Enter Community 'About' Text" +// /> +// setRules(e.target.value)} +// error={isRulesErr} +// helperText={rulesErr} +// label='Enter Community Rules' +// /> +// +// +// ); +// }; -export default NewCommunityPage; +// export default NewCommunityPage; From 4999143083d5f26a97e345d21f13b418845974d2 Mon Sep 17 00:00:00 2001 From: K-maker411 Date: Fri, 7 Mar 2025 19:30:13 -0600 Subject: [PATCH 008/323] Creating community form works, temporary routing, will add actual page soon --- client/src/components/fakestackoverflow.tsx | 3 + .../components/main/communityPage/index.tsx | 60 +++++++++ .../components/main/newCommunity/index.tsx | 114 +++++++++--------- .../src/components/main/sideBarNav/index.tsx | 2 +- client/src/hooks/useCommunityMessagingPage.ts | 90 ++++++++++++++ client/src/hooks/useCommunityPage.ts | 113 +++++++++++++++++ client/src/hooks/useNewCommunity.ts | 3 +- client/src/services/communityService.ts | 2 +- server/controllers/community.controller.ts | 13 ++ shared/types/community.d.ts | 2 +- 10 files changed, 341 insertions(+), 61 deletions(-) create mode 100644 client/src/components/main/communityPage/index.tsx create mode 100644 client/src/hooks/useCommunityMessagingPage.ts create mode 100644 client/src/hooks/useCommunityPage.ts diff --git a/client/src/components/fakestackoverflow.tsx b/client/src/components/fakestackoverflow.tsx index 8701b23..ac2ade4 100644 --- a/client/src/components/fakestackoverflow.tsx +++ b/client/src/components/fakestackoverflow.tsx @@ -17,6 +17,7 @@ import UsersListPage from './main/usersListPage'; import ProfileSettings from './profileSettings'; import AllGamesPage from './main/games/allGamesPage'; import GamePage from './main/games/gamePage'; +import NewCommunityPage from './main/newCommunity'; const ProtectedRoute = ({ user, @@ -66,6 +67,8 @@ const FakeStackOverflow = ({ socket }: { socket: FakeSOSocket | null }) => { } /> } /> } /> + } /> + {/* TODO - add /community/:communityID route here */} } diff --git a/client/src/components/main/communityPage/index.tsx b/client/src/components/main/communityPage/index.tsx new file mode 100644 index 0000000..4a1295f --- /dev/null +++ b/client/src/components/main/communityPage/index.tsx @@ -0,0 +1,60 @@ +// filepath: /Users/kaushikbalantrapu/Documents/Year 3/CS 4530/spring25-team-project-spring25-project-group-212/client/src/components/main/communityPage/index.tsx +import React from 'react'; +import useCommunityMessagingPage from '../../../hooks/useCommunityMessagingPage'; +import QuestionPage from '../questionPage'; +import './index.css'; + +const CommunityPage = () => { + const { + messages, + newMessage, + setNewMessage, + handleSendMessage, + error: messageError, + } = useCommunityMessagingPage(community?.groupChatId); + + if (loading) { + return
Loading...
; + } + + if (error) { + return
{error}
; + } + + if (!community) { + return
Community not found
; + } + + return ( +
+

{community.name}

+

{community.about}

+
+
+ +
+
+
+ {messages.map((msg, index) => ( +
+ {msg.msgFrom}: {msg.msg} +
+ ))} +
+
+ setNewMessage(e.target.value)} + placeholder='Type a message...' + /> + + {messageError &&
{messageError}
} +
+
+
+
+ ); +}; + +export default CommunityPage; \ No newline at end of file diff --git a/client/src/components/main/newCommunity/index.tsx b/client/src/components/main/newCommunity/index.tsx index 08df18f..7c3a96d 100644 --- a/client/src/components/main/newCommunity/index.tsx +++ b/client/src/components/main/newCommunity/index.tsx @@ -1,59 +1,61 @@ -// import useNewCommunity from '../../../hooks/useNewCommunity'; +import useNewCommunity from '../../../hooks/useNewCommunity'; +import Form from '../baseComponents/form'; +import Input from '../baseComponents/input'; +import TextArea from '../baseComponents/textarea'; -// const NewCommunityPage = () => { -// const { -// name, -// setName, -// about, -// setAbout, -// rules, -// setRules, -// nameErr, -// aboutErr, -// rulesErr, -// postCommunity, -// err, -// } = useNewCommunity(); +const NewCommunityPage = () => { + const { + name, + setName, + about, + setAbout, + rules, + setRules, + nameErr, + aboutErr, + rulesErr, + postCommunity, + err, + } = useNewCommunity(); -// const isNameErr = nameErr !== ''; -// const isAboutErr = aboutErr !== ''; -// const isRulesErr = rulesErr !== ''; -// return ( -//
-// setName(e.target.value)} -// onError={isNameErr} -// helperText={nameErr} -// label='Enter Community Name' -// /> -// setAbout(e.target.value)} -// error={isAboutErr} -// helperText={aboutErr} -// label="Enter Community 'About' Text" -// /> -// setRules(e.target.value)} -// error={isRulesErr} -// helperText={rulesErr} -// label='Enter Community Rules' -// /> -// -// -// ); -// }; + return ( +
+ +