Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
96c8c34
installed zustand, react-router-dom, styled-components, framer-motion…
RPaemurd Feb 15, 2026
9232241
created files in backend and frontend, did globalstyles, theme and la…
RPaemurd Feb 19, 2026
d4ecf47
finished the loginpage
RPaemurd Feb 20, 2026
833b70b
deploy netlify
RPaemurd Feb 20, 2026
a84c498
added medicinpage, added tillbaka button, added remove and edit in me…
RPaemurd Feb 26, 2026
25a73fd
added my render adress
RPaemurd Feb 27, 2026
ff3c7d2
bux fixes for netlify deployment
RPaemurd Mar 2, 2026
c0077be
added .env in frontend, added a faqpage, and 404 that navigates to th…
RPaemurd Mar 3, 2026
2e83799
removed tillbaka button and just changed to the navbar as navigation,…
RPaemurd Mar 3, 2026
b26c198
removed missprint
RPaemurd Mar 3, 2026
cb8cf05
main landmark for lighthouse
RPaemurd Mar 3, 2026
7fb62e7
fixed border on loginpage
RPaemurd Mar 3, 2026
446d5c3
removed cornerlabels on every page
RPaemurd Mar 3, 2026
114baac
added the bottomnav on mobile - styling changes on mobile
RPaemurd Mar 3, 2026
e13e4af
styling changes for mobile
RPaemurd Mar 3, 2026
357e282
added close button on loginpage, and fontsize mobile
RPaemurd Mar 4, 2026
880477e
styling fixes
RPaemurd Mar 4, 2026
1c33d49
styling fixes
RPaemurd Mar 4, 2026
313fcba
styling fixes
RPaemurd Mar 4, 2026
b1e2082
style changes
RPaemurd Mar 4, 2026
29ddec9
styling fixes
RPaemurd Mar 4, 2026
eebbec6
styling
RPaemurd Mar 4, 2026
1f7b806
layout landingpage fix
RPaemurd Mar 4, 2026
2891005
styling changes
RPaemurd Mar 4, 2026
cb7405a
changed back mobile layout
RPaemurd Mar 4, 2026
c6f98af
styling
RPaemurd Mar 5, 2026
f45711b
bug fix
RPaemurd Mar 5, 2026
e9a206c
created log in and sign up
RPaemurd Mar 7, 2026
f3465a3
added a protectedroute around profilpage
RPaemurd Mar 7, 2026
9d4ff28
user email shown when logged in
RPaemurd Mar 7, 2026
3e1e9f5
comments
RPaemurd Mar 7, 2026
7afb8ac
added comments and my render url
RPaemurd Mar 8, 2026
3a920f8
added comments
RPaemurd Mar 8, 2026
527c6a3
funktionality change in profilpage
RPaemurd Mar 8, 2026
af24814
added useWindowSize hook
RPaemurd Mar 8, 2026
6c3e8f1
functionality changes in navbar
RPaemurd Mar 8, 2026
197f098
changed to button for accessibility
RPaemurd Mar 8, 2026
38f4dd0
bug fixes
RPaemurd Mar 8, 2026
ea966ad
added netlify to read me
RPaemurd Mar 8, 2026
6667c2e
added user email with zustand in profilpage
RPaemurd Mar 8, 2026
1f6a82b
changed funktionality in profilepage
RPaemurd Mar 9, 2026
82cdb0f
removed comments
RPaemurd Mar 9, 2026
f220f1a
fix
RPaemurd Mar 15, 2026
6763a14
style fix
RPaemurd Mar 15, 2026
4b3687c
added loading state when logging in and removed dead code and comment…
RPaemurd Mar 24, 2026
748736b
changed all the href to links with react-router-dom
RPaemurd Mar 24, 2026
8612aa7
adding jwt token in auth
RPaemurd Mar 24, 2026
cf43131
removed the express-session, implemented access token
RPaemurd Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ Describe how you approached to problem, and what tools and techniques you used t

## View 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.
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.

https://stoppa-proppen.netlify.app/
25 changes: 25 additions & 0 deletions backend/models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import mongoose from "mongoose";
import crypto from "crypto";

const userSchema = new mongoose.Schema({

email: {
type: String,
required: true,
unique: true,
lowercase: true,
},
password: {
type: String,
required: true
},
accessToken: {
type: String,
default: () => crypto.randomUUID()
}
}, { timestamps: true });
//creates a user modell based on schema
const User = mongoose.model("User", userSchema)


export default User
5 changes: 4 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"bcrypt": "^6.0.0",
"cors": "^2.8.5",
"dotenv": "^17.3.1",
"express": "^4.17.3",
"express-session": "^1.19.0",
"mongoose": "^8.4.0",
"nodemon": "^3.0.1"
}
}
}
90 changes: 90 additions & 0 deletions backend/routes/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import express from "express";
import User from "../models/User.js";
import bcrypt from "bcrypt";

const router = express.Router();

// Middleware to protect routes — checks the Authorization header for a valid token
export const authenticateUser = async (req, res, next) => {
const token = req.headers.authorization?.replace("Bearer ", "");
if (!token) {
return res.status(401).json({ message: "Ingen token angiven" });
}
const user = await User.findOne({ accessToken: token });
if (!user) {
return res.status(401).json({ message: "Ogiltig token" });
}
req.user = user;
next();
};

// async - means the route contains code that takes time (talking to the database)
// without async/await we cannot wait for a response from MongoDB
router.post("/register", async (req, res) => {

// body contains the data the user submitted from the form
// destructuring = we extract email and password directly instead of req.body.email
const { email, password } = req.body;

// Checks if the user has actually filled in the fields
// ! means "not" - so if email is missing
if (!email || !password) {

// stop the router here so the rest of the code does not run
// status 400
return res.status(400).json({ message: "Email och lösenord krävs"});
}

// checks if the email already exists in the database, findOne looks for a matching document, returns null if nothing found
const existingUser = await User.findOne({ email });

if (existingUser) {
return res.status(400).json({ message: "Email redan registrerad"});
}
// 10 means the password is hashed 10 times
// await wait until encryption is complete before proceeding
const hashedPassword = await bcrypt.hash(password, 10);

// newUser creates a new user object with our schema
// we pass in email and the hashed password
const newUser = new User({
email: email,
password: hashedPassword,
});
// saves the user to MongoDB
// await - wait until saving is complete
await newUser.save();

res.status(201).json({ message: "Konto skapat!" });
});

router.post("/login", async (req, res) => {

const { email, password } = req.body;

if (!email || !password) {
return res.status(400).json({ message: 'Email och lösenord krävs' });
}

// looks for the user's email in the database
const user = await User.findOne({ email });
// if null is returned the login is denied
if (!user) {
return res.status(401).json({ message: "Fel email eller lösenord"});
}

// compare compares the password with the hashed one, await wait until compared, returns true if they match false otherwise
const isMatch = await bcrypt.compare(password, user.password);
// if the password does not match login is denied
if(!isMatch){
return res.status(401).json({ message: "Fel email eller lösenord"})
}
res.status(200).json({
message: 'Inloggad!',
email: user.email,
id: user._id,
accessToken: user.accessToken
});
})

export default router;
28 changes: 19 additions & 9 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import express from "express";
import cors from "cors";
import mongoose from "mongoose";
import dotenv from 'dotenv';
import authRoutes from "./routes/auth";

const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project";
mongoose.connect(mongoUrl);
mongoose.Promise = Promise;
dotenv.config();

const port = process.env.PORT || 8080;
const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project";
/* mongoose.Promise = Promise; */
const port = process.env.PORT || 8081;
const app = express();

/* Middleware */
app.use(cors());
app.use(express.json());

app.get("/", (req, res) => {
res.send("Hello Technigo!");
});
// tells express that all routes in auth.js should start with /api
// register becomes /api/register
app.use("/api", authRoutes);

// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
mongoose.connect(mongoUrl)
.then(() => {
console.log("✅ Succéss! We are conected to MongoDB Atlas.");
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
})
.catch((err) => {
console.error("❌ Could not connect to the database:", err);
});
5 changes: 4 additions & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Technigo React Vite Boiler Plate</title>
<title>Stoppa Proppen</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;1,300;1,400&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
Expand Down
9 changes: 8 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@
"preview": "vite preview"
},
"dependencies": {
"date-fns": "^4.1.0",
"framer-motion": "^12.34.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-router-dom": "^7.13.0",
"react-toastify": "^11.0.5",
"styled-components": "^6.3.9",
"usehooks-ts": "^3.1.1",
"zustand": "^5.0.11"
},
"devDependencies": {
"@types/react": "^18.2.15",
Expand Down
1 change: 1 addition & 0 deletions frontend/public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
1 change: 0 additions & 1 deletion frontend/public/vite.svg

This file was deleted.

34 changes: 31 additions & 3 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
import LandingPage from "./pages/LandingPage";
import { ThemeProvider } from "styled-components";
import GlobalStyles from "./styles/Globalstyles";
import { theme } from "./styles/theme";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import LoginPage from "./pages/LoginPage";
import MedicinPage from "./pages/MedicinPage";
import FaqPage from "./pages/FaqPage";
import OmOssPage from "./pages/OmossPage";
import HurdetFungerar from "./pages/Hurdetfungerarpage"
import ProtectedRoute from "./components/ProtectedRoute";
import ProfilPage from "./pages/ProfilPage";

export const App = () => {

return (
<>
<h1>Welcome to Final Project!</h1>
</>
<ThemeProvider theme={theme}>
<GlobalStyles />
<BrowserRouter>
<Routes>
<Route path="/" element={<LandingPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/profil" element={
<ProtectedRoute>
<ProfilPage />
</ProtectedRoute> } />
<Route path="/medicin" element={<MedicinPage />} />
<Route path="/oss" element={<OmOssPage />} />
<Route path="/hurdetfungerar" element={<HurdetFungerar />} />
<Route path="/faq" element={<FaqPage />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</BrowserRouter>
</ThemeProvider>
);
};
18 changes: 0 additions & 18 deletions frontend/src/assets/boiler-plate.svg

This file was deleted.

1 change: 0 additions & 1 deletion frontend/src/assets/react.svg

This file was deleted.

Loading