diff --git a/.gitignore b/.gitignore
index 2cd0c01c..c094933b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,4 +23,6 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
-.vscode
\ No newline at end of file
+.vscode
+.idea
+
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..877839d2
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,3 @@
+
+src/*
+src/
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 6891fbef..d9caa348 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,13 +15,15 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-modal": "^3.16.1",
+ "react-password-checklist": "^1.8.1",
"react-router-dom": "^6.8.0",
"react-scripts": "5.0.1",
+ "reactjs-popup": "^2.0.6",
"web-vitals": "^3.1.1"
},
"devDependencies": {
"eslint": "^8.57.1",
- "eslint-config-prettier": "^9.1.0",
+ "eslint-config-prettier": "^9.1.2",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-n": "^16.6.2",
@@ -7360,9 +7362,9 @@
}
},
"node_modules/eslint-config-prettier": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
- "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz",
+ "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==",
"dev": true,
"license": "MIT",
"bin": {
@@ -14974,6 +14976,15 @@
"react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
}
},
+ "node_modules/react-password-checklist": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/react-password-checklist/-/react-password-checklist-1.8.1.tgz",
+ "integrity": "sha512-QHIU/OejxoH4/cIfYLHaHLb+yYc8mtL0Vr4HTmULxQg3ZNdI9Ni/yYf7pwLBgsUh4sseKCV/GzzYHWpHqejTGw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">16.0.0-alpha || >17.0.0-alpha || >18.0.0-alpha"
+ }
+ },
"node_modules/react-refresh": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@@ -15084,6 +15095,19 @@
}
}
},
+ "node_modules/reactjs-popup": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.6.tgz",
+ "integrity": "sha512-A+tt+x9wdgZiZjv0e2WzYLD3IfFwJALaRaqwrCSXGjo0iQdsry/EtBEbQXRSmQs7cHmOi5eytCiSlOm8k4C+dg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
diff --git a/package.json b/package.json
index 249b6b05..1825e548 100644
--- a/package.json
+++ b/package.json
@@ -10,8 +10,10 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-modal": "^3.16.1",
+ "react-password-checklist": "^1.8.1",
"react-router-dom": "^6.8.0",
"react-scripts": "5.0.1",
+ "reactjs-popup": "^2.0.6",
"web-vitals": "^3.1.1"
},
"scripts": {
@@ -43,7 +45,7 @@
},
"devDependencies": {
"eslint": "^8.57.1",
- "eslint-config-prettier": "^9.1.0",
+ "eslint-config-prettier": "^9.1.2",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-n": "^16.6.2",
diff --git a/src/App.css b/src/App.css
index ec62bd1a..1222108c 100644
--- a/src/App.css
+++ b/src/App.css
@@ -14,3 +14,8 @@
.ReactModal__Html--open {
overflow: hidden;
}
+
+.border-line {
+ border-bottom: 1px solid var(--color-blue5);
+ padding: 20px 10px;
+}
diff --git a/src/App.js b/src/App.js
index 136c3a15..0a4b97d5 100644
--- a/src/App.js
+++ b/src/App.js
@@ -8,18 +8,21 @@ import Verification from './pages/verification';
import { AuthProvider, ProtectedRoute } from './context/auth';
import { ModalProvider } from './context/modal';
import Welcome from './pages/welcome';
+import { FormProvider } from './context/form';
+import Cohort from './pages/cohort';
+
const App = () => {
return (
<>
+
} />
} />
} />
} />
-
{
}
/>
+
+
+
+ }
+ />
+
>
);
diff --git a/src/components/dropdown/index.js b/src/components/dropdown/index.js
new file mode 100644
index 00000000..9149a65e
--- /dev/null
+++ b/src/components/dropdown/index.js
@@ -0,0 +1,75 @@
+import React, { useState, useRef, useEffect } from "react";
+
+function DropdownMenu({
+ label,
+ options = [],
+ value,
+ onChange,
+ placeholder = "Select an option",
+}) {
+ const [isOpen, setIsOpen] = useState(false);
+ const dropdownRef = useRef(null);
+
+ const toggleDropdown = () => setIsOpen((prev) => !prev);
+
+ const handleOptionClick = (optionValue) => {
+ onChange(optionValue);
+ setIsOpen(false);
+ };
+
+ const handleClickOutside = (event) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
+ setIsOpen(false);
+ }
+ };
+
+ useEffect(() => {
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, []);
+
+ const normalizedOptions = Array.isArray(options)
+ ? options.map((opt) =>
+ typeof opt === "object"
+ ? { label: opt.label, value: opt.value }
+ : { label: opt, value: opt }
+ )
+ : [];
+
+ const selectedOption = normalizedOptions.find((opt) => opt.value === value);
+
+ return (
+
+ {label &&
}
+
+
+ {isOpen && (
+
+ {normalizedOptions.map((option) => (
+ - handleOptionClick(option.value)}
+ style={{
+ padding: "0.5rem 1rem",
+ cursor: "pointer",
+ backgroundColor: value === option.value ? "#f0f0f0" : "#fff",
+ }}
+ >
+ {option.label}
+
+ ))}
+
+ )}
+
+ );
+}
+
+export default DropdownMenu;
diff --git a/src/components/dropdown/style.css b/src/components/dropdown/style.css
new file mode 100644
index 00000000..9619e527
--- /dev/null
+++ b/src/components/dropdown/style.css
@@ -0,0 +1,45 @@
+/* Dropdown Button */
+.dropbtn {
+ background-color: #3498DB;
+ color: white;
+ padding: 16px;
+ font-size: 16px;
+ border: none;
+ cursor: pointer;
+}
+
+/* Dropdown button on hover & focus */
+.dropbtn:hover, .dropbtn:focus {
+ background-color: #2980B9;
+}
+
+/* The container - needed to position the dropdown content */
+.dropdown {
+ position: relative;
+ left: 0;
+ display: inline-block;
+}
+
+/* Dropdown Content (Hidden by Default) */
+.dropdown-content {
+ display: none;
+ position: absolute;
+ background-color: #f1f1f1;
+ min-width: 160px;
+ box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+ z-index: 1;
+}
+
+/* Links inside the dropdown */
+.dropdown-content a {
+ color: black;
+ padding: 12px 16px;
+ text-decoration: none;
+ display: block;
+}
+
+/* Change color of dropdown links on hover */
+.dropdown-content a:hover {background-color: #ddd;}
+
+/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
+.show {display:block;}
\ No newline at end of file
diff --git a/src/components/form/numberInput/index.js b/src/components/form/numberInput/index.js
new file mode 100644
index 00000000..00205226
--- /dev/null
+++ b/src/components/form/numberInput/index.js
@@ -0,0 +1,25 @@
+
+const NumberInput = ({ value, onChange, name, label, icon, type = 'number',placeholder}) => {
+
+ return (
+
+ {label && }
+ {
+ if (e.target.value.length > 11) {
+ e.target.value = e.target.value.slice(0, 11);
+ }}}
+ />
+ {icon && {icon}}
+
+ );
+ }
+
+export default NumberInput;
\ No newline at end of file
diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js
index 39da3cae..06d7feb5 100644
--- a/src/components/form/textInput/index.js
+++ b/src/components/form/textInput/index.js
@@ -1,8 +1,8 @@
import { useState } from 'react';
-const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => {
- const [input, setInput] = useState('');
+const TextInput = ({ value, onChange, name, label, icon, type = 'text', placeholder }) => {
const [showpassword, setShowpassword] = useState(false);
+ const [input, setInput] = useState(value);
if (type === 'password') {
return (
@@ -11,9 +11,10 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => {
type={type}
name={name}
value={value}
+ placeholder = {placeholder}
onChange={(e) => {
onChange(e);
- setInput(e.target.value);
+ setInput(e.target.value)
}}
/>
{showpassword && }
@@ -22,6 +23,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => {
onClick={(e) => {
e.preventDefault();
setShowpassword(!showpassword);
+
}}
>
@@ -36,6 +38,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => {
type={type}
name={name}
value={value}
+ placeholder = {placeholder}
onChange={onChange}
className={icon && 'input-has-icon'}
/>
@@ -43,7 +46,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = 'text' }) => {
);
}
-};
+}
const EyeLogo = () => {
return (
diff --git a/src/components/navigation/index.js b/src/components/navigation/index.js
index b31393a8..10eec500 100644
--- a/src/components/navigation/index.js
+++ b/src/components/navigation/index.js
@@ -28,7 +28,7 @@ const Navigation = () => {
-
+
Cohort
diff --git a/src/components/posts/index.js b/src/components/posts/index.js
index 79756c41..f68a3450 100644
--- a/src/components/posts/index.js
+++ b/src/components/posts/index.js
@@ -6,17 +6,32 @@ const Posts = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
- getPosts().then(setPosts);
+ async function fetchPosts() {
+ try {
+ const posts = await getPosts();
+ setPosts(posts);
+ } catch (error) {
+ console.error('Error fetching posts:', error);
+ setPosts([]);
+ }
+ }
+ fetchPosts();
}, []);
+
return (
<>
{posts.map((post) => {
+ // Handle missing author gracefully
+ const authorName = post.author
+ ? `${post.author.first_name || 'Unknown'} ${post.author.last_name || 'User'}`
+ : 'Unknown User';
+
return (
diff --git a/src/components/profile-icon/index.js b/src/components/profile-icon/index.js
new file mode 100644
index 00000000..02eaf3cf
--- /dev/null
+++ b/src/components/profile-icon/index.js
@@ -0,0 +1,33 @@
+import './style.css';
+import SeeProfile from '../seeProfile';
+import Popup from 'reactjs-popup';
+
+const UserIcon = ({initials, firstname, lastname, role}) => {
+
+ return (
+
+
+
+
{firstname} {lastname}
+
{role}
+
+
+ ...
} position="right center"
+ closeOnDocumentClick
+ arrow={false}>
+
+
+
+ )
+}
+
+export default UserIcon;
\ No newline at end of file
diff --git a/src/components/profile-icon/style.css b/src/components/profile-icon/style.css
new file mode 100644
index 00000000..a0dcbd6c
--- /dev/null
+++ b/src/components/profile-icon/style.css
@@ -0,0 +1,40 @@
+.user {
+ display: flex;
+ align-items: center;
+ padding: 8px 12px;
+ gap: 12px;
+}
+
+.user-info {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 auto;
+ min-width: 0;
+}
+
+.profile-circle{
+ min-width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #4cc0e5;
+}
+
+
+.user-name {
+ margin: 0;
+ font-weight: 600;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.pop-up {
+ position: absolute;
+ top: 100%; /* under .edit-icon */
+ right: 0; /* høyrejustert med ikonet */
+ margin-top: 8px;
+ z-index: 100;
+}
\ No newline at end of file
diff --git a/src/components/seeProfile/index.js b/src/components/seeProfile/index.js
new file mode 100644
index 00000000..bc8888df
--- /dev/null
+++ b/src/components/seeProfile/index.js
@@ -0,0 +1,37 @@
+import Card from '../card';
+import './style.css';
+import { NavLink } from 'react-router-dom';
+import ProfileIcon from '../../assets/icons/profileIcon';
+
+const SeeProfile = ({initials, firstname, lastname, role}) => {
+
+ return (
+
+
+
+
+
+
+
{firstname} {lastname}
+
{role}, Cohort 3
+
+
+
+
+
+
+ )
+
+}
+
+export default SeeProfile;
\ No newline at end of file
diff --git a/src/components/seeProfile/style.css b/src/components/seeProfile/style.css
new file mode 100644
index 00000000..0b4797a9
--- /dev/null
+++ b/src/components/seeProfile/style.css
@@ -0,0 +1,5 @@
+
+
+.card {
+ width: 450px;
+}
\ No newline at end of file
diff --git a/src/components/stepper/index.js b/src/components/stepper/index.js
index c9e5f259..3f745c15 100644
--- a/src/components/stepper/index.js
+++ b/src/components/stepper/index.js
@@ -4,7 +4,7 @@ import Button from '../button';
import './style.css';
import { useState } from 'react';
-const Stepper = ({ header, children, onComplete }) => {
+const Stepper = ({ header, children, onComplete, data }) => {
const [currentStep, setCurrentStep] = useState(0);
const onBackClick = () => {
@@ -22,6 +22,33 @@ const Stepper = ({ header, children, onComplete }) => {
setCurrentStep(currentStep + 1);
};
+ const validateName = (data) => {
+ if(!data) {
+ alert("OBSS!!! Please write first_name and last_name")
+ return false
+ } else {
+ return true
+ }
+ }
+
+ const validateUsername = (data) => {
+ if(data.username.length < 7) {
+ alert("Username is too short. Input must be at least 7 characters long")
+ return false
+ } else {
+ return true
+ }
+ }
+
+ const validateMobile = (data) => {
+ if(data.length < 8) {
+ alert("Mobile number is too short. Input must be at least 8 characters long")
+ return false
+ } else {
+ return true
+ }
+ }
+
return (
{header}
@@ -33,14 +60,34 @@ const Stepper = ({ header, children, onComplete }) => {
-
);
};
-export default Stepper;
+export default Stepper;
\ No newline at end of file
diff --git a/src/context/auth.js b/src/context/auth.js
index 47cd66c9..94f6fe94 100644
--- a/src/context/auth.js
+++ b/src/context/auth.js
@@ -19,7 +19,7 @@ const AuthProvider = ({ children }) => {
useEffect(() => {
const storedToken = localStorage.getItem('token');
- if (storedToken) {
+ if (storedToken && !token) {
setToken(storedToken);
navigate(location.state?.from?.pathname || '/');
}
@@ -34,8 +34,8 @@ const AuthProvider = ({ children }) => {
localStorage.setItem('token', res.data.token);
- setToken(res.token);
- navigate(location.state?.from?.pathname || '/');
+ setToken(res.data.token);
+ navigate(location.state?.from?.pathname || '/');
};
const handleLogout = () => {
@@ -45,15 +45,17 @@ const AuthProvider = ({ children }) => {
const handleRegister = async (email, password) => {
const res = await register(email, password);
- setToken(res.data.token);
+
+ localStorage.setItem('token', res.data.token);
+ setToken(res.data.token);
navigate('/verification');
};
- const handleCreateProfile = async (firstName, lastName, githubUrl, bio) => {
+ const handleCreateProfile = async (first_name, last_name, username, github_username, mobile, bio, role, specialism, cohort, start_date, end_date, photo) => {
const { userId } = jwt_decode(token);
- await createProfile(userId, firstName, lastName, githubUrl, bio);
+ await createProfile(userId, first_name, last_name, username, github_username, mobile, bio, role, specialism, cohort, start_date, end_date, photo);
localStorage.setItem('token', token);
navigate('/');
diff --git a/src/context/form.js b/src/context/form.js
new file mode 100644
index 00000000..c602786e
--- /dev/null
+++ b/src/context/form.js
@@ -0,0 +1,15 @@
+import React, { createContext, useContext, useState } from 'react';
+
+const FormContext = createContext();
+
+export const FormProvider = ({ children }) => {
+ const [formData, setFormData] = useState({ email: '', password: '' });
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useFormData = () => useContext(FormContext);
diff --git a/src/pages/cohort/cohort.css b/src/pages/cohort/cohort.css
new file mode 100644
index 00000000..e69de29b
diff --git a/src/pages/cohort/exercises/exercises.css b/src/pages/cohort/exercises/exercises.css
new file mode 100644
index 00000000..e8764745
--- /dev/null
+++ b/src/pages/cohort/exercises/exercises.css
@@ -0,0 +1,27 @@
+.value {
+ color: var(--color-blue1);
+ margin-bottom: 15px;
+}
+
+.label {
+ color: var(--color-blue1);
+ margin-bottom: 15px;
+}
+
+.see-more-button {
+ background-color: var(--color-blue5);
+}
+
+.exercise-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 8px;
+}
+
+.label {
+ font-weight: 500;
+}
+
+.value {
+ color: var(--color-blue1);
+}
diff --git a/src/pages/cohort/exercises/index.js b/src/pages/cohort/exercises/index.js
new file mode 100644
index 00000000..e1b6a573
--- /dev/null
+++ b/src/pages/cohort/exercises/index.js
@@ -0,0 +1,32 @@
+import Card from "../../../components/card";
+import './exercises.css'
+
+const Exercises = () => {
+ return (
+ <>
+
+ My Exercises
+
+
+
+ Modules:
+ 2/7 completed
+
+
+
+ Units:
+ 4/10 completed
+
+
+
+ Exercise:
+ 34/58 completed
+
+
+ See exercises
+
+ >
+ )
+}
+
+export default Exercises;
\ No newline at end of file
diff --git a/src/pages/cohort/index.js b/src/pages/cohort/index.js
new file mode 100644
index 00000000..302b0748
--- /dev/null
+++ b/src/pages/cohort/index.js
@@ -0,0 +1,19 @@
+import Teachers from './teachers';
+import Exercises from "./exercises";
+
+const Cohort = () => {
+ return (
+ <>
+
+
+
+
+ >
+ )
+
+}
+
+export default Cohort;
diff --git a/src/pages/cohort/students/index.js b/src/pages/cohort/students/index.js
new file mode 100644
index 00000000..1a0b5886
--- /dev/null
+++ b/src/pages/cohort/students/index.js
@@ -0,0 +1,7 @@
+function Students() {
+ return (
+ <>>
+ )
+}
+
+export default Students;
\ No newline at end of file
diff --git a/src/pages/cohort/teachers/index.js b/src/pages/cohort/teachers/index.js
new file mode 100644
index 00000000..ea621275
--- /dev/null
+++ b/src/pages/cohort/teachers/index.js
@@ -0,0 +1,19 @@
+import Card from "../../../components/card";
+import './style.css';
+import UserIcon from "../../../components/profile-icon";
+
+const Teachers = () => {
+
+ return (
+ <>
+
+ Teachers
+
+
+ >
+ );
+}
+
+export default Teachers;
diff --git a/src/pages/cohort/teachers/style.css b/src/pages/cohort/teachers/style.css
new file mode 100644
index 00000000..9323386a
--- /dev/null
+++ b/src/pages/cohort/teachers/style.css
@@ -0,0 +1,8 @@
+.card {
+ background: white;
+ padding: 24px;
+ border-radius: 8px;
+ width: 50%;
+ margin-bottom: 25px;
+ border: 1px #e6ebf5 solid;
+}
\ No newline at end of file
diff --git a/src/pages/login/index.js b/src/pages/login/index.js
index 08df7d5a..1dcb7382 100644
--- a/src/pages/login/index.js
+++ b/src/pages/login/index.js
@@ -36,7 +36,16 @@ const Login = () => {
onLogin(formData.email, formData.password)}
+ onClick={async () => {
+ try {
+ await onLogin(formData.email, formData.password);
+ }
+ catch (err) {
+ if (err.status === 401) {
+ alert("Email or password is wrong");
+ }
+ }
+ }}
classes="green width-full"
/>
diff --git a/src/pages/register/index.js b/src/pages/register/index.js
index 5cc70e32..56404695 100644
--- a/src/pages/register/index.js
+++ b/src/pages/register/index.js
@@ -1,19 +1,44 @@
-import { useState } from 'react';
import Button from '../../components/button';
import TextInput from '../../components/form/textInput';
import useAuth from '../../hooks/useAuth';
import CredentialsCard from '../../components/credentials';
import './register.css';
+import ReactPasswordChecklist from 'react-password-checklist';
+import { useFormData } from '../../context/form';
const Register = () => {
const { onRegister } = useAuth();
- const [formData, setFormData] = useState({ email: '', password: '' });
+ const {formData, setFormData} = useFormData()
const onChange = (e) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};
+ const validateEmail = (email) => {
+ const mailFormat = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/;
+ if (email.match(mailFormat)) {
+ return true;
+ }
+ else {
+ alert("You have entered an invalid email address");
+ return false;
+ }
+
+ }
+
+ const validatePassword = (password) => {
+ const passwordFormat = /^(?=.*?[A-Z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/;
+ if (password.match(passwordFormat)) {
+ return true;
+ }
+ else {
+ alert("Your password is not in the right format");
+ return false;
+ }
+ }
+
+
return (
{
type="email"
name="email"
label={'Email *'}
+ required
/>
{
name="password"
label={'Password *'}
type={'password'}
+ required
/>
+
onRegister(formData.email, formData.password)}
+ onClick={async () => {
+ if (validateEmail(formData.email) && validatePassword(formData.password)) {
+ try {
+ await onRegister(formData.email, formData.password);
+ }
+ catch (err) {
+ if (err.status === 400) {
+ alert("Email is already in use");
+ }
+ }
+ }
+ }}
classes="green width-full"
/>
diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js
index 85af11ab..cd6175ff 100644
--- a/src/pages/welcome/index.js
+++ b/src/pages/welcome/index.js
@@ -3,16 +3,28 @@ import Stepper from '../../components/stepper';
import useAuth from '../../hooks/useAuth';
import StepOne from './stepOne';
import StepTwo from './stepTwo';
+import StepFour from './stepFour';
import './style.css';
+import { useFormData } from '../../context/form';
+import StepThree from './stepThree';
const Welcome = () => {
const { onCreateProfile } = useAuth();
+ const { formData } = useFormData();
const [profile, setProfile] = useState({
- firstName: '',
- lastName: '',
- githubUsername: '',
- bio: ''
+ first_name: '',
+ last_name: '',
+ username: '',
+ github_username: '',
+ mobile: '',
+ bio: '',
+ role: 'ROLE_STUDENT',
+ specialism: 'Software Development',
+ cohort: 1,
+ start_date: '2025-09-14',
+ end_date: '2025-10-15',
+ photo: ''
});
const onChange = (event) => {
@@ -25,9 +37,40 @@ const Welcome = () => {
};
const onComplete = () => {
- onCreateProfile(profile.firstName, profile.lastName, profile.githubUsername, profile.bio);
+ onCreateProfile(
+ profile.first_name,
+ profile.last_name,
+ profile.username,
+ profile.mobile,
+ profile.github_username,
+ profile.bio,
+ profile.role,
+ profile.specialism,
+ profile.cohort,
+ profile.start_date,
+ profile.end_date,
+ profile.photo
+ );
};
+
+ const handleFileChange = (event, close) => {
+
+ const file = event.target.files[0];
+ if (file) {
+ const url = URL.createObjectURL(file)
+ setProfile(prevProfile => ({
+ ...prevProfile,
+ photo: url
+ }));
+ close()
+ console.log("profile:" + profile.photo)
+ }
+ }
+
+
+
+
return (
@@ -35,9 +78,11 @@ const Welcome = () => {
Create your profile to get started
- } onComplete={onComplete}>
-
-
+ } onComplete={onComplete}>
+
+
+
+
);
diff --git a/src/pages/welcome/stepFour/index.js b/src/pages/welcome/stepFour/index.js
new file mode 100644
index 00000000..77ab95a8
--- /dev/null
+++ b/src/pages/welcome/stepFour/index.js
@@ -0,0 +1,29 @@
+import Form from '../../../components/form';
+
+const StepFour = ({ data, setData }) => {
+ return (
+
+ <>
+
+
Bio
+
+
+ >
+ );
+};
+
+export default StepFour
diff --git a/src/pages/welcome/stepOne/index.js b/src/pages/welcome/stepOne/index.js
index 317940f8..7c50155f 100644
--- a/src/pages/welcome/stepOne/index.js
+++ b/src/pages/welcome/stepOne/index.js
@@ -1,8 +1,12 @@
+import Popup from 'reactjs-popup';
import ProfileIcon from '../../../assets/icons/profileIcon';
+
import Form from '../../../components/form';
import TextInput from '../../../components/form/textInput';
+import Card from '../../../components/card';
+
-const StepOne = ({ data, setData }) => {
+const StepOne = ({ data, setData, handleFileChange }) => {
return (
<>
@@ -11,25 +15,82 @@ const StepOne = ({ data, setData }) => {
+ >
+ )
+}
+
+export default StepThree;
\ No newline at end of file
diff --git a/src/pages/welcome/stepTwo/index.js b/src/pages/welcome/stepTwo/index.js
index f40dad3e..e6073fb7 100644
--- a/src/pages/welcome/stepTwo/index.js
+++ b/src/pages/welcome/stepTwo/index.js
@@ -1,14 +1,39 @@
+
import Form from '../../../components/form';
+import NumberInput from '../../../components/form/numberInput';
+import TextInput from '../../../components/form/textInput';
-const StepTwo = ({ data, setData }) => {
+const StepTwo = ({ data, setData, formData }) => {
return (
<>
-
Bio
+ Basic info
@@ -16,4 +41,4 @@ const StepTwo = ({ data, setData }) => {
);
};
-export default StepTwo;
+export default StepTwo;
\ No newline at end of file
diff --git a/src/pages/welcome/style.css b/src/pages/welcome/style.css
index 7ff35605..798591f7 100644
--- a/src/pages/welcome/style.css
+++ b/src/pages/welcome/style.css
@@ -25,7 +25,7 @@
.welcome-form-inputs {
display: grid;
grid-template-rows: repeat(auto, auto);
- gap: 58px;
+ gap: 5px;
margin-bottom: 48px;
}
.welcome-form-popup-wrapper {
@@ -42,3 +42,35 @@
grid-template-columns: 1fr 1fr;
gap: 24px;
}
+
+.welcome-counter {
+ margin-left: 10px;
+ font-size: 10px;
+ color:grey
+}
+
+.bio-label {
+ font-size: 10px;
+}
+
+.bio-heading {
+ font-size: 25px;
+}
+
+.addHeadshot {
+ height: 55px;
+ color:#64648c;
+ display: flex;
+}
+
+
+
+.upload-label {
+ background-color: var(--color-blue);
+ color: white;
+ padding: 14px 24px;
+ border-radius: 4px;
+ cursor: pointer;
+ text-align: center;
+ font-size: 20px;
+}
diff --git a/src/service/apiClient.js b/src/service/apiClient.js
index 5f3cdbcf..bd58f1e1 100644
--- a/src/service/apiClient.js
+++ b/src/service/apiClient.js
@@ -5,16 +5,23 @@ async function login(email, password) {
}
async function register(email, password) {
- await post('users', { email, password }, false);
+ await post('signup', { email, password }, false);
return await login(email, password);
}
-async function createProfile(userId, firstName, lastName, githubUrl, bio) {
- return await patch(`users/${userId}`, { firstName, lastName, githubUrl, bio });
+async function createProfile(userId, first_name, last_name, username, github_username, mobile, bio, role, specialism, cohort, start_date, end_date, photo) {
+ console.log(userId, first_name, last_name, username, github_username, mobile, bio, role, specialism, cohort, start_date, end_date, photo)
+
+ cohort = parseInt(cohort)
+ photo = JSON.stringify(photo)
+
+ await post(`profiles`, { userId, first_name, last_name, username, github_username, mobile, bio, role, specialism, cohort, start_date, end_date, photo });
+ return await patch(`users/${userId}`, {})
}
async function getPosts() {
const res = await get('posts');
+ console.log(res.data.posts + " <- this is from apiClient.js");
return res.data.posts;
}
@@ -49,6 +56,12 @@ async function request(method, endpoint, data, auth = true) {
const response = await fetch(`${API_URL}/${endpoint}`, opts);
+ if (!response.ok) {
+ const error = new Error(response.message || `Request failed with status ${response.status}`);
+ error.status = response.status;
+ throw error;
+ }
+
return response.json();
}
diff --git a/src/service/mockData.js b/src/service/mockData.js
index d49e98a4..6b93d5f8 100644
--- a/src/service/mockData.js
+++ b/src/service/mockData.js
@@ -5,8 +5,8 @@ const user = {
email: 'test@email.com',
cohortId: 1,
role: 'STUDENT',
- firstName: 'Joe',
- lastName: 'Bloggs',
+ first_name: 'Joe',
+ last_name: 'Bloggs',
bio: 'Lorem ipsum dolor sit amet.',
githubUrl: 'https://github.com/vherus'
}
@@ -23,8 +23,8 @@ const posts = [
id: 1,
cohortId: 1,
role: 'STUDENT',
- firstName: 'Sam',
- lastName: 'Fletcher',
+ first_name: 'Sam',
+ last_name: 'Fletcher',
bio: 'Lorem ipsum dolor sit amet.',
githubUrl: 'https://github.com/vherus'
}
@@ -39,8 +39,8 @@ const posts = [
id: 2,
cohortId: 1,
role: 'STUDENT',
- firstName: 'Dolor',
- lastName: 'Lobortis',
+ first_name: 'Dolor',
+ last_name: 'Lobortis',
bio: 'Lorem ipsum dolor sit amet.',
githubUrl: 'https://github.com/vherus'
},