From aa7184518f034dca658c41fe121f5b1e1c350910 Mon Sep 17 00:00:00 2001 From: Sofia Johansson <153744881+sofia32057@users.noreply.github.com> Date: Sun, 26 May 2024 16:17:43 +0200 Subject: [PATCH 001/126] First commit --- backend/package.json | 4 +++- backend/server.js | 27 +++++++++++++-------------- frontend/package.json | 6 ++++-- frontend/tailwind.config.js | 0 4 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 frontend/tailwind.config.js diff --git a/backend/package.json b/backend/package.json index 08f29f244..d4eababc8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,9 +12,11 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^5.1.1", "cors": "^2.8.5", "express": "^4.17.3", + "express-list-endpoints": "^7.1.0", "mongoose": "^8.4.0", "nodemon": "^3.0.1" } -} \ No newline at end of file +} diff --git a/backend/server.js b/backend/server.js index 2c00e4802..7f402f205 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,16 +1,13 @@ import express from "express"; import cors from "cors"; -import mongoose from 'mongoose' +import mongoose, { model } from "mongoose"; +import expressListEndpoints from "express-list-endpoints"; +import bcrypt from "bcrypt"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/flowershop" -mongoose.connect(mongoUrl) -mongoose.Promise = Promise +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/our-wedding"; +mongoose.connect(mongoUrl); +mongoose.Promise = Promise; - - -// Defines the port the app will run on. Defaults to 8080, but can be overridden -// when starting the server. Example command to overwrite PORT env variable value: -// PORT=9000 npm start const port = process.env.PORT || 8080; const app = express(); @@ -19,13 +16,15 @@ app.use(cors()); app.use(express.json()); // Start defining your routes here -// http://localhost:8080/ -app.get("/", (req, res) => { - res.send("Hello Technigo!"); +app.route("/").get(async (req, res) => { + const endpoints = expressListEndpoints(app); + res.json({ + message: "Hello Technigo, welcome to our wedding site API", + endpoints: endpoints, + }); }); - // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); -}); \ No newline at end of file +}); diff --git a/frontend/package.json b/frontend/package.json index 41a2a3164..6a5a63332 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "zustand": "^4.5.2" }, "devDependencies": { "@types/react": "^18.2.15", @@ -21,6 +22,7 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", + "tailwindcss": "^3.4.3", "vite": "^4.4.5" } -} \ No newline at end of file +} diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 000000000..e69de29bb From d1bbfda52dc351a08196a2aa3d9425aa8acc5ff5 Mon Sep 17 00:00:00 2001 From: Sofia Johansson <153744881+sofia32057@users.noreply.github.com> Date: Sun, 26 May 2024 16:30:00 +0200 Subject: [PATCH 002/126] Update - env and README basics --- README.md | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 31466b54c..1394348c8 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,41 @@ -# Final Project +# Final Project - Our wedding site -Replace this readme with your own information about your project. +## Project Description -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +- What your application does +- Why you used the technologies you used +- Some of the challenges you faced and features you hope to implement in the future + +## How to Install and Run the Project + +Provide a step-by-step description of how to get the development environment set and running + +## How to Use the Project + +You can make use of visual aids by including materials like screenshots to show examples of the running project and also the structure and design principles used in your project. + +Also, if your project requires authentication like passwords or usernames, this is a good section to include the credentials. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +## MoSCoW + +Here you can describe the stretch goal(s) and possible features you would like to add in future versions + +### Must have + +### Should have + +### Could have + +### Wont have + +## Collaborators & Credits + +If you worked on the project as a team or an organization, list your collaborators/team members. You should also include links to their GitHub profiles and social media. + +Big thanks to [Citronsskal](https://github.com/Citronskal) and [Trista1987](https://github.com/trista1987) for helping us with the idea and pitch. -## View it live +## See it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. \ No newline at end of file +Deploy your app with Netlify (don’t forget to take care of the routes configuration by adding a .toml file), and paste the link in your README From badb7c3c6d4565735980dae64df22d5bf0afb2ec Mon Sep 17 00:00:00 2001 From: Sofia Johansson <153744881+sofia32057@users.noreply.github.com> Date: Sun, 26 May 2024 18:16:51 +0200 Subject: [PATCH 003/126] Update - added get and post for guests + model --- backend/README.md | 31 +++++++++++++- backend/server.js | 104 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/backend/README.md b/backend/README.md index d1438c910..ed101718c 100644 --- a/backend/README.md +++ b/backend/README.md @@ -5,4 +5,33 @@ This project includes the packages and babel setup for an express server, and is ## Getting Started 1. Install the required dependencies using `npm install`. -2. Start the development server using `npm run dev`. \ No newline at end of file +2. Start the development server using `npm run dev`. + +## Endpoints and routes + +### / + +### /guests + +Example of guest + +``` + { + "firstname": "Test2", + "lastname": "Testsson", + "email": "name@domain.io", + "password": "test", + "plusOne": { + "isAllowed": true, + "name": "Plus One", + "foodChoice": "Fish" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Sister of the bride", + "willAttend": true + } +``` diff --git a/backend/server.js b/backend/server.js index 7f402f205..f0f708b09 100644 --- a/backend/server.js +++ b/backend/server.js @@ -7,6 +7,7 @@ import bcrypt from "bcrypt"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/our-wedding"; mongoose.connect(mongoUrl); mongoose.Promise = Promise; +mongoose.set("setDefaultsOnInsert", false); const port = process.env.PORT || 8080; const app = express(); @@ -15,7 +16,63 @@ const app = express(); app.use(cors()); app.use(express.json()); -// Start defining your routes here +const Guest = model("Guest", { + accessToken: { + type: String, + default: () => bcrypt.genSaltSync(), + }, + firstname: { + type: String, + required: true, + }, + lastname: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + }, + password: { + type: String, + required: true, + }, + willAttend: { + type: Boolean, + default: false, + }, + plusOne: { + isAllowed: { type: Boolean, default: false }, + name: { type: String }, + foodChoice: { type: String }, + }, + speech: { + isAllowed: { + type: Boolean, + }, + willMakeSpeech: { + type: Boolean, + default: false, + }, + }, + foodChoice: { + type: String, + }, + relation: { + type: String, + required: true, + }, + // Combine name a unique username + // name: { + // type: String, + // default: () => { + // `${this.firstname} ${this.lastname}`; + // }, + // unique: true, + // }, +}); + +// ROUTES app.route("/").get(async (req, res) => { const endpoints = expressListEndpoints(app); res.json({ @@ -24,6 +81,51 @@ app.route("/").get(async (req, res) => { }); }); +app + .route("/guests") + // Get all guests (specific fields) + .get(async (req, res) => { + const guests = await Guest.find( + {}, + "firstname lastname relation willAttend" + ); + res.send(guests); + }) + // Post new guest + .post(async (req, res) => { + try { + // Deconstruct request body + const { + firstname, + lastname, + email, + password, + plusOne, + speech, + foodChoice, + relation, + willAttend, + } = req.body; + + // Create and save new guest + const newGuest = await new Guest({ + firstname, + lastname, + email, + password: bcrypt.hashSync(password, 10), + plusOne, + speech, + foodChoice, + relation, + willAttend, + }).save(); + + res.status(201).send(`Created guest ID ${newGuest._id}`); + } catch (err) { + res.status(400).json({ message: "Post request failed", error: err }); + } + }); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); From 16b032c1ecf99d1520e186dcb524618f9c0e336a Mon Sep 17 00:00:00 2001 From: Sofia Johansson <153744881+sofia32057@users.noreply.github.com> Date: Sun, 26 May 2024 20:04:15 +0200 Subject: [PATCH 004/126] Update - Seeded DB with guest list + added endpoint for specific guest --- backend/README.md | 17 +- backend/guests.json | 904 ++++++++++++++++++++++++++++++++++++++++++++ backend/server.js | 31 +- 3 files changed, 946 insertions(+), 6 deletions(-) create mode 100644 backend/guests.json diff --git a/backend/README.md b/backend/README.md index ed101718c..f0fbfccfb 100644 --- a/backend/README.md +++ b/backend/README.md @@ -13,17 +13,20 @@ This project includes the packages and babel setup for an express server, and is ### /guests -Example of guest +- .get: get all guests +- .post: post new guest + +#### Example of guest ``` { - "firstname": "Test2", - "lastname": "Testsson", - "email": "name@domain.io", + "firstname": "Judy", + "lastname": "Dench", + "email": "judy@worddomination.io", "password": "test", "plusOne": { "isAllowed": true, - "name": "Plus One", + "name": "Emma Thompson", "foodChoice": "Fish" }, "speech": { @@ -35,3 +38,7 @@ Example of guest "willAttend": true } ``` + +### /guests/:guestId + +- .get: get guest by id diff --git a/backend/guests.json b/backend/guests.json new file mode 100644 index 000000000..400316976 --- /dev/null +++ b/backend/guests.json @@ -0,0 +1,904 @@ +[ + { + "firstname": "John", + "lastname": "Smith", + "email": "john.smith@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Jane Smith", + "foodChoice": "Chicken" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Beef", + "relation": "Groom's father" + }, + { + "firstname": "Jane", + "lastname": "Smith", + "email": "jane.smith@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's mother" + }, + { + "firstname": "Michael", + "lastname": "Johnson", + "email": "michael.johnson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Vegetarian", + "relation": "Groom's friend" + }, + { + "firstname": "Sarah", + "lastname": "Johnson", + "email": "sarah.johnson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Fish", + "relation": "Bride's mother" + }, + { + "firstname": "Robert", + "lastname": "Wilson", + "email": "robert.wilson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Elizabeth Brown", + "foodChoice": "Chicken" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Beef", + "relation": "Bride's father" + }, + { + "firstname": "Elizabeth", + "lastname": "Brown", + "email": "elizabeth.brown@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Vegetarian", + "relation": "Robert's girlfriend" + }, + { + "firstname": "James", + "lastname": "Anderson", + "email": "james.anderson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Mary Anderson", + "foodChoice": "Fish" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's friend" + }, + { + "firstname": "Mary", + "lastname": "Anderson", + "email": "mary.anderson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "James's wife" + }, + { + "firstname": "Joseph", + "lastname": "Thomas", + "email": "joseph.thomas@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Patricia Thomas", + "foodChoice": "Beef" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Fish", + "relation": "Groom's cousin" + }, + { + "firstname": "Patricia", + "lastname": "Thomas", + "email": "patricia.thomas@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Joseph's wife" + }, + { + "firstname": "Christopher", + "lastname": "Harris", + "email": "christopher.harris@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Laura Harris", + "foodChoice": "Chicken" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Beef", + "relation": "Bride's cousin" + }, + { + "firstname": "Laura", + "lastname": "Harris", + "email": "laura.harris@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Christopher's wife" + }, + { + "firstname": "Daniel", + "lastname": "Martin", + "email": "daniel.martin@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Jennifer White", + "foodChoice": "Fish" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's coworker" + }, + { + "firstname": "Jennifer", + "lastname": "White", + "email": "jennifer.white@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Daniel's girlfriend" + }, + { + "firstname": "Anthony", + "lastname": "Martinez", + "email": "anthony.martinez@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Karen Martinez", + "foodChoice": "Beef" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Fish", + "relation": "Bride's childhood friend" + }, + { + "firstname": "Karen", + "lastname": "Martinez", + "email": "karen.martinez@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Vegetarian", + "relation": "Anthony's fiancée" + }, + { + "firstname": "Steven", + "lastname": "Rodriguez", + "email": "steven.rodriguez@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Nancy Clark", + "foodChoice": "Chicken" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Beef", + "relation": "Bride's friend" + }, + { + "firstname": "Nancy", + "lastname": "Clark", + "email": "nancy.clark@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Steven's wife" + }, + { + "firstname": "Paul", + "lastname": "Lewis", + "email": "paul.lewis@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Angela Scott", + "foodChoice": "Fish" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's college friend" + }, + { + "firstname": "Angela", + "lastname": "Scott", + "email": "angela.scott@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Paul's girlfriend" + }, + { + "firstname": "Joshua", + "lastname": "Allen", + "email": "joshua.allen@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Jessica Adams", + "foodChoice": "Beef" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Fish", + "relation": "Groom's cousin" + }, + { + "firstname": "Jessica", + "lastname": "Adams", + "email": "jessica.adams@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Joshua's wife" + }, + { + "firstname": "Kevin", + "lastname": "Young", + "email": "kevin.young@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Megan Young", + "foodChoice": "Chicken" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Beef", + "relation": "Bride's brother" + }, + { + "firstname": "Megan", + "lastname": "Young", + "email": "megan.young@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Kevin's wife" + }, + { + "firstname": "Jason", + "lastname": "Wright", + "email": "jason.wright@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Samantha Hill", + "foodChoice": "Fish" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's colleague" + }, + { + "firstname": "Samantha", + "lastname": "Hill", + "email": "samantha.hill@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Jason's girlfriend" + }, + { + "firstname": "Timothy", + "lastname": "Lopez", + "email": "timothy.lopez@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Rebecca Morgan", + "foodChoice": "Beef" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Fish", + "relation": "Bride's friend" + }, + { + "firstname": "Rebecca", + "lastname": "Morgan", + "email": "rebecca.morgan@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Timothy's wife" + }, + { + "firstname": "Justin", + "lastname": "Green", + "email": "justin.green@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Rachel Moore", + "foodChoice": "Chicken" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Beef", + "relation": "Groom's friend" + }, + { + "firstname": "Rachel", + "lastname": "Moore", + "email": "rachel.moore@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Justin's girlfriend" + }, + { + "firstname": "Brandon", + "lastname": "Adams", + "email": "brandon.adams@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Olivia Edwards", + "foodChoice": "Fish" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's cousin" + }, + { + "firstname": "Olivia", + "lastname": "Edwards", + "email": "olivia.edwards@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Brandon's wife" + }, + { + "firstname": "George", + "lastname": "Patterson", + "email": "george.patterson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Carol Patterson", + "foodChoice": "Beef" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Fish", + "relation": "Bride's friend" + }, + { + "firstname": "Carol", + "lastname": "Patterson", + "email": "carol.patterson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "George's wife" + }, + { + "firstname": "Susan", + "lastname": "Watson", + "email": "susan.watson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": true, + "name": "Lisa Watson", + "foodChoice": "Chicken" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Beef", + "relation": "Bride's sister" + }, + { + "firstname": "Lisa", + "lastname": "Watson", + "email": "lisa.watson@example.com", + "password": "password123", + "willAttend": true + +, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": true + }, + "foodChoice": "Vegetarian", + "relation": "Susan's wife" + }, + { + "firstname": "David", + "lastname": "Brown", + "email": "david.brown@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Fish", + "relation": "College friend of the groom" + }, + { + "firstname": "Emily", + "lastname": "Davis", + "email": "emily.davis@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Vegetarian", + "relation": "Bride's cousin" + }, + { + "firstname": "William", + "lastname": "Moore", + "email": "william.moore@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's coworker" + }, + { + "firstname": "Linda", + "lastname": "Taylor", + "email": "linda.taylor@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Beef", + "relation": "Bride's neighbor" + }, + { + "firstname": "Charles", + "lastname": "Jackson", + "email": "charles.jackson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Fish", + "relation": "Groom's uncle" + }, + { + "firstname": "Barbara", + "lastname": "White", + "email": "barbara.white@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Vegetarian", + "relation": "Bride's childhood friend" + }, + { + "firstname": "Matthew", + "lastname": "Thompson", + "email": "matthew.thompson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's high school friend" + }, + { + "firstname": "Susan", + "lastname": "Garcia", + "email": "susan.garcia@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Beef", + "relation": "Bride's college roommate" + }, + { + "firstname": "Mark", + "lastname": "Robinson", + "email": "mark.robinson@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Fish", + "relation": "Bride's colleague" + }, + { + "firstname": "Lisa", + "lastname": "Clark", + "email": "lisa.clark@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Vegetarian", + "relation": "Groom's cousin" + }, + { + "firstname": "Andrew", + "lastname": "Walker", + "email": "andrew.walker@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Chicken", + "relation": "Groom's gym buddy" + }, + { + "firstname": "Michelle", + "lastname": "Hall", + "email": "michelle.hall@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Beef", + "relation": "Bride's colleague" + }, + { + "firstname": "Brian", + "lastname": "Hernandez", + "email": "brian.hernandez@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Fish", + "relation": "Groom's friend" + }, + { + "firstname": "Donna", + "lastname": "King", + "email": "donna.king@example.com", + "password": "password123", + "willAttend": true, + "plusOne": { + "isAllowed": false, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": false, + "willMakeSpeech": false + }, + "foodChoice": "Vegetarian", + "relation": "Bride's aunt" + } +] diff --git a/backend/server.js b/backend/server.js index f0f708b09..ba88a6f46 100644 --- a/backend/server.js +++ b/backend/server.js @@ -3,6 +3,7 @@ import cors from "cors"; import mongoose, { model } from "mongoose"; import expressListEndpoints from "express-list-endpoints"; import bcrypt from "bcrypt"; +import guestData from "./guests.json"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/our-wedding"; mongoose.connect(mongoUrl); @@ -72,6 +73,19 @@ const Guest = model("Guest", { // }, }); +// Seed database with guestlist +if (process.env.RESET_DB) { + console.log("Resetting the database!"); + const seedDatabase = async () => { + await Guest.deleteMany({}); + + guestData.forEach(async guest => { + new Guest(guest).save(); + }); + }; + seedDatabase(); +} + // ROUTES app.route("/").get(async (req, res) => { const endpoints = expressListEndpoints(app); @@ -87,7 +101,7 @@ app .get(async (req, res) => { const guests = await Guest.find( {}, - "firstname lastname relation willAttend" + "firstname lastname relation willAttend plusOne.name" ); res.send(guests); }) @@ -126,6 +140,21 @@ app } }); +app + .route("/guests/:guestId") + // Find specific guest + .get(async (req, res) => { + try { + const guest = await Guest.findById( + req.params.guestId, + "_id token firstname lastname relation willAttend plusOne.name" + ).exec(); + res.status(201).json(guest); + } catch (err) { + res.status(400).json({ message: "Used not found", error: err }); + } + }); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); From d45ec65ece35180d8e156e057ef515c4f7600951 Mon Sep 17 00:00:00 2001 From: ohitsnathalie <152981339+ohitsnathalie@users.noreply.github.com> Date: Mon, 27 May 2024 18:53:53 +0200 Subject: [PATCH 005/126] setting up tailwind --- frontend/.prettierrc | 3 +++ frontend/package.json | 6 +++++- frontend/postcss.config.js | 6 ++++++ frontend/src/index.css | 3 +++ frontend/tailwind.config.js | 13 +++++++++++++ 5 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 frontend/.prettierrc create mode 100644 frontend/postcss.config.js diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 000000000..b4bfed357 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/frontend/package.json b/frontend/package.json index 6a5a63332..9ddaa2e97 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,11 +18,15 @@ "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.0.3", + "autoprefixer": "^10.4.19", "eslint": "^8.45.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", + "postcss": "^8.4.38", + "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.14", "tailwindcss": "^3.4.3", - "vite": "^4.4.5" + "vite": "^5.2.11" } } diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 000000000..2e7af2b7f --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/src/index.css b/frontend/src/index.css index e69de29bb..b5c61c956 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index e69de29bb..ff406f3b9 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -0,0 +1,13 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + screens: { + sm: "320px", + md: "744px", + lg: "1440px", + }, + extend: {}, + }, + plugins: [], +}; From f0d43a6be97e974729b6e2b3fbe4e0a0060c6dd5 Mon Sep 17 00:00:00 2001 From: Sofia Johansson <153744881+sofia32057@users.noreply.github.com> Date: Tue, 28 May 2024 20:25:12 +0200 Subject: [PATCH 006/126] Update - Added login endpoint and hashed passwords in db seed function --- backend/README.md | 5 +++++ backend/guests.json | 18 +++++++++++++++++ backend/server.js | 49 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/backend/README.md b/backend/README.md index f0fbfccfb..cbe76668e 100644 --- a/backend/README.md +++ b/backend/README.md @@ -11,6 +11,11 @@ This project includes the packages and babel setup for an express server, and is ### / +### /login + +Login to app by using email and password. +Test user: test@example.com | testPassword + ### /guests - .get: get all guests diff --git a/backend/guests.json b/backend/guests.json index 400316976..d0167e0ed 100644 --- a/backend/guests.json +++ b/backend/guests.json @@ -1,4 +1,22 @@ [ + { + "firstname": "Test", + "lastname": "Testsson", + "email": "test@example.com", + "password": "testPassword", + "willAttend": false, + "plusOne": { + "isAllowed": true, + "name": "", + "foodChoice": "" + }, + "speech": { + "isAllowed": true, + "willMakeSpeech": false + }, + "foodChoice": "", + "relation": "Groom's father" + }, { "firstname": "John", "lastname": "Smith", diff --git a/backend/server.js b/backend/server.js index ba88a6f46..7a23f46df 100644 --- a/backend/server.js +++ b/backend/server.js @@ -33,6 +33,7 @@ const Guest = model("Guest", { email: { type: String, required: true, + unique: true, }, password: { type: String, @@ -73,14 +74,36 @@ const Guest = model("Guest", { // }, }); -// Seed database with guestlist +// Seed database with guestlist - temporary solution if (process.env.RESET_DB) { console.log("Resetting the database!"); const seedDatabase = async () => { await Guest.deleteMany({}); guestData.forEach(async guest => { - new Guest(guest).save(); + const { + firstname, + lastname, + email, + password, + plusOne, + speech, + foodChoice, + relation, + willAttend, + } = guest; + + new Guest({ + firstname, + lastname, + email, + password: bcrypt.hashSync(password, 10), + plusOne, + speech, + foodChoice, + relation, + willAttend, + }).save(); }); }; seedDatabase(); @@ -155,6 +178,28 @@ app } }); +//Login +app.route("/login").post(async (req, res) => { + // Find user by name + const guest = await Guest.findOne({ email: req.body.email }).exec(); + console.log(guest); + + // Check if password is correct + if (guest && bcrypt.compareSync(req.body.password, guest.password)) { + // a. User name and password match + res.status(201).json({ + message: "User logged in successfully", + accessToken: guest.accessToken, + }); + } else if (guest) { + // b. user exists but password did not match + res.status(401).json({ message: "Password did not match" }); + } else { + // c. user does not exists + res.status(400).json({ message: "guest name invalid" }); + } +}); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); From cc8324ef97d59453f9374db385df724bb9d5c788 Mon Sep 17 00:00:00 2001 From: Sofia Johansson <153744881+sofia32057@users.noreply.github.com> Date: Wed, 29 May 2024 21:20:54 +0200 Subject: [PATCH 007/126] Update - added patch for updating guest --- backend/README.md | 3 ++ backend/server.js | 80 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/backend/README.md b/backend/README.md index cbe76668e..e9d11015e 100644 --- a/backend/README.md +++ b/backend/README.md @@ -11,6 +11,8 @@ This project includes the packages and babel setup for an express server, and is ### / +Se all REST API endpoints + ### /login Login to app by using email and password. @@ -47,3 +49,4 @@ Test user: test@example.com | testPassword ### /guests/:guestId - .get: get guest by id +- .patch: update guest, primarily for RSVP diff --git a/backend/server.js b/backend/server.js index 7a23f46df..d564765d8 100644 --- a/backend/server.js +++ b/backend/server.js @@ -17,6 +17,7 @@ const app = express(); app.use(cors()); app.use(express.json()); +// Mongoose model const Guest = model("Guest", { accessToken: { type: String, @@ -109,6 +110,8 @@ if (process.env.RESET_DB) { seedDatabase(); } +// Auth + // ROUTES app.route("/").get(async (req, res) => { const endpoints = expressListEndpoints(app); @@ -118,13 +121,36 @@ app.route("/").get(async (req, res) => { }); }); +//Login +app.route("/login").post(async (req, res) => { + // Find user by name + const guest = await Guest.findOne({ email: req.body.email }).exec(); + console.log(guest); + + // Check if password is correct + if (guest && bcrypt.compareSync(req.body.password, guest.password)) { + // a. User name and password match + res.status(201).json({ + message: "User logged in successfully", + accessToken: guest.accessToken, + }); + } else if (guest) { + // b. user exists but password did not match + res.status(401).json({ message: "Password did not match" }); + } else { + // c. user does not exists + res.status(400).json({ message: "guest name invalid" }); + } +}); + +// Guests app .route("/guests") - // Get all guests (specific fields) + // Get all guests (specific fields) --- NOT SECURE! Do not share personal info .get(async (req, res) => { const guests = await Guest.find( {}, - "firstname lastname relation willAttend plusOne.name" + "firstname lastname relation willAttend plusOne speech" ); res.send(guests); }) @@ -165,40 +191,42 @@ app app .route("/guests/:guestId") - // Find specific guest + // Find and return specific guest .get(async (req, res) => { try { const guest = await Guest.findById( req.params.guestId, - "_id token firstname lastname relation willAttend plusOne.name" + "_id firstname lastname relation willAttend plusOne.name" ).exec(); res.status(201).json(guest); } catch (err) { - res.status(400).json({ message: "Used not found", error: err }); + res.status(400).json({ message: "User not found", error: err }); } - }); + }) + // Find and update guest + .patch(async (req, res) => { + try { + const guest = await Guest.findById(req.params.guestId).exec(); -//Login -app.route("/login").post(async (req, res) => { - // Find user by name - const guest = await Guest.findOne({ email: req.body.email }).exec(); - console.log(guest); + // Update properties + guest.willAttend = req.body.willAttend; + guest.foodChoice = req.body.foodChoice; + if (guest.plusOne.isAllowed) { + guest.plusOne.name = req.body.plusOne.name; + guest.plusOne.foodChoice = req.body.plusOne.foodChoice; + } + if (guest.speech.isAllowed) { + guest.willMakeSpeech = req.body.willMakeSpeech; + } - // Check if password is correct - if (guest && bcrypt.compareSync(req.body.password, guest.password)) { - // a. User name and password match - res.status(201).json({ - message: "User logged in successfully", - accessToken: guest.accessToken, - }); - } else if (guest) { - // b. user exists but password did not match - res.status(401).json({ message: "Password did not match" }); - } else { - // c. user does not exists - res.status(400).json({ message: "guest name invalid" }); - } -}); + res + .status(201) + // .json(guest); + .json({ message: `Updated guest ID ${guest._id} successfully` }); + } catch (err) { + res.status(400).json({ message: "User not found", error: err }); + } + }); // Start the server app.listen(port, () => { From 4d2e4d54a30bb52b161282441481b880f3245e44 Mon Sep 17 00:00:00 2001 From: Sofia Johansson <153744881+sofia32057@users.noreply.github.com> Date: Sat, 1 Jun 2024 14:31:27 +0200 Subject: [PATCH 008/126] Update - Pages, sections and components added. NOTE! Sections to me moved to Section folder --- frontend/components/Accordion.jsx | 3 +++ frontend/components/Button.jsx | 3 +++ frontend/components/Carousel.jsx | 3 +++ frontend/components/CarouselCard.jsx | 3 +++ frontend/components/ContactForm.jsx | 3 +++ frontend/components/ContentSection.jsx | 3 +++ frontend/components/Countdown.jsx | 3 +++ frontend/components/FishtankSection.jsx | 3 +++ frontend/components/Heading.jsx | 3 +++ frontend/components/HeroSection.jsx | 3 +++ frontend/components/Icon.jsx | 3 +++ frontend/components/Image.jsx | 3 +++ frontend/components/ImageTextSection.jsx | 3 +++ frontend/components/Input.jsx | 3 +++ frontend/components/List.jsx | 3 +++ frontend/components/LoginForm.jsx | 3 +++ frontend/components/Map.jsx | 3 +++ frontend/components/Party.jsx | 3 +++ frontend/components/PartyCard.jsx | 3 +++ frontend/components/RsvpForm.jsx | 3 +++ frontend/components/Schedule.jsx | 3 +++ frontend/components/VenueSection.jsx | 3 +++ frontend/package.json | 1 + frontend/pages/Homepage.jsx | 5 +++++ frontend/pages/LandingPage.jsx | 5 +++++ frontend/pages/RsvpPage.jsx | 5 +++++ 26 files changed, 82 insertions(+) create mode 100644 frontend/components/Accordion.jsx create mode 100644 frontend/components/Button.jsx create mode 100644 frontend/components/Carousel.jsx create mode 100644 frontend/components/CarouselCard.jsx create mode 100644 frontend/components/ContactForm.jsx create mode 100644 frontend/components/ContentSection.jsx create mode 100644 frontend/components/Countdown.jsx create mode 100644 frontend/components/FishtankSection.jsx create mode 100644 frontend/components/Heading.jsx create mode 100644 frontend/components/HeroSection.jsx create mode 100644 frontend/components/Icon.jsx create mode 100644 frontend/components/Image.jsx create mode 100644 frontend/components/ImageTextSection.jsx create mode 100644 frontend/components/Input.jsx create mode 100644 frontend/components/List.jsx create mode 100644 frontend/components/LoginForm.jsx create mode 100644 frontend/components/Map.jsx create mode 100644 frontend/components/Party.jsx create mode 100644 frontend/components/PartyCard.jsx create mode 100644 frontend/components/RsvpForm.jsx create mode 100644 frontend/components/Schedule.jsx create mode 100644 frontend/components/VenueSection.jsx create mode 100644 frontend/pages/Homepage.jsx create mode 100644 frontend/pages/LandingPage.jsx create mode 100644 frontend/pages/RsvpPage.jsx diff --git a/frontend/components/Accordion.jsx b/frontend/components/Accordion.jsx new file mode 100644 index 000000000..dc5eb834c --- /dev/null +++ b/frontend/components/Accordion.jsx @@ -0,0 +1,3 @@ +export const Accordion = () => { + return
+ Flowbite is an open-source library of interactive components built + on top of Tailwind CSS including buttons, dropdowns, modals, + navbars, and more. +
++ Check out this guide to learn how to{" "} + + get started + {" "} + and start developing websites even faster with components on top + of Tailwind CSS. +
++ Flowbite is first conceptualized and designed using the Figma + software so everything you see in the library has a design + equivalent in our Figma file. +
++ Check out the{" "} + + Figma design system + + based on the utility classes from Tailwind CSS and components from + Flowbite. +
++ The main difference is that the core components from Flowbite are + open source under the MIT license, whereas Tailwind UI is a paid + product. Another difference is that Flowbite relies on smaller and + standalone components, whereas Tailwind UI offers sections of + pages. +
++ However, we actually recommend using both Flowbite, Flowbite Pro, + and even Tailwind UI as there is no technical reason stopping you + from using the best of two worlds. +
++ Learn more about these technologies: +
++ This accordion component is part of a larger, open-source library of + Tailwind CSS components. Learn more by going to the official{" "} + + Flowbite Documentation + + . +
+ ++ Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui + lorem cupidatat commodo. Elit sunt amet fugiat veniam occaecat + fugiat aliqua. +
+404
++ Sorry, we couldn’t find the page you’re looking for. +
+- Flowbite is an open-source library of interactive components built - on top of Tailwind CSS including buttons, dropdowns, modals, - navbars, and more. -
-- Check out this guide to learn how to{" "} - - get started - {" "} - and start developing websites even faster with components on top - of Tailwind CSS. -
-- Flowbite is first conceptualized and designed using the Figma - software so everything you see in the library has a design - equivalent in our Figma file. -
-- Check out the{" "} - - Figma design system - - based on the utility classes from Tailwind CSS and components from - Flowbite. -
-- The main difference is that the core components from Flowbite are - open source under the MIT license, whereas Tailwind UI is a paid - product. Another difference is that Flowbite relies on smaller and - standalone components, whereas Tailwind UI offers sections of - pages. -
-- However, we actually recommend using both Flowbite, Flowbite Pro, - and even Tailwind UI as there is no technical reason stopping you - from using the best of two worlds. -
-- Learn more about these technologies: -
-- This accordion component is part of a larger, open-source library of - Tailwind CSS components. Learn more by going to the official{" "} - - Flowbite Documentation - - . -
- -+ Lost your invitation?{" "} + + Contact us and we'll send you your login. + +
+