Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions frontend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import js from '@eslint/js';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import {defineConfig, globalIgnores} from 'eslint/config';
import { defineConfig, globalIgnores } from 'eslint/config';
import globals from 'globals';
import process from 'process';

Expand All @@ -19,18 +19,35 @@ export default defineConfig([
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: {jsx: true},
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
rules: {
'no-unused-vars': ['warn', {varsIgnorePattern: '^[A-Z_]'}],
// ----- TES RÈGLES -----
'no-unused-vars': ['warn', { varsIgnorePattern: '^[A-Z_]' }],
'no-console':
process.env.NODE_ENV === 'production' ? 'error' : 'warn',
'no-undef': 'error',
'no-trailing-spaces': 'error',
'no-debugger':
process.env.NODE_ENV === 'production' ? 'error' : 'warn',
eqeqeq: ['error', 'always'],
curly: ['error', 'all'],
quotes: ['error', 'single', { avoidEscape: true }],
semi: ['error', 'always'],
indent: ['error', 4, { SwitchCase: 1 }],
'comma-dangle': ['error', 'always-multiline'],
'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }],
'object-curly-spacing': ['error', 'always'],
'prefer-const': 'error',
'prefer-arrow-callback': 'error',
'arrow-spacing': ['error', { before: true, after: true }],
'no-var': 'error',
'no-fallthrough': 'error',
'no-unreachable': 'error',
'no-extra-bind': 'error',
'no-empty-function': 'warn',
},
},
]);
12 changes: 6 additions & 6 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, {Suspense} from 'react';
import {BrowserRouter, Outlet, Route, Routes} from 'react-router-dom';
import {NavBar} from './components/layout/NavBar';
import {ProtectedRoute} from './components/routing/ProtectedRoute';
import {Loader} from './components/ui/Loader';
import React, { Suspense } from 'react';
import { BrowserRouter, Outlet, Route, Routes } from 'react-router-dom';
import { NavBar } from './components/layout/NavBar';
import { ProtectedRoute } from './components/routing/ProtectedRoute';
import { Loader } from './components/ui/Loader';
import _404 from './pages/errors/_404';
import Profile from './pages/Profile';
import {AuthProviders} from './providers/AuthProviders';
import { AuthProviders } from './providers/AuthProviders';
const Home = React.lazy(() => import('./pages/Home'));
const SignIn = React.lazy(() => import('./pages/SignIn'));
const SignUp = React.lazy(() => import('./pages/SignUp'));
Expand Down
27 changes: 14 additions & 13 deletions frontend/src/components/features/posts/Post.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {memo, useEffect, useState} from 'react';
import {Link} from 'react-router-dom';
import { memo, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import CommentsModal from '../../../layout/CommentModal';
import {DiffDate} from '../../utils/DiffDate';
import {handleLikes} from '../../utils/HandleLikes';
import { DiffDate } from '../../utils/DiffDate';
import { handleLikes } from '../../utils/HandleLikes';
import ActionButtonsPost from './../../ui/ActionButtonsPost';

function Post({
Expand All @@ -27,16 +27,17 @@ function Post({
const savePP = author_imgUrl;
const [newComments, setNewComments] = useState(comments);

useEffect((id) => {
if (userLikes[id]) setLiked(true);
}, []);
useEffect(() => {
if (userLikes[id]) {
setLiked(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id]);

useEffect(
(id) => {
handleLikes(setNewLikes, liked, id);
},
[liked]
);
useEffect(() => {
handleLikes(setNewLikes, liked, id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [liked]);

useEffect(() => {
const interval = setInterval(() => {
Expand Down
35 changes: 19 additions & 16 deletions frontend/src/components/features/posts/PostDetail.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import useMediaQuery from '@mui/material/useMediaQuery';
import {memo, useEffect, useState} from 'react';
import {useAuth} from '../../../providers/AuthProviders';
import { memo, useEffect, useState } from 'react';
import { useAuth } from '../../../providers/AuthProviders';
import MobileOverlay from '../../layout/MobileOverlay';
import {fetchData} from '../../services/Fetch';
import { fetchData } from '../../services/Fetch';
import throwError from '../../services/throwError';
import ActionButtonsPost from '../../ui/ActionButtonsPost';
import {Comments} from '../../ui/Comments';
import {DiffDate} from '../../utils/DiffDate';
import { Comments } from '../../ui/Comments';
import { DiffDate } from '../../utils/DiffDate';
import FormatForm from '../../utils/FormatForm';
import {handleLikes} from '../../utils/HandleLikes';
import { handleLikes } from '../../utils/HandleLikes';

function PostDetail({
author = 'default_user',
Expand All @@ -29,9 +30,9 @@ function PostDetail({
const [commented, setComment] = useState(false);
const [saved, setSaved] = useState(false);
const [IsOpen, setIsOpen] = useState(isDesktop);
const [authorPP, setAuthorPP] = useState(author_image_url);
const authorPP = author_image_url;

const {currentUser} = useAuth();
const { currentUser } = useAuth();
const currentToken = localStorage.getItem('token');
const currentUserName = currentUser.username;
const currentUserPP = currentUser.image_url;
Expand All @@ -40,11 +41,13 @@ function PostDetail({
const [newComments, setNewComments] = useState(comments);

useEffect(() => {
if (userLikes[id]) setLiked(true);
if (userLikes[id]) {setLiked(true);}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
handleLikes(setNewLikes, liked, id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [liked]);

useEffect(() => {
Expand All @@ -61,23 +64,23 @@ function PostDetail({
token,
username,
user_image_url,
id
id,
) {
event.preventDefault();

const {comment} = FormatForm(event);
const { comment } = FormatForm(event);

if (!comment) return;
if (!comment) {return;}

fetchData(`${apiUrl}api/posts/${id}/comments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({comment: comment}),
body: JSON.stringify({ comment: comment }),
}).catch((error) => {
console.error(error);
throwError(error);
});

setComment((prev) => [
Expand Down Expand Up @@ -291,7 +294,7 @@ function PostDetail({
currentToken,
currentUserName,
currentUserPP,
id
id,
);
}}
>
Expand Down Expand Up @@ -369,7 +372,7 @@ function PostDetail({
currentToken,
currentUserName,
currentUserPP,
id
id,
)
}
>
Expand Down
15 changes: 9 additions & 6 deletions frontend/src/components/features/posts/PostList.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {useEffect, useState} from 'react';
import {fetchData} from '../../services/Fetch';
import {Loader} from './../../ui/Loader';
import { useEffect, useState } from 'react';
import { fetchData } from '../../services/Fetch';
import throwError from '../../services/throwError';
import { Loader } from './../../ui/Loader';
import Post from './Post';

export default function PostList() {
Expand All @@ -13,14 +14,16 @@ export default function PostList() {
.then((data) => {
setPosts(data.posts ?? []);
})
.catch(() => {})
.catch((error) => {
throwError(error);
})
.finally(() => setIsLoading(false));
}, []);
}, [apiUrl]);

if (isLoading) {
return (
<div className="flex flex-wrap gap-12 my-12">
{Array.from({length: 8}).map((_, i) => (
{Array.from({ length: 8 }).map((_, i) => (
<Loader loader="post-load" key={i} />
))}
</div>
Expand Down
27 changes: 14 additions & 13 deletions frontend/src/components/features/profiles/ProfileForm.jsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import {useRef, useState} from 'react';
import {MdEdit} from 'react-icons/md';
import {useAuth} from '../../../providers/AuthProviders';
import {fetchData} from '../../services/Fetch';
import { useRef, useState } from 'react';
import { MdEdit } from 'react-icons/md';
import { useAuth } from '../../../providers/AuthProviders';
import { fetchData } from '../../services/Fetch';
import throwError from '../../services/throwError';

export default function ProfileForm({username, image_url, onClose}) {
export default function ProfileForm({ username, image_url, onClose }) {
const [name, setName] = useState(username);
const [avatar, setAvatar] = useState(image_url);
const [isLoading, setIsLoading] = useState(false);
const [errors, setErrors] = useState({name: '', avatar: ''});
const [errors, setErrors] = useState({ name: '', avatar: '' });
const [succes, setSucces] = useState(false);

const initialName = useRef(username);
const initialAvatar = useRef(image_url);

const {setCurrentUser} = useAuth();
const { setCurrentUser } = useAuth();
const token = localStorage.getItem('token');
const apiUrl = import.meta.env.VITE_API_URL;

Expand All @@ -25,26 +26,26 @@ export default function ProfileForm({username, image_url, onClose}) {
const avatarChanged = avatar !== initialAvatar.current;

if (name.trim() === '') {
setErrors({...errors, name: 'Le nom ne peut pas Γͺtre vide.'});
setErrors({ ...errors, name: 'Le nom ne peut pas Γͺtre vide.' });
setSucces(false);
setIsLoading(false);
return;
}

const urlRegex = /^https?:\/\/[^\s]+$/i;
if (avatar.trim() !== '' && !urlRegex.test(avatar)) {
setErrors({...errors, avatar: "URL d'avatar invalide."});
setErrors({ ...errors, avatar: "URL d'avatar invalide." });
setSucces(false);
setIsLoading(false);
return;
}

if (!nameChanged && !avatarChanged)
return setSucces(false) || setIsLoading(false);
{return setSucces(false) || setIsLoading(false);}

const newData = {};
if (nameChanged) newData.username = name;
if (avatarChanged) newData.image_url = avatar;
if (nameChanged) {newData.username = name;}
if (avatarChanged) {newData.image_url = avatar;}

const data = await fetchData(`${apiUrl}api/auth/update-profile`, {
method: 'PUT',
Expand All @@ -56,7 +57,7 @@ export default function ProfileForm({username, image_url, onClose}) {
});

if (!data || data.error) {
console.log(data);
throwError(data);
setIsLoading(false);
return;
} else {
Expand Down
14 changes: 7 additions & 7 deletions frontend/src/components/layout/NavBar.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import useMediaQuery from '@mui/material/useMediaQuery';
import {useState} from 'react';
import {FiHome, FiLogIn, FiLogOut, FiUserPlus} from 'react-icons/fi';
import {Link, useLocation} from 'react-router-dom';
import {useAuth} from '../../providers/AuthProviders';
import { useState } from 'react';
import { FiHome, FiLogIn, FiLogOut, FiUserPlus } from 'react-icons/fi';
import { Link } from 'react-router-dom';
import { useAuth } from '../../providers/AuthProviders';

export const NavBar = () => {
const location = useLocation();
const {currentUser, setCurrentUser, isAuthenticate} = useAuth();
const { currentUser, setCurrentUser, isAuthenticate } = useAuth();
const [isAuthenticated, setIsAuthenticated] = useState(isAuthenticate);
const isDesktop = useMediaQuery('(min-width:1024px)');

// eslint-disable-next-line no-unused-vars
const [token, setToken] = useState(localStorage.getItem('token') || null);

function handleLogOut() {
Expand Down Expand Up @@ -66,7 +66,7 @@ export const NavBar = () => {
onClick={() => {
handleLogOut(
currentUser,
isAuthenticate
isAuthenticate,
);
}}
>
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/components/routing/ProtectedRoute.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {Navigate, useLocation} from 'react-router-dom';
import {useAuth} from '../../providers/AuthProviders';
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '../../providers/AuthProviders';

export const ProtectedRoute = ({children}) => {
const {currentUser, isAuthenticate, loading} = useAuth();
export const ProtectedRoute = ({ children }) => {
const { isAuthenticate, loading } = useAuth();
const location = useLocation();
if (loading) return <div>Loading...</div>;
if (loading) {return <div>Loading...</div>;}
if (!isAuthenticate)
return <Navigate to="/signin" state={{from: location}} replace />;
{return <Navigate to="/signin" state={{ from: location }} replace />;}
return <div>{children}</div>;
};
3 changes: 2 additions & 1 deletion frontend/src/components/services/Fetch.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import throwError from './throwError';
export const fetchData = async (url, options = {}) => {
try {
const res = await fetch(url, {
Expand All @@ -14,7 +15,7 @@ export const fetchData = async (url, options = {}) => {

return await res.json();
} catch (error) {
console.error('Fetch error:', error);
throwError(error, 'Fetch error');
return null;
}
};
5 changes: 5 additions & 0 deletions frontend/src/components/services/throwError.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function throwError(error, message = 'Une erreur est survenue') {
throw new Error(
`${message} : ${error instanceof Error ? error.message : error}`,
);
}
2 changes: 1 addition & 1 deletion frontend/src/components/ui/ActionButtonsPost.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function ActionButtonsPost({
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
const handleClick = (label) => {
if (label === 'comment') {
if (onOpenComments) onOpenComments();
if (onOpenComments) {onOpenComments();}
} else {
setAction((prev) => !prev);
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/ui/Comments.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const Comments = ({comment, index}) => {
export const Comments = ({ comment, index }) => {
return (
<div key={index} className="flex items-start gap-3">
<img
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/ui/Loader.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const Loader = ({loader = ''}) => {
export const Loader = ({ loader = '' }) => {
switch (loader) {
case 'dote':
return (
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/ui/ShowError.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const ShowError = ({name, content, children = null}) => {
if (typeof content === 'boolean') return null;
export const ShowError = ({ name, content, children = null }) => {
if (typeof content === 'boolean') {return null;}

return (
<p aria-label={`${name} error`} className="text-red-500 text-sm">
Expand Down
Loading
Loading