Skip to content

Commit

Permalink
Merge pull request #131 from Arquisoft/UO287747-Develop
Browse files Browse the repository at this point in the history
Mejora interfaz Registro de usuarios
  • Loading branch information
UO287687 authored Apr 1, 2024
2 parents 55f94c1 + ad2283d commit 6edb9e6
Show file tree
Hide file tree
Showing 18 changed files with 206 additions and 47 deletions.
1 change: 1 addition & 0 deletions users/authservice/auth-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: String,
password: String,
profileImage: String,
createdAt: Date,
});

Expand Down
2 changes: 1 addition & 1 deletion users/authservice/auth-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ app.post('/login', async (req, res) => {
// Generate a JWT token
const token = jwt.sign({ userId: user._id }, 'your-secret-key', { expiresIn: '1h' });
// Respond with the token and user information
res.json({ token: token, username: username, createdAt: user.createdAt });
res.json({ token: token, username: username, createdAt: user.createdAt, profileImage: user.profileImage });
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
Expand Down
4 changes: 4 additions & 0 deletions users/userservice/user-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const userSchema = new mongoose.Schema({
type: String,
required: true,
},
profileImage: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
Expand Down
3 changes: 2 additions & 1 deletion users/userservice/user-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ function validateRequiredFields(req, requiredFields) {
app.post('/adduser', async (req, res) => {
try {
// Check if required fields are present in the request body
validateRequiredFields(req, ['username', 'password']);
validateRequiredFields(req, ['username', 'password', 'profileImage']);

// Encrypt the password before saving it
const hashedPassword = await bcrypt.hash(req.body.password, 10);

const newUser = new User({
username: req.body.username,
password: hashedPassword,
profileImage: req.body.profileImage,
});

await newUser.save();
Expand Down
1 change: 1 addition & 0 deletions users/userservice/user-service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('User Service', () => {
const newUser = {
username: 'testuser',
password: 'testpassword',
profileImage: 'perfil2.jpg',
};

const response = await request(app).post('/adduser').send(newUser);
Expand Down
Binary file added webapp/src/assets/perfil2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/src/assets/perfil3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/src/assets/perfil4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/src/assets/perfil5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 55 additions & 3 deletions webapp/src/components/AddUser.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
// src/components/AddUser.js
import React, { useState } from 'react';
import axios from 'axios';
import { Container, Typography, TextField, Button, Snackbar } from '@mui/material';
import { Container, Typography, TextField, Button, Snackbar, IconButton } from '@mui/material';

import profileImg1 from '../assets/defaultImgProfile.jpg';
import profileImg2 from '../assets/perfil2.jpg';
import profileImg3 from '../assets/perfil3.jpg';
import profileImg4 from '../assets/perfil4.jpg';
import profileImg5 from '../assets/perfil5.jpg';

const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';

const AddUser = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [profileImage, setProfileImage] = useState('defaultImgProfile.jpg');

const [error, setError] = useState('');
const [openSnackbar, setOpenSnackbar] = useState(false);

const addUser = async () => {
try {
await axios.post(`${apiEndpoint}/adduser`, { username, password });
if (password !== confirmPassword) {
setError('Passwords do not match');
return;
}

await axios.post(`${apiEndpoint}/adduser`, { username, password, profileImage });
setOpenSnackbar(true);
} catch (error) {
setError(error.response.data.error);
Expand All @@ -24,8 +38,12 @@ const AddUser = () => {
setOpenSnackbar(false);
};

const handleImageClick = (imageName) => {
setProfileImage(imageName);
}

return (
<Container component="main" maxWidth="xs" sx={{ marginTop: 4 }}>
<Container component="div" maxWidth="xs" sx={{ marginTop: 4 }}>
<Typography component="h1" variant="h5">
Add User
</Typography>
Expand All @@ -46,6 +64,40 @@ const AddUser = () => {
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<TextField
name="confirmPassword"
margin="normal"
fullWidth
label="Confirm Password"
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
<Typography component="h2" variant="h5">
Select a profile image
</Typography>
<div id='fotosPerfil'>
<IconButton className={`fotoPerfilBtn`} onClick={() => handleImageClick('defaultImgProfile.jpg')}>
<img className={`fotoPerfil ${profileImage === 'defaultImgProfile.jpg' ? 'selectedImg' : ''}`}
src={profileImg1} alt='Imagen Perfil 1' />
</IconButton>
<IconButton className={`fotoPerfilBtn`} onClick={() => handleImageClick('perfil2.jpg')}>
<img className={`fotoPerfil ${profileImage === 'perfil2.jpg' ? 'selectedImg' : ''}`}
src={profileImg2} alt='Imagen Perfil 2' />
</IconButton>
<IconButton className={`fotoPerfilBtn`} onClick={() => handleImageClick('perfil3.jpg')}>
<img className={`fotoPerfil ${profileImage === 'perfil3.jpg' ? 'selectedImg' : ''}`}
src={profileImg3} alt='Imagen Perfil 3' />
</IconButton>
<IconButton className={`fotoPerfilBtn`} onClick={() => handleImageClick('perfil4.jpg')}>
<img className={`fotoPerfil ${profileImage === 'perfil4.jpg' ? 'selectedImg' : ''}`}
src={profileImg4} alt='Imagen Perfil 4' />
</IconButton>
<IconButton className={`fotoPerfilBtn`} onClick={() => handleImageClick('perfil5.jpg')}>
<img className={`fotoPerfil ${profileImage === 'perfil5.jpg' ? 'selectedImg' : ''}`}
src={profileImg5} alt='Imagen Perfil 5' />
</IconButton>
</div>
<Button variant="contained" color="primary" onClick={addUser}>
Add User
</Button>
Expand Down
35 changes: 5 additions & 30 deletions webapp/src/components/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ const Login = ({ goTo }) => {

const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [profileImage, setProfileImage] = useState('');
const [error, setError] = useState('');
const [loginSuccess, setLoginSuccess] = useState(false);
const [createdAt, setCreatedAt] = useState('');
const [openSnackbar, setOpenSnackbar] = useState(false);
const [timeStart, setTimeStart] = useState(0);
const [timeElapsed, setTimeElapsed] = useState(0);

const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';

Expand All @@ -24,26 +24,19 @@ const Login = ({ goTo }) => {
const response = await axios.post(`${apiEndpoint}/login`, { username, password });

// Extract data from the response
const { createdAt: userCreatedAt, username: loggedInUsername } = response.data;
const { createdAt: userCreatedAt, username: loggedInUsername, profileImage: profileImage } = response.data;

setTimeStart(Date.now());
setCreatedAt(userCreatedAt);
setLoginSuccess(true);
saveSessionData({ username: loggedInUsername, createdAt: userCreatedAt });
saveSessionData({ username: loggedInUsername, createdAt: userCreatedAt, profileImage: profileImage });
console.log(profileImage+'\n\n\n');
setOpenSnackbar(true);
} catch (error) {
setError(error.response.data.error);
}
};

const calculateTime = async () => {
try {
setTimeElapsed((Date.now() - timeStart) / 1000);
} catch (error) {
setError(error.response.data.error);
}
};

const handleCloseSnackbar = () => {
setOpenSnackbar(false);
};
Expand All @@ -55,24 +48,7 @@ const Login = ({ goTo }) => {
}, [loginSuccess, goTo]);

return (
<Container component="main" maxWidth="xs" sx={{ marginTop: 4 }}>
{loginSuccess ? (

<div>
<Typography component="h1" variant="h5" sx={{ textAlign: 'center' }}>
Hello {username}!
</Typography>
<Typography component="p" variant="body1" sx={{ textAlign: 'center', marginTop: 2 }}>
Your account was created on {new Date(createdAt).toLocaleDateString()}.
</Typography>
<Typography component="p" variant="body1" sx={{ textAlign: 'center', marginTop: 2 }}>
Han pasado {timeElapsed} segundos.
</Typography>
<Button variant="contained" color="primary" onClick={calculateTime}>
Calcular tiempo
</Button>
</div>
) : (
<Container component="div" maxWidth="xs" sx={{ marginTop: 4 }}>
<div>
<Typography component="h1" variant="h5">
Login :D
Expand Down Expand Up @@ -100,7 +76,6 @@ const Login = ({ goTo }) => {
<Snackbar open={!!error} autoHideDuration={6000} onClose={() => setError('')} message={`Error: ${error}`} />
)}
</div>
)}
</Container>
);
};
Expand Down
6 changes: 4 additions & 2 deletions webapp/src/components/Nav.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import AdbIcon from '@mui/icons-material/Adb';

import { useContext } from 'react';
import { SessionContext } from '../SessionContext';
import profileImage from '../assets/defaultImgProfile.jpg';
import defaultProfileImg from '../assets/defaultImgProfile.jpg';

function Nav({ goTo }) {

const { sessionData } = useContext(SessionContext);
const username = sessionData ? sessionData.username : 'noUser';
const profileImgSrc = sessionData && sessionData.profileImage ?
require(`../assets/${sessionData.profileImage}`) : defaultProfileImg;

const [anchorElNav, setAnchorElNav] = React.useState(null);
const [anchorElUser, setAnchorElUser] = React.useState(null);
Expand Down Expand Up @@ -122,7 +124,7 @@ function Nav({ goTo }) {
<Typography sx={{ marginRight: 2, fontFamily: 'Roboto Slab'}} >{username}</Typography>
<Tooltip title="Open settings">
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
<Avatar alt="Remy Sharp" src={profileImage}/>
<Avatar alt="Remy Sharp" src={profileImgSrc}/>
</IconButton>
</Tooltip>
<Menu
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/components/User.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function User({ goTo }) {
<Container component="main" maxWidth="xs">
<CssBaseline />
<Typography component="h1" variant="h5" align="center" sx={{ marginTop: 2 }}>
Welcome to the 2024 edition of the Software Architecture course
ASW - WIQ Quiz
</Typography>
{showLogin ? <Login goTo={(x) => goTo(x)} /> : <AddUser />}
<Typography component="div" align="center" sx={{ marginTop: 2 }}>
Expand Down
30 changes: 30 additions & 0 deletions webapp/src/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,36 @@ button:focus-visible {
vertical-align: middle;
}

#fotosPerfil {
display: grid;
justify-content: center;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
padding: 20px 0 20px 0;
}

.fotoPerfilBtn {
border-radius: 0% !important;
border: none;
cursor: pointer;
width: 3.5em;
padding: 0 !important;
}

.fotoPerfil {
background-color: #3498db;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
width: 3.5em;
border-radius: 10%;
}

.selectedImg {
border: 4px solid #FFF !important;
}

@media (prefers-color-scheme: light) {
:root {
color: #213547;
Expand Down
Loading

0 comments on commit 6edb9e6

Please sign in to comment.