Skip to content

Commit b4ce4c9

Browse files
authored
Merge pull request #4 from khuongdn16/feature/client/graphQL
Implement Dashboard for with actual data base
2 parents d941fd1 + 0d44638 commit b4ce4c9

File tree

16 files changed

+422
-47
lines changed

16 files changed

+422
-47
lines changed

app/src/Dashboard/Form.jsx

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,54 @@
11

22
import React from 'react';
3+
import { gql, useMutation } from '@apollo/client';
4+
import PropTypes from 'prop-types';
35

46

5-
const Form = (props) => {
7+
const Form = ({ description, id, likes }) => {
8+
9+
const ADD_LIKE = gql`
10+
mutation UpdateTest($id: ID, $name: String, $likes: Int) {
11+
updateTest(id: $id, name: $name, likes: $likes )
12+
{
13+
description
14+
id
15+
likes
16+
}
17+
}`;
18+
19+
const [updateTest] = useMutation(ADD_LIKE);
20+
21+
// go to bed
22+
function handleClick(e) {
23+
e.preventDefault();
24+
// IMPORTANT: DO NOT ADD extra comma to the last line of the variable object, the query will not work
25+
const myVar = {
26+
variables:
27+
{
28+
id,
29+
name: 'testname',
30+
likes: likes + 1,
31+
},
32+
};
33+
// send Mutation
34+
updateTest(myVar);
35+
}
36+
637
return (
738
<div className = 'form'>
8-
<h2>{ props.description }</h2>
9-
<button id={ props.id }>heart</button>
10-
</div>);
39+
<h2>{ description }</h2>
40+
<button id={ id } onClick={ handleClick }>heart</button>
41+
<h3>Likes: { likes }</h3>
42+
</div>
43+
);
1144
};
1245

46+
// Variable validation using propTypes
47+
Form.propTypes = {
48+
description: PropTypes.string.isRequired,
49+
id: PropTypes.string.isRequired,
50+
likes: PropTypes.number.isRequired,
51+
};
52+
53+
1354
export default Form;

app/src/Dashboard/FormsContainer.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,24 @@ import Form from './Form.jsx';
1010

1111
const FormsContainer = () => {
1212
// define the graphQL query string
13-
const GET_TESTS = gql`query {readAllTests { description id }}`;
13+
const GET_TESTS = gql`query {readAllTests { description id likes }}`;
1414
// useQuery hook abstracts fetch request
15-
const { loading, error, data } = useQuery(GET_TESTS);
15+
const { loading, error, data } = useQuery(GET_TESTS, { pollInterval: 1000 } );
1616
if (loading) return <p>Loading...</p>;
1717
if (error) return <p>Error :{error}</p>;
1818
// based on resolver(readAllTests) for this query, the data is stored in the data object with the key 'readAllTests'
1919
const myTests = data.readAllTests;
2020
// generate an array of Form components based on data
21-
const forms = myTests.map((test, index) => <Form key={index} id={test.id} description={test.description} />);
21+
const forms = myTests.map((test, index) => <Form key={index} id={test.id} description={test.description} likes={test.likes}/>);
2222

2323
return (
2424
<div>
2525
<Link to="/">
2626
<button type="button">Go Back</button>
2727
</Link>
28-
{forms}
28+
<div className = "formContainer">
29+
{forms}
30+
</div>
2931
</div>
3032
);
3133
};

app/src/Dashboard/Project.jsx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import React from 'react';
2+
import { gql, useMutation } from '@apollo/client';
3+
import PropTypes from 'prop-types';
4+
5+
6+
const Project = ({ name, likes, id, userId, username }) => {
7+
8+
// IMPORTANT:
9+
// 1) schema change projId => id to allows Apollo Client cache auto-update. Only works with 'id'
10+
// 2) always request the 'id' in a mutation request
11+
12+
const ADD_LIKE = gql`
13+
mutation AddLike($projId: ID!, $likes: Int!) {
14+
addLike(projId: $projId, likes: $likes)
15+
{
16+
id
17+
likes
18+
}
19+
}`;
20+
21+
const [addLike] = useMutation(ADD_LIKE);
22+
23+
24+
const MAKE_COPY = gql`
25+
mutation MakeCopy ($userId: ID!, $projId: ID!, $username: String!) {
26+
makeCopy(userId: $userId, projId: $projId, username: $username)
27+
{
28+
id
29+
}
30+
}`;
31+
32+
const [ makeCopy ] = useMutation(MAKE_COPY);
33+
34+
35+
function handleLike(e) {
36+
e.preventDefault();
37+
// IMPORTANT: DO NOT ADD extra comma to the last line of the variable object, the query will not work
38+
const myVar = {
39+
variables:
40+
{
41+
projId: id,
42+
likes: likes + 1,
43+
},
44+
};
45+
// send Mutation
46+
addLike(myVar);
47+
}
48+
49+
// Use current user info to make a make copy of another user's project
50+
const currUserSSID = window.localStorage.getItem('ssid') || 'unavailable';
51+
const currUsername = window.localStorage.getItem('username') || 'unavailable';
52+
// if (userSSID !== 'guest') {
53+
// myVar = { userId: userSSID };
54+
// }
55+
56+
function handleDownload(e) {
57+
e.preventDefault();
58+
const myVar = {
59+
variables:
60+
{
61+
projId: id,
62+
userId: currUserSSID,
63+
username: currUsername,
64+
},
65+
};
66+
// send Mutation
67+
makeCopy(myVar);
68+
}
69+
70+
return (
71+
<div className = 'project'>
72+
<h2>Project: { name }</h2>
73+
{/* <h3>Project ID: {projId} </h3> */}
74+
<h3>Author: { username }</h3>
75+
<h3>Likes: { likes }</h3>
76+
<div>
77+
<button onClick={ handleLike }>like me!</button>
78+
<button onClick={ handleDownload }>download me!</button>
79+
</div>
80+
</div>
81+
);
82+
};
83+
84+
// Variable validation using propTypes
85+
Project.propTypes = {
86+
name: PropTypes.string.isRequired,
87+
id: PropTypes.string.isRequired,
88+
userId: PropTypes.string.isRequired,
89+
username: PropTypes.string.isRequired,
90+
likes: PropTypes.number.isRequired,
91+
};
92+
93+
94+
export default Project;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React from 'react';
2+
import { Link } from 'react-router-dom';
3+
4+
import { gql, useQuery } from '@apollo/client';
5+
6+
import Project from './Project.jsx';
7+
// Implement Apollo Client useQuery hook to retrieve data from the server through graphQL. This includes 2 steps:
8+
// 1) Impliment Apollo Provider in the top component in ./src/index.js, this allows children components access to the queried data
9+
// 2) useQuery hook will update the data stored in Apollo Client's cache and automatically trigger child components rendering
10+
11+
const ProjectContainer = () => {
12+
// define the graphQL query string
13+
const GET_PROJECTS = gql`query GetAllProjects($userId: ID) {
14+
getAllProjects(userId: $userId) {
15+
name
16+
likes
17+
id
18+
userId
19+
username
20+
}
21+
}`;
22+
23+
let myVar = {};
24+
// Need this for the individual user dasboard, for now, dashboard shows all projects from all users
25+
const userSSID = window.localStorage.getItem('ssid') || 'unavailable';
26+
const username = window.localStorage.getItem('username') || 'unavailable';
27+
// if (userSSID !== 'guest') {
28+
// myVar = { userId: userSSID };
29+
// }
30+
31+
// useQuery hook abstracts fetch request
32+
const { loading, error, data } = useQuery(GET_PROJECTS, { pollInterval: 2000, variables: myVar } ); // Need to find where the userId is stored for the logged in user.
33+
if (loading) return <p>Loading...</p>;
34+
if (error) return <p>Error :{error}</p>;
35+
// based on resolver(getAllProject) for this query, the data is stored in the data object with the key 'getAllProjects'
36+
const myProjs = data.getAllProjects;
37+
console.log('Projects >>> ', myProjs);
38+
// generate an array of Project components based on data
39+
const projects = myProjs.map((proj, index) => <Project
40+
key= { index }
41+
name = {proj.name}
42+
likes = {proj.likes}
43+
userId = {proj.userId}
44+
username = {proj.username}
45+
id = {proj.id}
46+
/>);
47+
48+
return (
49+
<div>
50+
<h1> Public Dashboard </h1>
51+
<Link to="/">
52+
<button type="button">Go Back</button>
53+
</Link>
54+
<div className = "projectContainer">
55+
{projects}
56+
</div>
57+
</div>
58+
);
59+
};
60+
61+
export default ProjectContainer;
File renamed without changes.

app/src/Dashboard/styles.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ $light-grey: #bfcbd4;
88
$light-blue: #759cc9;
99
$button-blue: #24BCFF; */
1010

11-
.form {
11+
.form , .project{
1212
display: flex;
1313
flex-direction: column;
1414
background-color: white;
@@ -23,3 +23,8 @@ $button-blue: #24BCFF; */
2323
width: 250px;
2424
}
2525

26+
.formContainer , .projectContainer{
27+
display: flex;
28+
flex-flow: row wrap;
29+
}
30+

app/src/components/top/NavBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export default function NavBar(props) {
168168
</Typography>
169169

170170
{/* ==================================Dashboard Button================================================== */}
171-
<Link to='/dashboard'>
171+
<Link to='/dashboard' style ={ {textDecoration: 'none'} }>
172172
<Button
173173
variant="contained"
174174
color="primary"

app/src/helperFunctions/auth.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ export const sessionIsCreated = (
3030
if (data.sessionId && typeof data.sessionId === 'string') {
3131
// check that a session id was passed down
3232
window.localStorage.setItem('ssid', data.sessionId);
33+
// save username locally, will be added to saved project for each user
34+
window.localStorage.setItem('username', username);
35+
3336
return 'Success';
3437
}
3538
return data; // error message returned from userController.verifyUser
@@ -61,6 +64,8 @@ export const newUserIsCreated = (
6164
if (data.sessionId && typeof data.sessionId === 'string') {
6265
// check that a session id was passed down
6366
window.localStorage.setItem('ssid', data.sessionId);
67+
// save username locally, will be added to saved project for each user
68+
window.localStorage.setItem('username', username);
6469
return 'Success';
6570
}
6671
return data; // response is either Email Taken or Username Taken, refer to userController.createUser

app/src/helperFunctions/projectGetSaveDel.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ export const saveProject = (
3232
const body = JSON.stringify({
3333
name,
3434
project: workspace,
35-
userId: window.localStorage.getItem('ssid')
35+
userId: window.localStorage.getItem('ssid'),
36+
username: window.localStorage.getItem('username'),
3637
});
3738
const project = fetch(`${serverURL}/saveProject`, {
3839
method: 'POST',

app/src/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import {
2020
/*
2121
* Dashboard
2222
*/
23-
import Dashboard from './Dashboard/FormsContainer.jsx';
23+
// import TestDashboard from './Dashboard/FormsContainer.jsx';
24+
import ProjectDashboard from './Dashboard/ProjectContainer.jsx';
25+
2426
import styles from './Dashboard/styles.css';
2527
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
2628

@@ -56,7 +58,7 @@ ReactDOM.render(
5658
<Route exact path="/signup" component={SignUp} />
5759
<Route exact path="/password" component={FBPassWord} />
5860
<PrivateRoute exact path="/" component={App} />
59-
<Route exact path="/dashboard" component={Dashboard} />
61+
<Route exact path="/dashboard" component={ProjectDashboard} />
6062
<Route exact path="/tutorial" component={Tutorial} />
6163
<Route exact path="/tutorialPage/:learn" component={TutorialPage} />
6264
</Switch>

server/controllers/projectController.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ const projectController = {};
66

77
projectController.saveProject = (req, res, next) => {
88
// pull project name and project itself from body
9-
const { name, project, userId } = req.body;
9+
const { name, project, userId, username } = req.body;
1010
// pull ssid from cookies for user id
1111
Projects.findOneAndUpdate(
1212
// looks in projects collection for project by user and name
13-
{ name, userId },
13+
{ name, userId, username },
1414
// update or insert the project
1515
{ project },
1616
// Options:

0 commit comments

Comments
 (0)