From aa4af0ab71d17953b28ea548d82f0526877511ca Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Mon, 1 Mar 2021 10:49:51 -0800 Subject: [PATCH 001/116] working on GraphQl --- app/src/helperFunctions/auth.ts | 45 +++++++++------------ package.json | 8 +++- server/controllers/projectController.js | 32 +++++++-------- server/routes/graphql.js | 28 +++++++++++++ server/server.js | 54 +++++++++++++++---------- 5 files changed, 100 insertions(+), 67 deletions(-) create mode 100644 server/routes/graphql.js diff --git a/app/src/helperFunctions/auth.ts b/app/src/helperFunctions/auth.ts index 7dbb2d18f..acd4c2eb4 100644 --- a/app/src/helperFunctions/auth.ts +++ b/app/src/helperFunctions/auth.ts @@ -1,6 +1,6 @@ -const fetch = require("node-fetch"); +const fetch = require('node-fetch'); -const isDev = process.env.NODE_ENV === 'development' ; +const isDev = process.env.NODE_ENV === 'development'; let serverURL = 'https://reactype.herokuapp.com'; if (isDev) { serverURL = 'http://localhost:5000'; @@ -9,62 +9,55 @@ if (isDev) { export const sessionIsCreated = ( username: string, password: string, - isFbOauth: boolean + isFbOauth: boolean, ): Promise => { const body = JSON.stringify({ username, password, - isFbOauth + isFbOauth, }); - + const result = fetch(`${serverURL}/login`, { method: 'POST', credentials: 'include', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, - body + body, }) - .then(res => { - return res.json(); - }) - .then(data => { + .then(res => res.json()) + .then((data) => { if (data.sessionId && typeof data.sessionId === 'string') { // check that a session id was passed down window.localStorage.setItem('ssid', data.sessionId); return 'Success'; - } else { - return data; // error message returned from userController.verifyUser } + return data; // error message returned from userController.verifyUser }) - .catch(err => { - return 'Error'; - }); + .catch(err => 'Error'); return result; }; export const newUserIsCreated = ( username: string, email: string, - password: string + password: string, ): Promise => { const body = JSON.stringify({ username, email, - password + password, }); const result = fetch(`${serverURL}/signup`, { method: 'POST', credentials: 'include', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, - body + body, }) - .then(res => { - return res.json(); - }) - .then(data => { + .then(res => res.json()) + .then((data) => { if (data.sessionId && typeof data.sessionId === 'string') { // check that a session id was passed down window.localStorage.setItem('ssid', data.sessionId); @@ -72,8 +65,6 @@ export const newUserIsCreated = ( } return data; // response is either Email Taken or Username Taken, refer to userController.createUser }) - .catch(err => { - return 'Error'; - }); + .catch(err => 'Error'); return result; }; diff --git a/package.json b/package.json index 1841f0c7a..67564d23e 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "scripts": { "postinstall": "ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=true electron-builder install-app-deps", "dev-server": "cross-env NODE_ENV=development webpack-dev-server --config ./webpack.development.js", - "dev": "concurrently --success first \"npm run dev-server\" \"cross-env NODE_ENV=development electron .\" \"cross-env NODE_ENV=development npm run server\" -k", + "dev": "concurrently --success first \"nodemon server/server.js\" \"cross-env NODE_ENV=development electron .\" \"cross-env NODE_ENV=development npm run server\" -k", "p": "concurrently --success first \"npm run dev-server\" \"cross-env NODE_ENV=production electron .\" \"cross-env NODE_ENV=production npm run server\" -k", "prod-build": "cross-env NODE_ENV=production npx webpack --mode=production --config ./webpack.production.js", "prod": "npm run prod-build && electron . --no-sandbox", @@ -107,6 +107,7 @@ "@types/react-redux": "^7.0.8", "@types/react-router-dom": "^5.1.5", "ace-builds": "^1.4.12", + "apollo-server-express": "^2.21.0", "app-root-path": "^3.0.0", "autoprefixer": "^9.0.1", "babel-polyfill": "^6.26.0", @@ -123,11 +124,14 @@ "electron-splashscreen": "^1.0.0", "enzyme": "^3.4.1", "enzyme-adapter-react-16": "^1.2.0", + "express-graphql": "^0.12.0", + "graphql": "^15.5.0", "immutable": "^4.0.0-rc.12", "js-cookie": "^2.2.1", "localforage": "^1.7.2", "lodash": "^4.17.20", "node-fetch": "^2.6.0", + "npm": "^7.6.0", "passport": "^0.4.1", "passport-github2": "^0.1.12", "prettier": "^1.19.1", @@ -170,7 +174,7 @@ "electron-builder": "^22.7.0", "enzyme-to-json": "^3.5.0", "eslint": "^4.19.1", - "eslint-config-airbnb-base": "^13.0.0", + "eslint-config-airbnb-base": "^13.2.0", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-import": "^2.22.0", "eslint-plugin-jest": "^21.21.0", diff --git a/server/controllers/projectController.js b/server/controllers/projectController.js index 094afed20..14cb7ccb1 100644 --- a/server/controllers/projectController.js +++ b/server/controllers/projectController.js @@ -1,4 +1,5 @@ const { Projects } = require('../models/reactypeModels'); + const projectController = {}; // saveProject saves current workspace to database @@ -21,14 +22,13 @@ projectController.saveProject = (req, res, next) => { return next({ log: `Error in projectController.saveProject: ${err}`, message: { - err: `Error in projectController.saveProject, check server logs for details` - } + err: 'Error in projectController.saveProject, check server logs for details', + }, }); - } else { - res.locals.savedProject = result; - return next(); } - } + res.locals.savedProject = result; + return next(); + }, ); }; @@ -40,14 +40,13 @@ projectController.getProjects = (req, res, next) => { return next({ log: `Error in projectController.getProjects: ${err}`, message: { - err: `Error in projectController.getProjects, check server logs for details` - } + err: 'Error in projectController.getProjects, check server logs for details', + }, }); - } else { - // so it returns each project like it is in state, not the whole object in DB - res.locals.projects = projects.map(elem => elem.project); - return next(); } + // so it returns each project like it is in state, not the whole object in DB + res.locals.projects = projects.map(elem => elem.project); + return next(); }); }; @@ -61,13 +60,12 @@ projectController.deleteProject = (req, res, next) => { return next({ log: `Error in projectController.deleteProject: ${err}`, message: { - err: `Error in projectController.deleteProject, check server logs for details` - } + err: 'Error in projectController.deleteProject, check server logs for details', + }, }); - } else { - res.locals.deleted = deleted; - return next(); } + res.locals.deleted = deleted; + return next(); }); }; diff --git a/server/routes/graphql.js b/server/routes/graphql.js new file mode 100644 index 000000000..930f0b360 --- /dev/null +++ b/server/routes/graphql.js @@ -0,0 +1,28 @@ + +const express = require('express'); + + +const router = express.Router(); + + +const { graphqlExpress } = require('apollo-server-express'); + +// Construct a schema, using GraphQL schema language +const typeDefs = gql` + type Query { + hello: String + } +`; + +// Provide resolver functions for your schema fields +const resolvers = { + Query: { + hello: () => 'Hello world!', + }, +}; + +// bodyParser is needed just for POST. +router.use('/', graphqlExpress({ schema: myGraphQLSchema })); + + +module.exports = router; diff --git a/server/server.js b/server/server.js index 008e4a6e1..a7c33982b 100644 --- a/server/server.js +++ b/server/server.js @@ -19,8 +19,8 @@ app.use(cookieParser()); app.use( cors({ origin: ['http://localhost:8080', 'app://rse'], - credentials: true - }) + credentials: true, + }), ); // TODO: github Oauth still needs debugging @@ -49,14 +49,36 @@ app.use( // } // ); + +/* +GraphQl Router +*/ +/*********************************************************************/ +const { ApolloServer, gql } = require('apollo-server-express'); + +const typeDefs = gql` + type Query { + hello: String + } +`; + +const resolvers = { + Query: { + hello: () => 'Hello world!', + }, +}; + +const server = new ApolloServer({ typeDefs, resolvers }); +server.applyMiddleware({ app }); + +/*********************************************************************/ + app.post( '/signup', userController.createUser, cookieController.setSSIDCookie, sessionController.startSession, - (req, res) => { - return res.status(200).json({ sessionId: res.locals.ssid }); - } + (req, res) => res.status(200).json({ sessionId: res.locals.ssid }), ); app.post( @@ -64,9 +86,7 @@ app.post( userController.verifyUser, cookieController.setSSIDCookie, sessionController.startSession, - (req, res) => { - return res.status(200).json({ sessionId: res.locals.ssid }); - } + (req, res) => res.status(200).json({ sessionId: res.locals.ssid }), ); // user must be logged in to get or save projects, otherwise they will be redirected to login page @@ -74,33 +94,25 @@ app.post( '/saveProject', sessionController.isLoggedIn, projectController.saveProject, - (req, res) => { - return res.status(200).json(res.locals.savedProject); - } + (req, res) => res.status(200).json(res.locals.savedProject), ); app.post( '/getProjects', sessionController.isLoggedIn, projectController.getProjects, - (req, res) => { - return res.status(200).json(res.locals.projects); - } + (req, res) => res.status(200).json(res.locals.projects), ); app.delete( '/deleteProject', sessionController.isLoggedIn, projectController.deleteProject, - (req, res) => { - return res.status(200).json(res.locals.deleted); - } + (req, res) => res.status(200).json(res.locals.deleted), ); // catch-all route handler -app.use('*', (req, res) => { - return res.status(404).send('Page not found'); -}); +app.use('*', (req, res) => res.status(404).send('Page not found')); // Global error handler app.use((err, req, res, next) => { @@ -108,7 +120,7 @@ app.use((err, req, res, next) => { const defaultErr = { log: 'Express error handler caught unknown middleware', status: 500, - message: { err: 'An error occurred' } + message: { err: 'An error occurred' }, }; const errorObj = Object.assign({}, defaultErr, err); From 51ac9af20f8cad447c1834368727929a736027d1 Mon Sep 17 00:00:00 2001 From: Shana Hoehn Date: Mon, 1 Mar 2021 10:52:19 -0800 Subject: [PATCH 002/116] updated eslint --- package.json | 2 +- server/server.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1841f0c7a..1c3eedf2b 100644 --- a/package.json +++ b/package.json @@ -170,7 +170,7 @@ "electron-builder": "^22.7.0", "enzyme-to-json": "^3.5.0", "eslint": "^4.19.1", - "eslint-config-airbnb-base": "^13.0.0", + "eslint-config-airbnb-base": "^14.2.1", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-import": "^2.22.0", "eslint-plugin-jest": "^21.21.0", diff --git a/server/server.js b/server/server.js index 008e4a6e1..e285633c9 100644 --- a/server/server.js +++ b/server/server.js @@ -49,6 +49,12 @@ app.use( // } // ); + + + + + + app.post( '/signup', userController.createUser, From 7b4e42442d5f3e6469d82925c30fb8a496bbcc2f Mon Sep 17 00:00:00 2001 From: Shana Hoehn Date: Tue, 2 Mar 2021 15:11:42 -0800 Subject: [PATCH 003/116] modfied package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 67564d23e..c2020c2c4 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "scripts": { "postinstall": "ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=true electron-builder install-app-deps", "dev-server": "cross-env NODE_ENV=development webpack-dev-server --config ./webpack.development.js", - "dev": "concurrently --success first \"nodemon server/server.js\" \"cross-env NODE_ENV=development electron .\" \"cross-env NODE_ENV=development npm run server\" -k", + "dev": "concurrently \"cross-env NODE_ENV=development electron .\" \"nodemon server/server.js\" ", "p": "concurrently --success first \"npm run dev-server\" \"cross-env NODE_ENV=production electron .\" \"cross-env NODE_ENV=production npm run server\" -k", "prod-build": "cross-env NODE_ENV=production npx webpack --mode=production --config ./webpack.production.js", "prod": "npm run prod-build && electron . --no-sandbox", From 37c22b1dc113764be5e75df50d9966988dc7526b Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Tue, 2 Mar 2021 15:14:25 -0800 Subject: [PATCH 004/116] commit --- app/electron/main.js | 56 ++++++++++++++----------------- package.json | 2 +- server/apollo-server.js | 0 server/graphQL/authors.json | 13 +++++++ server/graphQL/books.json | 10 ++++++ server/graphQL/graphql.js | 28 ++++++++++++++++ server/graphQL/resolvers.js | 11 ++++++ server/graphQL/typedef.js | 31 +++++++++++++++++ server/routes/graphql.js | 28 ---------------- server/server.js | 67 +++++++++++++++++++++++++++++-------- 10 files changed, 173 insertions(+), 73 deletions(-) create mode 100644 server/apollo-server.js create mode 100644 server/graphQL/authors.json create mode 100644 server/graphQL/books.json create mode 100644 server/graphQL/graphql.js create mode 100644 server/graphQL/resolvers.js create mode 100644 server/graphQL/typedef.js delete mode 100644 server/routes/graphql.js diff --git a/app/electron/main.js b/app/electron/main.js index 9df1600a1..013021527 100644 --- a/app/electron/main.js +++ b/app/electron/main.js @@ -1,6 +1,6 @@ /* @description: main.js is what controls the lifecycle of the electron application from initialization to termination. -@actions: codes for Github Oauth has been commented out because of lack of functionality. +@actions: codes for Github Oauth has been commented out because of lack of functionality. */ require('dotenv').config(); const path = require('path'); @@ -20,7 +20,7 @@ const { resolve } = require('app-root-path'); // to install react dev tool extension const { default: installExtension, - REACT_DEVELOPER_TOOLS + REACT_DEVELOPER_TOOLS, } = require('electron-devtools-installer'); const debug = require('electron-debug'); @@ -30,8 +30,7 @@ const Protocol = require('./protocol'); const MenuBuilder = require('./menu'); // mode that the app is running in -const isDev = - process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'; +const isDev = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'; const port = 8080; const selfHost = `http://localhost:${port}`; @@ -60,7 +59,7 @@ async function createWindow() { height: 1080, minWidth: 980, // window title - title: `ReacType`, + title: 'ReacType', // the browser window will not display initially as it's loading // once the browser window renders, a function is called below that hides the splash screen and displays the browser window show: false, @@ -83,8 +82,8 @@ async function createWindow() { enableRemoteModule: false, // path of preload script. preload is how the renderer page will have access to electron functionality preload: path.join(__dirname, 'preload.js'), - nativeWindowOpen: true - } + nativeWindowOpen: true, + }, }); // Splash screen that appears while loading @@ -99,7 +98,7 @@ async function createWindow() { logo: resolve('app/src/public/icons/png/64x64.png'), color: '#3BBCAF', website: 'www.reactype.io', - text: 'Initializing ...' + text: 'Initializing ...', }); // Load app @@ -122,7 +121,7 @@ async function createWindow() { if (isDev) { win.webContents.once('dom-ready', () => { debug(); - //win.webContents.openDevTools(); + // win.webContents.openDevTools(); }); } @@ -147,13 +146,13 @@ async function createWindow() { ses .fromPartition(partition) .setPermissionRequestHandler((webContents, permission, callback) => { - let allowedPermissions = []; // Full list here: https://developer.chrome.com/extensions/declare_permissions#manifest + const allowedPermissions = []; // Full list here: https://developer.chrome.com/extensions/declare_permissions#manifest if (allowedPermissions.includes(permission)) { callback(true); // Approve permission request } else { console.error( - `The application tried to request permission for '${permission}'. This permission was not whitelisted and has been blocked.` + `The application tried to request permission for '${permission}'. This permission was not whitelisted and has been blocked.`, ); callback(false); // Deny @@ -165,10 +164,10 @@ async function createWindow() { // we could use this over _all_ urls ses .fromPartition(partition) - .webRequest.onBeforeRequest({ urls: ['http://localhost./*'] }, listener => { + .webRequest.onBeforeRequest({ urls: ['http://localhost./*'] }, (listener) => { if (listener.url.indexOf('http://') >= 0) { listener.callback({ - cancel: true + cancel: true, }); } }); @@ -185,9 +184,9 @@ protocol.registerSchemesAsPrivileged([ standard: true, secure: true, allowServiceWorkers: true, - supportFetchAPI: true - } - } + supportFetchAPI: true, + }, + }, ]); // This method will be called when Electron has finished @@ -223,16 +222,15 @@ app.on('web-contents-created', (event, contents) => { 'https://www.facebook.com', 'https://developer.mozilla.org', 'https://www.smashingmagazine.com', - 'https://www.html5rocks.com' + 'https://www.html5rocks.com', ]; // Log and prevent the app from navigating to a new page if that page's origin is not whitelisted if (!validOrigins.includes(parsedUrl.origin)) { console.error( - `The application tried to navigate to the following address: '${parsedUrl}'. This origin is not whitelisted attempt to navigate was blocked.` + `The application tried to navigate to the following address: '${parsedUrl}'. This origin is not whitelisted attempt to navigate was blocked.`, ); // if the requested URL is not in the whitelisted array, then don't navigate there event.preventDefault(); - return; } }); @@ -247,20 +245,19 @@ app.on('web-contents-created', (event, contents) => { 'https://developer.mozilla.org', 'https://www.facebook.com', 'https://www.smashingmagazine.com', - 'https://www.html5rocks.com' + 'https://www.html5rocks.com', ]; // Log and prevent the app from redirecting to a new page if ( - !validOrigins.includes(parsedUrl.origin) && - !validOrigins.includes(parsedUrl.href) + !validOrigins.includes(parsedUrl.origin) + && !validOrigins.includes(parsedUrl.href) ) { console.error( - `The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.` + `The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.`, ); event.preventDefault(); - return; } }); @@ -290,16 +287,15 @@ app.on('web-contents-created', (event, contents) => { 'https://github.com', 'https://www.facebook.com', 'https://www.smashingmagazine.com', - 'https://www.html5rocks.com' + 'https://www.html5rocks.com', ]; // Log and prevent the app from navigating to a new page if that page's origin is not whitelisted if (!validOrigins.includes(parsedUrl.origin)) { console.error( - `The application tried to open a new window at the following address: '${navigationUrl}'. This attempt was blocked.` + `The application tried to open a new window at the following address: '${navigationUrl}'. This attempt was blocked.`, ); // if the requested URL is not in the whitelisted array, then don't navigate there event.preventDefault(); - return; } }); }); @@ -331,15 +327,15 @@ app.on('remote-get-current-web-contents', (event, webContents) => { // When a user selects "Export project", a function (chooseAppDir loaded via preload.js) // is triggered that sends a "choose_app_dir" message to the main process // when the "choose_app_dir" message is received it triggers this event listener -ipcMain.on('choose_app_dir', event => { +ipcMain.on('choose_app_dir', (event) => { // dialog displays the native system's dialogue for selecting files // once a directory is chosen send a message back to the renderer with the path of the directory dialog .showOpenDialog(win, { properties: ['openDirectory'], - buttonLabel: 'Export' + buttonLabel: 'Export', }) - .then(directory => { + .then((directory) => { if (!directory) return; event.sender.send('app_dir_selected', directory.filePaths[0]); }) diff --git a/package.json b/package.json index 67564d23e..8e1443fec 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "dev": "concurrently --success first \"nodemon server/server.js\" \"cross-env NODE_ENV=development electron .\" \"cross-env NODE_ENV=development npm run server\" -k", "p": "concurrently --success first \"npm run dev-server\" \"cross-env NODE_ENV=production electron .\" \"cross-env NODE_ENV=production npm run server\" -k", "prod-build": "cross-env NODE_ENV=production npx webpack --mode=production --config ./webpack.production.js", - "prod": "npm run prod-build && electron . --no-sandbox", + "prod": "npm run prod-build && electron . --no-sandbox", "pack": "electron-builder --dir", "dist": "npm run prod-build && electron-builder", "dist-mac": "npm run prod-build && electron-builder --mac", diff --git a/server/apollo-server.js b/server/apollo-server.js new file mode 100644 index 000000000..e69de29bb diff --git a/server/graphQL/authors.json b/server/graphQL/authors.json new file mode 100644 index 000000000..cde52651f --- /dev/null +++ b/server/graphQL/authors.json @@ -0,0 +1,13 @@ +[ + { + "name": "Kate Chopin", + "books": [{ + "title": "The Awakening", + "author": "Kate Chopin" + }, + { + "title": "City of Glass", + "author": "Paul Auster" + }] + } +] \ No newline at end of file diff --git a/server/graphQL/books.json b/server/graphQL/books.json new file mode 100644 index 000000000..c0bcd1e30 --- /dev/null +++ b/server/graphQL/books.json @@ -0,0 +1,10 @@ +[ + { + "title": "The Awakening", + "author": "Kate Chopin" + }, + { + "title": "City of Glass", + "author": "Paul Auster" + } +] \ No newline at end of file diff --git a/server/graphQL/graphql.js b/server/graphQL/graphql.js new file mode 100644 index 000000000..cdb11e3c2 --- /dev/null +++ b/server/graphQL/graphql.js @@ -0,0 +1,28 @@ + +// const express = require('express'); + + +// const router = express.Router(); + + +// const { graphqlExpress } = require('apollo-server-express'); + +// // Construct a schema, using GraphQL schema language +// const typeDefs = gql` +// type Query { +// hello: String +// } +// `; + +// // Provide resolver functions for your schema fields +// const resolvers = { +// Query: { +// hello: () => 'Hello world!', +// }, +// }; + +// // bodyParser is needed just for POST. +// router.use('/', graphqlExpress({ schema: myGraphQLSchema })); + + +// module.exports = router; diff --git a/server/graphQL/resolvers.js b/server/graphQL/resolvers.js new file mode 100644 index 000000000..298d18db8 --- /dev/null +++ b/server/graphQL/resolvers.js @@ -0,0 +1,11 @@ +const books = require('./books.json'); + +const authors = require('./authors.json'); + +module.exports = { + Query: { + books: () => books, + authors: () => authors, + }, + +}; diff --git a/server/graphQL/typedef.js b/server/graphQL/typedef.js new file mode 100644 index 000000000..98aa5d1e3 --- /dev/null +++ b/server/graphQL/typedef.js @@ -0,0 +1,31 @@ +const { gql } = require('apollo-server-express'); + + +module.exports = { + typeDefs: gql` + # Comments in GraphQL strings (such as this one) start with the hash (#) symbol. + + # This "Book" type defines the queryable fields for every book in our data source. + type Book { + title: String + author: String + } + + type Author { + name: String + books: [Book] + } + + # The "Query" type is special: it lists all of the available queries that + # clients can execute, along with the return type for each. In this + # case, the "books" query returns an array of zero or more Books (defined above). + type Query { + books: [Book] + authors: [Author] + } + + type Mutation { + addBook(title: String, author: String): Book + } +`, +}; diff --git a/server/routes/graphql.js b/server/routes/graphql.js deleted file mode 100644 index 930f0b360..000000000 --- a/server/routes/graphql.js +++ /dev/null @@ -1,28 +0,0 @@ - -const express = require('express'); - - -const router = express.Router(); - - -const { graphqlExpress } = require('apollo-server-express'); - -// Construct a schema, using GraphQL schema language -const typeDefs = gql` - type Query { - hello: String - } -`; - -// Provide resolver functions for your schema fields -const resolvers = { - Query: { - hello: () => 'Hello world!', - }, -}; - -// bodyParser is needed just for POST. -router.use('/', graphqlExpress({ schema: myGraphQLSchema })); - - -module.exports = router; diff --git a/server/server.js b/server/server.js index a7c33982b..f8006abc9 100644 --- a/server/server.js +++ b/server/server.js @@ -54,22 +54,61 @@ app.use( GraphQl Router */ /*********************************************************************/ -const { ApolloServer, gql } = require('apollo-server-express'); - -const typeDefs = gql` - type Query { - hello: String - } -`; - -const resolvers = { - Query: { - hello: () => 'Hello world!', - }, +// const { ApolloServer } = require('apollo-server-express'); + +// const { typeDefs } = require('./graphQL/typedef'); +// const resolvers = require('./graphQL/resolvers'); + +// const server = new ApolloServer({ typeDefs, resolvers }); +// server.applyMiddleware({ app }); + + + +const { gql } = require('apollo-server-express'); +const { graphqlHTTP } = require('express-graphql'); +const { buildSchema } = require('graphql'); +const books = require('./graphQL/books.json'); + +const rootValue = { + books: () => books, }; -const server = new ApolloServer({ typeDefs, resolvers }); -server.applyMiddleware({ app }); +const schema = buildSchema(` +# Comments in GraphQL strings (such as this one) start with the hash (#) symbol. + +# This "Book" type defines the queryable fields for every book in our data source. +type Book { + title: String + author: String +} + +type Author { + name: String + books: [Book] +} + +# The "Query" type is special: it lists all of the available queries that +# clients can execute, along with the return type for each. In this +# case, the "books" query returns an array of zero or more Books (defined above). +type Query { + books: [Book] + authors: [Author] +} + +type Mutation { + addBook(title: String, author: String): Book +} +`); + +app.use( + '/graphql', + graphqlHTTP({ + schema, + rootValue, + graphiql: { headerEditorEnabled: true }, + }), +); + /*********************************************************************/ From be2afe03922a56d7018ae79c09c5b6771b4423d2 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Tue, 2 Mar 2021 15:40:40 -0800 Subject: [PATCH 005/116] working --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 837619d4b..3286275f4 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,8 @@ "scripts": { "postinstall": "ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=true electron-builder install-app-deps", "dev-server": "cross-env NODE_ENV=development webpack-dev-server --config ./webpack.development.js", - "dev": "concurrently \"cross-env NODE_ENV=development electron .\" \"nodemon server/server.js\" ", - "p": "concurrently --success first \"npm run dev-server\" \"cross-env NODE_ENV=production electron .\" \"cross-env NODE_ENV=production npm run server\" -k", + "dev": "concurrently --success first \"cross-env NODE_ENV=development webpack-dev-server --config ./webpack.development.js\" \"cross-env NODE_ENV=development electron .\" \"nodemon server/server.js\" ", + "p": "concurrently --success first \"npm run dev-server\" \"cross-env NODE_ENV=production electron .\" \"nodemon server/server.js\" -k", "prod-build": "cross-env NODE_ENV=production npx webpack --mode=production --config ./webpack.production.js", "prod": "npm run prod-build && electron . --no-sandbox", "pack": "electron-builder --dir", From e84231475bf7a581be30f3803c3788b0e244d6e6 Mon Sep 17 00:00:00 2001 From: Edward Park Date: Wed, 3 Mar 2021 11:07:08 -0800 Subject: [PATCH 006/116] added form nesting, renamed index to app when exporting --- app/src/components/bottom/BottomTabs.tsx | 3 +++ app/src/components/bottom/CodePreview.tsx | 4 +++- app/src/components/left/HTMLPanel.tsx | 4 ++-- app/src/components/main/Canvas.tsx | 5 ++++- .../components/main/DirectChildHTMLNestable.tsx | 14 +++++++++----- app/src/context/initialState.ts | 2 +- app/src/helperFunctions/generateCode.ts | 10 ++++++---- app/src/helperFunctions/renderChildren.tsx | 8 ++++---- app/src/tree/useResizeObserver.js | 12 ++++++------ package.json | 7 ++++++- 10 files changed, 44 insertions(+), 25 deletions(-) diff --git a/app/src/components/bottom/BottomTabs.tsx b/app/src/components/bottom/BottomTabs.tsx index 58e6c7c4c..9cde4ea45 100644 --- a/app/src/components/bottom/BottomTabs.tsx +++ b/app/src/components/bottom/BottomTabs.tsx @@ -25,6 +25,8 @@ const BottomTabs = () => { // breaks if handleChange is commented out const handleChange = (event: React.ChangeEvent, value: number) => { + // console.log('value', value) + // console.log('setTab', setTab) setTab(value); }; // Allows users to toggle project between "next.js" and "Classic React" @@ -34,6 +36,7 @@ const BottomTabs = () => { dispatch({ type: 'CHANGE PROJECT TYPE', payload: { projectType } }); }; const { components, HTMLTypes } = state; + // console.log('components', components) const changeTheme = e => { setTheme(e.target.value); diff --git a/app/src/components/bottom/CodePreview.tsx b/app/src/components/bottom/CodePreview.tsx index 285905c73..1349b2553 100644 --- a/app/src/components/bottom/CodePreview.tsx +++ b/app/src/components/bottom/CodePreview.tsx @@ -16,7 +16,9 @@ const CodePreview: React.FC<{ setTheme: any | null; }> = ({ theme, setTheme }) => { const wrapper = useRef(); + // console.log('wrapper', wrapper) const dimensions = useResizeObserver(wrapper); + // console.log('dimensions', dimensions) const { width, height } = dimensions || 0; @@ -25,7 +27,7 @@ const CodePreview: React.FC<{ const currentComponent = state.components.find( (elem: Component) => elem.id === state.canvasFocus.componentId ); - +console.log('currentComp in CodePreview', currentComponent) const handleCodeSnipChange = val => { currentComponent.code = val; }; diff --git a/app/src/components/left/HTMLPanel.tsx b/app/src/components/left/HTMLPanel.tsx index d676ff4be..fe6e29435 100644 --- a/app/src/components/left/HTMLPanel.tsx +++ b/app/src/components/left/HTMLPanel.tsx @@ -184,7 +184,7 @@ const HTMLPanel = (props): JSX.Element => { type="text" name="Tag" value={tag} - autocomplete="off" + autoComplete="off" onChange={handleTagChange} className={isThemeLight ? `${classes.input} ${classes.lightThemeFontColor}` : `${classes.input} ${classes.darkThemeFontColor}`} style={{ marginBottom: '10px' }} @@ -206,7 +206,7 @@ const HTMLPanel = (props): JSX.Element => { name="Tag Name" value={name} onChange={handleNameChange} - autocomplete="off" + autoComplete="off" className={isThemeLight ? `${classes.input} ${classes.lightThemeFontColor}` : `${classes.input} ${classes.darkThemeFontColor}`} /> diff --git a/app/src/components/main/Canvas.tsx b/app/src/components/main/Canvas.tsx index 04c738b77..bc9410076 100644 --- a/app/src/components/main/Canvas.tsx +++ b/app/src/components/main/Canvas.tsx @@ -12,7 +12,8 @@ function Canvas() { const currentComponent: Component = state.components.find( (elem: Component) => elem.id === state.canvasFocus.componentId ); - + // console.log('currentComponent', currentComponent) + // changes focus of the canvas to a new component / child const changeFocus = (componentId: number, childId: number | null) => { dispatch({ type: 'CHANGE FOCUS', payload: { componentId, childId } }); @@ -31,6 +32,7 @@ function Canvas() { if (didDrop) { return; } + console.log('item', item) // if item dropped is going to be a new instance (i.e. it came from the left panel), then create a new child component if (item.newInstance) { dispatch({ @@ -47,6 +49,7 @@ function Canvas() { dispatch({ type: 'CHANGE POSITION', payload: { + // name: item.name, currentChildId: item.childId, newParentChildId: null } diff --git a/app/src/components/main/DirectChildHTMLNestable.tsx b/app/src/components/main/DirectChildHTMLNestable.tsx index 59e069658..52447757b 100644 --- a/app/src/components/main/DirectChildHTMLNestable.tsx +++ b/app/src/components/main/DirectChildHTMLNestable.tsx @@ -12,11 +12,13 @@ function DirectChildHTMLNestable({ type, typeId, style, - children + children, + name, }: ChildElement) { const [state, dispatch] = useContext(StateContext); const ref = useRef(null); - + // console.log('name', name) + // console.log('children', children) // find the HTML element corresponding with this instance of an HTML element // find the current component to render on the canvas const HTMLType: HTMLType = state.HTMLTypes.find( @@ -31,7 +33,8 @@ function DirectChildHTMLNestable({ newInstance: false, childId: childId, instanceType: type, - instanceTypeId: typeId + instanceTypeId: typeId, + name: name //added code <-- }, canDrag: HTMLType.id !== 1000, // dragging not permitted if element is separator collect: (monitor: any) => { @@ -58,7 +61,7 @@ function DirectChildHTMLNestable({ payload: { type: item.instanceType, typeId: item.instanceTypeId, - childId: childId + childId: childId, } }); } @@ -68,7 +71,7 @@ function DirectChildHTMLNestable({ type: 'CHANGE POSITION', payload: { currentChildId: item.childId, - newParentChildId: childId + newParentChildId: childId, } }); } @@ -111,6 +114,7 @@ function DirectChildHTMLNestable({ drag(drop(ref)); return (
+ {HTMLType.placeHolderShort} {renderChildren(children)}
); diff --git a/app/src/context/initialState.ts b/app/src/context/initialState.ts index a55b44cfc..aba5ed6ee 100644 --- a/app/src/context/initialState.ts +++ b/app/src/context/initialState.ts @@ -8,7 +8,7 @@ const initialState: State = { components: [ { id: 1, - name: 'index', + name: 'App', style: {}, code: '
Drag in a component or HTML element into the canvas!
', children: [], diff --git a/app/src/helperFunctions/generateCode.ts b/app/src/helperFunctions/generateCode.ts index 2132e5fbf..62deec57f 100644 --- a/app/src/helperFunctions/generateCode.ts +++ b/app/src/helperFunctions/generateCode.ts @@ -34,6 +34,7 @@ const generateUnformattedCode = ( // declare an array of enriched children const enrichedChildren = currentComponent.children.map((elem: any) => { + //enrichedChildren is iterating through the children array const child = { ...elem }; // check if child is a component @@ -52,7 +53,8 @@ const generateUnformattedCode = ( child['tag'] = referencedHTML.tag; if ( referencedHTML.tag === 'div' || - referencedHTML.tag === 'separator' + referencedHTML.tag === 'separator' || + referencedHTML.tag === 'form' ) { child.children = getEnrichedChildren(child); } @@ -95,9 +97,9 @@ const generateUnformattedCode = ( child.tag }>`; } else if (child.tag === 'form') { - return `<${child.tag}${formatStyles(child.style)}>FORM`; + return `<${child.tag}${formatStyles( + child.style + )}>${writeNestedElements(child.children)}`; } else if (child.tag === 'p') { return `<${child.tag}${formatStyles( child.style diff --git a/app/src/helperFunctions/renderChildren.tsx b/app/src/helperFunctions/renderChildren.tsx index a35a461a0..a0adff98c 100644 --- a/app/src/helperFunctions/renderChildren.tsx +++ b/app/src/helperFunctions/renderChildren.tsx @@ -30,8 +30,8 @@ const renderChildren = (children: ChildElement[]) => { /> ); } - // child is a non-nestable type of HTML element (everything except for divs) - else if (type === 'HTML Element' && typeId !== 11 && typeId !== 1000) { + // child is a non-nestable type of HTML element (everything except for divs and forms) + else if (type === 'HTML Element' && typeId !== 11 && typeId !== 1000 && typeId !== 2) { return ( { /> ); } - // child is a nestable type of HTML element (divs) - else if (type === 'HTML Element' && typeId === 11) { + // child is a nestable type of HTML element (divs and forms) + else if (type === 'HTML Element' && (typeId === 11 || typeId === 2)) { return ( { +const useResizeObserver = (ref) => { const [dimensions, setDimensions] = useState(null); useEffect(() => { // the element being observed (div with green border) const observeTarget = ref.current; - const resizeObserver = new ResizeObserver(entries => { - entries.forEach(entry => { - // contentRect is an object containing the dimensions of the observed element + const resizeObserver = new ResizeObserver((entries) => { + entries.forEach((entry) => { + // contentRect is an object containing the dimensions of the observed element setDimensions(entry.contentRect); }); }); diff --git a/package.json b/package.json index 1841f0c7a..9d6a207a5 100644 --- a/package.json +++ b/package.json @@ -10,21 +10,26 @@ "Adam Singer", "Alex Wolinsky", "Andrew Cho", + "Anthony Torrero", "Brian Han", "Charles Finocchiaro", "Chelsey Fewer", "Christian Padilla", "Diego Vazquez", + "Edward Park", + "Elena Conn", "Eliot Nguyen", "Fredo Chen", "Jesse Zuniga", "Jin Soo Lim", "Julie Wu", + "Khuong Nguyen", "Linh Tran", "Luke Madden", "Mitchel Severe", "Natalie Vick", "Sean Sadykoff", + "Shana Hoehn", "Shlomo Porges", "Stormi Hashimoto", "Sophia Huttner", @@ -170,7 +175,7 @@ "electron-builder": "^22.7.0", "enzyme-to-json": "^3.5.0", "eslint": "^4.19.1", - "eslint-config-airbnb-base": "^13.0.0", + "eslint-config-airbnb-base": "^13.2.0", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-import": "^2.22.0", "eslint-plugin-jest": "^21.21.0", From b6d6cf78e7228a630f1efa264502f03eb4dd9abc Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Wed, 3 Mar 2021 12:18:13 -0800 Subject: [PATCH 007/116] Added Route for Dashboard --- app/electron/main.js | 21 +++++++++++++++++++-- app/src/Dashboard/Form.jsx | 15 +++++++++++++++ app/src/Dashboard/FormsContainer.jsx | 18 ++++++++++++++++++ app/src/Dashboard/home.html | 15 +++++++++++++++ app/src/Dashboard/home.js | 11 +++++++++++ app/src/Dashboard/styles.css | 25 +++++++++++++++++++++++++ app/src/components/top/NavBar.tsx | 23 +++++++++++++++++++++++ app/src/index.js | 10 +++++++++- package.json | 8 +++++--- server/server.js | 8 ++++++++ webpack.config.js | 5 +++-- 11 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 app/src/Dashboard/Form.jsx create mode 100644 app/src/Dashboard/FormsContainer.jsx create mode 100644 app/src/Dashboard/home.html create mode 100644 app/src/Dashboard/home.js create mode 100644 app/src/Dashboard/styles.css diff --git a/app/electron/main.js b/app/electron/main.js index 013021527..5a4dc2b8a 100644 --- a/app/electron/main.js +++ b/app/electron/main.js @@ -13,6 +13,10 @@ const { ipcMain, } = require('electron'); +// ELECTRON WINDOW MANAGER +const windowManager = require('electron-window-manager'); + + // The splash screen is what appears while the app is loading const { initSplashScreen, OfficeTemplate } = require('electron-splashscreen'); const { resolve } = require('app-root-path'); @@ -55,8 +59,10 @@ async function createWindow() { // Create the browser window. win = new BrowserWindow({ // full screen - width: 1920, - height: 1080, + // width: 1920, + // height: 1080, + width: 1024, + height: 576, minWidth: 980, // window title title: 'ReacType', @@ -194,6 +200,17 @@ protocol.registerSchemesAsPrivileged([ // Some APIs can only be used after this event occurs. app.on('ready', createWindow); +// TRYING ELECTRON-WINDOW-MANAGER When the application is ready + +// app.on('ready', () => { +// windowManager.setDefaultSetup({'width': 800, 'height': 450, 'position': 'right'}); +// // Open Dashboard window +// const dashboard = windowManager.open('home', 'Welcome ...', 'http://localhost:5000/home.html'); +// dashboard.toggleDevTools(false); +// // Load ReacType window +// createWindow(); +// }); + // Quit when all windows are closed. app.on('window-all-closed', () => { app.quit(); diff --git a/app/src/Dashboard/Form.jsx b/app/src/Dashboard/Form.jsx new file mode 100644 index 000000000..374fa9b25 --- /dev/null +++ b/app/src/Dashboard/Form.jsx @@ -0,0 +1,15 @@ + +import React from 'react'; + + +const Form = (props) => { + return (
+

Form 1

+

Blah

+

Blah

+

Blah

+ +
); +} + +export default Form; diff --git a/app/src/Dashboard/FormsContainer.jsx b/app/src/Dashboard/FormsContainer.jsx new file mode 100644 index 000000000..0b10f17f8 --- /dev/null +++ b/app/src/Dashboard/FormsContainer.jsx @@ -0,0 +1,18 @@ +import React, { useContext } from 'react'; +import { Link } from "react-router-dom"; + +import Form from './Form.jsx'; + + +const FormsContainer = () => { + return ( +
+ + + +
+
+ ); +}; + +export default FormsContainer; diff --git a/app/src/Dashboard/home.html b/app/src/Dashboard/home.html new file mode 100644 index 000000000..1ab888713 --- /dev/null +++ b/app/src/Dashboard/home.html @@ -0,0 +1,15 @@ + + + + + + + Document + + +

Dashboard

+
+
+ + + \ No newline at end of file diff --git a/app/src/Dashboard/home.js b/app/src/Dashboard/home.js new file mode 100644 index 000000000..7200a8fec --- /dev/null +++ b/app/src/Dashboard/home.js @@ -0,0 +1,11 @@ +// import React from 'react'; +import { render } from 'react-dom'; +import Form from './Form.jsx'; + +// uncomment so that webpack can bundle styles +// import styles from './styles.scss'; + +render( +
Some
, + document.getElementById('root') +); diff --git a/app/src/Dashboard/styles.css b/app/src/Dashboard/styles.css new file mode 100644 index 000000000..f7c3f3c56 --- /dev/null +++ b/app/src/Dashboard/styles.css @@ -0,0 +1,25 @@ +/* @import url('https://fonts.googleapis.com/css2?family=Cabin&family=Oswald&display=swap'); + + +$primary-black: #22292f; +$secondary-black: #606f7b; +$teriary-grey: #f0f0f0; +$light-grey: #bfcbd4; +$light-blue: #759cc9; +$button-blue: #24BCFF; */ + +.form { + display: flex; + flex-direction: column; + background-color: white; + border: 1px solid #f0f0f0; + box-shadow: 0 0 5px rgba(0,0,0,0.3); + margin: 5px; + border-radius: 5px; + color: #606f7b; + align-items: center; + height: 300px; + justify-content: min; + width: 250px; +} + diff --git a/app/src/components/top/NavBar.tsx b/app/src/components/top/NavBar.tsx index 675f6ef82..cc672ab31 100644 --- a/app/src/components/top/NavBar.tsx +++ b/app/src/components/top/NavBar.tsx @@ -28,6 +28,14 @@ import createModal from '../right/createModal'; import StateContext from '../../context/context'; import logo from '../../public/icons/win/logo.png'; + + +// ROUTING TO DASHBOARD +import { Link } from "react-router-dom"; + + + + // NavBar text and button styling const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -158,6 +166,21 @@ export default function NavBar(props) { ReacType + + {/* ==================================Dashboard Button================================================== */} + + + + {/* ==================================Dashboard Button================================================== */} + - + {forms} ); }; diff --git a/app/src/Dashboard/home.html b/app/src/Dashboard/home.html deleted file mode 100644 index 1ab888713..000000000 --- a/app/src/Dashboard/home.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - Document - - -

Dashboard

-
-
- - - \ No newline at end of file diff --git a/app/src/Dashboard/home.js b/app/src/Dashboard/home.js deleted file mode 100644 index 7200a8fec..000000000 --- a/app/src/Dashboard/home.js +++ /dev/null @@ -1,11 +0,0 @@ -// import React from 'react'; -import { render } from 'react-dom'; -import Form from './Form.jsx'; - -// uncomment so that webpack can bundle styles -// import styles from './styles.scss'; - -render( -
Some
, - document.getElementById('root') -); diff --git a/server/graphQL/resolvers.js b/server/graphQL/resolvers.js deleted file mode 100644 index 298d18db8..000000000 --- a/server/graphQL/resolvers.js +++ /dev/null @@ -1,11 +0,0 @@ -const books = require('./books.json'); - -const authors = require('./authors.json'); - -module.exports = { - Query: { - books: () => books, - authors: () => authors, - }, - -}; diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index a140dfd82..22b459bef 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -3,12 +3,12 @@ const { Tests } = require('../../models/reactypeModels'); module.exports = { readTest: async (parent, args) => { const resp = await Tests.findOne({ _id: args.id }); - if (resp) return { description: args.id }; + if (resp) return { description: resp.name }; return { description: 'Error reading' }; }, readAllTests: async () => { const resp = await Tests.find({}); - console.log('resp', resp); + // console.log('resp', resp); if (resp) { return resp.map(elem => ({ description: elem.name })); } diff --git a/server/graphQL/typedef.js b/server/graphQL/typedef.js deleted file mode 100644 index 98aa5d1e3..000000000 --- a/server/graphQL/typedef.js +++ /dev/null @@ -1,31 +0,0 @@ -const { gql } = require('apollo-server-express'); - - -module.exports = { - typeDefs: gql` - # Comments in GraphQL strings (such as this one) start with the hash (#) symbol. - - # This "Book" type defines the queryable fields for every book in our data source. - type Book { - title: String - author: String - } - - type Author { - name: String - books: [Book] - } - - # The "Query" type is special: it lists all of the available queries that - # clients can execute, along with the return type for each. In this - # case, the "books" query returns an array of zero or more Books (defined above). - type Query { - books: [Book] - authors: [Author] - } - - type Mutation { - addBook(title: String, author: String): Book - } -`, -}; From 444d7adeb3036e5f3ebd8282e6fb3701968354b0 Mon Sep 17 00:00:00 2001 From: Anthonytorrero Date: Wed, 3 Mar 2021 20:27:16 -0800 Subject: [PATCH 011/116] update export file name from index to App --- app/src/components/bottom/BottomTabs.tsx | 2 ++ app/src/components/main/DirectChildHTMLNestable.tsx | 1 + app/src/context/initialState.ts | 2 +- app/src/helperFunctions/generateCode.ts | 9 +++++---- app/src/helperFunctions/renderChildren.tsx | 6 +++--- package.json | 2 +- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/src/components/bottom/BottomTabs.tsx b/app/src/components/bottom/BottomTabs.tsx index 58e6c7c4c..f3482bfa0 100644 --- a/app/src/components/bottom/BottomTabs.tsx +++ b/app/src/components/bottom/BottomTabs.tsx @@ -25,6 +25,7 @@ const BottomTabs = () => { // breaks if handleChange is commented out const handleChange = (event: React.ChangeEvent, value: number) => { + // console.log('value ==>', value) // value is 'code'; setTab(value); }; // Allows users to toggle project between "next.js" and "Classic React" @@ -157,3 +158,4 @@ const useStyles = makeStyles(theme => ({ })); export default BottomTabs; + diff --git a/app/src/components/main/DirectChildHTMLNestable.tsx b/app/src/components/main/DirectChildHTMLNestable.tsx index 59e069658..8c9b5f606 100644 --- a/app/src/components/main/DirectChildHTMLNestable.tsx +++ b/app/src/components/main/DirectChildHTMLNestable.tsx @@ -111,6 +111,7 @@ function DirectChildHTMLNestable({ drag(drop(ref)); return (
+ {HTMLType.placeHolderShort} {renderChildren(children)}
); diff --git a/app/src/context/initialState.ts b/app/src/context/initialState.ts index a55b44cfc..aba5ed6ee 100644 --- a/app/src/context/initialState.ts +++ b/app/src/context/initialState.ts @@ -8,7 +8,7 @@ const initialState: State = { components: [ { id: 1, - name: 'index', + name: 'App', style: {}, code: '
Drag in a component or HTML element into the canvas!
', children: [], diff --git a/app/src/helperFunctions/generateCode.ts b/app/src/helperFunctions/generateCode.ts index 2132e5fbf..a77e2bc4f 100644 --- a/app/src/helperFunctions/generateCode.ts +++ b/app/src/helperFunctions/generateCode.ts @@ -52,7 +52,8 @@ const generateUnformattedCode = ( child['tag'] = referencedHTML.tag; if ( referencedHTML.tag === 'div' || - referencedHTML.tag === 'separator' + referencedHTML.tag === 'separator' || + referencedHTML.tag === 'form' ) { child.children = getEnrichedChildren(child); } @@ -95,9 +96,9 @@ const generateUnformattedCode = ( child.tag }>`; } else if (child.tag === 'form') { - return `<${child.tag}${formatStyles(child.style)}>FORM`; + return `<${child.tag}${formatStyles( + child.style + )}>${writeNestedElements(child.children)}`; } else if (child.tag === 'p') { return `<${child.tag}${formatStyles( child.style diff --git a/app/src/helperFunctions/renderChildren.tsx b/app/src/helperFunctions/renderChildren.tsx index a35a461a0..0086c1b01 100644 --- a/app/src/helperFunctions/renderChildren.tsx +++ b/app/src/helperFunctions/renderChildren.tsx @@ -31,7 +31,7 @@ const renderChildren = (children: ChildElement[]) => { ); } // child is a non-nestable type of HTML element (everything except for divs) - else if (type === 'HTML Element' && typeId !== 11 && typeId !== 1000) { + else if (type === 'HTML Element' && typeId !== 11 && typeId !== 1000 && typeId !== 2) { return ( { ); } // child is a nestable type of HTML element (divs) - else if (type === 'HTML Element' && typeId === 11) { + else if (type === 'HTML Element' && typeId === 11 || typeId === 2) { return ( { /> ); } - else if (type === 'HTML Element' && typeId === 1000) { + else if (type === 'HTML Element' && typeId === 1000 /* || typeId === 2 */) { return ( Date: Wed, 3 Mar 2021 20:35:04 -0800 Subject: [PATCH 012/116] impliment Apollo Client --- app/src/Dashboard/Form.jsx | 1 + app/src/Dashboard/FormsContainer.jsx | 85 ++++++++++++++++++---------- app/src/index.js | 13 ++++- package.json | 1 + server/graphQL/resolvers/query.js | 8 +-- server/graphQL/typeDefs.js | 1 + server/server.js | 5 -- 7 files changed, 72 insertions(+), 42 deletions(-) diff --git a/app/src/Dashboard/Form.jsx b/app/src/Dashboard/Form.jsx index bb3956e3e..afbc61b42 100644 --- a/app/src/Dashboard/Form.jsx +++ b/app/src/Dashboard/Form.jsx @@ -6,6 +6,7 @@ const Form = (props) => { return (

{ props.description }

+
); }; diff --git a/app/src/Dashboard/FormsContainer.jsx b/app/src/Dashboard/FormsContainer.jsx index 869ebfa46..f63b3eded 100644 --- a/app/src/Dashboard/FormsContainer.jsx +++ b/app/src/Dashboard/FormsContainer.jsx @@ -1,42 +1,67 @@ import React, { useContext, useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; +import { + ApolloClient, InMemoryCache, gql, ApolloProvider, useQuery, +} from '@apollo/client'; import Form from './Form.jsx'; const FormsContainer = () => { - const [tests, updateTests] = useState([]); - - useEffect(() => { - console.log('inside useEffect'); - - fetch('http://localhost:5000/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - body: JSON.stringify({ query: '{readAllTests { description }}' }), - }) - .then(res => res.json()) - .then((resp) => { - // console.log('resp: ', resp); - const myTests = resp.data.readAllTests; - console.log('myTests: ', myTests); - updateTests(myTests); - }) - .catch(err => console.log('error in readAllTests', err)); - }, []); - - const forms = tests.map((test, index) => ); + // const [tests, updateTests] = useState([]); + /* GET using fetch */ + // useEffect(() => { + // console.log('inside useEffect'); + + // fetch('http://localhost:5000/graphql', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // Accept: 'application/json', + // }, + // body: JSON.stringify({ query: '{readAllTests { description }}' }), + // }) + // .then(res => res.json()) + // .then((resp) => { + // // console.log('resp: ', resp); + // const myTests = resp.data.readAllTests; + // console.log('myTests: ', myTests); + // updateTests(myTests); + // }) + // .catch(err => console.log('error in readAllTests', err)); + // }, []); + + + /* RULES OF HOOKS: DO NOT use hooks within hooks or conditionals */ + // useEffect(() => { + // const GET_TESTS = gql`query {readAllTests { description }}`; + // const { loading, error, data } = useQuery(GET_TESTS); + // if (loading) console.log('Loading...'); + // if (error) console.log(`Error :${error}`); + // const myTests = data.readAllTests; + // console.log('myTests: ', myTests); + // updateTests(myTests); + // }, []); + + + const GET_TESTS = gql`query {readAllTests { description id }}`; + + const { loading, error, data } = useQuery(GET_TESTS); + if (loading) return

Loading...

; + if (error) return

Error :{error}

; + + const myTests = data.readAllTests; + console.log('myTests: ', myTests); + + const forms = myTests.map((test, index) => ); return ( -
- - - - {forms} -
+
+ + + + {forms} +
); }; diff --git a/app/src/index.js b/app/src/index.js index 52611cfd4..0e34c6394 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -8,13 +8,12 @@ import SignUp from './components/login/SignUp.tsx'; import FBPassWord from './components/login/FBPassWord.tsx'; import Tutorial from './tutorial/Tutorial.tsx'; import TutorialPage from './tutorial/TutorialPage.tsx'; - /* * Dashboard */ import Dashboard from './Dashboard/FormsContainer.jsx'; import styles from './Dashboard/styles.css'; - +import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; import { HashRouter as Router, @@ -36,7 +35,14 @@ const PrivateRoute = ({ component: Component, ...rest }) => ( /> ); + +const client = new ApolloClient({ + uri: 'http://localhost:5000/graphql', + cache: new InMemoryCache() +}); + ReactDOM.render( + @@ -47,6 +53,7 @@ ReactDOM.render( - , + + , document.getElementById('app') ); diff --git a/package.json b/package.json index e47f8dd6a..355aa0217 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ }, "homepage": "https://github.com/open-source-labs/ReacType#readme", "dependencies": { + "@apollo/client": "^3.3.11", "@babel/cli": "^7.10.4", "@babel/register": "^7.10.4", "@material-ui/core": "^4.11.0", diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index 22b459bef..bb25c045c 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -3,16 +3,16 @@ const { Tests } = require('../../models/reactypeModels'); module.exports = { readTest: async (parent, args) => { const resp = await Tests.findOne({ _id: args.id }); - if (resp) return { description: resp.name }; - return { description: 'Error reading' }; + if (resp) return { description: resp.name, id: resp._id }; + return { description: 'Error reading', id: '0' }; }, readAllTests: async () => { const resp = await Tests.find({}); // console.log('resp', resp); if (resp) { - return resp.map(elem => ({ description: elem.name })); + return resp.map(elem => ({ description: elem.name, id: elem._id })); } // TODO: Go back to this to see how to handle error later - return [{ description: 'Error reading all tests' }]; + return [{ description: 'Error reading all tests', id: '0' }]; }, }; diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 6671321a1..4a02d9986 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -21,6 +21,7 @@ const typeDefs = gql` } type Test { description: String + id: ID } type Query { readTest(id: String): Test diff --git a/server/server.js b/server/server.js index 40e77b4db..d6a875f94 100644 --- a/server/server.js +++ b/server/server.js @@ -72,11 +72,6 @@ const server = new ApolloServer({ typeDefs, resolvers }); server.applyMiddleware({ app }); /** ****************************************************************** */ -// const path = require('path'); -// // Serve Static Assets -// app.use(express.static(path.resolve(__dirname, './assets'))); -/* ******************************************************************* */ - app.post( From 057324bc585c13b80e5da8c0654d0bfa9a06f467 Mon Sep 17 00:00:00 2001 From: Shana Hoehn Date: Thu, 4 Mar 2021 10:29:45 -0800 Subject: [PATCH 013/116] test useQuery --- server/graphQL/typeDefs.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 4a02d9986..3f6b381b1 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -4,16 +4,7 @@ const { gql } = require('apollo-server-express'); const typeDefs = gql` # Comments in GraphQL strings (such as this one) start with the hash (#) symbol. - - # This "Book" type defines the queryable fields for every book in our data source. - type Book { - title: String - author: Author - } - type Author { - name: String - books: [Book] - } + type Mutation { addTest(name: String): Test updateTest(id: String, name: String): Test From 20f00af4500cbf8f6144b8e2c8d89c84b6b6c702 Mon Sep 17 00:00:00 2001 From: Shana Hoehn Date: Thu, 4 Mar 2021 10:58:32 -0800 Subject: [PATCH 014/116] add psuedo-code --- app/src/Dashboard/FormsContainer.jsx | 53 +++++----------------------- app/src/index.js | 53 ++++++++++++++++------------ server/graphQL/resolvers/mutation.js | 13 ++++--- server/graphQL/resolvers/query.js | 3 ++ server/graphQL/typeDefs.js | 7 ++-- server/models/reactypeModels.js | 5 ++- server/server.js | 7 +++- 7 files changed, 65 insertions(+), 76 deletions(-) diff --git a/app/src/Dashboard/FormsContainer.jsx b/app/src/Dashboard/FormsContainer.jsx index f63b3eded..2f4086944 100644 --- a/app/src/Dashboard/FormsContainer.jsx +++ b/app/src/Dashboard/FormsContainer.jsx @@ -1,58 +1,23 @@ -import React, { useContext, useState, useEffect } from 'react'; +import React from 'react'; import { Link } from 'react-router-dom'; -import { - ApolloClient, InMemoryCache, gql, ApolloProvider, useQuery, -} from '@apollo/client'; +import { gql, useQuery } from '@apollo/client'; import Form from './Form.jsx'; +// Implement Apollo Client useQuery hook to retrieve data from the server through graphQL. This includes 2 steps: +// 1) Impliment Apollo Provider in the top component in ./src/index.js, this allows children components access to the queried data +// 2) useQuery hook will update the data stored in Apollo Client's cache and automatically trigger child components rendering const FormsContainer = () => { - // const [tests, updateTests] = useState([]); - /* GET using fetch */ - // useEffect(() => { - // console.log('inside useEffect'); - - // fetch('http://localhost:5000/graphql', { - // method: 'POST', - // headers: { - // 'Content-Type': 'application/json', - // Accept: 'application/json', - // }, - // body: JSON.stringify({ query: '{readAllTests { description }}' }), - // }) - // .then(res => res.json()) - // .then((resp) => { - // // console.log('resp: ', resp); - // const myTests = resp.data.readAllTests; - // console.log('myTests: ', myTests); - // updateTests(myTests); - // }) - // .catch(err => console.log('error in readAllTests', err)); - // }, []); - - - /* RULES OF HOOKS: DO NOT use hooks within hooks or conditionals */ - // useEffect(() => { - // const GET_TESTS = gql`query {readAllTests { description }}`; - // const { loading, error, data } = useQuery(GET_TESTS); - // if (loading) console.log('Loading...'); - // if (error) console.log(`Error :${error}`); - // const myTests = data.readAllTests; - // console.log('myTests: ', myTests); - // updateTests(myTests); - // }, []); - - + // define the graphQL query string const GET_TESTS = gql`query {readAllTests { description id }}`; - + // useQuery hook abstracts fetch request const { loading, error, data } = useQuery(GET_TESTS); if (loading) return

Loading...

; if (error) return

Error :{error}

; - + // based on resolver(readAllTests) for this query, the data is stored in the data object with the key 'readAllTests' const myTests = data.readAllTests; - console.log('myTests: ', myTests); - + // generate an array of Form components based on data const forms = myTests.map((test, index) => ); return ( diff --git a/app/src/index.js b/app/src/index.js index 0e34c6394..bfb659aac 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -8,12 +8,6 @@ import SignUp from './components/login/SignUp.tsx'; import FBPassWord from './components/login/FBPassWord.tsx'; import Tutorial from './tutorial/Tutorial.tsx'; import TutorialPage from './tutorial/TutorialPage.tsx'; -/* -* Dashboard -*/ -import Dashboard from './Dashboard/FormsContainer.jsx'; -import styles from './Dashboard/styles.css'; -import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; import { HashRouter as Router, @@ -22,10 +16,28 @@ import { Switch, } from 'react-router-dom'; + +/* +* Dashboard +*/ +import Dashboard from './Dashboard/FormsContainer.jsx'; +import styles from './Dashboard/styles.css'; +import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; + +const client = new ApolloClient({ + uri: 'http://localhost:5000/graphql', + cache: new InMemoryCache() +}); + +/* +* +*/ + + const PrivateRoute = ({ component: Component, ...rest }) => ( { + render={ (props) => { return Cookies.get('ssid') || window.localStorage.getItem('ssid') ? ( ) : ( @@ -36,24 +48,19 @@ const PrivateRoute = ({ component: Component, ...rest }) => ( ); -const client = new ApolloClient({ - uri: 'http://localhost:5000/graphql', - cache: new InMemoryCache() -}); - ReactDOM.render( - - - - - - - - - - - + + + + + + + + + + + , document.getElementById('app') ); diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index ee29ecb7b..c34b0b541 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -1,26 +1,29 @@ const { Tests } = require('../../models/reactypeModels'); +/* +* resolvers are functions that handles graphQL requests. This file defines the logic for graphQL mutation requests +* Link to Apollo Mutations: +* https://www.apollographql.com/docs/apollo-server/data/resolvers/#defining-a-resolver +*/ module.exports = { - + addTest: async (parent, args) => { const resp = await Tests.create({ name: args.name }); - // console.log('Response >>> ', resp); + console.log('Added test', resp); if (resp) return { description: args.name }; return { description: 'Error creating test' }; }, - updateTest: async (parent, args, context, info) => { + updateTest: async (parent, args) => { const filter = { _id: args.id }; const update = { name: args.name }; - const resp = await Tests.updateOne(filter, update); console.log('Updated database with', resp); if (resp) return { description: args.name }; return { description: 'Error updating' }; }, - deleteTest: async (parent, args) => { const filter = { _id: args.id }; const resp = await Tests.deleteOne(filter); diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index bb25c045c..b3a75f91d 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -1,5 +1,8 @@ const { Tests } = require('../../models/reactypeModels'); +// Link to Apollo Query Types: +// https://www.apollographql.com/docs/apollo-server/data/resolvers/#defining-a-resolver + module.exports = { readTest: async (parent, args) => { const resp = await Tests.findOne({ _id: args.id }); diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 3f6b381b1..6fc674ef1 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -1,10 +1,13 @@ const { gql } = require('apollo-server-express'); +// Link to defining a schema in Apollo: +// https://www.apollographql.com/docs/apollo-server/schema/schema/ +// The schema specifies which queries and mutations are available for clients +// to execute against your data graph. const typeDefs = gql` - # Comments in GraphQL strings (such as this one) start with the hash (#) symbol. - + type Mutation { addTest(name: String): Test updateTest(id: String, name: String): Test diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index c3de39825..51cfd11fc 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -66,14 +66,17 @@ const projectSchema = new Schema({ createdAt: { type: Date, default: Date.now } }); +// Test schema for implementing GraphQL const testSchema = new Schema({ name: String, }); +const Tests = mongoose.model('Tests', testSchema); +/* *********************************************** */ + const Users = mongoose.model('Users', userSchema); const Sessions = mongoose.model('Sessions', sessionSchema); const Projects = mongoose.model('Projects', projectSchema); -const Tests = mongoose.model('Tests', testSchema); module.exports = { Users, Sessions, Projects, Tests, diff --git a/server/server.js b/server/server.js index d6a875f94..eee4297ef 100644 --- a/server/server.js +++ b/server/server.js @@ -58,16 +58,21 @@ GraphQl Router /* ******************************************************************* */ const { ApolloServer } = require('apollo-server-express'); +// Query resolvers const query = require('./graphQL/resolvers/query'); - +// Mutation resolvers const mutation = require('./graphQL/resolvers/mutation'); +// package resolvers into one variable to pass to Apollo Server const resolvers = { Query: query, Mutation: mutation, }; +// schemas used for graphQL const { typeDefs } = require('./graphQL/typeDefs'); + +// instantiate Apollo server and attach to Express server, mounted at 'http://localhost:PORT/graphql' const server = new ApolloServer({ typeDefs, resolvers }); server.applyMiddleware({ app }); /** ****************************************************************** */ From 4fa387c11f4a6fb03b9aa7592b0c22dca875f92b Mon Sep 17 00:00:00 2001 From: Shana Hoehn Date: Thu, 4 Mar 2021 11:09:27 -0800 Subject: [PATCH 015/116] add comments for Apollo Server and Apollo Client> --- server/graphQL/typeDefs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 6fc674ef1..12ac91dc0 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -4,7 +4,7 @@ const { gql } = require('apollo-server-express'); // Link to defining a schema in Apollo: // https://www.apollographql.com/docs/apollo-server/schema/schema/ // The schema specifies which queries and mutations are available for clients -// to execute against your data graph. +// to execute against your data graph const typeDefs = gql` From 5bb9a2e31c492784d0e0c200fba6f9eaa6084dbe Mon Sep 17 00:00:00 2001 From: Shana Hoehn Date: Thu, 4 Mar 2021 11:14:06 -0800 Subject: [PATCH 016/116] =?UTF-8?q?add=20comments=20for=20Apollo=20Server?= =?UTF-8?q?=20and=20Apollo=20Client=20Co-authored-by:=20khuong=20Nguyen=20?= =?UTF-8?q?=20Co-authored-by:=20Shana=20Hoehn?= =?UTF-8?q?=20=E2=80=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/graphQL/typeDefs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 12ac91dc0..6fc674ef1 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -4,7 +4,7 @@ const { gql } = require('apollo-server-express'); // Link to defining a schema in Apollo: // https://www.apollographql.com/docs/apollo-server/schema/schema/ // The schema specifies which queries and mutations are available for clients -// to execute against your data graph +// to execute against your data graph. const typeDefs = gql` From 32e6b53a95c9deed27f7905522f58af5be1c257f Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Thu, 4 Mar 2021 14:05:36 -0800 Subject: [PATCH 017/116] prepare to pull from Shana branch router --- app/src/Dashboard/Form.jsx | 1 + app/src/Dashboard/FormsContainer.jsx | 21 ++++++++------------- app/src/Dashboard/styles.css | 5 +++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/Dashboard/Form.jsx b/app/src/Dashboard/Form.jsx index afbc61b42..30e1e82ea 100644 --- a/app/src/Dashboard/Form.jsx +++ b/app/src/Dashboard/Form.jsx @@ -7,6 +7,7 @@ const Form = (props) => {

{ props.description }

+
); }; diff --git a/app/src/Dashboard/FormsContainer.jsx b/app/src/Dashboard/FormsContainer.jsx index f63b3eded..27eef8b78 100644 --- a/app/src/Dashboard/FormsContainer.jsx +++ b/app/src/Dashboard/FormsContainer.jsx @@ -8,11 +8,11 @@ import Form from './Form.jsx'; const FormsContainer = () => { - // const [tests, updateTests] = useState([]); + /* GET using fetch */ + // const [tests, updateTests] = useState([]); // useEffect(() => { // console.log('inside useEffect'); - // fetch('http://localhost:5000/graphql', { // method: 'POST', // headers: { @@ -32,16 +32,9 @@ const FormsContainer = () => { // }, []); - /* RULES OF HOOKS: DO NOT use hooks within hooks or conditionals */ - // useEffect(() => { - // const GET_TESTS = gql`query {readAllTests { description }}`; - // const { loading, error, data } = useQuery(GET_TESTS); - // if (loading) console.log('Loading...'); - // if (error) console.log(`Error :${error}`); - // const myTests = data.readAllTests; - // console.log('myTests: ', myTests); - // updateTests(myTests); - // }, []); + /* RULES OF HOOKS: DO NOT useState and useQuery together */ + /* updateTest triggers re-rendering which in turn triggers useQuery and create an infinite loop. Just use the */ + /* NOTE: useEffect is use for async function such as fetching. When using useQuery, loading is immediately return and the component will automatically render when the result is returned */ const GET_TESTS = gql`query {readAllTests { description id }}`; @@ -60,7 +53,9 @@ const FormsContainer = () => { - {forms} +
+ {forms} +
); }; diff --git a/app/src/Dashboard/styles.css b/app/src/Dashboard/styles.css index f7c3f3c56..d6cb05edb 100644 --- a/app/src/Dashboard/styles.css +++ b/app/src/Dashboard/styles.css @@ -23,3 +23,8 @@ $button-blue: #24BCFF; */ width: 250px; } +.formContainer { + display: flex; + flex-flow: row wrap; +} + From 21cdd772250351d8b553fc5ffb14c168ebffe3b0 Mon Sep 17 00:00:00 2001 From: Shana Hoehn Date: Thu, 4 Mar 2021 17:12:45 -0800 Subject: [PATCH 018/116] add likes button --- server/graphQL/resolvers/mutation.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index c34b0b541..cccb9a80d 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -6,8 +6,7 @@ const { Tests } = require('../../models/reactypeModels'); */ -module.exports = { - +module.exports = { addTest: async (parent, args) => { const resp = await Tests.create({ name: args.name }); console.log('Added test', resp); From 832567efec84433fe86295b76d5b0a39d5e36159 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Thu, 4 Mar 2021 21:24:03 -0800 Subject: [PATCH 019/116] implement mutation with variables --- app/src/Dashboard/Form.jsx | 42 ++++++++++++++++++++++++++-- app/src/Dashboard/FormsContainer.jsx | 4 +-- server/apollo-server.js | 0 server/graphQL/resolvers/mutation.js | 23 +++++++++------ server/graphQL/resolvers/query.js | 8 +++--- server/graphQL/typeDefs.js | 9 +++--- server/models/reactypeModels.js | 1 + 7 files changed, 65 insertions(+), 22 deletions(-) delete mode 100644 server/apollo-server.js diff --git a/app/src/Dashboard/Form.jsx b/app/src/Dashboard/Form.jsx index 30e1e82ea..5b6dc4350 100644 --- a/app/src/Dashboard/Form.jsx +++ b/app/src/Dashboard/Form.jsx @@ -1,14 +1,50 @@ import React from 'react'; +import { gql, useMutation } from '@apollo/client'; + const Form = (props) => { + // const TEST = gql`mutation UpdateTest { + // updateTest (id:"6041a075cf29434bf8e4552e", name: "first entry with HEC", likes: 23) { + // description + // id + // likes + // } + // }`; + const ADD_LIKE = gql` + mutation UpdateTest($id: ID, $name: String, $likes: Int) { + updateTest(id: $id, name: $name, likes: $likes ) + { + description + id + likes + } + } + `; + const [ updateTest, { data }] = useMutation(ADD_LIKE); + // console.log('mutation returned data >>> ', data); + + // go to bed + function handleClick(e) { + e.preventDefault(); + // IMPORTANT: DO NOT ADD extra comma to the last line of the variable object, the query will not work + const myVar = { variables: { + id: props.id, + name: "testname", + likes: (props.likes + 1) + } + }; + updateTest(myVar); + } + return (

{ props.description }

- - -
); + +

Likes: { props.likes }

+ + ); }; export default Form; diff --git a/app/src/Dashboard/FormsContainer.jsx b/app/src/Dashboard/FormsContainer.jsx index 78a72a150..a10d785c5 100644 --- a/app/src/Dashboard/FormsContainer.jsx +++ b/app/src/Dashboard/FormsContainer.jsx @@ -10,7 +10,7 @@ import Form from './Form.jsx'; const FormsContainer = () => { // define the graphQL query string - const GET_TESTS = gql`query {readAllTests { description id }}`; + const GET_TESTS = gql`query {readAllTests { description id likes }}`; // useQuery hook abstracts fetch request const { loading, error, data } = useQuery(GET_TESTS); if (loading) return

Loading...

; @@ -18,7 +18,7 @@ const FormsContainer = () => { // based on resolver(readAllTests) for this query, the data is stored in the data object with the key 'readAllTests' const myTests = data.readAllTests; // generate an array of Form components based on data - const forms = myTests.map((test, index) => ); + const forms = myTests.map((test, index) => ); return (
diff --git a/server/apollo-server.js b/server/apollo-server.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index c34b0b541..cb01aa017 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -9,25 +9,30 @@ const { Tests } = require('../../models/reactypeModels'); module.exports = { addTest: async (parent, args) => { - const resp = await Tests.create({ name: args.name }); + const resp = await Tests.create({ name: args.name, likes: args.likes }); console.log('Added test', resp); - if (resp) return { description: args.name }; - return { description: 'Error creating test' }; + if (resp) return { description: resp.name, id: resp._id, likes: args.likes }; + return { description: 'Error creating test', id: 'unknown', likes: 0 }; }, updateTest: async (parent, args) => { + // console.log('args >>> ', args); const filter = { _id: args.id }; - const update = { name: args.name }; - const resp = await Tests.updateOne(filter, update); + const update = { name: args.name, likes: args.likes }; + const options = { new: true }; + const resp = await Tests.findOneAndUpdate(filter, update, options); + console.log('Updated database with', resp); - if (resp) return { description: args.name }; - return { description: 'Error updating' }; + + + if (resp) return { description: resp.name, id: resp._id, likes: resp.likes }; + return { description: 'Error updating', id: resp._id, likes: 0 }; }, deleteTest: async (parent, args) => { const filter = { _id: args.id }; const resp = await Tests.deleteOne(filter); - if (resp) return { description: args.name }; - return { description: 'Error updating' }; + if (resp) return { description: resp.name, id: resp._id, likes: 0 }; + return { description: 'Error updating', likes: 0 }; }, }; diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index b3a75f91d..d85097b0e 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -6,16 +6,16 @@ const { Tests } = require('../../models/reactypeModels'); module.exports = { readTest: async (parent, args) => { const resp = await Tests.findOne({ _id: args.id }); - if (resp) return { description: resp.name, id: resp._id }; - return { description: 'Error reading', id: '0' }; + if (resp) return { description: resp.name, id: resp._id, likes: resp.likes }; + return { description: 'Error reading', id: '0', likes: '0' }; }, readAllTests: async () => { const resp = await Tests.find({}); // console.log('resp', resp); if (resp) { - return resp.map(elem => ({ description: elem.name, id: elem._id })); + return resp.map(elem => ({ description: elem.name, id: elem._id, likes: elem.likes })); } // TODO: Go back to this to see how to handle error later - return [{ description: 'Error reading all tests', id: '0' }]; + return [{ description: 'Error reading all tests', id: '0', likes: '0' }]; }, }; diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 6fc674ef1..0366ab585 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -9,16 +9,17 @@ const { gql } = require('apollo-server-express'); const typeDefs = gql` type Mutation { - addTest(name: String): Test - updateTest(id: String, name: String): Test - deleteTest(id: String): Test + addTest(name: String, likes: Int): Test + updateTest(id: ID, name: String, likes: Int): Test + deleteTest(id: ID): Test } type Test { description: String + likes: Int id: ID } type Query { - readTest(id: String): Test + readTest(id: ID): Test readAllTests: [Test] } `; diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index 51cfd11fc..bd05d7a5e 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -69,6 +69,7 @@ const projectSchema = new Schema({ // Test schema for implementing GraphQL const testSchema = new Schema({ name: String, + likes: Number, }); const Tests = mongoose.model('Tests', testSchema); /* *********************************************** */ From e4d37c689480c31af08646c6b8e485b49aa55fbc Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Fri, 5 Mar 2021 10:01:14 -0800 Subject: [PATCH 020/116] implement variable validation with propType in Form.jsx --- app/src/Dashboard/Form.jsx | 52 +++++++++++++++------------- app/src/Dashboard/FormsContainer.jsx | 2 +- app/src/components/top/NavBar.tsx | 2 +- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/app/src/Dashboard/Form.jsx b/app/src/Dashboard/Form.jsx index 5b6dc4350..4e8fbec45 100644 --- a/app/src/Dashboard/Form.jsx +++ b/app/src/Dashboard/Form.jsx @@ -1,50 +1,54 @@ import React from 'react'; import { gql, useMutation } from '@apollo/client'; +import PropTypes from 'prop-types'; +const Form = ({ description, id, likes }) => { -const Form = (props) => { - // const TEST = gql`mutation UpdateTest { - // updateTest (id:"6041a075cf29434bf8e4552e", name: "first entry with HEC", likes: 23) { - // description - // id - // likes - // } - // }`; const ADD_LIKE = gql` mutation UpdateTest($id: ID, $name: String, $likes: Int) { updateTest(id: $id, name: $name, likes: $likes ) - { - description - id - likes + { + description + id + likes } - } - `; - const [ updateTest, { data }] = useMutation(ADD_LIKE); - // console.log('mutation returned data >>> ', data); + }`; + + const [updateTest] = useMutation(ADD_LIKE); // go to bed function handleClick(e) { e.preventDefault(); // IMPORTANT: DO NOT ADD extra comma to the last line of the variable object, the query will not work - const myVar = { variables: { - id: props.id, - name: "testname", - likes: (props.likes + 1) - } + const myVar = { + variables: + { + id, + name: 'testname', + likes: likes + 1, + }, }; + // send Mutation updateTest(myVar); } return (
-

{ props.description }

- -

Likes: { props.likes }

+

{ description }

+ +

Likes: { likes }

); }; +// Variable validation using propTypes +Form.propTypes = { + description: PropTypes.string.isRequired, + id: PropTypes.id.isRequired, + likes: PropTypes.number.isRequired, +}; + + export default Form; diff --git a/app/src/Dashboard/FormsContainer.jsx b/app/src/Dashboard/FormsContainer.jsx index a10d785c5..9ec823163 100644 --- a/app/src/Dashboard/FormsContainer.jsx +++ b/app/src/Dashboard/FormsContainer.jsx @@ -12,7 +12,7 @@ const FormsContainer = () => { // define the graphQL query string const GET_TESTS = gql`query {readAllTests { description id likes }}`; // useQuery hook abstracts fetch request - const { loading, error, data } = useQuery(GET_TESTS); + const { loading, error, data } = useQuery(GET_TESTS, { pollInterval: 1000 } ); if (loading) return

Loading...

; if (error) return

Error :{error}

; // based on resolver(readAllTests) for this query, the data is stored in the data object with the key 'readAllTests' diff --git a/app/src/components/top/NavBar.tsx b/app/src/components/top/NavBar.tsx index cc672ab31..7d60a9376 100644 --- a/app/src/components/top/NavBar.tsx +++ b/app/src/components/top/NavBar.tsx @@ -168,7 +168,7 @@ export default function NavBar(props) { {/* ==================================Dashboard Button================================================== */} - + +
+ ); +}; + +// Variable validation using propTypes +Project.propTypes = { + name: PropTypes.string.isRequired, + projId: PropTypes.string.isRequired, + userId: PropTypes.string.isRequired, + likes: PropTypes.number.isRequired, +}; + + +export default Project; diff --git a/app/src/Dashboard/ProjectContainer.jsx b/app/src/Dashboard/ProjectContainer.jsx new file mode 100644 index 000000000..ab95bf04a --- /dev/null +++ b/app/src/Dashboard/ProjectContainer.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +import { gql, useQuery } from '@apollo/client'; + +import Project from './Project.jsx'; +// Implement Apollo Client useQuery hook to retrieve data from the server through graphQL. This includes 2 steps: +// 1) Impliment Apollo Provider in the top component in ./src/index.js, this allows children components access to the queried data +// 2) useQuery hook will update the data stored in Apollo Client's cache and automatically trigger child components rendering + +const ProjectContainer = () => { + // define the graphQL query string + const GET_PROJECTS = gql`query GetAllProjects($userId: ID) { + getAllProjects(userId: $userId) { + name + likes + id + } + }`; + + // For now, if cookie exists, pull projects for the specific user only, otherwise pull all projects + const userSSID = window.localStorage.getItem('ssid') || ''; + let myVar = {}; + if (userSSID !== 'guest') { + myVar = { userId: userSSID }; + } + // useQuery hook abstracts fetch request + const { loading, error, data } = useQuery(GET_PROJECTS, { pollInterval: 2000, variables: myVar } ); // Need to find where the userId is stored for the logged in user. + if (loading) return

Loading...

; + if (error) return

Error :{error}

; + // based on resolver(getAllProject) for this query, the data is stored in the data object with the key 'getAllProjects' + const myProjs = data.getAllProjects; + console.log('Projects >>> ', myProjs); + // generate an array of Project components based on data + const projects = myProjs.map((proj, index) => ); + + return ( +
+ + + +
+ {projects} +
+
+ ); +}; + +export default ProjectContainer; diff --git a/app/src/Dashboard/navbar.jsx b/app/src/Dashboard/navbar.jsx new file mode 100644 index 000000000..e69de29bb diff --git a/app/src/Dashboard/styles.css b/app/src/Dashboard/styles.css index d6cb05edb..fb98a4c33 100644 --- a/app/src/Dashboard/styles.css +++ b/app/src/Dashboard/styles.css @@ -8,7 +8,7 @@ $light-grey: #bfcbd4; $light-blue: #759cc9; $button-blue: #24BCFF; */ -.form { +.form , .project{ display: flex; flex-direction: column; background-color: white; @@ -23,7 +23,7 @@ $button-blue: #24BCFF; */ width: 250px; } -.formContainer { +.formContainer , .projectContainer{ display: flex; flex-flow: row wrap; } diff --git a/app/src/index.js b/app/src/index.js index bfb659aac..d0dd0cf34 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -20,7 +20,9 @@ import { /* * Dashboard */ -import Dashboard from './Dashboard/FormsContainer.jsx'; +// import TestDashboard from './Dashboard/FormsContainer.jsx'; +import ProjectDashboard from './Dashboard/ProjectContainer.jsx'; + import styles from './Dashboard/styles.css'; import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; @@ -56,7 +58,7 @@ ReactDOM.render( - + diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index b39a8a70e..969fec776 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -45,7 +45,7 @@ const Project = { if (resp) { return ({ name: resp.name, - projId: resp._id, + id: resp._id, userId: resp.userId, likes: resp.likes, })} @@ -53,7 +53,7 @@ const Project = { // TODO: Go back to this to see how to handle error later return { name: 'Error', - projId: 'Error', + id: 'Error', userId: 'Error', likes: -1, }; @@ -74,7 +74,7 @@ const Project = { if (resp) { return ({ name: resp.name, - projId: resp._id, + id: resp._id, userId: resp.userId, likes: resp.likes, })} @@ -82,7 +82,7 @@ const Project = { // TODO: Go back to this to see how to handle error later return { name: 'Error', - projId: 'Error', + id: 'Error', userId: 'Error', likes: -1, }; diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index 3d996d8a4..1248fd02f 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -30,39 +30,48 @@ const Project = { if (resp) { return ({ name: resp.name, - projId: resp._id, + id: resp._id, userId: resp.userId, likes: resp.likes, - })} + }); + } // TODO: Go back to this to see how to handle error later - return { + return { name: 'Error', - projId: 'Error', + id: 'Error', userId: 'Error', likes: -1, }; }, - getAllProjects: async () => { - const resp = await Projects.find({}); + getAllProjects: async (parent, { userId }) => { + let resp = await Projects.find({}); + // console.log('resp >>> ', resp); + if (userId) { + // use loosely equal for the callback because there are some discrepancy between the type of userId from the db vs from the mutation query + resp = resp.filter(proj => proj.userId == userId); + } + if (resp) { return resp.map(proj => ({ name: proj.name, - projId: proj._id, + id: proj._id, userId: proj.userId, likes: proj.likes, })); } + // TODO: Go back to this to see how to handle error later - return [{ + return [{ name: 'Error', - projId: 'Error', + id: 'Error', userId: 'Error', likes: -1, }]; }, + }; module.exports = { diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 5ed202401..8048731fc 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -35,13 +35,14 @@ const Project = gql` type Project { name: String! likes: Int - projId: ID! + id: ID! userId: ID! } type Query { getProject(projId: ID!): Project - getAllProjects: [Project] + getAllProjects(userId: ID): [Project] + getLikedProjects(userId: ID): [Project] } `; diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index b3577f6fd..98ccdcd74 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -58,7 +58,7 @@ const sessionSchema = new Schema({ const projectSchema = new Schema({ name: String, - likes: Number, + likes: { type: Number, default: 0 }, project: { type: Object, required: true }, userId: { type: Schema.Types.ObjectId, From 99b701b2bf124e2358d4f5a82f11d6bb5911ad25 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Sat, 6 Mar 2021 00:24:46 -0800 Subject: [PATCH 023/116] implement project download feature for each user --- app/src/Dashboard/Project.jsx | 44 ++++++++++++++++++-- app/src/Dashboard/ProjectContainer.jsx | 20 +++++---- app/src/helperFunctions/auth.ts | 5 +++ app/src/helperFunctions/projectGetSaveDel.ts | 3 +- server/controllers/projectController.js | 4 +- server/graphQL/resolvers/mutation.js | 7 +++- server/graphQL/resolvers/query.js | 9 +++- server/graphQL/typeDefs.js | 4 +- server/models/reactypeModels.js | 1 + 9 files changed, 79 insertions(+), 18 deletions(-) diff --git a/app/src/Dashboard/Project.jsx b/app/src/Dashboard/Project.jsx index 25ccde4f7..cf6eec383 100644 --- a/app/src/Dashboard/Project.jsx +++ b/app/src/Dashboard/Project.jsx @@ -3,7 +3,7 @@ import { gql, useMutation } from '@apollo/client'; import PropTypes from 'prop-types'; -const Project = ({ name, likes, projId, userId }) => { +const Project = ({ name, likes, projId, userId, username }) => { // IMPORTANT: // 1) schema change projId => id to allows Apollo Client cache auto-update. Only works with 'id' @@ -20,7 +20,19 @@ const Project = ({ name, likes, projId, userId }) => { const [addLike] = useMutation(ADD_LIKE); - function handleClick(e) { + + const MAKE_COPY = gql` + mutation MakeCopy ($userId: ID!, $projId: ID!, $username: String!) { + makeCopy(userId: $userId, projId: $projId, username: $username) + { + id + } + }`; + + const [ makeCopy ] = useMutation(MAKE_COPY); + + + function handleLike(e) { e.preventDefault(); // IMPORTANT: DO NOT ADD extra comma to the last line of the variable object, the query will not work const myVar = { @@ -34,12 +46,37 @@ const Project = ({ name, likes, projId, userId }) => { addLike(myVar); } + // Use current user info to make a make copy of another user's project + const currUserSSID = window.localStorage.getItem('ssid') || 'unavailable'; + const currUsername = window.localStorage.getItem('username') || 'unavailable'; + // if (userSSID !== 'guest') { + // myVar = { userId: userSSID }; + // } + + function handleDownload(e) { + e.preventDefault(); + const myVar = { + variables: + { + projId, + userId: currUserSSID, + username: currUsername, + }, + }; + // send Mutation + makeCopy(myVar); + } + return (

Project: { name }

{/*

Project ID: {projId}

*/} +

Author: { username }

Likes: { likes }

- +
+ + +
); }; @@ -49,6 +86,7 @@ Project.propTypes = { name: PropTypes.string.isRequired, projId: PropTypes.string.isRequired, userId: PropTypes.string.isRequired, + username: PropTypes.string.isRequired, likes: PropTypes.number.isRequired, }; diff --git a/app/src/Dashboard/ProjectContainer.jsx b/app/src/Dashboard/ProjectContainer.jsx index ab95bf04a..bdaa10675 100644 --- a/app/src/Dashboard/ProjectContainer.jsx +++ b/app/src/Dashboard/ProjectContainer.jsx @@ -14,16 +14,20 @@ const ProjectContainer = () => { getAllProjects(userId: $userId) { name likes - id + id + userId + username } }`; - // For now, if cookie exists, pull projects for the specific user only, otherwise pull all projects - const userSSID = window.localStorage.getItem('ssid') || ''; let myVar = {}; - if (userSSID !== 'guest') { - myVar = { userId: userSSID }; - } + // Need this for the individual user dasboard, for now, dashboard shows all projects from all users + const userSSID = window.localStorage.getItem('ssid') || 'unavailable'; + const username = window.localStorage.getItem('username') || 'unavailable'; + // if (userSSID !== 'guest') { + // myVar = { userId: userSSID }; + // } + // useQuery hook abstracts fetch request const { loading, error, data } = useQuery(GET_PROJECTS, { pollInterval: 2000, variables: myVar } ); // Need to find where the userId is stored for the logged in user. if (loading) return

Loading...

; @@ -36,12 +40,14 @@ const ProjectContainer = () => { key= { index } name = {proj.name} likes = {proj.likes} - userId = {userSSID} + userId = {proj.userId} + username = {proj.username} projId = {proj.id} />); return (
+

Public Dashboard

diff --git a/app/src/helperFunctions/auth.ts b/app/src/helperFunctions/auth.ts index acd4c2eb4..d1f1ae8eb 100644 --- a/app/src/helperFunctions/auth.ts +++ b/app/src/helperFunctions/auth.ts @@ -30,6 +30,9 @@ export const sessionIsCreated = ( if (data.sessionId && typeof data.sessionId === 'string') { // check that a session id was passed down window.localStorage.setItem('ssid', data.sessionId); + // save username locally, will be added to saved project for each user + window.localStorage.setItem('username', username); + return 'Success'; } return data; // error message returned from userController.verifyUser @@ -61,6 +64,8 @@ export const newUserIsCreated = ( if (data.sessionId && typeof data.sessionId === 'string') { // check that a session id was passed down window.localStorage.setItem('ssid', data.sessionId); + // save username locally, will be added to saved project for each user + window.localStorage.setItem('username', username); return 'Success'; } return data; // response is either Email Taken or Username Taken, refer to userController.createUser diff --git a/app/src/helperFunctions/projectGetSaveDel.ts b/app/src/helperFunctions/projectGetSaveDel.ts index fa8b27dca..a88a4a1a7 100644 --- a/app/src/helperFunctions/projectGetSaveDel.ts +++ b/app/src/helperFunctions/projectGetSaveDel.ts @@ -32,7 +32,8 @@ export const saveProject = ( const body = JSON.stringify({ name, project: workspace, - userId: window.localStorage.getItem('ssid') + userId: window.localStorage.getItem('ssid'), + username: window.localStorage.getItem('username'), }); const project = fetch(`${serverURL}/saveProject`, { method: 'POST', diff --git a/server/controllers/projectController.js b/server/controllers/projectController.js index 14cb7ccb1..cbdd62758 100644 --- a/server/controllers/projectController.js +++ b/server/controllers/projectController.js @@ -6,11 +6,11 @@ const projectController = {}; projectController.saveProject = (req, res, next) => { // pull project name and project itself from body - const { name, project, userId } = req.body; + const { name, project, userId, username } = req.body; // pull ssid from cookies for user id Projects.findOneAndUpdate( // looks in projects collection for project by user and name - { name, userId }, + { name, userId, username }, // update or insert the project { project }, // Options: diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index 969fec776..5edc01dde 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -47,6 +47,7 @@ const Project = { name: resp.name, id: resp._id, userId: resp.userId, + username: resp.username, likes: resp.likes, })} @@ -55,11 +56,12 @@ const Project = { name: 'Error', id: 'Error', userId: 'Error', + username: 'Error', likes: -1, }; }, - makeCopy: async (parent, { projId, userId }) => { + makeCopy: async (parent, { projId, userId, username }) => { const filter = { _id: projId }; const target = await Projects.findOne(filter); // make a copy with the passed in userId @@ -68,6 +70,7 @@ const Project = { likes: target.likes, project: target.project, userId, + username, }; const resp = await Projects.create(copy); @@ -76,6 +79,7 @@ const Project = { name: resp.name, id: resp._id, userId: resp.userId, + username: resp.username, likes: resp.likes, })} @@ -84,6 +88,7 @@ const Project = { name: 'Error', id: 'Error', userId: 'Error', + username: 'Error', likes: -1, }; }, diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index 1248fd02f..94de3cbca 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -1,4 +1,4 @@ -const { Tests, Projects } = require('../../models/reactypeModels'); +const { Tests, Projects, Users } = require('../../models/reactypeModels'); // Link to Apollo Query Types: // https://www.apollographql.com/docs/apollo-server/data/resolvers/#defining-a-resolver @@ -32,6 +32,7 @@ const Project = { name: resp.name, id: resp._id, userId: resp.userId, + username: resp.username, likes: resp.likes, }); } @@ -41,13 +42,15 @@ const Project = { name: 'Error', id: 'Error', userId: 'Error', + username: 'Error', likes: -1, }; }, getAllProjects: async (parent, { userId }) => { + let resp = await Projects.find({}); - // console.log('resp >>> ', resp); + // console.log('getAllProjects resp >>> ', resp); if (userId) { // use loosely equal for the callback because there are some discrepancy between the type of userId from the db vs from the mutation query resp = resp.filter(proj => proj.userId == userId); @@ -58,6 +61,7 @@ const Project = { name: proj.name, id: proj._id, userId: proj.userId, + username: proj.username, likes: proj.likes, })); } @@ -67,6 +71,7 @@ const Project = { name: 'Error', id: 'Error', userId: 'Error', + username: 'Error', likes: -1, }]; }, diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 8048731fc..92c85d0d4 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -29,7 +29,7 @@ const Project = gql` type Mutation { addLike(projId: ID!, likes: Int!): Project - makeCopy(projId: ID!, userId: ID!): Project + makeCopy(projId: ID!, userId: ID!, username: String!): Project } type Project { @@ -37,12 +37,12 @@ const Project = gql` likes: Int id: ID! userId: ID! + username: String! } type Query { getProject(projId: ID!): Project getAllProjects(userId: ID): [Project] - getLikedProjects(userId: ID): [Project] } `; diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index 98ccdcd74..61bfbcbad 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -64,6 +64,7 @@ const projectSchema = new Schema({ type: Schema.Types.ObjectId, ref: 'Users', }, + username: {type: String, required: true }, createdAt: { type: Date, default: Date.now } }); From 602ebe8b6eeda8e5477cb05f423f664065097245 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Sat, 6 Mar 2021 00:26:05 -0800 Subject: [PATCH 024/116] add field: username to projectSchema --- server/models/reactypeModels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index 61bfbcbad..22b49a3d8 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -64,7 +64,7 @@ const projectSchema = new Schema({ type: Schema.Types.ObjectId, ref: 'Users', }, - username: {type: String, required: true }, + username: {type: String, required: true }, createdAt: { type: Date, default: Date.now } }); From 2a6051cfd0e0683b14fc7dd6ff34bd523b65c244 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Sat, 6 Mar 2021 00:26:43 -0800 Subject: [PATCH 025/116] add field:username to return Project type --- server/graphQL/resolvers/mutation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index 5edc01dde..3d8663f40 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -49,7 +49,7 @@ const Project = { userId: resp.userId, username: resp.username, likes: resp.likes, - })} + })} // TODO: Go back to this to see how to handle error later return { From 9f2a207e8e411cc3451d45d616fbaded9df0b917 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Sat, 6 Mar 2021 11:06:56 -0800 Subject: [PATCH 026/116] for sharing with Shana --- app/src/Dashboard/Project.jsx | 8 ++++---- app/src/Dashboard/ProjectContainer.jsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/Dashboard/Project.jsx b/app/src/Dashboard/Project.jsx index cf6eec383..cc6f08b89 100644 --- a/app/src/Dashboard/Project.jsx +++ b/app/src/Dashboard/Project.jsx @@ -3,7 +3,7 @@ import { gql, useMutation } from '@apollo/client'; import PropTypes from 'prop-types'; -const Project = ({ name, likes, projId, userId, username }) => { +const Project = ({ name, likes, id, userId, username }) => { // IMPORTANT: // 1) schema change projId => id to allows Apollo Client cache auto-update. Only works with 'id' @@ -38,7 +38,7 @@ const Project = ({ name, likes, projId, userId, username }) => { const myVar = { variables: { - projId, + projId: id, likes: likes + 1, }, }; @@ -58,7 +58,7 @@ const Project = ({ name, likes, projId, userId, username }) => { const myVar = { variables: { - projId, + projId: id, userId: currUserSSID, username: currUsername, }, @@ -84,7 +84,7 @@ const Project = ({ name, likes, projId, userId, username }) => { // Variable validation using propTypes Project.propTypes = { name: PropTypes.string.isRequired, - projId: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, userId: PropTypes.string.isRequired, username: PropTypes.string.isRequired, likes: PropTypes.number.isRequired, diff --git a/app/src/Dashboard/ProjectContainer.jsx b/app/src/Dashboard/ProjectContainer.jsx index bdaa10675..da044bd85 100644 --- a/app/src/Dashboard/ProjectContainer.jsx +++ b/app/src/Dashboard/ProjectContainer.jsx @@ -42,7 +42,7 @@ const ProjectContainer = () => { likes = {proj.likes} userId = {proj.userId} username = {proj.username} - projId = {proj.id} + id = {proj.id} />); return ( From 615d291fc8048d43ff056111a91294c19a600b5c Mon Sep 17 00:00:00 2001 From: Edward Park Date: Sat, 6 Mar 2021 16:36:16 -0800 Subject: [PATCH 027/116] nested undo not working --- app/src/components/bottom/CodePreview.tsx | 2 +- app/src/components/main/Canvas.tsx | 55 ++++++- app/src/components/main/CanvasContainer.tsx | 4 + app/src/containers/RightContainer.tsx | 89 +++++++++++ app/src/context/initialState.ts | 3 +- app/src/helperFunctions/customHook.ts | 16 ++ app/src/interfaces/Interfaces.ts | 10 ++ app/src/public/index-prod.ejs | 2 + app/src/public/index.ejs | 2 + app/src/reducers/componentReducer.ts | 160 +++++++++++++++++++- 10 files changed, 335 insertions(+), 8 deletions(-) create mode 100644 app/src/helperFunctions/customHook.ts diff --git a/app/src/components/bottom/CodePreview.tsx b/app/src/components/bottom/CodePreview.tsx index 1349b2553..b099ec4f4 100644 --- a/app/src/components/bottom/CodePreview.tsx +++ b/app/src/components/bottom/CodePreview.tsx @@ -27,7 +27,7 @@ const CodePreview: React.FC<{ const currentComponent = state.components.find( (elem: Component) => elem.id === state.canvasFocus.componentId ); -console.log('currentComp in CodePreview', currentComponent) +// console.log('currentComp in CodePreview', currentComponent) const handleCodeSnipChange = val => { currentComponent.code = val; }; diff --git a/app/src/components/main/Canvas.tsx b/app/src/components/main/Canvas.tsx index bc9410076..939a9b949 100644 --- a/app/src/components/main/Canvas.tsx +++ b/app/src/components/main/Canvas.tsx @@ -1,22 +1,28 @@ -import React, { useContext } from 'react'; +import React, { useState, useContext } from 'react'; import { useDrop, DropTargetMonitor } from 'react-dnd'; +import customHooks from '../helperFunctions/customHook'; +import _ from 'lodash'; import { ItemTypes } from '../../constants/ItemTypes'; import StateContext from '../../context/context'; import { Component, DragItem } from '../../interfaces/Interfaces'; import { combineStyles } from '../../helperFunctions/combineStyles'; import renderChildren from '../../helperFunctions/renderChildren'; +// const snapStateArr = []; function Canvas() { const [state, dispatch] = useContext(StateContext); + // const [ prevState, setPrevState ] = useState>([]); // NOT USING // find the current component to render on the canvas const currentComponent: Component = state.components.find( (elem: Component) => elem.id === state.canvasFocus.componentId - ); + ); + // console.log('currentComponent', currentComponent) // changes focus of the canvas to a new component / child const changeFocus = (componentId: number, childId: number | null) => { dispatch({ type: 'CHANGE FOCUS', payload: { componentId, childId } }); + }; // onClickHandler is responsible for changing the focused component and child component function onClickHandler(event) { @@ -24,15 +30,52 @@ function Canvas() { // note: a null value for the child id means that we are focusing on the top-level component rather than any child changeFocus(state.canvasFocus.componentId, null); } + function onChangeHandler(event) { + // console.log('working', event.target) + } + + // stores a limited snapshot of previous state to use in the useDrop function, for UNDO functionality + // const snapStateArr = []; + const snapShotFunc = () => { + // make a deep clone of state ( JSON.parse(JSON.stringify()) ? ) + // function inner() { + const deepCopiedState = JSON.parse(JSON.stringify(state)); + // console.log('deepCopiedState', deepCopiedState); + // stateSnapArr.push(deepCopiedState); + state.past.push(deepCopiedState.components[0].children); + // state.past.push(deepCopiedState); + // console.log('state after push', state) + console.log('state in canvas', state.past) + // return; + // snapStateArr.push(5); + // return snapStateArr; + // } + // inner(); + + // return; + // const prevCount = customHooks(state); + // console.log('prevCount', prevCount); + // console.log('state', state); + + // setPrevState( previousState => { + // return [...previousState].push(deepCopiedState); + // }) + // console.log('prevState: ', prevState) +} + // This hook will allow the user to drag items from the left panel on to the canvas const [{ isOver }, drop] = useDrop({ accept: ItemTypes.INSTANCE, drop: (item: DragItem, monitor: DropTargetMonitor) => { - const didDrop = monitor.didDrop(); // returns false for direct drop target + const didDrop = monitor.didDrop(); + // returns false for direct drop target + //code here + // 6.0 didDrop is firing when HTML tags are moved up + snapShotFunc(); // < ------ snapShotFunc here if (didDrop) { return; } - console.log('item', item) + // console.log('item', item) // if item dropped is going to be a new instance (i.e. it came from the left panel), then create a new child component if (item.newInstance) { dispatch({ @@ -75,10 +118,12 @@ function Canvas() { // Direct children are draggable/clickable const canvasStyle = combineStyles(defaultCanvasStyle, currentComponent.style); return ( -
+
{/* currentComponent is the selected component on Left Panel (eg: App or Index with green dot to the left) */} {renderChildren(currentComponent.children)}
); } +// export { snapStateArr }; export default Canvas; + diff --git a/app/src/components/main/CanvasContainer.tsx b/app/src/components/main/CanvasContainer.tsx index e3256c3f7..44f0e7606 100644 --- a/app/src/components/main/CanvasContainer.tsx +++ b/app/src/components/main/CanvasContainer.tsx @@ -9,6 +9,10 @@ function CanvasContainer() { border: '2px Solid grey', }; + function onChangeHandler(event) { + console.log('working', event.target); + } + return (
diff --git a/app/src/containers/RightContainer.tsx b/app/src/containers/RightContainer.tsx index 773411cee..43dc0d79b 100644 --- a/app/src/containers/RightContainer.tsx +++ b/app/src/containers/RightContainer.tsx @@ -3,7 +3,10 @@ import React, { useContext, useEffect, useMemo, + useRef, } from 'react'; +import customHooks from '../helperFunctions/customHook'; +import initialState from '../context/initialState'; import { makeStyles } from '@material-ui/core/styles'; import FormControl from '@material-ui/core/FormControl'; import Select from '@material-ui/core/Select'; @@ -41,6 +44,31 @@ const RightContainer = ({isThemeLight}): JSX.Element => { const [deleteComponentError, setDeleteComponentError] = useState(false); const { style } = useContext(styleContext); const [modal, setModal] = useState(null); + const [prevState, setPrevState] = useState(state); + // const [ref, setRef] = useRef(); + + // const prevCountRef = useRef(); + // useEffect(() => { + // prevCountRef.current = prevState; + // }); + // const prevCount = prevCountRef.current; + // console.log('prevCount <-- before', prevCount); + // console.log('prevState <-- now', prevState); + // console.log('prevState', prevState); +//state.components[0].children +// function usePrevious(value) { +// const ref = useRef(); +// useEffect(() => { +// ref.current = value; +// }); +// return ref.current; +// } +// const prevCount = customHooks(state); +// console.log('prevCount <-- before', prevCount); +// console.log('prevState <-- now', prevState) +// console.log('state in rightContainer', state); + + const resetFields = () => { const style = configTarget.child @@ -193,6 +221,45 @@ const RightContainer = ({isThemeLight}): JSX.Element => { return styleObj; }; +/****************************** UNDO AND REDO ****************************************** */ +// undo functionality +// onClick this function will be invoked. +// set current state components.children to previous state and store current state children array in another place holder to not lose current state before reassigning to previous position. +const undoAction = () => { + dispatch({ type: 'UNDO', payload: {} }); +// const undoAction = () => { + // dispatch({ type: 'UNDO', payload: {value} }) + // const ref = useRef(); + // useEffect(() => { + // ref.current = value; + // }); + // console.log('ref.curr', ref.current) + // return ref.current; + // dispatch({ type: 'UNDO', payload: {} }); + // const childrenArr = state.components[0].children; + // const result = []; + // // console.log('state.components', state.components[0].children) + // for(let i = 0; i < prevState.children.length - 3; i++) { + // result.push(prevState.children[i]); + // } + // setPrevState(result); +}; +// }; + +// const prevCount = undoAction(prevState); +// console.log('prevCount', prevCount); + +const handleClick = (e, data) => { + console.log(data); +} + +const redoAction = () => { + dispatch({ type: 'REDO', payload: {} }); +} + + + + // placeholder for handling deleting instance const handleDelete = () => { dispatch({ type: 'DELETE CHILD', payload: {} }); @@ -502,6 +569,28 @@ const RightContainer = ({isThemeLight}): JSX.Element => {
)} +
+ + +
diff --git a/app/src/context/initialState.ts b/app/src/context/initialState.ts index aba5ed6ee..7f59441a8 100644 --- a/app/src/context/initialState.ts +++ b/app/src/context/initialState.ts @@ -21,7 +21,8 @@ const initialState: State = { nextComponentId: 2, nextChildId: 1, nextTopSeparatorId: 1000, - HTMLTypes + HTMLTypes, + past: [] }; export default initialState; diff --git a/app/src/helperFunctions/customHook.ts b/app/src/helperFunctions/customHook.ts new file mode 100644 index 000000000..c109d536e --- /dev/null +++ b/app/src/helperFunctions/customHook.ts @@ -0,0 +1,16 @@ +import React, {useRef, useEffect} from 'react'; + + +function usePrevious(value) { + const ref = useRef(); + // console.log('ref', ref) + useEffect(() => { + ref.current = value; + // console.log('useEffect', ref.current); + // return ref.current; + }); + // console.log('ref.current', ref.current) + return ref.current; +} + +export default usePrevious; diff --git a/app/src/interfaces/Interfaces.ts b/app/src/interfaces/Interfaces.ts index 6b8e080dd..a9ed58768 100644 --- a/app/src/interfaces/Interfaces.ts +++ b/app/src/interfaces/Interfaces.ts @@ -13,6 +13,16 @@ export interface State { nextBottomSeparatorId: number; nextChildId: number; HTMLTypes: HTMLType[]; + past: any[]; +} +export interface PastElement { + type: string; + typeId: number; + name: string; + childId: number; + style: object; + attributes?: object; + children?: PastElement[]; } export interface ChildElement { diff --git a/app/src/public/index-prod.ejs b/app/src/public/index-prod.ejs index 70d96a41a..cec00ffdd 100644 --- a/app/src/public/index-prod.ejs +++ b/app/src/public/index-prod.ejs @@ -2,6 +2,8 @@ + + diff --git a/app/src/public/index.ejs b/app/src/public/index.ejs index 23908ec2d..4ef496357 100644 --- a/app/src/public/index.ejs +++ b/app/src/public/index.ejs @@ -2,6 +2,8 @@ + + diff --git a/app/src/reducers/componentReducer.ts b/app/src/reducers/componentReducer.ts index 8aa14a0e6..e14bf6fe0 100644 --- a/app/src/reducers/componentReducer.ts +++ b/app/src/reducers/componentReducer.ts @@ -11,7 +11,7 @@ import manageSeparators from '../helperFunctions/manageSeparators'; let separator = initialState.HTMLTypes[1]; -// } + const reducer = (state: State, action: Action) => { // if the project type is set as Next.js or Gatsby.js, next/gatsby component code should be generated // otherwise generate classic react code @@ -192,6 +192,15 @@ const reducer = (state: State, action: Action) => { } }; +/******************************* REDO ARRAY ********************************* */ +// let redoArr = []; +// let stateSnap = [state]; +// console.log('stateSnap', stateSnap) +// const redoArrFunc = (...htmlTag) => { + // redoArr.push(...htmlTag); + // return redoArr; +// } + switch (action.type) { case 'ADD COMPONENT': { if ( @@ -612,6 +621,155 @@ const reducer = (state: State, action: Action) => { HTMLTypes }; } + case 'UNDO': { + //iterate through past arr, grab the 1 before the end, reassign state to equal that grabbed obj + // let past: object[] = state.past[state.past.length - 1]; + // console.log() + console.log('state before past', state); + // const pastBy = state.past[state.past.length - 1]; + // console.log('pastBy', pastBy); + // console.log('state.past - 1', state.past[state.past.length - 1]) + // state.past = []; + + + //check if state.components[0].children is nested + //if it is nested, go into that object and do something + //else, have state.comp.children = state.past[state.past.length - 1] + //pop from past + // const child = state.components[0].children + // for(let i = 0; i < child.length; i++) { + // // console.log('each children obj', child[i]) + // if(child[i].children.length === 2) { + // // child[i].children = state.past[state.past.length - 2]//[]; + // child[i].children = []; + // console.log('children of div or form', child[i]) + + // // child[i].children = state.past[state.past.length - 1] + // } + // // child[i] = state.past[state.past.length - 1] + // } + let nestedChildren: any[] = state.components[0].children[0].children; + // check if there are any tags in the children of the children (nested tags) + if(nestedChildren.length === 2){ + // if there are, execute undo code specific to nested children, otherwise, execute code below for non-nested children + nestedChildren = []; + } else if(nestedChildren.length > 2) { + nestedChildren = nestedChildren[nestedChildren.length - 2]; + } else { + state.components[0].children = state.past[state.past.length - 1] + state.past.pop(); + } + console.log('state after past reassigned', state); + state.components.forEach((el, i) => { + console.log('el in undo', el) + el.code = generateCode( + state.components, + state.components[i].id, + state.rootComponents, + state.projectType, + state.HTMLTypes + ); + }); + return { + ...state + } + } + + + + // console.log('state', state); + // const childrenArr = state.components[0].children; + // const children: Array = []; + // const children = []; + // console.log('state.components', state.components[0].children) + + // // console.log('state', state); + // // const childrenArr = state.components[0].children; + // // const children: Array = []; + // const children = state.components[0].children; + // const past = []; + + // // console.log('children', children) + // // console.log('state.components', state.components) + // children.forEach(htmlTag => past.push(htmlTag)); + // // console.log('past', past); + // redoArr.push(past[past.length-2], past[past.length-1]); + + + // if(state.past.length >= 2) { + // // state.past.splice(0, state.past.length -1); + // state.past.length = 2; + // } else { + // state.past.push(past[past.length-2], past[past.length-1]) + // } + + + // // redoArrFunc(past[past.length - 2], past[past.length-1]) + // console.log('redoArr', redoArr); + // state.components[0].children = past.slice(0, past.length - 2); + // // console.log('state with past', state.components[0].children) + // // console.log('entire state with new past', state) + // state.components.forEach((el, i) => { + // // console.log('el in undo', el) + // el.code = generateCode( + // state.components, + // state.components[i].id, + // state.rootComponents, + // state.projectType, + // state.HTMLTypes + // ); + // }); + // // state.components[0].children.splice(0, state.components[0].children.length); + // console.log('the final boss in UNDO', state) + // return { + // ...state, + // } + + // console.log('state', state); + // let stateSnap = [state]; + // stateSnap = stateSnap.slice(0, stateSnap.length +1); + // console.log('stateSnap in UNDOOOO', stateSnap); + // console.log('stateSnap in UNDO before push', stateSnap) + // stateSnap.push(state); + // // redoArr.push(stateSnap); + // console.log('stateSnap in UNDO after push', stateSnap) + // console.log('redoArr in undo', redoArr); + + + + // } +////////////Reference ADD ELEMENT case to see how id is being incremented + case 'REDO': { + // const children = state.components[0].children; + // if(redoArr.length >= 2) redoArr.length = 2; + // children.push(redoArr); + let {past} = state; + const children = state.components[0].children; + // while(past.length <= 2) { + children.push(...past) + // children.push(past[past.length -2]); + // } + state.components.forEach((el, i) => { + // console.log('el in undo', el) + el.code = generateCode( + state.components, + state.components[i].id, + state.rootComponents, + state.projectType, + state.HTMLTypes + ); + }); + past.splice(0, past.length); + console.log('the final boss in REDO', state) + // console.log('past destructured', past) + // console.log('redoItems', redoItems) + // console.log('state in redo', state) + // console.log(redoArr())eeeeeeeeeeeeeeweeeee + // console.log('children in Redo', children) + return { + ...state + } + } default: return state; From adb96034a82a5744d3e55d910afd88055a4d9fcc Mon Sep 17 00:00:00 2001 From: Shana Hoehn Date: Sat, 6 Mar 2021 16:38:20 -0800 Subject: [PATCH 028/116] add delete button and functionality --- app/src/Dashboard/Project.jsx | 32 ++++++++++++++++++++---- app/src/index.js | 1 - server/graphQL/resolvers/mutation.js | 37 +++++++++++++++++++++++----- server/graphQL/typeDefs.js | 1 + 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/app/src/Dashboard/Project.jsx b/app/src/Dashboard/Project.jsx index cc6f08b89..b142c85d5 100644 --- a/app/src/Dashboard/Project.jsx +++ b/app/src/Dashboard/Project.jsx @@ -3,9 +3,10 @@ import { gql, useMutation } from '@apollo/client'; import PropTypes from 'prop-types'; -const Project = ({ name, likes, id, userId, username }) => { - - // IMPORTANT: +const Project = ({ + name, likes, id, userId, username, +}) => { + // IMPORTANT: // 1) schema change projId => id to allows Apollo Client cache auto-update. Only works with 'id' // 2) always request the 'id' in a mutation request @@ -28,9 +29,18 @@ const Project = ({ name, likes, id, userId, username }) => { id } }`; - - const [ makeCopy ] = useMutation(MAKE_COPY); + const [makeCopy] = useMutation(MAKE_COPY); + + const DELETE_PROJECT = gql` + mutation DeleteProject($projId: ID!) { + deleteProject(projId: $projId) + { + id + } + }`; + + const [deleteProject] = useMutation(DELETE_PROJECT); function handleLike(e) { e.preventDefault(); @@ -67,6 +77,17 @@ const Project = ({ name, likes, id, userId, username }) => { makeCopy(myVar); } + function handleDelete(e) { + e.preventDefault(); + const myVar = { + variables: + { + projId: id, + }, + }; + deleteProject(myVar); + } + return (

Project: { name }

@@ -76,6 +97,7 @@ const Project = ({ name, likes, id, userId, username }) => {
+
); diff --git a/app/src/index.js b/app/src/index.js index d0dd0cf34..9b6195c18 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -16,7 +16,6 @@ import { Switch, } from 'react-router-dom'; - /* * Dashboard */ diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index 3d8663f40..4c5003839 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -19,9 +19,9 @@ const Test = { const update = { name: args.name, likes: args.likes }; const options = { new: true }; const resp = await Tests.findOneAndUpdate(filter, update, options); - + console.log('Updated database with', resp); - + if (resp) return { description: resp.name, id: resp._id, likes: resp.likes }; return { description: 'Error updating', id: resp._id, likes: 0 }; }, @@ -36,12 +36,12 @@ const Test = { const Project = { - addLike: async (parent, { projId, likes}) => { + addLike: async (parent, { projId, likes }) => { const filter = { _id: projId }; const update = { likes }; const options = { new: true }; const resp = await Projects.findOneAndUpdate(filter, update, options); - + if (resp) { return ({ name: resp.name, @@ -49,7 +49,8 @@ const Project = { userId: resp.userId, username: resp.username, likes: resp.likes, - })} + }); + } // TODO: Go back to this to see how to handle error later return { @@ -81,7 +82,8 @@ const Project = { userId: resp.userId, username: resp.username, likes: resp.likes, - })} + }); + } // TODO: Go back to this to see how to handle error later return { @@ -92,6 +94,29 @@ const Project = { likes: -1, }; }, + + deleteProject: async (parent, { projId }) => { + const filter = { _id: projId }; + const options = { strict: true }; + const resp = await Projects.findOneAndDelete(filter, options); + console.log("resp", resp); + if (resp) { + return ({ + name: resp.name, + likes: resp.likes, + id: resp._id, + userId: resp.userId, + username: resp.username, + }); + } + return { + name: 'Error', + id: 'Error', + userId: 'Error', + username: 'Error', + likes: -1, + }; + }, }; module.exports = { diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 92c85d0d4..0e4b79556 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -30,6 +30,7 @@ const Project = gql` type Mutation { addLike(projId: ID!, likes: Int!): Project makeCopy(projId: ID!, userId: ID!, username: String!): Project + deleteProject(projId: ID!): Project } type Project { From 0d44638aa26e53c52984a4c05ed5366be16464cf Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Sat, 6 Mar 2021 18:20:24 -0800 Subject: [PATCH 029/116] disable minimize feature for projectSchema to prevent Mongoose from deleting empty {}. Downloaded project can be open in cavas now --- server/graphQL/resolvers/mutation.js | 9 +++++++-- server/models/reactypeModels.js | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index 3d8663f40..b23a2647e 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -1,4 +1,5 @@ const { Tests, Projects } = require('../../models/reactypeModels'); + /* * resolvers are functions that handles graphQL requests. This file defines the logic for graphQL mutation requests * Link to Apollo Mutations: @@ -64,14 +65,18 @@ const Project = { makeCopy: async (parent, { projId, userId, username }) => { const filter = { _id: projId }; const target = await Projects.findOne(filter); + // make a copy with the passed in userId + // IMPORTANT: DO NOT CHANGE copy.name, it will create a nother copy of the document with the origional project name. const copy = { - name: target.name, - likes: target.likes, + name: target.name, project: target.project, userId, username, }; + + // IMPORTANT: MUST MAKE A DEEP COPY OF target.project, otherwise, field 'style' is lost during the copy process. See 'minimize' option in projectSchema + const resp = await Projects.create(copy); if (resp) { diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index 22b49a3d8..d0c6498c1 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -66,7 +66,8 @@ const projectSchema = new Schema({ }, username: {type: String, required: true }, createdAt: { type: Date, default: Date.now } -}); +}, { minimize: false }); +// option 'minimize' prevent Mongoose from removing empty 'style' value in the copy // Test schema for implementing GraphQL const testSchema = new Schema({ From ab95470d4891ff11abf7f5db769992643672f6d7 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Mon, 8 Mar 2021 12:13:27 -0800 Subject: [PATCH 030/116] Implement public dashboard and user dashboard with in seprate view --- app/src/Dashboard/ProjectContainer.jsx | 38 +++++++++++++++++--------- server/graphQL/resolvers/mutation.js | 2 +- server/models/reactypeModels.js | 3 +- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/app/src/Dashboard/ProjectContainer.jsx b/app/src/Dashboard/ProjectContainer.jsx index da044bd85..e0a3f8576 100644 --- a/app/src/Dashboard/ProjectContainer.jsx +++ b/app/src/Dashboard/ProjectContainer.jsx @@ -33,28 +33,40 @@ const ProjectContainer = () => { if (loading) return

Loading...

; if (error) return

Error :{error}

; // based on resolver(getAllProject) for this query, the data is stored in the data object with the key 'getAllProjects' - const myProjs = data.getAllProjects; - console.log('Projects >>> ', myProjs); + const projects = data.getAllProjects; + console.log('Projects >>> ', projects); // generate an array of Project components based on data - const projects = myProjs.map((proj, index) => ); + const publicProjects = []; + const userProjects = []; + projects.forEach((proj, index) => { + const component = ; + if (username === proj.username) userProjects.push(component); + else publicProjects.push(component); + }); return (
-

Public Dashboard

+

Public Dashboard

+
+ {publicProjects} +
+
+

User Dashboard

- {projects} + {userProjects} +
+
- ); }; diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index ac9fd41b7..59f5cc431 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -104,7 +104,7 @@ const Project = { const filter = { _id: projId }; const options = { strict: true }; const resp = await Projects.findOneAndDelete(filter, options); - console.log("resp", resp); + // console.log("resp", resp); if (resp) { return ({ name: resp.name, diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index d0c6498c1..ead5ad16d 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -66,7 +66,8 @@ const projectSchema = new Schema({ }, username: {type: String, required: true }, createdAt: { type: Date, default: Date.now } -}, { minimize: false }); +}, { minimize: false } +); // option 'minimize' prevent Mongoose from removing empty 'style' value in the copy // Test schema for implementing GraphQL From c8dfb63156d88048e816cd55553447553a223fd5 Mon Sep 17 00:00:00 2001 From: Edward Park Date: Mon, 8 Mar 2021 12:35:09 -0800 Subject: [PATCH 031/116] undo button for nested has bugs --- app/src/components/main/Canvas.tsx | 10 +-- .../main/DirectChildHTMLNestable.tsx | 9 +++ app/src/reducers/componentReducer.ts | 72 +++++++++++++------ 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/app/src/components/main/Canvas.tsx b/app/src/components/main/Canvas.tsx index 939a9b949..156a529f9 100644 --- a/app/src/components/main/Canvas.tsx +++ b/app/src/components/main/Canvas.tsx @@ -40,12 +40,14 @@ function Canvas() { // make a deep clone of state ( JSON.parse(JSON.stringify()) ? ) // function inner() { const deepCopiedState = JSON.parse(JSON.stringify(state)); + state.past.push(deepCopiedState.components[0].children); + console.log('state.past in canvas', state.past) + } // console.log('deepCopiedState', deepCopiedState); // stateSnapArr.push(deepCopiedState); - state.past.push(deepCopiedState.components[0].children); // state.past.push(deepCopiedState); // console.log('state after push', state) - console.log('state in canvas', state.past) + // console.log('state in canvas', state); // return; // snapStateArr.push(5); // return snapStateArr; @@ -61,7 +63,6 @@ function Canvas() { // return [...previousState].push(deepCopiedState); // }) // console.log('prevState: ', prevState) -} // This hook will allow the user to drag items from the left panel on to the canvas const [{ isOver }, drop] = useDrop({ @@ -71,7 +72,8 @@ function Canvas() { // returns false for direct drop target //code here // 6.0 didDrop is firing when HTML tags are moved up - snapShotFunc(); // < ------ snapShotFunc here + snapShotFunc(); + console.log('snapShotFunc invoked'); // < ------ snapShotFunc here if (didDrop) { return; } diff --git a/app/src/components/main/DirectChildHTMLNestable.tsx b/app/src/components/main/DirectChildHTMLNestable.tsx index 52447757b..0acf88ec4 100644 --- a/app/src/components/main/DirectChildHTMLNestable.tsx +++ b/app/src/components/main/DirectChildHTMLNestable.tsx @@ -17,6 +17,14 @@ function DirectChildHTMLNestable({ }: ChildElement) { const [state, dispatch] = useContext(StateContext); const ref = useRef(null); + + // const snapShotFunc = () => { + // // make a deep clone of state ( JSON.parse(JSON.stringify()) ? ) + // // function inner() { + // const deepCopiedState = JSON.parse(JSON.stringify(state)); + // state.past.push(deepCopiedState.components[0].children); + // console.log('state.past in directChildHTMLNest', state.past) + // } // console.log('name', name) // console.log('children', children) // find the HTML element corresponding with this instance of an HTML element @@ -50,6 +58,7 @@ function DirectChildHTMLNestable({ // triggered on drop drop: (item: any, monitor: DropTargetMonitor) => { const didDrop = monitor.didDrop(); + // snapShotFunc(); if (didDrop) { return; } diff --git a/app/src/reducers/componentReducer.ts b/app/src/reducers/componentReducer.ts index e14bf6fe0..6c35b6d21 100644 --- a/app/src/reducers/componentReducer.ts +++ b/app/src/reducers/componentReducer.ts @@ -8,6 +8,7 @@ import { import initialState from '../context/initialState'; import generateCode from '../helperFunctions/generateCode'; import manageSeparators from '../helperFunctions/manageSeparators'; +import { isObjectType } from 'graphql'; let separator = initialState.HTMLTypes[1]; @@ -260,6 +261,7 @@ const reducer = (state: State, action: Action) => { if (type === 'Component') { components.forEach(comp => { + console.log('comp in ADDD CHILD', comp) if (comp.id === typeId) { componentName = comp.name; componentChildren = comp.children; @@ -625,7 +627,7 @@ const reducer = (state: State, action: Action) => { //iterate through past arr, grab the 1 before the end, reassign state to equal that grabbed obj // let past: object[] = state.past[state.past.length - 1]; // console.log() - console.log('state before past', state); + // console.log('state before past', state); // const pastBy = state.past[state.past.length - 1]; // console.log('pastBy', pastBy); // console.log('state.past - 1', state.past[state.past.length - 1]) @@ -636,32 +638,56 @@ const reducer = (state: State, action: Action) => { //if it is nested, go into that object and do something //else, have state.comp.children = state.past[state.past.length - 1] //pop from past - // const child = state.components[0].children - // for(let i = 0; i < child.length; i++) { - // // console.log('each children obj', child[i]) - // if(child[i].children.length === 2) { - // // child[i].children = state.past[state.past.length - 2]//[]; - // child[i].children = []; - // console.log('children of div or form', child[i]) - - // // child[i].children = state.past[state.past.length - 1] + // const pastArr = state.past; + // let deletedNested = false; + // for(let i = 0; i < pastArr.length; i++) { + // // console.log('each children obj', pastArr[i]) + // for(let currObj in pastArr[i]) { + // // console.log('for in loop currObj', currObj) + // if(pastArr[i][currObj].name === "div" /*deletedNested === false*/) { + // console.log(pastArr[i][currObj].children, '<== pastArr[i][currObj]'); + // if(pastArr[i][currObj].children.length === 2) { + // // pastArr[i][currObj].children = []; + // pastArr[i][currObj].children = []; + // // deletedNested = true; + // // // state.components[0].children = pastArr[i][currObj].children + // // console.log('for in loop, val for div --> ', state) + // // if(pastArr[i][currObj].children.length > 2) { + // } else if(pastArr[i][currObj].children.length > 2) { + // console.log('past greater than 2', pastArr[i][currObj].children) + // } + // } // } - // // child[i] = state.past[state.past.length - 1] + // } - let nestedChildren: any[] = state.components[0].children[0].children; - // check if there are any tags in the children of the children (nested tags) - if(nestedChildren.length === 2){ - // if there are, execute undo code specific to nested children, otherwise, execute code below for non-nested children - nestedChildren = []; - } else if(nestedChildren.length > 2) { - nestedChildren = nestedChildren[nestedChildren.length - 2]; - } else { - state.components[0].children = state.past[state.past.length - 1] - state.past.pop(); - } + + // if(child[i].children.length === 2) { + // console.log(child[i]) + // // child[i].children = state.past[state.past.length - 2]//[]; + // child[i].children = []; + // console.log('children of div or form', child[i]) + + // // child[i].children = state.past[state.past.length - 1] + // } + // child[i] = state.past[state.past.length - 1] + // let nestedChildren: any[] = state.components[0].children[0].children; + // // check if there are any tags in the children of the children (nested tags) + // if(nestedChildren.length === 2){ + // // if there are, execute undo code specific to nested children, otherwise, execute code below for non-nested children + // nestedChildren = []; + // state.components[0].children[0].children = nestedChildren; + // } else if(nestedChildren.length > 2) { + // nestedChildren = nestedChildren[nestedChildren.length - 2]; + // } else { + // } + console.log('state before past reassigned', state); + state.components[0].children = state.past.pop(); + // state.past[state.past.length - 1]; + // state.past.pop(); + // deletedNested = false; console.log('state after past reassigned', state); state.components.forEach((el, i) => { - console.log('el in undo', el) + // console.log('el in undo', el) el.code = generateCode( state.components, state.components[i].id, From ec0fb181ad2f7bebc002e4a4e0110d3f4f0fee34 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Mon, 8 Mar 2021 13:58:05 -0800 Subject: [PATCH 032/116] switching branch --- app/src/Dashboard/Project.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/Dashboard/Project.jsx b/app/src/Dashboard/Project.jsx index b142c85d5..4c76b5e69 100644 --- a/app/src/Dashboard/Project.jsx +++ b/app/src/Dashboard/Project.jsx @@ -96,7 +96,11 @@ const Project = ({

Likes: { likes }

- + { + currUsername !== username + ? + : null + }
From a563a1381aa8840354688c4a579dd076977496b4 Mon Sep 17 00:00:00 2001 From: Edward Park Date: Mon, 8 Mar 2021 14:00:51 -0800 Subject: [PATCH 033/116] undo functional, but undo for nested elements require double clicks > > Co-authored-by: elenaconn Co-authored-by: Anthonytorrero --- app/src/components/main/Canvas.tsx | 5 +++-- app/src/components/main/DirectChildHTML.tsx | 1 + .../main/DirectChildHTMLNestable.tsx | 17 +++++++++-------- app/src/containers/RightContainer.tsx | 2 +- app/src/context/initialState.ts | 3 ++- app/src/interfaces/Interfaces.ts | 19 ++++++++++--------- app/src/reducers/componentReducer.ts | 8 +++++--- 7 files changed, 31 insertions(+), 24 deletions(-) diff --git a/app/src/components/main/Canvas.tsx b/app/src/components/main/Canvas.tsx index 156a529f9..9ec6a63d5 100644 --- a/app/src/components/main/Canvas.tsx +++ b/app/src/components/main/Canvas.tsx @@ -41,7 +41,8 @@ function Canvas() { // function inner() { const deepCopiedState = JSON.parse(JSON.stringify(state)); state.past.push(deepCopiedState.components[0].children); - console.log('state.past in canvas', state.past) + // state.future.push(deepCopiedState.components[0].children); + console.log('state.past in canvas', state) } // console.log('deepCopiedState', deepCopiedState); // stateSnapArr.push(deepCopiedState); @@ -73,7 +74,7 @@ function Canvas() { //code here // 6.0 didDrop is firing when HTML tags are moved up snapShotFunc(); - console.log('snapShotFunc invoked'); // < ------ snapShotFunc here + // console.log('snapShotFunc invoked'); // < ------ snapShotFunc here if (didDrop) { return; } diff --git a/app/src/components/main/DirectChildHTML.tsx b/app/src/components/main/DirectChildHTML.tsx index 8d5db77e8..e50448853 100644 --- a/app/src/components/main/DirectChildHTML.tsx +++ b/app/src/components/main/DirectChildHTML.tsx @@ -24,6 +24,7 @@ function DirectChildHTML({ (type: HTMLType) => type.id === typeId ); + // hook that allows component to be draggable const [{ isDragging }, drag] = useDrag({ // setting item attributes to be referenced when updating state with new instance of dragged item diff --git a/app/src/components/main/DirectChildHTMLNestable.tsx b/app/src/components/main/DirectChildHTMLNestable.tsx index 0acf88ec4..ee2023f2d 100644 --- a/app/src/components/main/DirectChildHTMLNestable.tsx +++ b/app/src/components/main/DirectChildHTMLNestable.tsx @@ -18,13 +18,14 @@ function DirectChildHTMLNestable({ const [state, dispatch] = useContext(StateContext); const ref = useRef(null); - // const snapShotFunc = () => { - // // make a deep clone of state ( JSON.parse(JSON.stringify()) ? ) - // // function inner() { - // const deepCopiedState = JSON.parse(JSON.stringify(state)); - // state.past.push(deepCopiedState.components[0].children); - // console.log('state.past in directChildHTMLNest', state.past) - // } + const snapShotFunc = () => { + // make a deep clone of state ( JSON.parse(JSON.stringify()) ? ) + // function inner() { + const deepCopiedState = JSON.parse(JSON.stringify(state)); + state.past.push(deepCopiedState.components[0].children); + // state.future.push(deepCopiedState.components[0].children); + console.log('state.past in directChildHTMLNest', state) + } // console.log('name', name) // console.log('children', children) // find the HTML element corresponding with this instance of an HTML element @@ -58,7 +59,7 @@ function DirectChildHTMLNestable({ // triggered on drop drop: (item: any, monitor: DropTargetMonitor) => { const didDrop = monitor.didDrop(); - // snapShotFunc(); + snapShotFunc(); if (didDrop) { return; } diff --git a/app/src/containers/RightContainer.tsx b/app/src/containers/RightContainer.tsx index 43dc0d79b..15c874d87 100644 --- a/app/src/containers/RightContainer.tsx +++ b/app/src/containers/RightContainer.tsx @@ -44,7 +44,7 @@ const RightContainer = ({isThemeLight}): JSX.Element => { const [deleteComponentError, setDeleteComponentError] = useState(false); const { style } = useContext(styleContext); const [modal, setModal] = useState(null); - const [prevState, setPrevState] = useState(state); + // const [prevState, setPrevState] = useState(state); // const [ref, setRef] = useRef(); // const prevCountRef = useRef(); diff --git a/app/src/context/initialState.ts b/app/src/context/initialState.ts index 7f59441a8..d814cf61b 100644 --- a/app/src/context/initialState.ts +++ b/app/src/context/initialState.ts @@ -22,7 +22,8 @@ const initialState: State = { nextChildId: 1, nextTopSeparatorId: 1000, HTMLTypes, - past: [] + past: [], + future: [], }; export default initialState; diff --git a/app/src/interfaces/Interfaces.ts b/app/src/interfaces/Interfaces.ts index a9ed58768..83f0684f0 100644 --- a/app/src/interfaces/Interfaces.ts +++ b/app/src/interfaces/Interfaces.ts @@ -14,16 +14,17 @@ export interface State { nextChildId: number; HTMLTypes: HTMLType[]; past: any[]; + future: any[]; } -export interface PastElement { - type: string; - typeId: number; - name: string; - childId: number; - style: object; - attributes?: object; - children?: PastElement[]; -} +// export interface PastElement { +// type: string; +// typeId: number; +// name: string; +// childId: number; +// style: object; +// attributes?: object; +// children?: PastElement[]; +// } export interface ChildElement { type: string; diff --git a/app/src/reducers/componentReducer.ts b/app/src/reducers/componentReducer.ts index 6c35b6d21..24648d3d2 100644 --- a/app/src/reducers/componentReducer.ts +++ b/app/src/reducers/componentReducer.ts @@ -681,9 +681,11 @@ const reducer = (state: State, action: Action) => { // } else { // } console.log('state before past reassigned', state); - state.components[0].children = state.past.pop(); - // state.past[state.past.length - 1]; - // state.past.pop(); + console.log('checking if the one before equals the one after', state.past[state.past.length - 1] == state.past[state.past.length - 2]) + // if(state.past[state.past.length - 1] === state.past[state.past.length - 2]) + state.components[0].children = state.past[state.past.length - 1]; + // state.future.push(state.past[state.past.length - 1]); + state.past.pop(); // deletedNested = false; console.log('state after past reassigned', state); state.components.forEach((el, i) => { From b1b9cab492c6acc5c662a5a309c93e6005f60a2b Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Mon, 8 Mar 2021 14:56:05 -0800 Subject: [PATCH 034/116] preapre to pull from Shana --- app/src/Dashboard/{Form.jsx => Form.tsx} | 0 ...{FormsContainer.jsx => FormsContainer.tsx} | 0 .../Dashboard/{Project.jsx => Project.tsx} | 0 ...jectContainer.jsx => ProjectContainer.tsx} | 2 +- app/src/Dashboard/gqlStrings.ts | 20 +++++++++++++++++++ app/src/components/top/NavBar.tsx | 4 ++-- app/src/index.js | 4 ++-- 7 files changed, 25 insertions(+), 5 deletions(-) rename app/src/Dashboard/{Form.jsx => Form.tsx} (100%) rename app/src/Dashboard/{FormsContainer.jsx => FormsContainer.tsx} (100%) rename app/src/Dashboard/{Project.jsx => Project.tsx} (100%) rename app/src/Dashboard/{ProjectContainer.jsx => ProjectContainer.tsx} (98%) create mode 100644 app/src/Dashboard/gqlStrings.ts diff --git a/app/src/Dashboard/Form.jsx b/app/src/Dashboard/Form.tsx similarity index 100% rename from app/src/Dashboard/Form.jsx rename to app/src/Dashboard/Form.tsx diff --git a/app/src/Dashboard/FormsContainer.jsx b/app/src/Dashboard/FormsContainer.tsx similarity index 100% rename from app/src/Dashboard/FormsContainer.jsx rename to app/src/Dashboard/FormsContainer.tsx diff --git a/app/src/Dashboard/Project.jsx b/app/src/Dashboard/Project.tsx similarity index 100% rename from app/src/Dashboard/Project.jsx rename to app/src/Dashboard/Project.tsx diff --git a/app/src/Dashboard/ProjectContainer.jsx b/app/src/Dashboard/ProjectContainer.tsx similarity index 98% rename from app/src/Dashboard/ProjectContainer.jsx rename to app/src/Dashboard/ProjectContainer.tsx index da044bd85..60850fe5e 100644 --- a/app/src/Dashboard/ProjectContainer.jsx +++ b/app/src/Dashboard/ProjectContainer.tsx @@ -3,7 +3,7 @@ import { Link } from 'react-router-dom'; import { gql, useQuery } from '@apollo/client'; -import Project from './Project.jsx'; +import Project from './Project.tsx'; // Implement Apollo Client useQuery hook to retrieve data from the server through graphQL. This includes 2 steps: // 1) Impliment Apollo Provider in the top component in ./src/index.js, this allows children components access to the queried data // 2) useQuery hook will update the data stored in Apollo Client's cache and automatically trigger child components rendering diff --git a/app/src/Dashboard/gqlStrings.ts b/app/src/Dashboard/gqlStrings.ts new file mode 100644 index 000000000..fbdb92619 --- /dev/null +++ b/app/src/Dashboard/gqlStrings.ts @@ -0,0 +1,20 @@ +import { gql } from '@apollo/client'; + +export const { + ADD_LIKE: gql`mutation AddLike($projId: ID!, $likes: Int!) { + addLike(projId: $projId, likes: $likes) + { + id + likes + } + }`, + + MAKE_COPY: gql` + mutation MakeCopy ($userId: ID!, $projId: ID!, $username: String!) { + makeCopy(userId: $userId, projId: $projId, username: $username) + { + id + } + }`; + +} \ No newline at end of file diff --git a/app/src/components/top/NavBar.tsx b/app/src/components/top/NavBar.tsx index 7d60a9376..8aa0a65e7 100644 --- a/app/src/components/top/NavBar.tsx +++ b/app/src/components/top/NavBar.tsx @@ -168,7 +168,7 @@ export default function NavBar(props) { {/* ==================================Dashboard Button================================================== */} - + {state.isLoggedIn ? - + : } {/* ==================================Dashboard Button================================================== */} diff --git a/app/src/Dashboard/SortMenu.jsx b/app/src/Dashboard/SortMenu.jsx new file mode 100644 index 000000000..ade833e08 --- /dev/null +++ b/app/src/Dashboard/SortMenu.jsx @@ -0,0 +1,66 @@ +// import { supportsResultCaching } from '@apollo/client/cache/inmemory/entityStore'; +import React, { useState } from 'react'; +import Button from '@material-ui/core/Button'; + +const sortByRating = () => { + console.log("rating") +}; + +const sortByDate = () => { + console.log("date") +}; + +const sortByUser = () => { + console.log("user") +}; + +export default function SortMenu() { + + const [isOpen, setIsOpen] = useState(false); + const toggling = () => setIsOpen(!isOpen); + + return ( +
+ + + { + isOpen && ( +
+ + + +
+ ) + } +
+ ) +} diff --git a/app/src/Dashboard/navbar.jsx b/app/src/Dashboard/navbar.jsx deleted file mode 100644 index e69de29bb..000000000 From 7d71f26e65ddb288cb22ef3c6eb4b0ec0e885fc5 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Mon, 8 Mar 2021 16:28:55 -0800 Subject: [PATCH 036/116] prepare to merge typescript branch --- app/src/Dashboard/Project.jsx | 56 ----------------------- app/src/Dashboard/ProjectContainer.tsx | 61 ++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 56 deletions(-) delete mode 100644 app/src/Dashboard/Project.jsx create mode 100644 app/src/Dashboard/ProjectContainer.tsx diff --git a/app/src/Dashboard/Project.jsx b/app/src/Dashboard/Project.jsx deleted file mode 100644 index 25ccde4f7..000000000 --- a/app/src/Dashboard/Project.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { gql, useMutation } from '@apollo/client'; -import PropTypes from 'prop-types'; - - -const Project = ({ name, likes, projId, userId }) => { - - // IMPORTANT: - // 1) schema change projId => id to allows Apollo Client cache auto-update. Only works with 'id' - // 2) always request the 'id' in a mutation request - - const ADD_LIKE = gql` - mutation AddLike($projId: ID!, $likes: Int!) { - addLike(projId: $projId, likes: $likes) - { - id - likes - } - }`; - - const [addLike] = useMutation(ADD_LIKE); - - function handleClick(e) { - e.preventDefault(); - // IMPORTANT: DO NOT ADD extra comma to the last line of the variable object, the query will not work - const myVar = { - variables: - { - projId, - likes: likes + 1, - }, - }; - // send Mutation - addLike(myVar); - } - - return ( -
-

Project: { name }

- {/*

Project ID: {projId}

*/} -

Likes: { likes }

- -
- ); -}; - -// Variable validation using propTypes -Project.propTypes = { - name: PropTypes.string.isRequired, - projId: PropTypes.string.isRequired, - userId: PropTypes.string.isRequired, - likes: PropTypes.number.isRequired, -}; - - -export default Project; diff --git a/app/src/Dashboard/ProjectContainer.tsx b/app/src/Dashboard/ProjectContainer.tsx new file mode 100644 index 000000000..e9b4b9d25 --- /dev/null +++ b/app/src/Dashboard/ProjectContainer.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { useQuery } from '@apollo/client'; +import { GET_PROJECTS } from './gqlStrings'; +import Project from './Project.tsx'; + +// Implement Apollo Client useQuery hook to retrieve data from the server through graphQL. This includes 2 steps: +// 1) Impliment Apollo Provider in the top component in ./src/index.js, this allows children components access to the queried data +// 2) useQuery hook will update the data stored in Apollo Client's cache and automatically trigger child components rendering + +const ProjectContainer = () => { + let myVar = {}; + // Need this for the individual user dasboard, for now, dashboard shows all projects from all users + const userSSID = window.localStorage.getItem('ssid') || 'unavailable'; + const username = window.localStorage.getItem('username') || 'unavailable'; + // if (userSSID !== 'guest') { + // myVar = { userId: userSSID }; + // } + + // useQuery hook abstracts fetch request + const { loading, error, data } = useQuery(GET_PROJECTS, { pollInterval: 2000, variables: myVar }); // Need to find where the userId is stored for the logged in user. + if (loading) return

Loading...

; + if (error) return

Error :{error}

; + // based on resolver(getAllProject) for this query, the data is stored in the data object with the key 'getAllProjects' + const projects = data.getAllProjects; + console.log('Projects >>> ', projects); + // generate an array of Project components based on data + const publicProjects = []; + const userProjects = []; + projects.forEach((proj, index) => { + const component = ; + if (username === proj.username) userProjects.push(component); + else publicProjects.push(component); + }); + + return ( +
+ + + +

Public Dashboard

+
+ {publicProjects} +
+
+

User Dashboard

+
+ {userProjects} +
+ +
+ ); +}; +export default ProjectContainer; From 5b8789e798ea7e89ee13003c81d64e346eca3c29 Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Mon, 8 Mar 2021 21:15:54 -0800 Subject: [PATCH 037/116] Implement graphQL error handling --- app/src/Dashboard/ProjectContainer.tsx | 4 - package.json | 1 + server/graphQL/resolvers/mutation.js | 128 +++++++------------------ server/graphQL/resolvers/query.js | 52 +++------- server/graphQL/typeDefs.js | 20 ---- server/server.js | 7 +- 6 files changed, 53 insertions(+), 159 deletions(-) diff --git a/app/src/Dashboard/ProjectContainer.tsx b/app/src/Dashboard/ProjectContainer.tsx index a97488306..e9b4b9d25 100644 --- a/app/src/Dashboard/ProjectContainer.tsx +++ b/app/src/Dashboard/ProjectContainer.tsx @@ -9,10 +9,6 @@ import Project from './Project.tsx'; // 2) useQuery hook will update the data stored in Apollo Client's cache and automatically trigger child components rendering const ProjectContainer = () => { -<<<<<<< HEAD -======= - ->>>>>>> typescript let myVar = {}; // Need this for the individual user dasboard, for now, dashboard shows all projects from all users const userSSID = window.localStorage.getItem('ssid') || 'unavailable'; diff --git a/package.json b/package.json index d2199d341..80652b6e6 100644 --- a/package.json +++ b/package.json @@ -170,6 +170,7 @@ "@types/enzyme": "^3.10.5", "@types/enzyme-adapter-react-16": "^1.0.6", "@types/jest": "^25.1.5", + "apollo": "^2.32.5", "babel-eslint": "^8.2.6", "babel-jest": "^25.2.4", "babel-loader": "^8.1.0", diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index 59f5cc431..552341566 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -1,130 +1,74 @@ -const { Tests, Projects } = require('../../models/reactypeModels'); - +const { UserInputError } = require('apollo-server-express'); +const { Projects, Users } = require('../../models/reactypeModels'); /* * resolvers are functions that handles graphQL requests. This file defines the logic for graphQL mutation requests * Link to Apollo Mutations: * https://www.apollographql.com/docs/apollo-server/data/resolvers/#defining-a-resolver */ -const Test = { - addTest: async (parent, args) => { - const resp = await Tests.create({ name: args.name, likes: args.likes }); - console.log('Added test', resp); - if (resp) return { description: resp.name, id: resp._id, likes: args.likes }; - return { description: 'Error creating test', id: 'unknown', likes: 0 }; - }, - - updateTest: async (parent, args) => { - // console.log('args >>> ', args); - const filter = { _id: args.id }; - const update = { name: args.name, likes: args.likes }; - const options = { new: true }; - const resp = await Tests.findOneAndUpdate(filter, update, options); - - console.log('Updated database with', resp); - - if (resp) return { description: resp.name, id: resp._id, likes: resp.likes }; - return { description: 'Error updating', id: resp._id, likes: 0 }; - }, - - deleteTest: async (parent, args) => { - const filter = { _id: args.id }; - const resp = await Tests.deleteOne(filter); - if (resp) return { description: resp.name, id: resp._id, likes: 0 }; - return { description: 'Error updating', likes: 0 }; - }, -}; - - const Project = { addLike: async (parent, { projId, likes }) => { const filter = { _id: projId }; const update = { likes }; const options = { new: true }; const resp = await Projects.findOneAndUpdate(filter, update, options); - + if (resp) { return ({ name: resp.name, id: resp._id, userId: resp.userId, - username: resp.username, likes: resp.likes, }); } - // TODO: Go back to this to see how to handle error later - return { - name: 'Error', - id: 'Error', - userId: 'Error', - username: 'Error', - likes: -1, - }; + throw new UserInputError('Project is not found. Please try another project ID', { + argumentName: 'projId', + }); }, makeCopy: async (parent, { projId, userId, username }) => { const filter = { _id: projId }; const target = await Projects.findOne(filter); - - // make a copy with the passed in userId - // IMPORTANT: DO NOT CHANGE copy.name, it will create a nother copy of the document with the origional project name. - const copy = { - name: target.name, - project: target.project, - userId, - username, - }; - - // IMPORTANT: MUST MAKE A DEEP COPY OF target.project, otherwise, field 'style' is lost during the copy process. See 'minimize' option in projectSchema - - const resp = await Projects.create(copy); - if (resp) { - return ({ - name: resp.name, - id: resp._id, - userId: resp.userId, - username: resp.username, - likes: resp.likes, + if (!target) { + throw new UserInputError('Project is not found. Please try another project ID', { + argumentName: 'projId', }); } - // TODO: Go back to this to see how to handle error later - return { - name: 'Error', - id: 'Error', - userId: 'Error', - username: 'Error', - likes: -1, - }; - }, + // check if user account exists + const user = await Users.findOne({ _id: userId }); + console.log ('User >>> ', user); + if (user) { + // make a copy with the passed in userId + const copy = { + name: target.name, + project: target.project, + userId, + username, + }; - deleteProject: async (parent, { projId }) => { - const filter = { _id: projId }; - const options = { strict: true }; - const resp = await Projects.findOneAndDelete(filter, options); - // console.log("resp", resp); - if (resp) { - return ({ - name: resp.name, - likes: resp.likes, - id: resp._id, - userId: resp.userId, - username: resp.username, - }); + // Make a copy of the requested project + const resp = await Projects.create(copy); + if (resp) { + return ({ + name: resp.name, + id: resp._id, + userId: resp.userId, + username: resp.username, + likes: resp.likes, + })} + + throw new UserInputError('Internal Server Error'); } - return { - name: 'Error', - id: 'Error', - userId: 'Error', - username: 'Error', - likes: -1, - }; + + throw new UserInputError('User is not found. Please try another user ID', { + argumentName: 'userId', + }); }, }; module.exports = { - TestMutation: Test, ProjectMutation: Project, }; diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index 94de3cbca..a03fecb57 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -1,32 +1,13 @@ -const { Tests, Projects, Users } = require('../../models/reactypeModels'); - +const { UserInputError } = require('apollo-server-express'); +const { Projects } = require('../../models/reactypeModels'); // Link to Apollo Query Types: // https://www.apollographql.com/docs/apollo-server/data/resolvers/#defining-a-resolver -const Test = { - - readTest: async (parent, args) => { - const resp = await Tests.findOne({ _id: args.id }); - if (resp) return { description: resp.name, id: resp._id, likes: resp.likes }; - return { description: 'Error reading', id: '0', likes: '0' }; - }, - - readAllTests: async () => { - const resp = await Tests.find({}); - // console.log('resp', resp); - if (resp) { - return resp.map(elem => ({ description: elem.name, id: elem._id, likes: elem.likes })); - } - // TODO: Go back to this to see how to handle error later - return [{ description: 'Error reading all tests', id: '0', likes: '0' }]; - }, - -}; - const Project = { getProject: async (parent, { projId }) => { const resp = await Projects.findOne({ _id: projId }); + // console.log('getProject return >>> ', resp); if (resp) { return ({ name: resp.name, @@ -37,14 +18,10 @@ const Project = { }); } - // TODO: Go back to this to see how to handle error later - return { - name: 'Error', - id: 'Error', - userId: 'Error', - username: 'Error', - likes: -1, - }; + // resp is null if nothing is found based on the project ID + throw new UserInputError('Project is not found. Please try another project ID', { + argumentName: 'projId', + }); }, getAllProjects: async (parent, { userId }) => { @@ -54,6 +31,10 @@ const Project = { if (userId) { // use loosely equal for the callback because there are some discrepancy between the type of userId from the db vs from the mutation query resp = resp.filter(proj => proj.userId == userId); + // if resp = [] after the filtering, this means the userId doesnt exisit in the database, throw error as follow + throw new UserInputError(`Project for userId: "${userId}". Please try another id`, { + argumentName: 'userId', + }); } if (resp) { @@ -66,20 +47,13 @@ const Project = { })); } - // TODO: Go back to this to see how to handle error later - return [{ - name: 'Error', - id: 'Error', - userId: 'Error', - username: 'Error', - likes: -1, - }]; + // resp is null, return error message + throw new UserInputError('Internal Server Error'); }, }; module.exports = { - TestQuery: Test, ProjectQuery: Project, }; diff --git a/server/graphQL/typeDefs.js b/server/graphQL/typeDefs.js index 0e4b79556..12faa574d 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/typeDefs.js @@ -1,4 +1,3 @@ - const { gql } = require('apollo-server-express'); // Link to defining a schema in Apollo: @@ -6,24 +5,6 @@ const { gql } = require('apollo-server-express'); // The schema specifies which queries and mutations are available for clients // to execute against your data graph. -const Test = gql` - - type Mutation { - addTest(name: String, likes: Int): Test - updateTest(id: ID, name: String, likes: Int): Test - deleteTest(id: ID): Test - } - type Test { - description: String - likes: Int - id: ID - } - type Query { - readTest(id: ID): Test - readAllTests: [Test] - } -`; - //NOTE: Project type does not return the detail of the project's components, but info needed for the dashboard const Project = gql` @@ -48,6 +29,5 @@ const Project = gql` `; module.exports = { - typeDefsTest: Test, typeDefsProject: Project, }; diff --git a/server/server.js b/server/server.js index 65f036bcc..64117b968 100644 --- a/server/server.js +++ b/server/server.js @@ -59,9 +59,9 @@ GraphQl Router const { ApolloServer } = require('apollo-server-express'); // Query resolvers -const { TestQuery, ProjectQuery } = require('./graphQL/resolvers/query'); +const { ProjectQuery } = require('./graphQL/resolvers/query'); // Mutation resolvers -const { TestMutation, ProjectMutation } = require('./graphQL/resolvers/mutation'); +const { ProjectMutation } = require('./graphQL/resolvers/mutation'); // package resolvers into one variable to pass to Apollo Server const resolvers = { @@ -70,7 +70,7 @@ const resolvers = { }; // schemas used for graphQL -const { typeDefsTest, typeDefsProject } = require('./graphQL/typeDefs'); +const { typeDefsProject } = require('./graphQL/typeDefs'); // instantiate Apollo server and attach to Express server, mounted at 'http://localhost:PORT/graphql' const server = new ApolloServer({ typeDefs: typeDefsProject, resolvers }); @@ -78,7 +78,6 @@ server.applyMiddleware({ app }); /** ****************************************************************** */ - app.post( '/signup', userController.createUser, From 5211416c515f9d3fd65cb453781fc40b6ab2396b Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Tue, 9 Mar 2021 11:10:16 -0800 Subject: [PATCH 038/116] implmenet resolver and schema for publishProject --- server/graphQL/resolvers/mutation.js | 33 +++++++++++++++++++++---- server/graphQL/resolvers/query.js | 8 +++--- server/graphQL/{ => schema}/typeDefs.js | 6 ++--- server/models/reactypeModels.js | 8 +++--- server/server.js | 12 ++++----- 5 files changed, 45 insertions(+), 22 deletions(-) rename server/graphQL/{ => schema}/typeDefs.js (87%) diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index 552341566..46b9b153e 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -19,6 +19,7 @@ const Project = { id: resp._id, userId: resp.userId, likes: resp.likes, + published: resp.published, }); } @@ -39,7 +40,7 @@ const Project = { // check if user account exists const user = await Users.findOne({ _id: userId }); - console.log ('User >>> ', user); + if (user) { // make a copy with the passed in userId const copy = { @@ -58,7 +59,9 @@ const Project = { userId: resp.userId, username: resp.username, likes: resp.likes, - })} + published: resp.published, + }); + } throw new UserInputError('Internal Server Error'); } @@ -67,8 +70,28 @@ const Project = { argumentName: 'userId', }); }, -}; -module.exports = { - ProjectMutation: Project, + publishProject: async (parent, { projId, published }) => { + + const filter = { _id: projId }; + const update = { published }; + const options = { new: true }; + const resp = await Projects.findOneAndUpdate(filter, update, options); + if (resp) { + return ({ + name: resp.name, + id: resp._id, + userId: resp.userId, + likes: resp.likes, + published: resp.published, + }); + } + + throw new UserInputError('Project is not found. Please try another project ID', { + argumentName: 'projId', + }); + }, + }; + +module.exports = Project; diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index a03fecb57..31843a412 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -15,6 +15,7 @@ const Project = { userId: resp.userId, username: resp.username, likes: resp.likes, + published: resp.published, }); } @@ -44,16 +45,15 @@ const Project = { userId: proj.userId, username: proj.username, likes: proj.likes, + published: proj.published, })); } - // resp is null, return error message + // resp is null, return error message throw new UserInputError('Internal Server Error'); }, }; -module.exports = { - ProjectQuery: Project, -}; +module.exports = Project; diff --git a/server/graphQL/typeDefs.js b/server/graphQL/schema/typeDefs.js similarity index 87% rename from server/graphQL/typeDefs.js rename to server/graphQL/schema/typeDefs.js index 12faa574d..36a157b79 100644 --- a/server/graphQL/typeDefs.js +++ b/server/graphQL/schema/typeDefs.js @@ -12,11 +12,13 @@ const Project = gql` addLike(projId: ID!, likes: Int!): Project makeCopy(projId: ID!, userId: ID!, username: String!): Project deleteProject(projId: ID!): Project + publishProject(projId: ID!, published: Boolean!): Project } type Project { name: String! likes: Int + published: Boolean! id: ID! userId: ID! username: String! @@ -28,6 +30,4 @@ const Project = gql` } `; -module.exports = { - typeDefsProject: Project, -}; +module.exports = Project; diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index ead5ad16d..c652e9774 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -59,15 +59,15 @@ const sessionSchema = new Schema({ const projectSchema = new Schema({ name: String, likes: { type: Number, default: 0 }, + published: { type: Boolean, default: false }, project: { type: Object, required: true }, userId: { type: Schema.Types.ObjectId, ref: 'Users', }, - username: {type: String, required: true }, - createdAt: { type: Date, default: Date.now } -}, { minimize: false } -); + username: { type: String, required: true }, + createdAt: { type: Date, default: Date.now }, +}, { minimize: false }); // option 'minimize' prevent Mongoose from removing empty 'style' value in the copy // Test schema for implementing GraphQL diff --git a/server/server.js b/server/server.js index 64117b968..ce045962e 100644 --- a/server/server.js +++ b/server/server.js @@ -59,21 +59,21 @@ GraphQl Router const { ApolloServer } = require('apollo-server-express'); // Query resolvers -const { ProjectQuery } = require('./graphQL/resolvers/query'); +const Query = require('./graphQL/resolvers/query'); // Mutation resolvers -const { ProjectMutation } = require('./graphQL/resolvers/mutation'); +const Mutation = require('./graphQL/resolvers/mutation'); // package resolvers into one variable to pass to Apollo Server const resolvers = { - Query: ProjectQuery, - Mutation: ProjectMutation, + Query, + Mutation, }; // schemas used for graphQL -const { typeDefsProject } = require('./graphQL/typeDefs'); +const typeDefs = require('./graphQL/schema/typeDefs.js'); // instantiate Apollo server and attach to Express server, mounted at 'http://localhost:PORT/graphql' -const server = new ApolloServer({ typeDefs: typeDefsProject, resolvers }); +const server = new ApolloServer({ typeDefs, resolvers }); server.applyMiddleware({ app }); /** ****************************************************************** */ From cc6dff5e0a2da38e5b8e56abf57bdd1b43117f5b Mon Sep 17 00:00:00 2001 From: khuong nguyen Date: Tue, 9 Mar 2021 11:59:06 -0800 Subject: [PATCH 039/116] implement toggle publish state --- app/src/Dashboard/Project.tsx | 41 +++++++++++++++++++------- app/src/Dashboard/ProjectContainer.tsx | 5 ++-- app/src/Dashboard/gqlStrings.ts | 15 ++++++++-- server/graphQL/resolvers/mutation.js | 20 +++++++++++++ 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/app/src/Dashboard/Project.tsx b/app/src/Dashboard/Project.tsx index e9f9d7a8e..cd1e0f3f1 100644 --- a/app/src/Dashboard/Project.tsx +++ b/app/src/Dashboard/Project.tsx @@ -1,6 +1,11 @@ import React from 'react'; import { useMutation } from '@apollo/client'; -import { ADD_LIKE, MAKE_COPY, DELETE_PROJECT } from './gqlStrings'; +import { + ADD_LIKE, + MAKE_COPY, + DELETE_PROJECT, + PUBLISH_PROJECT, +} from './gqlStrings'; // Variable validation using typescript type props = { @@ -9,10 +14,15 @@ type props = { userId: string, username: string, likes: number, + published: boolean, }; +// Use current user info to make a make copy of another user's project +const currUserSSID = window.localStorage.getItem('ssid') || 'unavailable'; +const currUsername = window.localStorage.getItem('username') || 'unavailable'; + const Project = ({ - name, likes, id, username, + name, likes, id, username, published, }: props) : JSX.Element => { // IMPORTANT: // 1) schema change projId => id to allows Apollo Client cache auto-update. Only works with 'id' @@ -21,10 +31,10 @@ const Project = ({ const [addLike] = useMutation(ADD_LIKE); const [makeCopy] = useMutation(MAKE_COPY); const [deleteProject] = useMutation(DELETE_PROJECT); + const [publishProject] = useMutation(PUBLISH_PROJECT); function handleLike(e) { e.preventDefault(); - // IMPORTANT: DO NOT ADD extra comma to the last line of the variable object, the query will not work const myVar = { variables: { @@ -36,10 +46,6 @@ const Project = ({ addLike(myVar); } - // Use current user info to make a make copy of another user's project - const currUserSSID = window.localStorage.getItem('ssid') || 'unavailable'; - const currUsername = window.localStorage.getItem('username') || 'unavailable'; - function handleDownload(e) { e.preventDefault(); @@ -51,7 +57,6 @@ const Project = ({ username: currUsername, }, }; - // send Mutation makeCopy(myVar); } @@ -66,6 +71,19 @@ const Project = ({ deleteProject(myVar); } + function handlePublish(e) { + e.preventDefault(); + const myVar = { + variables: + { + projId: id, + published: !published, + }, + }; + publishProject(myVar); + } + + return (

Project: { name }

@@ -73,8 +91,11 @@ const Project = ({

Likes: { likes }

- {currUsername !== username ? : } - + {currUsername !== username ? : } + {currUsername === username ? : } + { currUsername === username + ? + : }
); diff --git a/app/src/Dashboard/ProjectContainer.tsx b/app/src/Dashboard/ProjectContainer.tsx index e9b4b9d25..fdd6c2706 100644 --- a/app/src/Dashboard/ProjectContainer.tsx +++ b/app/src/Dashboard/ProjectContainer.tsx @@ -26,18 +26,19 @@ const ProjectContainer = () => { console.log('Projects >>> ', projects); // generate an array of Project components based on data const publicProjects = []; - const userProjects = []; + const userProjects = []; projects.forEach((proj, index) => { const component = ; if (username === proj.username) userProjects.push(component); - else publicProjects.push(component); + if (proj.published) publicProjects.push(component); }); return ( diff --git a/app/src/Dashboard/gqlStrings.ts b/app/src/Dashboard/gqlStrings.ts index 5a2d7d179..3ec94ef05 100644 --- a/app/src/Dashboard/gqlStrings.ts +++ b/app/src/Dashboard/gqlStrings.ts @@ -8,7 +8,8 @@ export const GET_PROJECTS = gql`query GetAllProjects($userId: ID) { likes id userId - username + username + published } }`; @@ -19,11 +20,10 @@ export const ADD_LIKE = gql` addLike(projId: $projId, likes: $likes) { id - likes } }`; -export const MAKE_COPY = gql` +export const MAKE_COPY = gql` mutation MakeCopy ($userId: ID!, $projId: ID!, $username: String!) { makeCopy(userId: $userId, projId: $projId, username: $username) { @@ -38,3 +38,12 @@ export const DELETE_PROJECT = gql` id } }`; + +export const PUBLISH_PROJECT = gql` + mutation Publish($projId: ID!, $published: Boolean!) { + publishProject(projId: $projId, published: $published) + { + id + published + } + }`; diff --git a/server/graphQL/resolvers/mutation.js b/server/graphQL/resolvers/mutation.js index 46b9b153e..dcb6efb55 100644 --- a/server/graphQL/resolvers/mutation.js +++ b/server/graphQL/resolvers/mutation.js @@ -71,6 +71,26 @@ const Project = { }); }, + deleteProject: async (parent, { projId }) => { + const filter = { _id: projId }; + const options = { strict: true }; + const resp = await Projects.findOneAndDelete(filter, options); + + if (resp) { + return ({ + name: resp.name, + id: resp._id, + userId: resp.userId, + likes: resp.likes, + published: resp.published, + }); + } + + throw new UserInputError('Project is not found. Please try another project ID', { + argumentName: 'projId', + }); + }, + publishProject: async (parent, { projId, published }) => { const filter = { _id: projId }; From 65f7f72c5b1e2972c950114ddf3805a7b6ba77c0 Mon Sep 17 00:00:00 2001 From: elenaconn Date: Tue, 9 Mar 2021 18:00:30 -0500 Subject: [PATCH 040/116] Working Undo Functionality. > > Co-authored-by: eddypjr Co-authored-by: Anthonytorrero --- app/src/components/main/Canvas.tsx | 41 +---- .../main/DirectChildHTMLNestable.tsx | 10 +- app/src/containers/RightContainer.tsx | 55 +------ app/src/reducers/componentReducer.ts | 153 +++--------------- app/src/tree/TreeChart.tsx | 2 +- 5 files changed, 46 insertions(+), 215 deletions(-) diff --git a/app/src/components/main/Canvas.tsx b/app/src/components/main/Canvas.tsx index 939a9b949..2f2b937b3 100644 --- a/app/src/components/main/Canvas.tsx +++ b/app/src/components/main/Canvas.tsx @@ -1,6 +1,5 @@ import React, { useState, useContext } from 'react'; import { useDrop, DropTargetMonitor } from 'react-dnd'; -import customHooks from '../helperFunctions/customHook'; import _ from 'lodash'; import { ItemTypes } from '../../constants/ItemTypes'; import StateContext from '../../context/context'; @@ -11,6 +10,7 @@ import renderChildren from '../../helperFunctions/renderChildren'; // const snapStateArr = []; function Canvas() { const [state, dispatch] = useContext(StateContext); + console.log('state in Canvas', state); // const [ prevState, setPrevState ] = useState>([]); // NOT USING // find the current component to render on the canvas const currentComponent: Component = state.components.find( @@ -30,48 +30,23 @@ function Canvas() { // note: a null value for the child id means that we are focusing on the top-level component rather than any child changeFocus(state.canvasFocus.componentId, null); } - function onChangeHandler(event) { - // console.log('working', event.target) - } - // stores a limited snapshot of previous state to use in the useDrop function, for UNDO functionality - // const snapStateArr = []; + // stores a snapshot of state into the past array for UNDO const snapShotFunc = () => { - // make a deep clone of state ( JSON.parse(JSON.stringify()) ? ) - // function inner() { + // make a deep clone of state const deepCopiedState = JSON.parse(JSON.stringify(state)); - // console.log('deepCopiedState', deepCopiedState); - // stateSnapArr.push(deepCopiedState); state.past.push(deepCopiedState.components[0].children); - // state.past.push(deepCopiedState); - // console.log('state after push', state) - console.log('state in canvas', state.past) - // return; - // snapStateArr.push(5); - // return snapStateArr; - // } - // inner(); - - // return; - // const prevCount = customHooks(state); - // console.log('prevCount', prevCount); - // console.log('state', state); - - // setPrevState( previousState => { - // return [...previousState].push(deepCopiedState); - // }) - // console.log('prevState: ', prevState) -} + // console.log('state in snapShotFunc', state.past) + } + // This hook will allow the user to drag items from the left panel on to the canvas const [{ isOver }, drop] = useDrop({ accept: ItemTypes.INSTANCE, drop: (item: DragItem, monitor: DropTargetMonitor) => { const didDrop = monitor.didDrop(); // returns false for direct drop target - //code here - // 6.0 didDrop is firing when HTML tags are moved up - snapShotFunc(); // < ------ snapShotFunc here + snapShotFunc(); if (didDrop) { return; } @@ -118,7 +93,7 @@ function Canvas() { // Direct children are draggable/clickable const canvasStyle = combineStyles(defaultCanvasStyle, currentComponent.style); return ( -
+
{/* currentComponent is the selected component on Left Panel (eg: App or Index with green dot to the left) */} {renderChildren(currentComponent.children)}
diff --git a/app/src/components/main/DirectChildHTMLNestable.tsx b/app/src/components/main/DirectChildHTMLNestable.tsx index 52447757b..604a8a34f 100644 --- a/app/src/components/main/DirectChildHTMLNestable.tsx +++ b/app/src/components/main/DirectChildHTMLNestable.tsx @@ -17,8 +17,13 @@ function DirectChildHTMLNestable({ }: ChildElement) { const [state, dispatch] = useContext(StateContext); const ref = useRef(null); - // console.log('name', name) - // console.log('children', children) +// stores a snapshot of state into the past array for nested elements for the UNDO case +const snapShotFunc = () => { + const deepCopiedState = JSON.parse(JSON.stringify(state)); + state.past.push(deepCopiedState.components[0].children); + // state.future.push(deepCopiedState.components[0].children); + // console.log('state.past in directChildHTMLNest', state) +}; // find the HTML element corresponding with this instance of an HTML element // find the current component to render on the canvas const HTMLType: HTMLType = state.HTMLTypes.find( @@ -50,6 +55,7 @@ function DirectChildHTMLNestable({ // triggered on drop drop: (item: any, monitor: DropTargetMonitor) => { const didDrop = monitor.didDrop(); + snapShotFunc(); if (didDrop) { return; } diff --git a/app/src/containers/RightContainer.tsx b/app/src/containers/RightContainer.tsx index 43dc0d79b..507afee6f 100644 --- a/app/src/containers/RightContainer.tsx +++ b/app/src/containers/RightContainer.tsx @@ -5,7 +5,6 @@ import React, { useMemo, useRef, } from 'react'; -import customHooks from '../helperFunctions/customHook'; import initialState from '../context/initialState'; import { makeStyles } from '@material-ui/core/styles'; import FormControl from '@material-ui/core/FormControl'; @@ -45,29 +44,6 @@ const RightContainer = ({isThemeLight}): JSX.Element => { const { style } = useContext(styleContext); const [modal, setModal] = useState(null); const [prevState, setPrevState] = useState(state); - // const [ref, setRef] = useRef(); - - // const prevCountRef = useRef(); - // useEffect(() => { - // prevCountRef.current = prevState; - // }); - // const prevCount = prevCountRef.current; - // console.log('prevCount <-- before', prevCount); - // console.log('prevState <-- now', prevState); - // console.log('prevState', prevState); -//state.components[0].children -// function usePrevious(value) { -// const ref = useRef(); -// useEffect(() => { -// ref.current = value; -// }); -// return ref.current; -// } -// const prevCount = customHooks(state); -// console.log('prevCount <-- before', prevCount); -// console.log('prevState <-- now', prevState) -// console.log('state in rightContainer', state); - const resetFields = () => { @@ -227,38 +203,11 @@ const RightContainer = ({isThemeLight}): JSX.Element => { // set current state components.children to previous state and store current state children array in another place holder to not lose current state before reassigning to previous position. const undoAction = () => { dispatch({ type: 'UNDO', payload: {} }); -// const undoAction = () => { - // dispatch({ type: 'UNDO', payload: {value} }) - // const ref = useRef(); - // useEffect(() => { - // ref.current = value; - // }); - // console.log('ref.curr', ref.current) - // return ref.current; - // dispatch({ type: 'UNDO', payload: {} }); - // const childrenArr = state.components[0].children; - // const result = []; - // // console.log('state.components', state.components[0].children) - // for(let i = 0; i < prevState.children.length - 3; i++) { - // result.push(prevState.children[i]); - // } - // setPrevState(result); }; -// }; - -// const prevCount = undoAction(prevState); -// console.log('prevCount', prevCount); - -const handleClick = (e, data) => { - console.log(data); -} const redoAction = () => { dispatch({ type: 'REDO', payload: {} }); -} - - - +}; // placeholder for handling deleting instance const handleDelete = () => { @@ -583,6 +532,8 @@ const redoAction = () => {