setIsMenuVisible(!isMenuVisible)}>
- {isMenuVisible &&
}
+ {isMenuVisible &&
}
{initials}
@@ -24,10 +24,11 @@ const ProfileCircle = ({ initials }) => {
);
};
-const CascadingMenu = () => {
+const CascadingMenu = ({ id }) => {
+
return (
diff --git a/src/pages/profile/index.js b/src/pages/profile/index.js
new file mode 100644
index 00000000..b8dda618
--- /dev/null
+++ b/src/pages/profile/index.js
@@ -0,0 +1,17 @@
+import FullScreenCard from '../../components/fullscreenCard';
+import './profile.css';
+
+const ProfilePage = () => {
+ return (
+ <>
+
+ Profile
+
+
+
+
+ >
+ )
+}
+
+export default ProfilePage;
diff --git a/src/pages/profile/profile-data/index.js b/src/pages/profile/profile-data/index.js
new file mode 100644
index 00000000..bb77d316
--- /dev/null
+++ b/src/pages/profile/profile-data/index.js
@@ -0,0 +1,91 @@
+import './profile-data.css'
+
+const ProfileData = ({ user }) => {
+ const {email} = user;
+ const roleName = user.profile.role.name;
+ const { firstName, lastName, githubUrl, mobile, specialism, bio, photo } = user.profile;
+
+ const getReadableRole = (role) => {
+ switch (role) {
+ case 'ROLE_STUDENT':
+ return 'Student';
+ case 'ROLE_TEACHER':
+ return 'Teacher';
+ case 'ROLE_ADMIN':
+ return 'Administrator'
+ default:
+ return role;
+ }
+ };
+
+ return (
+
+
+

+ {(firstName || lastName) && (
+
{firstName} {lastName}
+ )}
+ {bio &&
{bio}
}
+
+
+
+ {(firstName || lastName) && (
+
+ Full Name:
+ {firstName} {lastName}
+
+ )}
+
+ {email && (
+
+ Email:
+ {email}
+
+ )}
+
+ {mobile && (
+
+ Mobile:
+ {mobile}
+
+ )}
+
+ {githubUrl && githubUrl.trim() !== '' && (
+
+ )}
+
+
+ {specialism && (
+
+ Specialism:
+ {specialism}
+
+ )}
+
+ {roleName && (
+
+ Role:
+ {getReadableRole(roleName)}
+
+ )}
+
+
+ );
+};
+
+export default ProfileData;
diff --git a/src/pages/profile/profile-data/profile-data.css b/src/pages/profile/profile-data/profile-data.css
new file mode 100644
index 00000000..00307e2e
--- /dev/null
+++ b/src/pages/profile/profile-data/profile-data.css
@@ -0,0 +1,48 @@
+.profile-container {
+ display: flex;
+ flex-direction: row;
+ gap: 3rem;
+ padding: 3rem;
+ font-family: 'Inter', sans-serif;
+ font-size: 1.4rem;
+}
+
+.info-section {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.info-row {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ font-size: 1.6rem;
+}
+
+.label {
+ font-weight: 600;
+ margin-right: 1rem;
+ min-width: 150px;
+}
+
+.value {
+ font-weight: 400;
+ flex: 1;
+}
+
+.value a {
+ color: #0077cc;
+ text-decoration: underline;
+ font-size: 1.6rem;
+}
+
+.bio-text {
+ margin-top: 1rem;
+ text-align: center;
+ font-style: italic;
+ color: #555;
+ font-size: 1.4rem;
+ max-width: 450px;
+}
diff --git a/src/pages/profile/profile.css b/src/pages/profile/profile.css
new file mode 100644
index 00000000..e69de29b
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
/>
+
diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js
index 85af11ab..5aeca849 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,39 @@ const Welcome = () => {
};
const onComplete = () => {
- onCreateProfile(profile.firstName, profile.lastName, profile.githubUsername, profile.bio);
+ onCreateProfile(
+ profile.first_name,
+ profile.last_name,
+ profile.username,
+ profile.github_username,
+ profile.mobile,
+ 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()
+ }
+ }
+
+
+
+
return (
@@ -35,9 +77,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..9164e24c
--- /dev/null
+++ b/src/pages/welcome/stepFour/index.js
@@ -0,0 +1,28 @@
+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..82ea8610 100644
--- a/src/pages/welcome/stepTwo/index.js
+++ b/src/pages/welcome/stepTwo/index.js
@@ -1,14 +1,41 @@
+
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 +43,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..3e37fce8 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: 12px;
+ 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..757ed184 100644
--- a/src/service/apiClient.js
+++ b/src/service/apiClient.js
@@ -5,22 +5,108 @@ 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);
}
+/* eslint-disable camelcase */
-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) {
+
+ cohort = parseInt(cohort)
+ photo = JSON.stringify(photo)
+
+ return await post(`profiles`, { userId,
+ first_name,
+ last_name,
+ username,
+ github_username,
+ mobile,
+ bio,
+ role,
+ specialism,
+ cohort,
+ start_date,
+ end_date,
+ photo }
+ );
}
async function getPosts() {
const res = await get('posts');
return res.data.posts;
}
+async function getComments(postId) {
+ const res = await get(`posts/${String(postId)}/comments`);
+ return res.data.comments;
+}
+
+async function getUserById(id) {
+ const res = await get(`users/${id}`);
+ return res.data.user;
+}
+
+async function getMyCohortProfiles(role) {
+ const token = localStorage.getItem('token');
+
+ if (!token) {
+ console.error('No token found');
+ }
+
+ const { userId } = jwt_decode(token);
+ const user = await getUserById(userId);
+ const res = await get(`cohorts/${user.profile.cohort.id}`);
+
+ if (role === "teacher") {
+ const teachers = res.data.cohort.profiles.filter((userid) => userid?.role?.name === "ROLE_TEACHER");
+ return teachers;
+ }
+ else if (role === "student") {
+ const students = res.data.cohort.profiles.filter((userid) => userid?.role?.name === "ROLE_STUDENT");
+ return students;
+ }
+}
+
+async function updateUserProfile(userId, formValues) {
+ const payload = {
+ photo: formValues.photo || "",
+ first_name: formValues.firstName || "",
+ last_name: formValues.lastName || "",
+ username: formValues.username || "",
+ github_username: formValues.githubUsername || "",
+ email: formValues.email || "",
+ mobile: formValues.mobile || "",
+ password: formValues.password || "",
+ bio: formValues.bio || ""
+ };
+
+ return await patch(`students/${userId}`, payload);
+}
+
async function post(endpoint, data, auth = true) {
return await request('POST', endpoint, data, auth);
}
+async function postTo(endpoint, data, auth = true) {
+ return await request('POST', endpoint, data, auth);
+}
+async function del(endpoint, data, auth = true) {
+ return await request('DELETE', endpoint, data, auth);
+}
+async function put(endpoint, data, auth = true) {
+ return await request('PUT', endpoint, data, auth);
+}
async function patch(endpoint, data, auth = true) {
return await request('PATCH', endpoint, data, auth);
@@ -49,7 +135,18 @@ 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();
}
-export { login, getPosts, register, createProfile };
+
+
+
+export { login, getPosts, register, createProfile, get, getUserById, getComments, post, patch, put, getMyCohortProfiles, updateUserProfile, postTo, del };
+
+
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'
},
diff --git a/src/styles/_globals.css b/src/styles/_globals.css
index eb9772c6..9539bdef 100644
--- a/src/styles/_globals.css
+++ b/src/styles/_globals.css
@@ -35,6 +35,7 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+ background-color: var(--color-offwhite);
}
code {