frontend et backend : Profil Public
This commit is contained in:
		
							
								
								
									
										18
									
								
								api/app.js
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								api/app.js
									
									
									
									
									
								
							| @@ -24,6 +24,8 @@ app.use('/functions', require('./routes/functions')); | ||||
| app.use('/categories', require('./routes/categories')); | ||||
| app.use('/users', require('./routes/users')); | ||||
| app.use('/admin', require('./routes/admin')); | ||||
| app.use('/favorites', require('./routes/favorites')); | ||||
| app.use('/comments', require('./routes/comments')); | ||||
|  | ||||
| /* Errors Handling */ | ||||
| app.use((_req, _res, next) => errorHandling(next, { statusCode: 404, message: "La route n'existe pas!" })); // 404 | ||||
| @@ -36,10 +38,26 @@ app.use((error, _req, res, _next) => { | ||||
| /* Database Relations */ | ||||
| const Functions  = require('./models/functions'); | ||||
| const Categories = require('./models/categories'); | ||||
| const Users      = require('./models/users'); | ||||
| const Favorites  = require('./models/favorites'); | ||||
| const Comments   = require('./models/comments'); | ||||
|  | ||||
| // A function has a category | ||||
| Categories.hasOne(Functions, { constraints: true, onDelete: 'CASCADE'}); | ||||
| Functions.belongsTo(Categories); | ||||
|  | ||||
| // Users can have favorites functions | ||||
| Users.hasMany(Favorites); | ||||
| Favorites.belongsTo(Users, { constraints: false }); | ||||
| Functions.hasMany(Favorites); | ||||
| Favorites.belongsTo(Functions, { constraints: false }); | ||||
|  | ||||
| // Users can post comments on functions | ||||
| Users.hasMany(Comments); | ||||
| Comments.belongsTo(Users); | ||||
| Functions.hasMany(Comments); | ||||
| Comments.belongsTo(Functions); | ||||
|  | ||||
| /* Server */ | ||||
| // sequelize.sync({ force: true }) | ||||
| sequelize.sync() | ||||
|   | ||||
							
								
								
									
										2
									
								
								api/controllers/comments.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								api/controllers/comments.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| const errorHandling = require('../assets/utils/errorHandling'); | ||||
| const Comments      = require('../models/comments'); | ||||
							
								
								
									
										2
									
								
								api/controllers/favorites.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								api/controllers/favorites.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| const errorHandling = require('../assets/utils/errorHandling'); | ||||
| const Favorites     = require('../models/categories'); | ||||
| @@ -9,6 +9,10 @@ const transporter                    = require('../assets/config/transporter'); | ||||
| const { EMAIL_INFO, HOST }           = require('../assets/config/config'); | ||||
| const { emailTemplate }              = require('../assets/config/emails'); | ||||
| const Users                          = require('../models/users'); | ||||
| const Favorites                      = require('../models/favorites'); | ||||
| const Functions                      = require('../models/functions'); | ||||
| const Categories                     = require('../models/categories'); | ||||
| const Comments                       = require('../models/comments'); | ||||
|  | ||||
| exports.register = async (req, res, next) => { | ||||
|     const { name, email, password } = req.body; | ||||
| @@ -130,4 +134,54 @@ exports.newPassword = async (req, res, next) => { | ||||
|         console.log(error); | ||||
|         errorHandling(next, serverError); | ||||
|     } | ||||
| } | ||||
|  | ||||
| exports.getUserInfo = async (req, res, next) => { | ||||
|     const { name } = req.params; | ||||
|     try { | ||||
|         const user = await Users.findOne({  | ||||
|             where: { name }, | ||||
|             attributes: { | ||||
|                 exclude: ["updatedAt", "isAdmin", "isConfirmed", "password", "tempToken", "tempExpirationToken"] | ||||
|             },  | ||||
|         }); | ||||
|         if (!user) { | ||||
|             return errorHandling(next, { message: "L'utilisateur n'existe pas.", statusCode: 404 }); | ||||
|         } | ||||
|         const favorites = await Favorites.findAll({  | ||||
|             where: { userId: user.id }, | ||||
|             include: [ | ||||
|                 { model: Functions, attributes: { exclude: ["updatedAt", "utilizationForm", "article", "isOnline"] }, include: { model: Categories, attributes: ["name", "color"] } } | ||||
|             ]  | ||||
|         }); | ||||
|         const favoritesArray = favorites.map((favorite) => favorite.function); | ||||
|         const comments = await Comments.findAll({ | ||||
|             where: { userId: user.id }, | ||||
|             include: [ | ||||
|                 { model: Functions, attributes: { exclude: ["updatedAt", "utilizationForm", "article", "isOnline"] } } | ||||
|             ] | ||||
|         }); | ||||
|         const commentsArray = comments.map((commentObject) => { | ||||
|             return { | ||||
|                 id: commentObject.id, | ||||
|                 message: commentObject.message, | ||||
|                 createdAt: commentObject.createdAt, | ||||
|                 function: commentObject.function.dataValues | ||||
|             }; | ||||
|         }); | ||||
|         const userObject = { | ||||
|             // Si Public Email | ||||
|             ... (user.isPublicEmail) && { email: user.email }, | ||||
|             name: user.name, | ||||
|             biography: user.biography, | ||||
|             logo: user.logo, | ||||
|             createdAt: user.createdAt, | ||||
|             favoritesArray, | ||||
|             commentsArray | ||||
|         }; | ||||
|         return res.status(200).json(userObject); | ||||
|     } catch (error) { | ||||
|         console.log(error); | ||||
|         errorHandling(next, serverError); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								api/models/comments.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								api/models/comments.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| const Sequelize = require('sequelize'); | ||||
| const sequelize = require('../assets/utils/database'); | ||||
|  | ||||
| module.exports = sequelize.define('comment', { | ||||
|     id: { | ||||
|         type: Sequelize.INTEGER, | ||||
|         allowNull: false, | ||||
|         autoIncrement: true, | ||||
|         primaryKey: true | ||||
|     }, | ||||
|     message: { | ||||
|         type: Sequelize.TEXT, | ||||
|         allowNull: false | ||||
|     } | ||||
| }); | ||||
							
								
								
									
										11
									
								
								api/models/favorites.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								api/models/favorites.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| const Sequelize = require('sequelize'); | ||||
| const sequelize = require('../assets/utils/database'); | ||||
|  | ||||
| module.exports = sequelize.define('favorite', { | ||||
|     id: { | ||||
|         type: Sequelize.INTEGER, | ||||
|         allowNull: false, | ||||
|         autoIncrement: true, | ||||
|         primaryKey: true | ||||
|     } | ||||
| }); | ||||
							
								
								
									
										11
									
								
								api/routes/comments.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								api/routes/comments.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| const { Router }         = require('express'); | ||||
| const commentsController = require('../controllers/comments'); | ||||
|  | ||||
| const CommentsRouter = Router(); | ||||
|  | ||||
| // CommentsRouter.route('/') | ||||
|  | ||||
| //     // Récupère les catégories | ||||
| //     .get(commentsController.getCategories); | ||||
|  | ||||
| module.exports = CommentsRouter; | ||||
							
								
								
									
										11
									
								
								api/routes/favorites.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								api/routes/favorites.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| const { Router }           = require('express'); | ||||
| const favoritesController = require('../controllers/categories'); | ||||
|  | ||||
| const FavoritesRouter = Router(); | ||||
|  | ||||
| // FavoritesRouter.route('/') | ||||
|  | ||||
| //     // Récupère les catégories | ||||
| //     .get(favoritesController.getCategories); | ||||
|  | ||||
| module.exports = FavoritesRouter; | ||||
| @@ -18,8 +18,8 @@ UsersRouter.post('/login', [ | ||||
|         .withMessage(requiredFields.message) | ||||
| ], usersController.login); | ||||
|  | ||||
| // TODO: Récupère les informations public d'un profil | ||||
| // UsersRouter.get('/profile/:userName', usersController.getUserInfo); | ||||
| // Récupère les informations public d'un profil | ||||
| UsersRouter.get('/:name', usersController.getUserInfo); | ||||
|  | ||||
| // Permet de s'inscrire | ||||
| UsersRouter.post('/register', [ | ||||
|   | ||||
| @@ -6,8 +6,8 @@ import './Header.css'; | ||||
|  | ||||
| export default function Header() { | ||||
|  | ||||
|     const { isAuth, logoutUser }  = useContext(UserContext); | ||||
|     const [isActive, setIsActive] = useState(false); | ||||
|     const { isAuth, logoutUser, user } = useContext(UserContext); | ||||
|     const [isActive, setIsActive]      = useState(false); | ||||
|  | ||||
|     const toggleNavbar = () => { | ||||
|         setIsActive(!isActive); | ||||
| @@ -44,7 +44,12 @@ export default function Header() { | ||||
|                                 </Fragment> | ||||
|                             : | ||||
|                                 <Fragment> | ||||
|                                     <NavigationLink name="Profil" path="/profile" /> | ||||
|                                     {/* <NavigationLink name="Mon Profil" path={`/profile/${user.name}`} /> */} | ||||
|                                     <li className="navbar-item"> | ||||
|                                         <Link href={"/profile/[name]"} as={`/profile/${user.name}`}> | ||||
|                                             <a className={"navbar-link"}>Mon Profil</a> | ||||
|                                         </Link> | ||||
|                                     </li> | ||||
|                                     <li className="navbar-item"> | ||||
|                                         <Link href={"/"}> | ||||
|                                             <a onClick={logoutUser} className="navbar-link">Se déconnecter</a> | ||||
|   | ||||
							
								
								
									
										5
									
								
								website/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								website/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -3229,6 +3229,11 @@ | ||||
|         "type": "^1.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "date-and-time": { | ||||
|       "version": "0.13.1", | ||||
|       "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.13.1.tgz", | ||||
|       "integrity": "sha512-/Uge9DJAT+s+oAcDxtBhyR8+sKjUnZbYmyhbmWjTHNtX7B7oWD8YyYdeXcBRbwSj6hVvj+IQegJam7m7czhbFw==" | ||||
|     }, | ||||
|     "debug": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|   "dependencies": { | ||||
|     "@zeit/next-css": "^1.0.1", | ||||
|     "axios": "^0.19.2", | ||||
|     "date-and-time": "^0.13.1", | ||||
|     "html-react-parser": "^0.10.2", | ||||
|     "next": "9.3.2", | ||||
|     "next-fonts": "^1.0.3", | ||||
|   | ||||
							
								
								
									
										92
									
								
								website/pages/profile/[name].js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								website/pages/profile/[name].js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| import Link from 'next/link'; | ||||
| import { Fragment } from 'react'; | ||||
| import date from 'date-and-time'; | ||||
| import HeadTag from '../../components/HeadTag'; | ||||
| import FunctionCard from '../../components/FunctionCard/FunctionCard'; | ||||
| import redirect from '../../utils/redirect'; | ||||
| import api from '../../utils/api'; | ||||
| import { API_URL } from '../../utils/config'; | ||||
| import '../../public/css/pages/profile.css'; | ||||
|  | ||||
| const Profile = (props) => { | ||||
|  | ||||
|     // Constantes | ||||
|     const createdAt = new Date(props.createdAt); | ||||
|     const publicationDate = `${('0'+createdAt.getDate()).slice(-2)}/${('0'+(createdAt.getMonth()+1)).slice(-2)}/${createdAt.getFullYear()}`; | ||||
|  | ||||
|     return ( | ||||
|         <Fragment> | ||||
|             <HeadTag title={`${props.name} - FunctionProject`} description={`Profil utilisateur de ${props.name}. ${(props.biography != undefined) ? props.biography : ""}`} /> | ||||
|  | ||||
|             <div className="container-fluid Profile__container"> | ||||
|                 <div className="row Profile__row"> | ||||
|                     <div className="col-20"> | ||||
|                         <div className="text-center"> | ||||
|                             <h1>Profil de <span className="important">{props.name}</span></h1> | ||||
|                         </div> | ||||
|                         <div className="row justify-content-center"> | ||||
|                             <div className="col-24 text-center"> | ||||
|                                 <img className="Profile__logo" src={API_URL + props.logo} alt={props.name} /> | ||||
|                             </div> | ||||
|                             <div className="col-24 text-center"> | ||||
|                                 {(props.biography != undefined) && <p>{props.biography}</p>} | ||||
|                                 {(props.email != undefined) && <p><span className="important">Email :</span> {props.email}</p>} | ||||
|                                 <p><span className="important">Date de création :</span> {publicationDate}</p> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                  | ||||
|                 {(props.favoritesArray.length > 0) && | ||||
|                     <div className="row justify-content-center"> | ||||
|                         <div className="col-24 text-center"> | ||||
|                             <h2>Fonctions en <span className="important">favoris :</span></h2> | ||||
|                         </div> | ||||
|                         <div className="col-24"> | ||||
|                             <div className="row justify-content-center"> | ||||
|                                 {props.favoritesArray.map((favorite) => { | ||||
|                                     return ( | ||||
|                                         <FunctionCard key={favorite.id} slug={favorite.slug} image={favorite.image} title={favorite.title} description={favorite.description} type={favorite.type} category={favorite.categorie} publicationDate={date.format(new Date(favorite.createdAt), 'DD/MM/YYYY', true)} /> | ||||
|                                     ); | ||||
|                                 })} | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 } | ||||
|  | ||||
|                 {(props.commentsArray.length > 0) && | ||||
|                     <div className="row justify-content-center"> | ||||
|                         <div className="col-24 text-center"> | ||||
|                             <h2>Derniers <span className="important">commentaires :</span></h2> | ||||
|                         </div> | ||||
|                         <div className="col-18 text-center"> | ||||
|                             {props.commentsArray.map((comment) => ( | ||||
|                                 <div key={comment.id} className="row Profile__row Profile__comment"> | ||||
|                                     <div className="col-20"> | ||||
|                                         <p> | ||||
|                                             Posté sur la fonction   | ||||
|                                             <Link href={(comment.function.type === 'form' || comment.function.type === 'article') ? "/functions/[slug]" : `/functions/${props.slug}`} as={`/functions/${comment.function.slug}`}> | ||||
|                                                 <a>{comment.function.title}</a> | ||||
|                                             </Link>  | ||||
|                                              le {date.format(new Date(comment.createdAt), 'DD/MM/YYYY à HH:mm', true)} | ||||
|                                         </p> | ||||
|                                         <p>"{comment.message}"</p> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             ))} | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 } | ||||
|             </div> | ||||
|         </Fragment> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export async function getServerSideProps(context) { | ||||
|     const { name } = context.params; | ||||
|     return api.get(`/users/${name}`) | ||||
|         .then((response) => ({ props: response.data })) | ||||
|         .catch(() => redirect(context, '/404')); | ||||
| } | ||||
|  | ||||
| export default Profile; | ||||
| @@ -24,6 +24,7 @@ const Register = () => { | ||||
|             .then(({ data }) => { | ||||
|                 setMessage(`<p class="form-success"><b>Succès:</b> ${data.result}</p>`); | ||||
|                 setIsLoading(false); | ||||
|                 setInputState({}); | ||||
|             }) | ||||
|             .catch((error) => { | ||||
|                 setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`); | ||||
|   | ||||
							
								
								
									
										25
									
								
								website/public/css/pages/profile.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								website/public/css/pages/profile.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| .Profile__container { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     justify-content: center; | ||||
|     margin: 30px 0 0 0; | ||||
| } | ||||
| .Profile__row { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     flex-direction: column; | ||||
|     word-wrap: break-word; | ||||
|     box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25); | ||||
|     border: 1px solid black; | ||||
|     border-radius: 1rem; | ||||
|     margin-bottom: 50px; | ||||
| } | ||||
| .Profile__logo { | ||||
|     border-radius: 50%; | ||||
|     object-fit: cover; | ||||
|     width: 150px; | ||||
|     height: 150px; | ||||
| } | ||||
| .Profile__comment { | ||||
|     margin: 0 0 50px 0; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user