diff --git a/api/app.js b/api/app.js index cae56c7..c320e8e 100644 --- a/api/app.js +++ b/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() diff --git a/api/controllers/comments.js b/api/controllers/comments.js new file mode 100644 index 0000000..80e01f5 --- /dev/null +++ b/api/controllers/comments.js @@ -0,0 +1,2 @@ +const errorHandling = require('../assets/utils/errorHandling'); +const Comments = require('../models/comments'); \ No newline at end of file diff --git a/api/controllers/favorites.js b/api/controllers/favorites.js new file mode 100644 index 0000000..0881997 --- /dev/null +++ b/api/controllers/favorites.js @@ -0,0 +1,2 @@ +const errorHandling = require('../assets/utils/errorHandling'); +const Favorites = require('../models/categories'); \ No newline at end of file diff --git a/api/controllers/users.js b/api/controllers/users.js index 189a473..ed602ec 100644 --- a/api/controllers/users.js +++ b/api/controllers/users.js @@ -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); + } } \ No newline at end of file diff --git a/api/models/comments.js b/api/models/comments.js new file mode 100644 index 0000000..8fc3709 --- /dev/null +++ b/api/models/comments.js @@ -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 + } +}); \ No newline at end of file diff --git a/api/models/favorites.js b/api/models/favorites.js new file mode 100644 index 0000000..2902772 --- /dev/null +++ b/api/models/favorites.js @@ -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 + } +}); \ No newline at end of file diff --git a/api/routes/comments.js b/api/routes/comments.js new file mode 100644 index 0000000..88563f7 --- /dev/null +++ b/api/routes/comments.js @@ -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; \ No newline at end of file diff --git a/api/routes/favorites.js b/api/routes/favorites.js new file mode 100644 index 0000000..3de7110 --- /dev/null +++ b/api/routes/favorites.js @@ -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; \ No newline at end of file diff --git a/api/routes/users.js b/api/routes/users.js index b8a4d73..0dfd6e8 100644 --- a/api/routes/users.js +++ b/api/routes/users.js @@ -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', [ diff --git a/website/components/Header/Header.js b/website/components/Header/Header.js index b11027d..f04ef31 100644 --- a/website/components/Header/Header.js +++ b/website/components/Header/Header.js @@ -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() { : - + {/* */} +
  • + + Mon Profil + +
  • Se déconnecter diff --git a/website/package-lock.json b/website/package-lock.json index 970c778..dfb0cec 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -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", diff --git a/website/package.json b/website/package.json index 1422328..abd9a3e 100644 --- a/website/package.json +++ b/website/package.json @@ -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", diff --git a/website/pages/profile/[name].js b/website/pages/profile/[name].js new file mode 100644 index 0000000..07a41e7 --- /dev/null +++ b/website/pages/profile/[name].js @@ -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 ( + + + +
    +
    +
    +
    +

    Profil de {props.name}

    +
    +
    +
    + {props.name} +
    +
    + {(props.biography != undefined) &&

    {props.biography}

    } + {(props.email != undefined) &&

    Email : {props.email}

    } +

    Date de création : {publicationDate}

    +
    +
    +
    +
    + + {(props.favoritesArray.length > 0) && +
    +
    +

    Fonctions en favoris :

    +
    +
    +
    + {props.favoritesArray.map((favorite) => { + return ( + + ); + })} +
    +
    +
    + } + + {(props.commentsArray.length > 0) && +
    +
    +

    Derniers commentaires :

    +
    +
    + {props.commentsArray.map((comment) => ( +
    +
    +

    + Posté sur la fonction  + + {comment.function.title} + +  le {date.format(new Date(comment.createdAt), 'DD/MM/YYYY à HH:mm', true)} +

    +

    "{comment.message}"

    +
    +
    + ))} +
    +
    + } +
    +
    + ); +} + +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; \ No newline at end of file diff --git a/website/pages/register.js b/website/pages/register.js index f7fe9f9..1f9923a 100644 --- a/website/pages/register.js +++ b/website/pages/register.js @@ -24,6 +24,7 @@ const Register = () => { .then(({ data }) => { setMessage(`

    Succès: ${data.result}

    `); setIsLoading(false); + setInputState({}); }) .catch((error) => { setMessage(`

    Erreur: ${error.response.data.message}

    `); diff --git a/website/public/css/pages/profile.css b/website/public/css/pages/profile.css new file mode 100644 index 0000000..78ecd3a --- /dev/null +++ b/website/public/css/pages/profile.css @@ -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; +} \ No newline at end of file