Merge branch 'dev' into 'feature/auth'
# Conflicts: # src/router.js # src/utils/login.js
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import styles from "./Event.module.css";
|
||||
import {useState, useEffect} from "react";
|
||||
import {Link} from "react-router";
|
||||
import Button from "/src/components/ui/button/button.jsx"
|
||||
|
||||
function Event({event}){
|
||||
const [eventMenu, seteventMenu] = useState(false);
|
||||
const [windowSize, setWindowSize] = useState({
|
||||
width: null,
|
||||
height: null,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
function handleResize() {
|
||||
setWindowSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
}
|
||||
window.addEventListener("resize", handleResize);
|
||||
handleResize();
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className={`${styles.eventContainer} glassCard`}>
|
||||
<div className={styles.eventContainerTop}>
|
||||
<div className={styles.eventContent}>
|
||||
<div className={`${styles.eventImageDiv}`}>
|
||||
{event.image == null ? (
|
||||
<img src="empty.png" className={styles.eventImage} alt="Pas de photo de l'événement" />
|
||||
) : (
|
||||
<img src={event.image + `.png`} alt="Photo de l'événement" />
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.eventInfos}>
|
||||
<p className={styles.eventTitle}>{event.name}</p>
|
||||
<p>{event.description}</p>
|
||||
<div className={eventMenu ? styles.eventMenuOpen : styles.eventMenuClose}>
|
||||
<ul>
|
||||
{event.tasks
|
||||
.slice(0,5)
|
||||
.map((task, index) => (
|
||||
<li key={index}> - {task.name}</li>
|
||||
))}
|
||||
{event.tasks.length > 5 ? (
|
||||
<li>...</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{windowSize.width > 768 ? (
|
||||
<div className={styles.eventButtons}>
|
||||
<button
|
||||
className={`${styles.moreButton} ${eventMenu ? styles.moreButtonClicked : ""}`}
|
||||
onClick={()=>{seteventMenu(!eventMenu)}}>
|
||||
{">"}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={styles.eventContainerBottom}>
|
||||
{(windowSize.width <= 768 || eventMenu) ? (
|
||||
<Link
|
||||
to={`/event/` + event.id}
|
||||
className={styles.moreLink}>
|
||||
<Button>En savoir plus →</Button>
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Event;
|
||||
@@ -0,0 +1,135 @@
|
||||
.eventContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 1rem 0;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.eventContainerTop {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.eventContainerBottom{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.moreLink {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.eventImageDiv {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
width: auto;
|
||||
height: 6rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.eventImage {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
|
||||
.eventContent {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.eventTitle {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.eventButtons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
width: fit-content;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.eventButtons a {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.moreButton {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #019AFF;
|
||||
font-size: 1.5rem;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.moreButton:hover {
|
||||
font-size: 2rem;
|
||||
background: rgba(191, 191, 191, 0.5);
|
||||
}
|
||||
|
||||
.moreButton:focus {
|
||||
outline: none;
|
||||
}
|
||||
.moreButton:focus-visible {
|
||||
outline: 2px solid black;
|
||||
}
|
||||
|
||||
.moreButtonClicked {
|
||||
rotate: 90deg;
|
||||
}
|
||||
|
||||
|
||||
.eventMenuOpen {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.eventMenuClose {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.eventImage {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.eventContainer{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.eventImageDiv {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.eventContent {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.eventInfos, .eventContainerBottom {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import styles from "./Filter.module.css";
|
||||
|
||||
function Filter({isFilterVisible, filters, setFilters}){
|
||||
const setAlphabeticalOrder = (order) => {
|
||||
setFilters(prev => ({
|
||||
...prev,
|
||||
alphabetical : prev.alphabetical === order ? null : order
|
||||
}));
|
||||
};
|
||||
|
||||
const setYearOrder = (order) => {
|
||||
setFilters(prev => ({
|
||||
...prev,
|
||||
yearOrder: prev.yearOrder === order ? null : order
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.filterContainer} ${isFilterVisible ? styles.filterOpen : styles.filterClose}`}>
|
||||
<abbr title="Tri alphabétique croissant (A à Z)">
|
||||
<button className={`${styles.filterTag} glassCard ${filters.alphabetical !== "asc" ? "" : styles.active}`}
|
||||
onClick={() => setAlphabeticalOrder("asc")}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||
<path d="m80-280 150-400h86l150 400h-82l-34-96H196l-32 96H80Zm140-164h104l-48-150h-6l-50 150Zm328 164v-76l202-252H556v-72h282v76L638-352h202v72H548ZM360-760l120-120 120 120H360ZM480-80 360-200h240L480-80Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</abbr>
|
||||
|
||||
<abbr title="Tri alphabétique décroissant (Z à A)">
|
||||
<button className={`${styles.filterTag} glassCard ${filters.alphabetical !== "desc" ? "" : styles.active}`}
|
||||
onClick={() => setAlphabeticalOrder("desc")}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||
<path d="M 480,-880 360,-760 H 600 Z M 360,-200 480,-80 600,-200 Z" />
|
||||
<path d="M 71.679924,-674.40094 H 398.50264 v 60.67705 l -208.59362,252.3436 h 214.5832 v 75.7812 H 65.690344 v -60.67705 L 274.28397,-598.61973 H 71.679924 Z" />
|
||||
<path d="M 769.04932,-356.43238 H 612.27858 l -24.73957,70.83329 H 486.75782 l 144.01033,-388.80185 h 119.53118 l 144.01033,388.80185 H 793.52847 Z m -131.77076,-72.13537 h 106.51035 l -53.12496,-154.68741 z" />
|
||||
</svg>
|
||||
</button>
|
||||
</abbr>
|
||||
|
||||
<abbr title="Tri chronologique croissant (du plus ancien au plus récent)">
|
||||
<button className={`${styles.filterTag} glassCard ${filters.yearOrder !== "asc" ? "" : styles.active}`}
|
||||
onClick={() => setYearOrder("asc")}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||
<path d="M340-160q-125 0-212.5-87.5T40-460q0-125 87.5-212.5T340-760q52 0 98 16.5t84 45.5l42-42 56 56-42 42q29 38 45.5 84.5T640-460q0 125-87.5 212.5T340-160Zm400 0v-488l-44 44-56-56 140-140 140 140-57 56-43-43v487h-80ZM240-800v-80h200v80H240Zm100 560q92 0 156-64t64-156q0-92-64-156t-156-64q-92 0-156 64t-64 156q0 92 64 156t156 64Zm-40-180h80v-200h-80v200Zm40-40Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</abbr>
|
||||
|
||||
<abbr title="Tri chronologique décroissant (du plus récent au plus ancien)">
|
||||
<button className={`${styles.filterTag} glassCard ${filters.yearOrder !== "desc" ? "" : styles.active}`}
|
||||
onClick={() => setYearOrder("desc")}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||
<path d="M340-160q-125 0-212.5-87.5T40-460q0-125 87.5-212.5T340-760q52 0 98 16.5t84 45.5l42-42 56 56-42 42q29 38 45.5 84.5T640-460q0 125-87.5 212.5T340-160Zm440 0L640-300l56-56 44 44v-488h80v487l43-43 57 56-140 140ZM240-800v-80h200v80H240Zm100 560q92 0 156-64t64-156q0-92-64-156t-156-64q-92 0-156 64t-64 156q0 92 64 156t156 64Zm-40-180h80v-200h-80v200Zm40-40Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</abbr>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Filter;
|
||||
@@ -0,0 +1,85 @@
|
||||
.filterContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
transition: max-height 0.5s ease, opacity 0.8s ease, transform 0.5s ease;
|
||||
}
|
||||
|
||||
.filterOpen {
|
||||
max-height: 200px;
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.filterClose{
|
||||
max-height: 200px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
.filterTag {
|
||||
border: none;
|
||||
background: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
|
||||
.filterTag:focus {
|
||||
outline: none;
|
||||
}
|
||||
.filterTag:focus-visible {
|
||||
outline: 2px solid black;
|
||||
}
|
||||
|
||||
.filterTag:hover {
|
||||
cursor: pointer;
|
||||
svg{
|
||||
fill: #019AFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.filterTag svg {
|
||||
transform: scale(1);
|
||||
color: black;
|
||||
}
|
||||
|
||||
|
||||
.active {
|
||||
border: 1px solid #019AFF;
|
||||
}
|
||||
|
||||
.active svg {
|
||||
fill: #019AFF;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px){
|
||||
.filterContainer {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filterOpen{
|
||||
display: flex;
|
||||
}
|
||||
.filterClose{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filterMenu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
abbr {
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,24 @@
|
||||
import styles from "./Header.module.css"
|
||||
import { Link, NavLink } from "react-router";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import getUserNotifications from "../../utils/getUserNotifications.js";
|
||||
|
||||
function Header() {
|
||||
const [notificationMenu, setnotificationMenu] = useState(false);
|
||||
const [unreadNotification, setunreadNotification] = useState(true);
|
||||
const [mobileMenu, setMobileMenu] = useState(false);
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
const notificationRef = useRef(null);
|
||||
|
||||
const [notifications, setNotifications] = useState([
|
||||
"Vous avez un nouveau message !",
|
||||
"Jean Paul est en attente de validation !",
|
||||
"Vous avez un nouveau message !"
|
||||
]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
async function loadNotifications() {
|
||||
const notifData = await getUserNotifications();
|
||||
setNotifications(notifData || []);
|
||||
}
|
||||
|
||||
loadNotifications();
|
||||
const handleClickOutside = (event) => {
|
||||
if (
|
||||
notificationRef.current &&
|
||||
@@ -37,7 +41,7 @@ function Header() {
|
||||
<nav className={`${mobileMenu ? styles.inactiveHeaderContent : styles.activeHeaderContent}`}>
|
||||
<div className={styles.headerLeftContent}>
|
||||
<NavLink className={styles.logoLink} to="/">
|
||||
<img src="/src/assets/logo.png" alt="logo" className={styles.logo} />
|
||||
<img src="logo.png" alt="logo" className={styles.logo} />
|
||||
</NavLink>
|
||||
<div className={styles.navLinks}>
|
||||
<NavLink to="/" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
|
||||
@@ -109,7 +113,7 @@ function Header() {
|
||||
) : (
|
||||
notifications.map((notification, index) => (
|
||||
<div className={styles.notification} key={index}>
|
||||
<p>{notification}</p>
|
||||
<p>{notification.content}</p>
|
||||
<button
|
||||
className={styles.closeBtn}
|
||||
onClick={() => setNotifications(prev => prev.filter((_, i) => i !== index))}
|
||||
|
||||
@@ -179,7 +179,7 @@ svg {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
z-index: 1;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.activeNotificationDiv {
|
||||
|
||||
Reference in New Issue
Block a user