diff --git a/backend/package-lock.json b/backend/package-lock.json index 73a9f77..8f2909e 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", "mongoose": "^8.5.4" @@ -247,6 +248,19 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -960,6 +974,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", diff --git a/backend/package.json b/backend/package.json index 86653d6..49d4d58 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,6 +10,7 @@ "author": "Jason", "license": "MIT", "dependencies": { + "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", "mongoose": "^8.5.4" diff --git a/backend/server.js b/backend/server.js index 6b85673..e797b10 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,4 +1,5 @@ const express = require('express'); +const cors = require('cors'); require('dotenv').config(); const port = process.env.PORT || 5000; const connectDB = require('./config/db'); @@ -16,6 +17,13 @@ app.get('/', (req, res) => { res.json({ message: 'Welcome to the RandomIdeas API' }); }); +//cors middleware +app.use( + cors({ + origin: ['http://localhost:5000', 'http://localhost:5173'], + }) +); + //registering the routes const ideasRouter = require('./routes/ideas'); app.use('/api/ideas', ideasRouter); diff --git a/README.md b/frontend/README.md similarity index 100% rename from README.md rename to frontend/README.md diff --git a/components/IdeaForm.js b/frontend/components/IdeaForm.js similarity index 63% rename from components/IdeaForm.js rename to frontend/components/IdeaForm.js index 4603dcb..081eab2 100644 --- a/components/IdeaForm.js +++ b/frontend/components/IdeaForm.js @@ -1,6 +1,11 @@ +import IdeasApi from '../services/ideasApi'; +import IdeaList from './IdeaList'; + class IdeaForm { constructor() { this._formModal = document.querySelector('#form-modal'); + //instantiating the idea list since it is a class + this._ideaList = new IdeaList(); } //add all event listeners in a function @@ -8,9 +13,22 @@ class IdeaForm { this._form.addEventListener('submit', this.handleSubmit.bind(this)); } - handleSubmit(e) { + async handleSubmit(e) { + //made this function async because we are dealing with a promise (axios) e.preventDefault(); //stops the submit and allows us to determine when it should run + if ( + !this._form.elements.text.value || + !this._form.elements.tag.value || + !this._form.elements.username.value + ) { + alert('Please enter all fields'); + return; + } + + //save user to local storage + localStorage.setItem('username', this._form.elements.username.value); + //grabs values from the form const idea = { text: this._form.elements.text.value, @@ -18,10 +36,17 @@ class IdeaForm { username: this._form.elements.username.value, }; + //add idea to database + const newIdea = await IdeasApi.createIdea(idea); + + //add idea to list and call render on the DOM + this._ideaList.addIdeaToList(newIdea.data.data_base); + //clear form fields after submit this._form.elements.text.value = ''; this._form.elements.tag.value = ''; this._form.elements.username.value = ''; + this.render(); //using the dispatch event method of the document object to dispatch a custom event //to the Modal from the IdeaForm, telling the Modal to close the form on submit @@ -29,12 +54,18 @@ class IdeaForm { } //create a render function like react that will output the HTML + //adding a turnary to check for username and saved in localstorage render() { this._formModal.innerHTML = `
- +
diff --git a/components/IdeaList.js b/frontend/components/IdeaList.js similarity index 74% rename from components/IdeaList.js rename to frontend/components/IdeaList.js index 8f2ad1e..77190f8 100644 --- a/components/IdeaList.js +++ b/frontend/components/IdeaList.js @@ -1,32 +1,13 @@ //this class returns a list of cards to the DOM //by mapping (looping) over an array +import IdeasApi from '../services/ideasApi'; + class IdeaList { constructor() { this._ideaListEl = document.querySelector('#idea-list'); - this._ideas = [ - { - id: 1, - text: 'Idea 1', - username: 'John', - date: '02/01/2023', - tag: 'Business', - }, - { - id: 2, - text: 'Idea 2', - username: 'Jimmy', - date: '02/01/2023', - tag: 'Technology', - }, - { - id: 3, - text: 'Idea 3', - username: 'Jill', - date: '02/01/2023', - tag: 'Fashion', - }, - ]; + this._ideas = []; + this._getIdeas(); //adding a set (datatype) this._validTags = new Set(); @@ -38,6 +19,23 @@ class IdeaList { this._validTags.add('inventions'); } + async _getIdeas() { + try { + const res = await IdeasApi.getIdeas(); + this._ideas = res.data.data_base; //in axios, when you make a request the info comes wrapped in an object called data, + //data refers to the axios object and data_base refers to the info being returned from the backend + this.render(); + // console.log(this._ideas); + } catch (error) { + console.log(error); + } + } + + addIdeaToList(idea) { + this._ideas.push(idea); + this.render(); + } + getTagClass(tag) { tag = tag.toLowerCase(); let tagClass = ''; diff --git a/components/Modal.js b/frontend/components/Modal.js similarity index 100% rename from components/Modal.js rename to frontend/components/Modal.js diff --git a/css/style.css b/frontend/css/style.css similarity index 100% rename from css/style.css rename to frontend/css/style.css diff --git a/index.html b/frontend/index.html similarity index 100% rename from index.html rename to frontend/index.html diff --git a/main.js b/frontend/main.js similarity index 76% rename from main.js rename to frontend/main.js index 44e85cf..8a74aa3 100644 --- a/main.js +++ b/frontend/main.js @@ -4,8 +4,7 @@ import IdeaList from './components/IdeaList'; import '@fortawesome/fontawesome-free/css/all.css'; import './css/style.css'; -const modal = new Modal(); +new Modal(); const ideaForm = new IdeaForm(); ideaForm.render(); -const ideaList = new IdeaList(); -ideaList.render(); +new IdeaList(); diff --git a/package-lock.json b/frontend/package-lock.json similarity index 87% rename from package-lock.json rename to frontend/package-lock.json index 39bdab3..18eb257 100644 --- a/package-lock.json +++ b/frontend/package-lock.json @@ -8,7 +8,8 @@ "name": "randomideas", "version": "0.0.0", "dependencies": { - "@fortawesome/fontawesome-free": "^6.6.0" + "@fortawesome/fontawesome-free": "^6.6.0", + "axios": "^1.7.5" }, "devDependencies": { "vite": "^5.3.4" @@ -645,6 +646,44 @@ "dev": true, "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -684,6 +723,40 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -699,6 +772,27 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -754,6 +848,12 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/rollup": { "version": "4.19.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.0.tgz", diff --git a/package.json b/frontend/package.json similarity index 78% rename from package.json rename to frontend/package.json index 16e5591..ebae63d 100644 --- a/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "vite": "^5.3.4" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.6.0" + "@fortawesome/fontawesome-free": "^6.6.0", + "axios": "^1.7.5" } } diff --git a/public/vite.svg b/frontend/public/vite.svg similarity index 100% rename from public/vite.svg rename to frontend/public/vite.svg diff --git a/frontend/services/ideasApi.js b/frontend/services/ideasApi.js new file mode 100644 index 0000000..1c34299 --- /dev/null +++ b/frontend/services/ideasApi.js @@ -0,0 +1,17 @@ +import axios from 'axios'; + +class IdeasApi { + constructor() { + this._apiUrl = 'http://localhost:5000/api/ideas'; + } + + getIdeas() { + return axios.get(this._apiUrl); + } + + createIdea(data) { + return axios.post(this._apiUrl, data); + } +} + +export default new IdeasApi();