Skip to content

Commit

Permalink
setup for sprint 3
Browse files Browse the repository at this point in the history
  • Loading branch information
myix765 committed Nov 3, 2024
1 parent 8b818de commit bb3584f
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 186 deletions.
9 changes: 9 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.21.1",
"express-mongo-sanitize": "^2.2.0",
"mongodb": "^6.9.0",
"mongoose": "^8.7.2"
},
Expand Down
165 changes: 106 additions & 59 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
require('dotenv').config();
const express = require('express');
const cors = require('cors')
const mongo = require("mongodb");
const mongoose = require("mongoose");
const cors = require('cors');
const mongo = require('mongodb');
const mongoose = require('mongoose');
const mongoSanitize = require('express-mongo-sanitize');

const app = express()
app.use(cors())
app.use(express.json())
app.use(mongoSanitize())

const PORT = process.env.PORT || 4000;
mongoose.connect(process.env.MONGODB_URI)
Expand Down Expand Up @@ -36,42 +38,79 @@ app.get('/', (req, res) => {
res.send('Server is running!')
});


//------------------ HELPER FUNCTIONS ------------------//

/*
purpose: check that the input key is allowed
argument types:
inputs: object
allowedFields: array
example of using to get filters for classes: validateInput(req.query, classFields)
*/
const validateInput = (input, allowedFields) => {
const filteredInput = {}

for (const key in input) {
if (allowedFields.includes(key)) {
filteredInput[key] = query[key]
}
}

return filteredInput
}

//------------------ MONGOOSE SCHEMAS ------------------//

const Schema = mongoose.Schema

// User Schema
const UserSchema = new Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: { type: String, required: true},
password: { type: String, required: true},
isAdmin: { type: Boolean, required: true},
username: { type: String, required: true},
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
isAdmin: { type: Boolean, required: true },
username: { type: String, required: true },
}, { collection: 'users' })

const User = mongoose.model("User", UserSchema)

// Contact Schema
const ContactSchema = new Schema ({
name: { type: String, required: false },
email: { type: String, required: true},
subject: { type: String, required: true},
message: { type: String, required: true}
const ContactSchema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true },
subject: { type: String, required: true },
message: { type: String, required: true }
}, { collection: 'contacts' })

const Contact = mongoose.model('Contact', ContactSchema);

// Class Schema
const ScheduleSchema = new Schema({
day: { type: String, required: true },
time: { type: String, required: true },
})
const ClassSchema = new Schema({
_id: {type: String, required: true},
title: {type: String, required: true},
level: {type: String, required: true},
ageGroup: {type: String, required: true},
instructor: {type: String, required: true},
schedule: {type: [String], required:true},
title: { type: String, required: true },
level: { type: String, required: true },
ageGroup: { type: String, required: true },
instructor: { type: String, required: true },
schedule: { type: [ScheduleSchema], required: true, default: [] },
}, { collection: 'classes' })

const Class = mongoose.model("Class", ClassSchema)

// Level Schema
const InstructorSchema = new Schema({ name: { type: String, required: true } })
const LevelSchema = new Schema({
level: { type: Number, required: true },
name: { type: String, required: true },
instructors: { type: [InstructorSchema], required: true, default: [] },
}, { collection: 'levels' })

const Level = mongoose.model("Level", LevelSchema)

//------------------ ENDPOINTS ------------------//

Expand All @@ -81,7 +120,7 @@ app.post('/api/users', async (req, res) => {
const { firstName, lastName, username, email, password } = req.body;

// Check if user already exists
const existingUser = await User.findOne({
const existingUser = await User.findOne({
$or: [
{ email: email },
{ username: username }
Expand Down Expand Up @@ -118,52 +157,57 @@ app.post('/api/users', async (req, res) => {

// Login
app.post('/api/login', async (req, res) => {
const { username, password } = req.body;

try {
const user = await User.findOne({ username });
console.log('Database query result:', user);

if (user) {
if (user.password === password) {
console.log('Login successful for user:', username);
res.status(200).send('Login successful!');
} else {
console.log('Login failed: Incorrect password.');
res.status(401).send('Invalid password.');
}
} else {
console.log('Login failed: User not found');
res.status(401).send('Invalid username.');
}
} catch (error) {
console.error('Error during login.', error);
res.status(500).send({ message: 'Server Error.' });
const { username, password } = req.body;

try {
const user = await User.findOne({ username });
console.log('Database query result:', user);

if (user) {
if (user.password === password) {
console.log('Login successful for user:', username);
res.status(200).send('Login successful!');
} else {
console.log('Login failed: Incorrect password.');
res.status(401).send('Invalid password.');
}
} else {
console.log('Login failed: User not found');
res.status(401).send('Invalid username.');
}
} catch (error) {
console.error('Error during login.', error);
res.status(500).send({ message: 'Server Error.' });
}
});

// Get Users
// TODO (Aryaa & Toki): Write an endpoint to retrieve all users from the database


// Contact
app.post('/api/contact', async (req, res) => {
const{ name, email, subject, message } = req.body
try {
const newContact = new Contact({
name,
email,
subject,
message
})
await newContact.save()

res.status(201).json({message: 'Inquiry submitted successfully'})
}
catch (err) {
console.error('Error submitting inquiry:', err);
res.status(500).json({message: 'Error submitting inquiry'})
}
const { name, email, subject, message } = req.body
try {
const newContact = new Contact({
name,
email,
subject,
message
})
await newContact.save()

res.status(201).json({ message: 'Inquiry submitted successfully' })
}
catch (err) {
console.error('Error submitting inquiry:', err);
res.status(500).json({ message: 'Error submitting inquiry' })
}
})

// Classes
app.get('/api/data', async (req, res) => {
// TODO (Donatello, Claire, Yi): Modify the endpoint to take in query params and filter classes with them
app.get('/api/classes', async (req, res) => {
try {
const data = await Class.find();
console.log(data);
Expand All @@ -172,3 +216,6 @@ app.get('/api/data', async (req, res) => {
res.status(500).send(err);
}
})

// Levels
// TODO (Fahim & Frank): Get the levels data from the database
16 changes: 16 additions & 0 deletions src/api/class-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import axios from 'axios'

const apiUrl = (endpoint) => `${import.meta.env.VITE_API_URL}${endpoint}`

const getClasses = async () => {
try {
const response = await axios.get(apiUrl("/api/classes"))
return response.data
} catch (error) {
console.error('Error fetching courses:', error)
}
}

export {
getClasses
}
21 changes: 21 additions & 0 deletions src/api/contact-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const apiUrl = (endpoint) => `${import.meta.env.VITE_API_URL}${endpoint}`

const postContact = async (body) => {
try {
const response = await fetch(apiUrl("/api/contact"), {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body)
})

return response
} catch (error) {
console.error('Contact endpoint post error:', error);
}
}

export {
postContact
}
Empty file added src/api/level-wrapper.js
Empty file.
33 changes: 33 additions & 0 deletions src/api/user-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import axios from 'axios';

const apiUrl = (endpoint) => `${import.meta.env.VITE_API_URL}${endpoint}`

const postUser = async (body) => {
try {
const response = await axios.post(apiUrl("/api/users"), body)
return response
} catch (error) {
throw error
}
}

const postLogin = async (body) => {
try {
const response = await fetch(apiUrl("/api/login"), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})

return response
} catch (error) {
console.error('Login endpoint post error:', error);
}
}

export {
postUser,
postLogin
}
9 changes: 5 additions & 4 deletions src/components/NavBar/NavBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Link } from 'wouter';
import dillarLogo from '/dillar_logo.png';
import NavLink from './NavLink';

// TODO (Tony & John): Add a dropdown to switch languages
const NavBar = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);

Expand Down Expand Up @@ -40,10 +41,10 @@ const NavBar = () => {
{/* Mobile menu */}
<div className={`sm:hidden ${isMenuOpen ? 'block' : 'hidden'}`}>
<div className="pt-2 pb-3 space-y-1">
<NavLink href="/about" isMobile={true}>About</NavLink>
<NavLink href="/contact" isMobile={true}>Contact</NavLink>
<NavLink href="/classes" isMobile={true}>Classes</NavLink>
<NavLink href="/signup" isMobile={true}>Sign Up</NavLink>
<NavLink href="/about" isMobile={true}>About</NavLink>
<NavLink href="/contact" isMobile={true}>Contact</NavLink>
<NavLink href="/classes" isMobile={true}>Classes</NavLink>
<NavLink href="/signup" isMobile={true}>Sign Up</NavLink>
</div>
<div className="pt-4 pb-3 border-t border-gray-200">
</div>
Expand Down
Loading

0 comments on commit bb3584f

Please sign in to comment.