├── .eslintrc.json
├── .github
└── workflows
│ └── webpack.yml
├── .gitignore
├── README.md
├── index.html
├── index.js
├── package-lock.json
├── package.json
├── server
├── controllers
│ ├── UserControllers.js
│ └── oauthController.js
├── models
│ └── UserModel.js
├── routes
│ ├── cohort.js
│ ├── oauthRouter.js
│ ├── organization.js
│ ├── resident.js
│ └── verifyRouter.js
└── server.js
├── src
├── App.jsx
├── App.scss
├── components
│ ├── HomePage.jsx
│ ├── LandingPage.jsx
│ ├── ResidentBox.jsx
│ ├── ResidentDetails.jsx
│ ├── SearchBar.jsx
│ └── SetCohort.jsx
├── containers
│ ├── CohortContainer.jsx
│ ├── HomeContainer.jsx
│ ├── MainContainer.jsx
│ ├── NavBarContainer.jsx
│ ├── OrganizationContainer.jsx
│ ├── ResidentsContainer.jsx
│ ├── ResidentsListContainer.jsx
│ ├── SearchContainer.jsx
│ └── UserContainer.jsx
├── index.html
└── index.js
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["**/test", "**/__tests__"],
4 | "env": {
5 | "node": true,
6 | "browser": true,
7 | "es2021": true
8 | },
9 | "plugins": ["react"],
10 | "extends": ["eslint:recommended", "plugin:react/recommended"],
11 | // "extends": "airbnb",
12 | "parserOptions": {
13 | "sourceType": "module",
14 | "ecmaFeatures": {
15 | "jsx": true
16 | }
17 | },
18 | "rules": {
19 | "indent": ["warn", 2],
20 | "no-unused-vars": ["off", { "vars": "local" }],
21 | "no-case-declarations": "off",
22 | "prefer-const": "warn",
23 | "quotes": ["warn", "single"],
24 | "react/prop-types": "off",
25 | "semi": ["warn", "always"],
26 | "space-infix-ops": "warn"
27 | },
28 | "settings": {
29 | "react": { "version": "detect"}
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.github/workflows/webpack.yml:
--------------------------------------------------------------------------------
1 | name: Codesmith Social Network Secrets
2 |
3 | on: pull
4 |
5 | jobs:
6 | jobs1:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - name: Use PG_URI_SECRET in code
11 | run: |
12 | echo ${{ secrets.PG_URI_SECRET }}
13 | env:
14 | PG_URI_SECURE: ${{ secrets.PG_URI_SECRET }}
15 |
16 | - name: Use LINKEDIN_CLIENT_SECRET
17 | run: |
18 | env:
19 | LINKEDIN_SECURE: ${{ secrets.LINKEDIN_CLIENT_SECRET }}
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Codesmith-Social-Network
2 | Social network application for current Codesmith residents and alumni
3 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CodeSmith Social (please change me!)
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import App from './src/App.jsx';
4 |
5 | // uncomment so that webpack can bundle styles
6 | // import styles from './App.scss';
7 |
8 | // required by react-bootstrap
9 | import 'bootstrap/dist/css/bootstrap.min.css';
10 |
11 | render(
12 | ,
13 | document.getElementById('root')
14 | );
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "y",
3 | "version": "1.0.0",
4 | "description": "Website for the DEER lab",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "cross-env NODE_ENV=production node server/server.js",
8 | "build": "cross-env NODE_ENV=production webpack",
9 | "dev": "concurrently \"cross-env NODE_ENV=development webpack-dev-server --progress --color\" \"nodemon ./server/server.js\"",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/nekrug/deer-lab-site.git"
15 | },
16 | "author": "Nick Krug",
17 | "license": "ISC",
18 | "dependencies": {
19 | "@popperjs/core": "^2.11.5",
20 | "axios": "^0.27.2",
21 | "bootstrap": "^5.2.0",
22 | "cookie-parser": "^1.4.6",
23 | "cors": "^2.8.5",
24 | "express": "^4.18.1",
25 | "express-fileupload": "^1.4.0",
26 | "node-fetch": "^2.6.7",
27 | "passport-oauth2": "^1.6.1",
28 | "pg": "^8.7.3",
29 | "react": "^16.8.0",
30 | "react-bootstrap": "^2.4.0",
31 | "react-dom": "^16.8.0",
32 | "react-hot-loader": "^4.13.0",
33 | "react-router": "^6.3.0",
34 | "react-router-bootstrap": "^0.26.2",
35 | "react-router-dom": "^6.3.0"
36 | },
37 | "devDependencies": {
38 | "@babel/core": "^7.18.9",
39 | "@babel/preset-env": "^7.1.0",
40 | "@babel/preset-react": "^7.0.0",
41 | "autoprefixer": "^10.4.7",
42 | "babel-loader": "^8.2.3",
43 | "concurrently": "^6.4.0",
44 | "cross-env": "^7.0.3",
45 | "css-loader": "^6.5.1",
46 | "eslint": "^7.17.0",
47 | "eslint-plugin-react": "^7.21.5",
48 | "html-webpack-plugin": "^5.5.0",
49 | "nodemon": "^2.0.19",
50 | "postcss-loader": "^7.0.1",
51 | "sass": "^1.54.0",
52 | "sass-loader": "^13.0.2",
53 | "style-loader": "^3.3.1",
54 | "webpack": "^5.64.1",
55 | "webpack-cli": "^4.9.1",
56 | "webpack-dev-server": "^4.5.0",
57 | "webpack-hot-middleware": "^2.24.3"
58 | },
59 | "nodemonConfig": {
60 | "ignore": [
61 | "server/data/*",
62 | "client/*"
63 | ]
64 | },
65 | "bugs": {
66 | "url": "https://github.com/nekrug/deer-lab-site/issues"
67 | },
68 | "homepage": "https://github.com/nekrug/deer-lab-site#readme"
69 | }
70 |
--------------------------------------------------------------------------------
/server/controllers/UserControllers.js:
--------------------------------------------------------------------------------
1 | const db = require('../models/UserModel');
2 | const { PG_URI } = require('../secrets');
3 |
4 | const userControllers = {};
5 |
6 | // Load list of all users when residents tab is clicked.
7 | userControllers.loadUsers = async (req, res, next) => {
8 | const text = 'SELECT * FROM residents ORDER BY name';
9 | console.log('Got to load Users');
10 | try {
11 | const usersLoad = await db.query(text);
12 | res.locals.usersLoad = usersLoad.rows;
13 | return next();
14 | } catch (error) {
15 | return next({ log: `userControllers.loadUsers error: ${error}`, message: 'Error found @ userControllers.loadUsers' });
16 | }
17 | };
18 |
19 | // Load list of all organizations when orgs tab is clicked.
20 | userControllers.loadOrgs = async (req, res, next) => {
21 | const text = 'SELECT DISTINCT organization FROM residents ORDER BY organization';
22 | try {
23 | const orgsLoad = await db.query(text);
24 | res.locals.orgsLoad = orgsLoad.rows;
25 | return next();
26 | } catch (error) {
27 | return next({ log: `userControllers.loadOrgs error: ${error}`, message: 'Error found @ userControllers.loadOrgs' });
28 | }
29 | };
30 |
31 | // Load list of all cohorts when cohorts tab is clicked.
32 | userControllers.loadCohorts = async (req, res, next) => {
33 | const text = 'SELECT DISTINCT cohort FROM residents ORDER BY cohort';
34 | try {
35 | const cohortsLoad = await db.query(text);
36 | res.locals.cohortsLoad = cohortsLoad.rows;
37 | return next();
38 | } catch (error) {
39 | return next({ log: `userControllers.loadCohorts error: ${error}`, message: 'Erorr found @ userControllers.loadCohorts' });
40 | }
41 | };
42 |
43 | // Loads user profile when user is clicked throughout tabs.
44 | userControllers.loadUserProfile = async (req, res, next) => {
45 | const { id } = req.params;
46 | const text = `SELECT * FROM residents WHERE id=${id}`;
47 | try {
48 | const profile = await db.query(text);
49 | res.locals.profile = profile.rows;
50 | return next();
51 | } catch (error) {
52 | return next({ log: `userControllers.loadUserProfile error: ${error}`, message: 'Erorr found @ userControllers.loadUserProfile' });
53 | }
54 | };
55 |
56 |
57 |
58 | //Controller to find user.
59 | //If req.query.query exists, we are trying to find a specific user
60 | //Otherwise return all users in table
61 | userControllers.findUserByName = async (req, res, next) => {
62 | const text = `SELECT * FROM residents
63 | WHERE LOWER(name) LIKE LOWER('%${req.body.name}%')
64 | or LOWER(cohort) LIKE LOWER('%${req.body.name}%')
65 | or LOWER(organization) LIKE LOWER('%${req.body.name}%')
66 | ORDER BY name`;
67 | try {
68 | const userFound = await db.query(text);
69 | res.locals.userFound = userFound.rows;
70 | return next();
71 | } catch (error) {
72 | return next({ log: `userControllers.findUser error: ${error}`, message: 'Erorr found @ userControllers.findUser' });
73 | }
74 | };
75 |
76 | //Controller to find user by Id
77 | userControllers.findUserById = async (req, res, next) => {
78 | console.log(req.body);
79 | const text = `SELECT * FROM residents WHERE id=${req.body.id}`;
80 | try {
81 | const userFound = await db.query(text);
82 | res.locals.userFound = userFound.rows[0];
83 | return next();
84 | } catch (error) {
85 | return next({ log: `userControllers.findUser error: ${error}`, message: 'Erorr found @ userControllers.findUser' });
86 | }
87 | };
88 |
89 | //Controller to find users that work at a specific organization
90 |
91 | userControllers.findUserByOrganization = async (req, res, next) => {
92 | console.log(req.body);
93 | const text = `SELECT * FROM residents WHERE LOWER(organization)=LOWER('${req.body.organization}')`;
94 |
95 | try {
96 | const usersFound = await db.query(text);
97 | res.locals.usersFound = usersFound.rows;
98 | return next();
99 | } catch (error) {
100 | return next({ log: `userControllers.findUserByOrganization error: ${error}`, message: 'Erorr found @ userControllers.findUserByOrganization' });
101 | }
102 | };
103 |
104 | //Controller to find users that work at a specific cohort
105 |
106 | userControllers.findUserByCohort = async (req, res, next) => {
107 | console.log(req.body.cohort);
108 | const text = `SELECT * FROM residents WHERE LOWER(cohort)=LOWER('${req.body.cohort}')`;
109 |
110 | try {
111 | const usersFound = await db.query(text);
112 | res.locals.usersFound = usersFound.rows;
113 | return next();
114 | } catch (error) {
115 | return next({ log: `userControllers.findUserByCohort error: ${error}`, message: 'Erorr found @ userControllers.findUserByCohort' });
116 | }
117 | };
118 |
119 | //Check to see if user already exists in Codesmith Social Network Database
120 | userControllers.verifyUserExists = async (req, res, next) => {
121 | //obtain email from prev res.locals.email stored during previous middleware function
122 | const email = res.locals.email;
123 | const text = 'SELECT id FROM residents WHERE email = $1';
124 |
125 | try {
126 | const idFound = await db.query(text, [email]);
127 | //if email exists: create property on res.locals to skip create user middleware
128 | if (idFound.rows.length) {
129 | console.log('We found an id',idFound.rows[0]);
130 | res.locals.shouldSkipCreateUser = true;
131 | res.cookie('userId', idFound.rows[0].id);
132 | } else {
133 | console.log('No such user exists. Creating one');
134 | res.locals.shouldSkipCreateUser = false;
135 | }
136 | return next();
137 | } catch (error) {
138 | return next({ log: `userControllers.verifyUserExists error: ${error}`, message: 'Erorr found @ userControllers.VerifyUserExists' });
139 |
140 | }
141 | };
142 |
143 | //create new User from either res.locals.newUser or req.body... Not sure from where yet.
144 | //@value ( res.locals.userCreated ) New user created in table residents
145 | userControllers.createUser = async (req, res, next) => {
146 | try {
147 | if(res.locals.shouldSkipCreateUser) return next();
148 | const {
149 | name,
150 | email,
151 | } = res.locals;
152 | const values = [name, '', '', '', '', '', email];
153 | const text = 'INSERT INTO residents (name, photo, cohort, organization, linkedin, message, email) VALUES($1, $2, $3, $4, $5, $6, $7)';
154 | await db.query(text, values);
155 | const userCreated = await db.query('SELECT id FROM residents ORDER BY id DESC LIMIT 1');
156 | console.log(userCreated.rows[0].id);
157 |
158 | res.cookie('userId', userCreated.rows[0].id);
159 |
160 | res.locals.userCreated = userCreated;
161 | return next();
162 | } catch (err) {
163 | return next({ log: `userControllers.createUser error: ${err}`, message: 'Erorr found @ userControllers.createUser' });
164 | }
165 | };
166 |
167 | //update user requiring @value ( req.body.id )
168 | //req.body must also have name, photo, cohort, organization and linkedin to be not undefined
169 | //@value ( res.locals.updateUser ) return updated user
170 | userControllers.updateUser = async (req, res, next) => {
171 | try {
172 | const {
173 | name,
174 | photo,
175 | cohort,
176 | organization,
177 | linkedin,
178 | email
179 | } = req.body.user;
180 | const values = [name, photo, cohort, organization, linkedin];
181 | const text = `UPDATE residents SET name='${name}', photo='${photo}', cohort='${cohort}', organization='${organization}', linkedin='${linkedin}', email='${email}' WHERE id='${req.body.id}'`;
182 | const updatedUser = await db.query(text);
183 | res.locals.updatedUser = updatedUser;
184 | return next();
185 | } catch (err) {
186 | return next({ log: `userControllers.updateUser error: ${err}`, message: 'Erorr found @ userControllers.updateUser' });
187 | }
188 | };
189 | // Register new user
190 | userControllers.registerUser = async (req, res, next) => {
191 | try {
192 | const {
193 | id,
194 | cohort,
195 | organization,
196 | linkedin
197 | } = req.body;
198 | const text = `UPDATE residents SET cohort='${cohort}', organization='${organization}', linkedin='${linkedin}' WHERE id='${id}'`;
199 | const registeredUser = await db.query(text);
200 | res.locals.registeredUser = registeredUser;
201 | return next();
202 | } catch (err) {
203 | return next({ log: `userControllers.registerUser error: ${err}`, message: 'Erorr found @ userControllers.registerUser' });
204 | }
205 | };
206 |
207 | //delete user requiring @value ( req.body.id )
208 | userControllers.deleteUser = async (req, res, next) => {
209 | try {
210 | const text = `DELETE FROM residents WHERE id=${req.body.id}`;
211 | const userDeleted = await db.query(text);
212 | res.locals.userDeleted = userDeleted;
213 |
214 | return next();
215 | } catch (err) {
216 | return next({ log: `userControllers.deleteUser error: ${err}`, message: 'Erorr found @ userControllers.deleteUser' });
217 | }
218 | };
219 |
220 | module.exports = userControllers;
--------------------------------------------------------------------------------
/server/controllers/oauthController.js:
--------------------------------------------------------------------------------
1 | const { CLIENT_SECRET } = require('../secrets.js');
2 | const db = require('../models/UserModel');
3 |
4 | const fetch = require('node-fetch');
5 | const { createProxy } = require('http-proxy');
6 | const CLIENT_ID = '78jexcndblghpj';
7 | const REDIRECT_URI = 'http%3A%2F%2Flocalhost%3A8080%2Flogin';
8 |
9 | const oauthController = {};
10 | // TODO: Refactor this route to use our NODE_ENV to point to localhost:3000 if production and 8080 if dev. Then switch localhost:3000 again once we get the site hosted.
11 | // TODO: Make actual error handling
12 | // TODO (stretch feature, to prevent CSRF attacks, which don't matter on our site, since a malicious actor can't do anything except mess with the user profile a bit): Generate unique state and store it in use cookies. https://auth0.com/docs/secure/attack-protection/state-parameters
13 | oauthController.exchangeCode = async (req, res, next) => {
14 | try {
15 | const authCode = req.query.code || req.cookies.linkedInAuthCode;
16 | const accessToken = await fetch(
17 | `https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code=${authCode}&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&redirect_uri=${REDIRECT_URI}`,
18 | {
19 | method: 'POST',
20 | headers: {
21 | 'Content-Type': 'application/x-www-form-urlencoded',
22 | }
23 | });
24 | const response = await accessToken.json();
25 | // console.log('Response: ', response);
26 | res.locals.accessToken = response.access_token;
27 | res.cookie('linkedInAuthCode', authCode);
28 | return next();
29 | }
30 | catch(err) {
31 | console.log(err);
32 | return next(err);
33 | }
34 | };
35 |
36 | oauthController.callMeAPI = async (req, res, next) => {
37 | try {
38 | const result = await fetch(
39 | 'https://api.linkedin.com/v2/me', {
40 | headers: {
41 | Authorization: 'Bearer ' + res.locals.accessToken
42 | }
43 | }
44 | );
45 | const parsedResult = await result.json();
46 | res.locals.name = parsedResult.localizedFirstName + ' ' + parsedResult.localizedLastName;
47 | // console.log('me API call result');
48 | // console.log(parsedResult);
49 | return next();
50 | }
51 | catch(err) {
52 | return next(err);
53 | }
54 | };
55 |
56 | oauthController.callEmailAPI = async (req, res, next) => {
57 | try {
58 | const result = await fetch(
59 | 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', {
60 | headers: {
61 | Authorization: 'Bearer ' + res.locals.accessToken
62 | }
63 | }
64 | );
65 | const parsedResult = await result.json();
66 | // console.log('email API call result');
67 | // console.log(parsedResult.elements[0]['handle~']);
68 | res.locals.email = parsedResult.elements[0]['handle~'].emailAddress;
69 | return next();
70 | }
71 | catch(err) {
72 | return next(err);
73 | }
74 | };
75 |
76 | oauthController.userComplete = async (req, res, next) => {
77 | const text = `SELECT cohort FROM residents WHERE id=${req.cookies.userId} AND cohort=''`;
78 | try {
79 | let complete = await db.query(text);
80 |
81 | console.log('Complete rows ',complete.rows);
82 | if (complete.rows.length === 0) complete = true;
83 | else complete = false;
84 | res.locals.complete = complete;
85 | return next();
86 | } catch (err) {
87 | return next({log: 'Error caight in userComplete' });
88 | }
89 | };
90 | // handle getting basic profile info
91 | // GET https://api.linkedin.com/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))
92 |
93 | module.exports = oauthController;
--------------------------------------------------------------------------------
/server/models/UserModel.js:
--------------------------------------------------------------------------------
1 | const { Pool } = require('pg');
2 | const { PG_URI } = require('../secrets');
3 |
4 | //Object with connectionString to our postgresURL
5 | const pool = new Pool({
6 | connectionString: PG_URI
7 | });
8 |
9 | //Export object with query method
10 | module.exports = {
11 | query: (text, params, callback) => {
12 | console.log('Query: ', text);
13 | return pool.query(text, params, callback);
14 | }
15 | }
16 |
17 |
18 |
19 |
20 |
21 | // Residents table in database
22 | // residents (
23 | // id serial PRIMARY KEY,
24 | // name varchar( 100 ) NOT NULL,
25 | // photo varchar( 150 ),
26 | // cohort varchar( 150 ) NOT NULL,
27 | // organization varchar( 150 ),
28 | // linkedin varchar( 150 ) NOT NULL
29 | // );
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | // MAYBE
44 | // case 'species':
45 | // const { classification, average_height, average_lifespan, language, homeworld } = details;
46 | // info = (
47 | //
48 | // - Classification: {classification}
49 | // - Average Height: {average_height}
50 | // - Average Lifespan: {average_lifespan}
51 | // - Language: {language}
52 | // - Homeworld: {homeworld}
53 | //
54 | // );
55 | // break;
56 | // case 'homeworld':
57 | // const { rotation_period, orbital_period, diameter, climate, gravity, terrain, surface_water, population } = details;
58 | // info = (
59 | //
60 | // - Rotation Period: {rotation_period}
61 | // - Orbital Period: {orbital_period}
62 | // - Diameter: {diameter}
63 | // - Climate: {climate}
64 | // - Gravity: {gravity}
65 | // - Terrain: {terrain}
66 | // - Surface Water: {surface_water}
67 | // - Population: {population}
68 | //
69 | // );
70 | // break;
71 | // case 'film':
72 | // const { episode_id, director, producer, release_date } = details;
73 | // info = (
74 | //
75 | // - Episode: {episode_id}
76 | // - Director {director}
77 | // - Producer: {producer}
78 | // - Release Date: {new Date(release_date).toDateString().slice(4)}
79 | //
80 | // );
81 | // break;
82 | // default:
83 | // info = (Unexpected modal type
);
84 | // >>>>>>> a5cbb72 (initial commit)
85 | // }
86 |
--------------------------------------------------------------------------------
/server/routes/cohort.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const userControllers = require('../controllers/UserControllers');
3 | const router = express.Router();
4 |
5 | router.get('/', userControllers.loadCohorts, (req, res) => {
6 | console.log('got to this endpoint');
7 | return res.status(200).json(res.locals.cohortsLoad);
8 | });
9 |
10 | //find residents by cohort
11 | router.post('/residents', userControllers.findUserByCohort, (req, res) => {
12 | return res.status(200).json(res.locals.usersFound);
13 | });
14 |
15 | module.exports = router;
--------------------------------------------------------------------------------
/server/routes/oauthRouter.js:
--------------------------------------------------------------------------------
1 | const { Router } = require('express');
2 | const { exchangeCode, callMeAPI, callEmailAPI, setAuthCodeCookie } = require('../controllers/oauthController');
3 | const { createUser, verifyUserExists } = require('../controllers/UserControllers');
4 | // TODO: (Nick) no idea if the below line is necessary. Test?
5 | // TODO: I don't know how to get to the homepage without React Router
6 | const { route } = require('../server');
7 |
8 | const router = Router();
9 |
10 | router.get('/',
11 | exchangeCode,
12 | callMeAPI,
13 | callEmailAPI,
14 | verifyUserExists,
15 | createUser,
16 | (req, res) => {
17 | return res.redirect('http://localhost:8080/');
18 | });
19 |
20 | module.exports = router;
--------------------------------------------------------------------------------
/server/routes/organization.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const userControllers = require('../controllers/UserControllers');
3 | const router = express.Router();
4 |
5 | router.get('/', userControllers.loadOrgs, (req, res) => {
6 | console.log('got to this endpoint');
7 | return res.status(200).json(res.locals.orgsLoad);
8 | });
9 |
10 | //find residents by organizaiton
11 | router.post('/residents', userControllers.findUserByOrganization, (req, res) => {
12 | return res.status(200).json(res.locals.usersFound);
13 | });
14 |
15 | module.exports = router;
--------------------------------------------------------------------------------
/server/routes/resident.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const userControllers = require('../controllers/UserControllers');
3 | const router = express.Router();
4 |
5 | router.get('/', userControllers.loadUsers, (req, res) => {
6 | console.log('got to this endpoint');
7 | return res.status(200).json(res.locals.usersLoad);
8 | });
9 |
10 | //Search resident by name
11 | router.post('/', userControllers.findUserByName, (req, res) => {
12 | return res.status(200).json(res.locals.userFound);
13 | });
14 |
15 | router.post('/register', userControllers.registerUser, (req, res) => {
16 | return res.status(200).json(res.locals.registeredUser);
17 | });
18 |
19 | router.post('/id', userControllers.findUserById, (req, res) => {
20 | return res.status(200).json(res.locals.userFound);
21 | });
22 |
23 | router.post('/update', userControllers.updateUser, (req, res) => {
24 | return res.status(200);
25 | });
26 |
27 | module.exports = router;
--------------------------------------------------------------------------------
/server/routes/verifyRouter.js:
--------------------------------------------------------------------------------
1 | const { Router } = require('express');
2 | const { route } = require('../server');
3 | const { exchangeCode, userComplete} = require('../controllers/oauthController');
4 |
5 | const router = Router();
6 |
7 | router.get('/',
8 | (req, res, next) => {
9 | console.log(req.cookies);
10 | return next()
11 | },
12 | exchangeCode,
13 | (req, res) => {
14 | res.status(200).send(true);
15 | }
16 | );
17 |
18 | router.get('/complete', userComplete, (req, res) => {
19 | return res.status(200).json(res.locals.complete);
20 | }
21 | );
22 |
23 | module.exports = router;
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 | const express = require('express');
4 | const cors = require('cors');
5 | // const https = require('https');
6 | const cookieParser = require('cookie-parser');
7 | const app = express();
8 | const PORT = 3000;
9 | const residentRouter = require('./routes/resident');
10 | const organizationRouter = require('./routes/organization');
11 | const cohortRouter = require('./routes/cohort');
12 | const oauthRouter = require('./routes/oauthRouter');
13 | const verifyRouter = require('./routes/verifyRouter');
14 |
15 | // const server = https.createServer({ key, cert }, app);
16 |
17 | app.use(express.json());
18 | app.use(express.urlencoded({ extended: true }));
19 | app.use(cookieParser());
20 | app.use(cors({origin: 'http://localhost:8080'}));
21 |
22 | app.use('/residents', residentRouter);
23 |
24 | app.use('/organizations', organizationRouter);
25 |
26 | app.use('/cohort', cohortRouter);
27 |
28 | app.use('/login', oauthRouter);
29 |
30 | app.use('/verifyuser', verifyRouter);
31 |
32 | // Once we have React router working, this will keep the page from breaking if you're not on the homepage.
33 | app.get('/*', (req, res) => {
34 | return res.status(200).redirect('/');
35 | });
36 |
37 | // catch-all route handler for any requests to an unknown route
38 | app.use('*', (req, res) => res.sendStatus(404));
39 |
40 |
41 |
42 | // global error handler
43 | app.use((err, req, res, next) => {
44 | const defaultErr = {
45 | log: 'Express error handler caught unknown middleware error',
46 | status: 400,
47 | message: { err: 'An error occurred' },
48 | };
49 |
50 | const errorObj = Object.assign(defaultErr, err);
51 | console.log(errorObj.log);
52 |
53 | res.status(errorObj.status).send(errorObj.message);
54 | });
55 |
56 | // start server
57 | app.listen(PORT, () => {
58 | console.log(`Server listening on port: ${PORT}`);
59 | });
60 |
61 | module.exports = app;
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import MainContainer from './containers/MainContainer.jsx';
3 |
4 | export default function App() {
5 | return (
6 |
7 | );
8 | }
9 |
--------------------------------------------------------------------------------
/src/App.scss:
--------------------------------------------------------------------------------
1 | body {
2 | color: rgb(145, 145, 145);
3 | }
4 |
5 | #codesmithImg {
6 | width: 27vw;
7 | height: auto;
8 | position: absolute;
9 | top: 0px;
10 | z-index: 1;
11 | }
12 |
13 | .MainContainer {
14 | height: 100vh;
15 | width: 100vw;
16 | background-color: rgb(212, 216, 220);
17 | }
18 |
19 | .LandingPage {
20 | width: 100vw;
21 | height: 100vh;
22 |
23 | background-color: #020C25;
24 |
25 | display: flex;
26 | flex-direction: column;
27 | justify-content: center;
28 | align-items:center;
29 | gap:5vw;
30 |
31 | margin-left: auto;
32 | margin-right: auto;
33 |
34 | padding: 20px;
35 |
36 |
37 |
38 | .LandingText {
39 | text-align: center;
40 | font-family: Verdana, Tahoma, sans-serif;
41 | font-size: 50px;
42 | color: whitesmoke;
43 | }
44 |
45 | .loginLink {
46 | text-align: center;
47 | font-family: Verdana, Tahoma, sans-serif;
48 | font-size: 30px;
49 | color: whitesmoke;
50 | }
51 |
52 | .LogInButton {
53 | cursor: pointer;
54 | width: 275px;
55 | height: 70px;
56 | background-image: url('https://i.stack.imgur.com/mKpeu.png');
57 | background-size: 265px;
58 | border-radius: 15px;
59 | border: 5px solid rgb(0, 126, 189);
60 | }
61 |
62 | .LogInButton:hover {
63 | border: 5px solid #019be8;
64 | box-shadow: -3px 4px 6px #00314a;
65 | transition: 0.3s;
66 | }
67 |
68 | .LogInButton:active {
69 | background-color: #ffbf00;
70 | }
71 | }
72 |
73 | .bigger {
74 | font-size: 250%;
75 | }
76 |
77 | .SetCohort {
78 | width: 100vw;
79 | height: 120vh;
80 | z-index: 1000;
81 |
82 | background-color: #020C25;
83 |
84 | display: flex;
85 | flex-direction: column;
86 | justify-content: center;
87 | align-items:center;
88 | gap:5vw;
89 |
90 | margin-left: auto;
91 | margin-right: auto;
92 | margin-top: 0vh;
93 |
94 | padding: 20px;
95 |
96 |
97 |
98 | .SetCohortText {
99 | text-align: center;
100 | font-family: Verdana, Tahoma, sans-serif;
101 | font-size: 25px;
102 | color: whitesmoke;
103 | width: 70vw;
104 | }
105 |
106 | .lineTitle {
107 | font-family: Verdana, Tahoma, sans-serif;
108 | font-size: 20px;
109 | color: whitesmoke;
110 | }
111 |
112 | #cohortSelect {
113 | padding: 6px;
114 | width: 8vw;
115 | height: 5vh;
116 | margin-bottom: 4vh;
117 | margin-right: 30px;
118 | border-radius: 8px;
119 | font-size: 15px !important;
120 | }
121 |
122 | #orgSelect {
123 | padding: 10px;
124 | width: 16vw;
125 | height: 5vh;
126 | margin-bottom: 4vh;
127 | margin-right: 30px;
128 | border-radius: 8px;
129 | font-size: 15px !important;
130 | }
131 |
132 | #linkedSelect {
133 | padding: 10px;
134 | width: 13.2vw;
135 | height: 5vh;
136 | margin-bottom: 4vh;
137 | margin-right: 30px;
138 | border-radius: 8px;
139 | font-size: 15px !important;
140 | }
141 |
142 | #cohortNumberSelect {
143 | padding: 10px;
144 | width: 6vw;
145 | height: 5vh;
146 | margin-bottom: 4vh;
147 | margin-right: 30px;
148 | border-radius: 8px;
149 | font-size: 15px !important;
150 | }
151 |
152 | .SetCohortInput {
153 | display: flex;
154 | flex-direction: column;
155 | justify-content: center;
156 | align-items: center;
157 | text-align: center;
158 |
159 | gap: 1vw;
160 |
161 | input {
162 | width: 10vw;
163 | }
164 |
165 | button {
166 | width: 15vw;
167 | height: 7vh;
168 | border-radius: 15px;
169 | color: whitesmoke;
170 | background-color: #008dd3;
171 | border: #008dd3;
172 | font-family: Verdana, Tahoma, sans-serif;
173 | font-weight: 500;
174 | }
175 | button:hover {
176 | background-color: #009fee;
177 | transition: 0.3s;
178 | }
179 |
180 | button:active {
181 | background-color: #0180c0;
182 | }
183 | }
184 | }
185 |
186 | .HomeContainerPage {
187 | display: flex;
188 | flex-direction: column;
189 | }
190 |
191 | .HomePage {
192 | font-size: 30px;
193 | font-family: Verdana, Tahoma, sans-serif;
194 | color: #00314a;
195 | }
196 |
197 | .DisplayBox {
198 | padding: 30px;
199 | text-align: center;
200 | color: #00314a;
201 | margin-top: 5vh;
202 | }
203 |
204 | .NavBar {
205 | width: 100%;
206 | height: 60px;
207 | top: 0;
208 | position: fixed;
209 | z-index: 1000;
210 |
211 | display: flex;
212 | flex-direction: row;
213 | align-items: center;
214 | align-content: center;
215 | justify-content: space-between;
216 |
217 | padding-top: 20px;
218 | padding-bottom: 20px;
219 | padding-left: 20px;
220 | padding-right: 20px;
221 |
222 | // background-color: #25355d;
223 | background-color: #203045;
224 | box-shadow: 0px 3px 1px #35373e;
225 |
226 | .NavItems {
227 | display: flex;
228 | flex-direction: row;
229 | gap: 15px;
230 | font-family: Verdana, Tahoma, sans-serif;
231 | font-size: 16px;
232 |
233 | .SearchBarButton {
234 | display: flex;
235 | flex-direction: row;
236 | justify-content: center;
237 | align-items: center;
238 | gap: 10px;
239 |
240 | input {
241 | border-style: none;
242 | border-radius: 10px;
243 | height: 30px;
244 | padding: 10px;
245 | width: 300px;
246 | }
247 |
248 | }
249 |
250 | button {
251 | // background-color: #25355d;
252 | height: 60px;
253 | width: max-content;
254 | min-width: 80px;
255 | background-color: #203045;
256 | color: rgb(221, 221, 221);
257 | border: none;
258 | font-family: Verdana, Tahoma, sans-serif;
259 | padding: 10px;
260 | }
261 | button:hover {
262 | color: white;
263 | background-color: #293d57;
264 | transition: 0.3s;
265 | }
266 | button:action {
267 | color: white;
268 | background-color: #213045;
269 | }
270 | }
271 | }
272 |
273 |
274 | .ResidentsPage {
275 | display: flex;
276 | flex-direction: column;
277 | text-align: left;
278 | font-size: 2.5em;
279 | font-family: Verdana, Tahoma, sans-serif;
280 |
281 | .ResidentsByCohort {
282 | display: grid;
283 | grid-template-columns: repeat(6, 1fr);
284 | gap: 50px;
285 | justify-items: center;
286 |
287 | .ResidentBox {
288 | height: 13vw;
289 | width: 13vw;
290 | min-height: 240px;
291 | min-width: 240px;
292 |
293 | display: flex;
294 | flex-direction: column;
295 | justify-content: center;
296 | align-items: center;
297 |
298 |
299 | border-style: none;
300 | border-radius: 10px;
301 |
302 | color: rgb(48, 48, 48);
303 | background-color: rgb(230, 230, 230);
304 | box-shadow: 0px 10px 10px rgb(170, 175, 185);
305 | }
306 | .ResidentBox:hover {
307 | background-color: rgb(243, 243, 243);
308 | transition: 0.3s;
309 | }
310 | .ResidentBox:action {
311 | background-color: rgb(230, 230, 230);
312 | }
313 | }
314 | }
315 |
316 | .ResidentsProfile {
317 | text-align: left;
318 | font-size: 2.5em;
319 | font-family: Verdana, Tahoma, sans-serif;
320 |
321 | .ResidentBox {
322 | height: 13vw;
323 | width: 13vw;
324 | min-height: 240px;
325 | min-width: 240px;
326 |
327 | display: flex;
328 | flex-direction: column;
329 | justify-content: center;
330 | align-items: center;
331 |
332 |
333 | border-style: none;
334 | border-radius: 10px;
335 |
336 | color: rgb(48, 48, 48);
337 | background-color: rgb(230, 230, 230);
338 | box-shadow: 0px 10px 10px rgb(170, 175, 185);
339 | }
340 | }
341 |
342 | .UserContainer {
343 | display: flex;
344 | flex-direction: row;
345 | justify-content: center;
346 | gap: 30px;
347 |
348 | .ResidentDetails {
349 | height: 85vh;
350 | width: 60vw;
351 | min-height: 240px;
352 | min-width: 240px;
353 |
354 | display: flex;
355 | flex-direction: column;
356 | justify-content: center;
357 | align-items: center;
358 |
359 |
360 | border-style: none;
361 | border-radius: 10px;
362 |
363 | color: rgb(48, 48, 48);
364 | background-color: rgb(230, 230, 230);
365 | box-shadow: 0px 10px 10px rgb(170, 175, 185);
366 | }
367 | }
368 |
369 |
370 | .ProfilePicture {
371 | width: 8vw;
372 | height: 8vw;
373 | object-fit: cover;
374 | border-radius: 50%;
375 | border: 1px solid #203045;
376 | }
377 |
378 | .EmptyPhoto {
379 | width: 8vw;
380 | height: 8vw;
381 | object-fit: cover;
382 | border-radius: 50%;
383 | border: 1px solid #203045;
384 | display: grid;
385 | justify-content: center;
386 | align-items: center;
387 | font-size: 1.1rem;
388 | background-color: #00314a;
389 | color: whitesmoke;
390 | font-weight: 600;
391 | }
392 |
393 | .cardInfo {
394 | font-size: 1.2rem;
395 | font-family: Verdana, Tahoma, sans-serif;
396 | font-weight: 600;
397 | color: #013b59;
398 | letter-spacing: .07em;
399 | margin-top: 10px;
400 | margin-bottom: 6px;
401 | }
402 |
403 | .userInfo {
404 | font-size: .9rem;
405 | color: #454751;
406 | font-family: Verdana, Tahoma, sans-serif;
407 | }
408 |
409 | .message {
410 | color: rgb(31, 31, 31);
411 | font-family: Verdana, Tahoma, sans-serif;
412 | }
413 |
414 | .cohortTitle {
415 | font-family: Verdana, Tahoma, sans-serif;
416 | font-size: 3em;
417 | margin-top: 20px;
418 | font-weight: 500;
419 | letter-spacing: .02em;
420 | text-align: center;
421 | width: 60vw;
422 | display: flex;
423 | justify-content: center;
424 | align-items: center;
425 | }
426 |
427 | .cohortButton {
428 | height: 8vw;
429 | width: 8vw;
430 | min-height: 100px;
431 | min-width: 100px;
432 |
433 | display: grid;
434 |
435 | justify-content: center;
436 | align-items: center;
437 | grid-template-columns: repeat(6, 1fr);
438 | gap: 40px;
439 |
440 | border-style: none;
441 | border-radius: 10px;
442 |
443 | font-family: Verdana, Tahoma, sans-serif;
444 | font-size: 1.4em;
445 | color: rgb(48, 48, 48);
446 | background-color: rgb(230, 230, 230);
447 | box-shadow: 0px 10px 10px rgb(170, 175, 185);
448 | }
449 | .cohortButton:hover {
450 | color: white;
451 | background-color: #034f75;
452 | border: #034f75;
453 | transition: 0.3s;
454 | }
455 | .cohortButton:action {
456 | color: white;
457 | background-color: #213045;
458 | }
459 |
460 | .CohortPage {
461 | display: flex;
462 | flex-direction: column;
463 | justify-content: center;
464 | align-items: center;
465 |
466 | .Cohortbox {
467 | display: flex;
468 | grid-template-columns: repeat(6, 1fr);
469 | gap: 50px;
470 | justify-items: center;
471 | margin-top: 80px;
472 |
473 | .cohortButton {
474 | height: 10vw;
475 | width: 10vw;
476 | min-height: 150px;
477 | min-width: 150px;
478 |
479 | display: flex;
480 | flex-direction: column;
481 | justify-content: center;
482 | align-items: center;
483 |
484 |
485 | border-style: none;
486 | border-radius: 10px;
487 |
488 | font-size: 25px;
489 | font-weight: 500;
490 | color: rgb(48, 48, 48);
491 | background-color: rgb(230, 230, 230);
492 | box-shadow: 0px 10px 10px rgb(170, 175, 185);
493 | }
494 | .cohortButton:hover {
495 | color: white;
496 | background-color: #034f75;
497 | border: #034f75;
498 | transition: 0.3s;
499 | }
500 | .cohortButton:action {
501 | color: white;
502 | background-color: #213045;
503 | }
504 | }
505 | }
506 |
507 | .BackButton {
508 | width: 7vw;
509 | height: 60px;
510 | border: 1px solid #034f75;
511 | border-radius: 20px;
512 | background-color: #034f75;
513 | color: whitesmoke;
514 | font-family: Verdana, Tahoma, sans-serif;
515 | z-index: 1000;
516 | font-size: 1.5rem;
517 | margin-top: 20px;
518 | }
519 | .BackButton:hover {
520 | color: white;
521 | background-color: rgb(6, 109, 161);
522 | border: rgb(6, 109, 161);
523 | transition: 0.3s;
524 | }
525 | .BackButton:action {
526 | color: white;
527 | background-color: #213045;
528 | }
--------------------------------------------------------------------------------
/src/components/HomePage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export const HomePage = (props) => {
4 |
5 | return (
6 |
7 | Welcome to
Codesmith Social
Connect with your cohort mates
on the implicit zero!
8 |
9 | );
10 | };
--------------------------------------------------------------------------------
/src/components/LandingPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | const CLIENT_ID = '78jexcndblghpj';
4 | const REDIRECT_URI = 'http%3A%2F%2Flocalhost%3A8080%2Flogin';
5 | const SCOPE = 'r_liteprofile r_emailaddress';
6 |
7 | export const LandingPage = (props) => {
8 |
9 | //Redirect user to LinkedIn OAuth then if successful set authenticated to true
10 | async function logIn() {
11 | //OAUTH REQUEST BELOW
12 | //const result = await fetch(To Server) => Server makes a request to LinkedInOAuth
13 | //Store acces token in server
14 |
15 | //result.isInSystem === true =>
16 | props.changeAuthenticated(true);
17 | }
18 |
19 | return (
20 |
21 |

22 |
Welcome to the
Codesmith Resident's & Alumni Portal
23 |
24 |
25 | );
26 | };
--------------------------------------------------------------------------------
/src/components/ResidentBox.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export const ResidentBox = (props) => {
4 | //
5 |
6 | return(
7 |
17 | );
18 | };
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/ResidentDetails.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useState } from 'react';
2 |
3 | export const ResidentDetails = (props) => {
4 |
5 | const elems = [];
6 | console.log(props.user);
7 | for (const key in props.user) {
8 | if (key !== 'id') {
9 | if (props.user[key] === '') {
10 | if (key === 'name') {
11 | elems.push( props.changeInput(e, key)}/>);
12 | } else if (key === 'email') {
13 | elems.push( props.changeInput(e, key)}/>);
14 | } else if (key === 'linkedin') {
15 | elems.push( props.changeInput(e, key)}/>);
16 | } else if (key === 'photo') {
17 | elems.push( props.changeInput(e, key)}/>);
18 | } else if (key === 'organization') {
19 | elems.push( props.changeInput(e, key)}/>);
20 | } else if (key === 'message') {
21 | elems.push( props.changeInput(e, key)}/>);
22 | }
23 | } else {
24 | elems.push( props.changeInput(e, key)}/>);
25 | }
26 | }
27 | }
28 |
29 | return (
30 |
31 | {elems}
32 |
33 |
39 |
40 | );
41 | };
--------------------------------------------------------------------------------
/src/components/SearchBar.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useState } from 'react';
2 |
3 | export const SearchBar = (props) => {
4 | const [textValue, setTextValue] = useState('');
5 | //Fetch for search value result
6 | function clickFunction() {
7 | changeValues();
8 | }
9 |
10 | function enterFunction(e) {
11 | if (e.key === 'Enter') {
12 | changeValues();
13 | }
14 | }
15 |
16 | function changeValues() {
17 | props.setActive('Search');
18 | props.setSearchValue(textValue);
19 | setTextValue('');
20 | }
21 |
22 | return (
23 |
24 | setTextValue(e.target.value)}>
25 |
26 |
27 | );
28 | };
--------------------------------------------------------------------------------
/src/components/SetCohort.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useState } from 'react';
2 | export const SetCohort = (props) => {
3 | const [cohortValue, changeCohort] = useState('FTRI');
4 | const [numberValue, changeNumber] = useState(1);
5 | const [orgValue, changeOrg] = useState();
6 | const [linkedinUrl, changeUrl] = useState();
7 | const cohortNums = [];
8 |
9 | const getCookie = (cookie) => {
10 | return document.cookie
11 | .split('; ')
12 | .find(row => row.startsWith(cookie + '='))
13 | ?.split('=')[1];
14 | };
15 |
16 |
17 | //Set Cohort and make a PATCH/PUT request to change user's cohort
18 | function cohortSet() {
19 | //FETCH REQUEST BELOW
20 | fetch('http://localhost:8080/residents/register', {
21 | method: 'POST',
22 | headers: { 'Content-Type': 'application/json' },
23 | body: JSON.stringify({ id: getCookie('userId'), cohort: `${cohortValue} ${numberValue}`, organization: orgValue, linkedin: linkedinUrl }),
24 | })
25 | .then(data => data.json())
26 | .then(result => {
27 | console.log(result);
28 | props.setCohort(true);
29 | }, []);
30 | //Change cohortIsSet to true if successful
31 | }
32 |
33 | for (let i = 1; i <= 40; i++) {
34 | cohortNums.push();
35 | }
36 |
37 | return (
38 |
39 |

40 |
Welcome!
It seems you're new here.
Please let us know just a little bit more info about you.
41 |
42 | {/*
changeInput(e.target.value)}> */}
43 |
44 |
Cohort:
45 |
51 |
54 |
Organization:
55 |
changeOrg(e.target.value)}>
56 |
57 |
58 |
LinkedIn Profile URL:
59 |
changeUrl(e.target.value)}>
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | );
69 | };
--------------------------------------------------------------------------------
/src/containers/CohortContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useEffect, useState } from 'react';
2 | import { ResidentsListContainer } from './ResidentsListContainer';
3 |
4 | export const CohortContainer = (props) => {
5 | const [cohortList, setCohortList] = useState([]);
6 | const [active, setActive] = useState(false);
7 | const [residentList, setResidentList] = useState([]);
8 |
9 | useEffect(() => {
10 | fetch('http://localhost:8080/cohort')
11 | .then(res => res.json())
12 | .then(res => setCohortList(res));
13 | }, [active]);
14 |
15 | async function findResidents(cohort) {
16 | console.log(cohort);
17 | fetch('http://localhost:8080/cohort/residents', {
18 | method: 'POST',
19 | headers: { 'Content-Type': 'application/json' },
20 | body: JSON.stringify({ cohort: cohort })
21 | })
22 | .then(res => res.json())
23 | .then(res => setResidentList(res))
24 | .then(setActive(true));
25 | }
26 |
27 | const cohorts = [];
28 |
29 | for (let i = 0; i < cohortList.length; i++) {
30 | cohorts.push();
31 | }
32 |
33 | return (
34 |
35 |
Search by Cohort
36 | {
37 | !active
38 | ?
39 |
40 | {cohorts}
41 |
42 | :
43 |
44 |
45 |
46 |
47 |
48 |
49 | }
50 |
51 | );
52 | };
--------------------------------------------------------------------------------
/src/containers/HomeContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useEffect, useState } from 'react';
2 | import { NavBar } from './NavBarContainer.jsx';
3 | import { CohortContainer } from './CohortContainer.jsx';
4 | import { OrganizationContainer } from './OrganizationContainer.jsx';
5 | import { SearchContainer } from './SearchContainer.jsx';
6 | import { HomePage } from '../components/HomePage.jsx';
7 | import { ResidentsContainer } from './ResidentsContainer.jsx';
8 | import { UserContainer } from './UserContainer.jsx';
9 |
10 | export const HomeContainer = (props) => {
11 | const [active, setActive] = useState('Home');
12 | const [searchValue, setSearchValue] = useState('');
13 | const [userId, setUserId] = useState(1);
14 | let elem;
15 |
16 | if (active === 'Home') {
17 | elem =
;
18 | } else if (active === 'Organization') {
19 | elem =
;
20 | } else if (active === 'Cohort') {
21 | elem =
;
22 | } else if (active === 'Search') {
23 | elem =
;
24 | } else if (active === 'Residents') {
25 | elem =
;
26 | } else if (active === 'User') {
27 | elem =
;
28 | }
29 |
30 | return (
31 |
32 |
33 | {elem}
34 |
35 | );
36 | };
--------------------------------------------------------------------------------
/src/containers/MainContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useState, useEffect } from 'react';
2 | import { HomeContainer } from './HomeContainer.jsx';
3 | import { LandingPage } from '../components/LandingPage.jsx';
4 | import { SetCohort } from '../components/SetCohort.jsx';
5 |
6 | export default function MainContainer() {
7 | const [isAuthenticated, changeAuthenticated] = useState(false);
8 | const [cohortIsSet, setCohort] = useState(false);
9 | // const [isComplete, changeComplete] = useState(false);
10 |
11 | const getCookie = (cookie) => {
12 | return document.cookie
13 | .split('; ')
14 | .find(row => row.startsWith(cookie + '='))
15 | ?.split('=')[1];
16 | };
17 |
18 | useEffect(() => {
19 | // On page load, we need to check if linkedInAccessToken and User ID cookies are set
20 | // if so, we need to redirect to the server to check if the cookie is valid
21 | // if the cookie is valid, we need to set isAuthenticated to true on the client side
22 | // if cookie is invalid, clear cookie and redirect back to homepage
23 | // if cookie is not set, load the landing page
24 | //Check if cohortIsSet when isAuthenticated is changed to true
25 | //Fetching to the server, whether user is Authenticated and cohortisset
26 |
27 | if (getCookie('userId') && getCookie('linkedInAuthCode') && getCookie('linkedInAuthCode') !== 'undefined') {
28 | console.log('found a user ID and auth code, going to verify user');
29 | fetch('http://localhost:8080/verifyuser', {
30 | credentials: 'same-origin',
31 | })
32 | .then(res => {
33 | console.log(res);
34 | if (res.status === 200) changeAuthenticated(true);
35 | });
36 | } else {
37 | changeAuthenticated(false);
38 | }
39 | if (getCookie('userId')) {
40 | fetch('http://localhost:8080/verifyuser/complete')
41 | .then(res => res.json())
42 | .then(res => {
43 | console.log(res);
44 | if (res) setCohort(true);
45 | });
46 | }
47 | });
48 |
49 | return (
50 |
51 | {
52 | isAuthenticated
53 | ? cohortIsSet
54 | ?
55 | :
56 | :
57 | // ?
58 | // :
59 | // :
60 | }
61 |
62 | {/* {isComplete && } */}
63 |
64 | );
65 | }
--------------------------------------------------------------------------------
/src/containers/NavBarContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { SearchBar } from '../components/SearchBar.jsx';
3 |
4 | export const NavBar = (props) => {
5 |
6 | function clickFunction(focus) {
7 | props.setActive(focus);
8 | }
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | };
--------------------------------------------------------------------------------
/src/containers/OrganizationContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useEffect, useState } from 'react';
2 | import { ResidentsListContainer } from './ResidentsListContainer';
3 |
4 | export const OrganizationContainer = (props) => {
5 | const [orgList, setOrgList] = useState([]);
6 | const [residents, setResList] = useState([]);
7 | const [active, setActive] = useState(false);
8 |
9 | useEffect(() => {
10 | fetch('http://localhost:8080/organizations')
11 | .then(res => res.json())
12 | .then(res => setOrgList(res));
13 | }, []);
14 |
15 | function findResidents(org) {
16 | console.log(org);
17 | fetch('http://localhost:8080/organizations/residents', {
18 | method: 'POST',
19 | headers: {'Content-Type': 'application/json'},
20 | body: JSON.stringify({ organization: org})
21 | })
22 | .then(res => res.json())
23 | .then(res => setResList(res))
24 | .then(setActive(true));
25 | }
26 |
27 | const orgs = [];
28 | for (let i = 0; i < orgList.length; i++) {
29 | if (orgList[i].organization !== 'Student') {
30 | orgs.push();
31 | }
32 | }
33 |
34 | console.log(residents);
35 | return (
36 |
37 |
Search by Organization
38 | {
39 | !active
40 | ?
41 |
42 |
43 | {orgs}
44 |
45 | :
46 |
47 |
48 |
49 |
50 |
51 |
52 | }
53 |
54 | );
55 | };
--------------------------------------------------------------------------------
/src/containers/ResidentsContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useEffect, useState } from 'react';
2 | import { ResidentsListContainer } from './ResidentsListContainer';
3 |
4 | export const ResidentsContainer = (props) => {
5 | const [residentList, setResidentList] = useState([]);
6 |
7 | useEffect(() => {
8 | fetch('http://localhost:8080/residents')
9 | .then(res => res.json())
10 | .then(res => setResidentList(res));
11 | }, []);
12 |
13 | return (
14 |
15 | );
16 | };
--------------------------------------------------------------------------------
/src/containers/ResidentsListContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useState } from 'react';
2 | import { ResidentBox } from '../components/ResidentBox';
3 |
4 | export const ResidentsListContainer = (props) => {
5 | const residents = [];
6 |
7 | for (let i = 0; i < props.residentList.length; i++) {
8 | residents.push(
9 | <
10 | ResidentBox
11 | key={`residentKey${props.residentList[i].id}`}
12 | id={`resident${props.residentList[i].id}`}
13 | name={props.residentList[i].name}
14 | photo={props.residentList[i].photo}
15 | organization={props.residentList[i].organization}
16 | cohort={props.residentList[i].cohort}
17 | linkedin={props.residentList[i].linkedin}
18 | />);
19 | }
20 |
21 | return (
22 |
23 | Residents
24 |
25 | {residents}
26 |
27 |
28 | );
29 | };
--------------------------------------------------------------------------------
/src/containers/SearchContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useEffect, useState } from 'react';
2 | import { ResidentsListContainer } from './ResidentsListContainer';
3 |
4 | export const SearchContainer = (props) => {
5 | console.log(props.searchValue);
6 | const [searchResult, setSearchResult] = useState([]);
7 |
8 | //Fetch request to server, requesting residents with name of props.searchValue
9 | useEffect(() => {
10 | fetch('http://localhost:8080/residents/', {
11 | method: 'POST',
12 | headers: { 'Content-Type': 'application/json' },
13 | body: JSON.stringify({ name: props.searchValue }),
14 | })
15 | .then(res => res.json())
16 | .then(res => setSearchResult(res));
17 | }, [props.searchValue]);
18 |
19 | return (
20 |
21 | );
22 | };
--------------------------------------------------------------------------------
/src/containers/UserContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, useEffect, useState } from 'react';
2 | import { ResidentsListContainer } from './ResidentsListContainer.jsx';
3 | import { ResidentBox } from '../components/ResidentBox.jsx';
4 | import { ResidentDetails } from '../components/ResidentDetails.jsx';
5 |
6 | export const UserContainer = (props) => {
7 | const [user, setUser] = useState({});
8 | const [userIcon, setUserIcon] = useState({});
9 | const [saved, changeSaved] = useState(false);
10 |
11 | useEffect(() => {
12 | fetch('http://localhost:8080/residents/id', {
13 | method: 'POST',
14 | headers: { 'Content-Type': 'application/json' },
15 | body: JSON.stringify({
16 | id: document.cookie.split('; userId=')[1]
17 | })
18 | })
19 | .then(res => res.json())
20 | .then(res => {
21 | setUser(res);
22 | return res;
23 | })
24 | .then(res => setUserIcon({ name: res.name, photo: res.photo }));
25 | },[saved]);
26 |
27 | useEffect(() => {
28 | if (saved) {
29 | fetch('http://localhost:8080/residents/id', {
30 | method: 'POST',
31 | headers: { 'Content-Type': 'application/json' },
32 | body: JSON.stringify({
33 | id: document.cookie.split('; userId=')[1]
34 | })
35 | })
36 | .then(res => res.json())
37 | .then(res => {
38 | setUser(res);
39 | return res;
40 | })
41 | .then(res => setUserIcon({ name: res.name, photo: res.photo }))
42 | .then(changeSaved(false));
43 | }
44 |
45 |
46 | }, [saved]);
47 |
48 | function changeInput(e, key) {
49 | console.log(key);
50 | console.log(e.target.value);
51 | setUser({
52 | ...user,
53 | [key]: e.target.value,
54 | });
55 | }
56 |
57 | function saveFunction() {
58 | console.log(user);
59 | fetch('http://localhost:8080/residents/update', {
60 | method: 'POST',
61 | headers: { 'Content-Type': 'application/json' },
62 | body: JSON.stringify({
63 | id: document.cookie.split('; userId=')[1],
64 | user: user,
65 | })
66 | })
67 | .then(changeSaved(true));
68 | }
69 |
70 | console.log(user);
71 | return (
72 |
78 | );
79 | };
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CodeSmith Social (please change me!)
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import App from './App.jsx';
4 |
5 | // uncomment so that webpack can bundle styles
6 | import styles from './App.scss';
7 |
8 | // required by react-bootstrap
9 | import 'bootstrap/dist/css/bootstrap.min.css';
10 |
11 | render(
12 | ,
13 | document.getElementById('root')
14 | );
15 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 |
5 | module.exports = {
6 | entry: [
7 | './src/index.js',
8 | ],
9 | output: {
10 | path: path.resolve(__dirname, 'dist'),
11 | publicPath: '/',
12 | filename: 'bundle.js',
13 | },
14 | mode: process.env.NODE_ENV,
15 | devServer: {
16 | host: 'localhost',
17 | port: 8080,
18 | hot: true,
19 | // fallback to root for other urls
20 | historyApiFallback: true,
21 | static: {
22 | // match the output path
23 | directory: path.resolve(__dirname, './dist/'),
24 | // match the output 'publicPath'
25 | publicPath: '/dist',
26 | },
27 | proxy: {
28 | '/dist/**': {
29 | target: 'http://localhost:3000/',
30 | secure: false,
31 | },
32 | '/**': {
33 | target: 'http://localhost:3000/',
34 | secure: false,
35 | },
36 | '/assets/**': {
37 | target: 'http://localhost:3000/',
38 | secure: false,
39 | },
40 | '/login/**': {
41 | target: 'http://localhost:3000/',
42 | secure: false,
43 | },
44 | '/login': {
45 | target: 'http://localhost:3000/',
46 | secure: false,
47 | },
48 | '/verifyuser': {
49 | target: 'http://localhost:3000/',
50 | secure: false,
51 | },
52 | },
53 | },
54 | plugins: [
55 | new HtmlWebpackPlugin({
56 | template: './src/index.html',
57 | }),
58 | ],
59 | resolve: {
60 | extensions: ['.js', '.jsx']
61 | },
62 | module: {
63 | rules: [
64 | {
65 | test: /.jsx?/,
66 | exclude: /node_modules/,
67 | use: {
68 | loader: 'babel-loader',
69 | options: {
70 | presets: [
71 | '@babel/preset-env',
72 | '@babel/preset-react',
73 | ]
74 | },
75 | },
76 | },
77 | {
78 | test: /\.scss$/,
79 | use: [
80 | {
81 | loader: 'style-loader'
82 | },
83 | {
84 | loader: 'css-loader'
85 | },
86 | {
87 | loader: 'postcss-loader',
88 | options: {
89 | postcssOptions: {
90 | plugins: () => [
91 | require('autoprefixer')
92 | ]
93 | }
94 | }
95 | },
96 | {
97 | loader: 'sass-loader'
98 | }
99 | ],
100 | },
101 | {
102 | test: /\.css$/,
103 | use: [
104 | 'style-loader',
105 | 'css-loader',
106 | ],
107 | },
108 | ],
109 | },
110 | };
--------------------------------------------------------------------------------