resolve conflicts
This commit is contained in:
@@ -20,38 +20,28 @@ function Filter({isFilterVisible, filters, setFilters}){
|
||||
<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>
|
||||
<img src={"sortByAlpha.svg"} alt="Tri alphabétique croissant (A à Z)"/>
|
||||
</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>
|
||||
<img src={"sortByAntiAlpha.svg"} alt="Tri alphabétique décroissant (Z à A)"/>
|
||||
</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-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>
|
||||
<img src={"timerArrowUp.svg"} alt="Tri chronologique décroissant (du plus récent au plus ancien)"/>
|
||||
</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-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>
|
||||
<img src={"timerArrowDown.svg"} alt="Tri chronologique croissant (du plus ancien au plus récent)"/>
|
||||
</button>
|
||||
</abbr>
|
||||
</div>
|
||||
|
||||
@@ -35,31 +35,14 @@
|
||||
.filterTag:focus {
|
||||
outline: none;
|
||||
}
|
||||
.filterTag:focus-visible {
|
||||
outline: 2px solid black;
|
||||
}
|
||||
|
||||
.filterTag:hover {
|
||||
cursor: pointer;
|
||||
svg{
|
||||
fill: #019AFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.filterTag svg {
|
||||
transform: scale(0.7);
|
||||
color: black;
|
||||
}
|
||||
|
||||
|
||||
.active {
|
||||
border: 1px solid #019AFF;
|
||||
}
|
||||
|
||||
.active svg {
|
||||
fill: #019AFF;
|
||||
transform: scale(0.8);
|
||||
border: 2px solid #019AFF;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px){
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import styles from "./Header.module.css"
|
||||
import { Link, NavLink } from "react-router";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {useContext, useEffect, useRef, useState} from "react";
|
||||
import getUserNotifications from "../../utils/getUserNotifications.js";
|
||||
import {AuthContext} from "../../contexts/auth/AuthContext.js";
|
||||
|
||||
function Header() {
|
||||
const [notificationMenu, setnotificationMenu] = useState(false);
|
||||
const [unreadNotification, setunreadNotification] = useState(true);
|
||||
const [mobileMenu, setMobileMenu] = useState(false);
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
const [notifications, setNotifications] = useState([{"content" : "ok"}, {"content" : "pourquoi pas antoine"}]);
|
||||
const notificationRef = useRef(null);
|
||||
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@@ -41,7 +43,7 @@ function Header() {
|
||||
<nav className={`${mobileMenu ? styles.inactiveHeaderContent : styles.activeHeaderContent}`}>
|
||||
<div className={styles.headerLeftContent}>
|
||||
<NavLink className={styles.logoLink} to="/">
|
||||
<img src="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}>
|
||||
@@ -53,6 +55,7 @@ function Header() {
|
||||
<NavLink to="/volunteers" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
|
||||
Bénévoles
|
||||
</NavLink>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -64,16 +67,12 @@ function Header() {
|
||||
setunreadNotification(false);
|
||||
}}
|
||||
>
|
||||
<svg width="auto" height="30" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M34.325 52.5C33.8855 53.2577 33.2546 53.8866 32.4956 54.3238C31.7365 54.761 30.8759 54.9911 30 54.9911C29.1241 54.9911 28.2635 54.761 27.5044 54.3238C26.7454 53.8866 26.1145 53.2577 25.675 52.5M45 20C45 16.0218 43.4196 12.2064 40.6066 9.3934C37.7936 6.58035 33.9782 5 30 5C26.0218 5 22.2064 6.58035 19.3934 9.3934C16.5804 12.2064 15 16.0218 15 20C15 37.5 7.5 42.5 7.5 42.5H52.5C52.5 42.5 45 37.5 45 20Z" stroke="currentStroke" />
|
||||
</svg>
|
||||
<img className={styles.notificationsImg} src="/bell.svg" alt="notifications"/>
|
||||
{unreadNotification && <span className={styles.notificationBadge}></span>}
|
||||
</button>
|
||||
|
||||
<Link to="/profile" className={styles.profileLink}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="auto" fill="currentFill">
|
||||
<path d="M480-492.31q-57.75 0-98.87-41.12Q340-574.56 340-632.31q0-57.75 41.13-98.87 41.12-41.13 98.87-41.13 57.75 0 98.87 41.13Q620-690.06 620-632.31q0 57.75-41.13 98.88-41.12 41.12-98.87 41.12ZM180-187.69v-88.93q0-29.38 15.96-54.42 15.96-25.04 42.66-38.5 59.3-29.07 119.65-43.61 60.35-14.54 121.73-14.54t121.73 14.54q60.35 14.54 119.65 43.61 26.7 13.46 42.66 38.5Q780-306 780-276.62v88.93H180Z" />
|
||||
</svg>
|
||||
<img className={styles.profileImg} src="/person.svg" alt="profile"/>
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -88,6 +87,7 @@ function Header() {
|
||||
<NavLink to="/volunteers" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
|
||||
Bénévoles
|
||||
</NavLink>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ function Header() {
|
||||
className={`${styles.notificationDiv} glassCard ${notificationMenu ? styles.activeNotificationDiv : ""}`}
|
||||
>
|
||||
{notifications.length === 0 ? (
|
||||
<p>Vous n'avez aucune notification !</p>
|
||||
<p className={styles.notification}>Vous n'avez aucune notification !</p>
|
||||
) : (
|
||||
notifications.map((notification, index) => (
|
||||
<div className={styles.notification} key={index}>
|
||||
@@ -118,9 +118,7 @@ function Header() {
|
||||
className={styles.closeBtn}
|
||||
onClick={() => setNotifications(prev => prev.filter((_, i) => i !== index))}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px">
|
||||
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
|
||||
</svg>
|
||||
<img src="/close.svg" alt="supprimer la notification"/>
|
||||
</button>
|
||||
</div>
|
||||
))
|
||||
|
||||
@@ -26,25 +26,16 @@
|
||||
pointer-events: auto;
|
||||
}
|
||||
.inactiveHeaderContent{
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
position: absolute;
|
||||
top: 0.8rem;
|
||||
left: 0.8rem;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.inactiveMobileMenu{
|
||||
max-height: 0;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.activeMobileMenu{
|
||||
transition: max-height 3s ease;
|
||||
max-height: 500px;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
@@ -105,10 +96,6 @@
|
||||
border-bottom: 2px solid #019AFF;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.bellBtn, .profileLink {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -124,11 +111,12 @@ svg {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bellBtn svg {
|
||||
.notificationsImg {
|
||||
stroke: black;
|
||||
stroke-width: 3.5;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.bellBtn:focus {
|
||||
@@ -141,33 +129,22 @@ svg {
|
||||
.activeBellBtn {
|
||||
transform: translateY(-4px);
|
||||
background: rgba(191, 191, 191, 0.5);
|
||||
svg {
|
||||
stroke: #019AFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.bellBtn:hover {
|
||||
transform: translateY(-4px);
|
||||
background: rgba(191, 191, 191, 0.5);
|
||||
svg {
|
||||
stroke: #019AFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.profileLink:hover {
|
||||
transform: translateY(-4px);
|
||||
background: rgba(191, 191, 191, 0.5);
|
||||
svg {
|
||||
fill: #019AFF;
|
||||
}
|
||||
}
|
||||
|
||||
.bellBtn svg {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.profileLink svg {
|
||||
.profileImg {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
@@ -185,15 +162,15 @@ svg {
|
||||
.activeNotificationDiv {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
z-index: 3;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
|
||||
.notification{
|
||||
margin-bottom: 7%;
|
||||
padding: 0.5em 1em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.notificationBadge {
|
||||
@@ -233,6 +210,7 @@ svg {
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
z-index: 10;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.burgerBar {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import React from "react";
|
||||
import styles from "./IncompleteEvent.module.css";
|
||||
|
||||
function IncompleteEvent() {
|
||||
const incompleteEvents = [
|
||||
{ title: "Événement A", tasks: ["Communication sur les réseaux", "Enormément de comm", "Toujours + de comm,Communication sur les réseaux",
|
||||
"Enormément de comm", "Toujours + de comm","Communication sur les réseaux", "Enormément de comm", "Toujours + de comm","Communication sur les réseaux", "Enormément de comm", "Toujours + de comm]"] },
|
||||
{ title: "Événement B", tasks: ["Réservation de la salle", "Vérification du matériel"] },
|
||||
{ title: "Événement C", tasks: ["Communication sur les réseaux", "Enormément de comm", "Toujours + de comm"] },
|
||||
{ title: "Événement D", tasks: ["Communication sur les réseaux", "Enormément de comm", "Toujours + de comm"] },
|
||||
{ title: "Événement E", tasks: ["Communication sur les réseaux", "Enormément de comm", "Toujours + de comm,Communication sur les réseaux",
|
||||
"Enormément de comm", "Toujours + de comm","Communication sur les réseaux", "Enormément de comm", "Toujours + de comm","Communication sur les réseaux", "Enormément de comm", "Toujours + de comm]"] },
|
||||
{ title: "Événement F", tasks: ["Communication sur les réseaux", "Enormément de comm", "Toujours + de comm,Communication sur les réseaux",
|
||||
"Enormément de comm", "Toujours + de comm","Communication sur les réseaux", "Enormément de comm", "Toujours + de comm","Communication sur les réseaux", "Enormément de comm", "Toujours + de comm]"] },
|
||||
{ title: "Événement G", tasks: ["Communication sur les réseaux", "Enormément de comm", "Toujours + de comm,Communication sur les réseaux",
|
||||
"Enormément de comm", "Toujours + de comm","Communication sur les réseaux", "Enormément de comm", "Toujours + de comm","Communication sur les réseaux", "Enormément de comm", "Toujours + de comm]"] },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={`${styles.rightBlock} glassCard`}>
|
||||
<h2>Événements incomplets</h2>
|
||||
<div className={styles.eventsContainer}>
|
||||
{incompleteEvents.map((event, index) => (
|
||||
<div key={index} className={`glassCard ${styles.eventCard}`}>
|
||||
<h3>{event.title}</h3>
|
||||
<ul className={styles.tasksList}>
|
||||
{event.tasks.map((task, taskIndex) => (
|
||||
<li key={taskIndex}>{task}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default IncompleteEvent;
|
||||
@@ -0,0 +1,135 @@
|
||||
.rightBlock {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rightBlock>h2 {
|
||||
font-size: 29px;
|
||||
}
|
||||
|
||||
.eventsContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
margin-top: 5px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding-bottom: 10px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.eventCard ul {
|
||||
align-items: center;
|
||||
margin-left: 8%;
|
||||
}
|
||||
|
||||
.eventCard ul li {
|
||||
text-align: left;
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
.eventsContainer::-webkit-scrollbar {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.eventsContainer::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.eventsContainer::-webkit-scrollbar-thumb {
|
||||
background: rgba(30, 96, 249, 0.5);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.eventsContainer::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(30, 96, 249, 0.7);
|
||||
}
|
||||
|
||||
.eventCard {
|
||||
min-width: calc((100% - 20px) / 3);
|
||||
max-width: calc((100% - 20px) / 3);
|
||||
min-height: 250px;
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
padding-bottom: 200px;
|
||||
box-sizing: border-box;
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
animation: slideInEvent 1s ease forwards;
|
||||
|
||||
flex-shrink: 1;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.eventCard:nth-child(1) {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.eventCard:nth-child(2) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.eventCard:nth-child(3) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.eventCard:nth-child(4) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.eventCard:nth-child(5) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.eventCard:nth-child(6) {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
.eventCard>h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@keyframes slideInEvent {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 760px) {
|
||||
.eventCard {
|
||||
min-width: 100%;
|
||||
min-height: 200px;
|
||||
max-height: fit-content;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.eventCard h3 {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.eventsContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
max-height: 300px;
|
||||
margin-top: 15px;
|
||||
height: fit-content;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 0px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.rightBlock>h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
import Button from "../ui/button/button";
|
||||
import styles from "./PendingMembers.module.css";
|
||||
|
||||
function pendingMembers() {
|
||||
const pendingMembers = [
|
||||
{ name: "Antoine Ordonneau" },
|
||||
{ name: "Giovanni Josserand" },
|
||||
{ name: "Quentin T'Jampens" },
|
||||
{ name: "Quentin T'Jampens" },
|
||||
{ name: "Quentin T'Jampens" },
|
||||
{ name: "Quentin T'Jampens" },
|
||||
{ name: "Quentin T'Jampens" }
|
||||
];
|
||||
|
||||
const handleValidate = (name) => {
|
||||
console.log(`Valider ${name}`);
|
||||
};
|
||||
|
||||
const handleReject = (name) => {
|
||||
console.log(`Refuser ${name}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.rightBlock} glassCard`}>
|
||||
<h2>Membres en attente de validation</h2>
|
||||
<div className={styles.membersContainer}>
|
||||
{pendingMembers.map((member, index) => (
|
||||
<div key={index} className={`glassCard ${styles.memberCard}`}>
|
||||
<p>{member.name}</p>
|
||||
<div className={styles.buttonGroup}>
|
||||
<Button onClick={() => handleValidate(member.name)} variant="primary">
|
||||
Valider
|
||||
</Button>
|
||||
<Button onClick={() => handleReject(member.name)} variant="danger">
|
||||
Refuser
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default pendingMembers;
|
||||
@@ -0,0 +1,81 @@
|
||||
.rightBlock {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rightBlock > h2 {
|
||||
font-size: 29px;
|
||||
}
|
||||
|
||||
.membersContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
max-height: 185px;
|
||||
overflow-y: auto;
|
||||
padding-right: 5px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
|
||||
.membersContainer::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.membersContainer::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.membersContainer::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.membersContainer::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(125, 249, 30, 0.7);
|
||||
}
|
||||
|
||||
.memberCard {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
flex-shrink: 0;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
animation: slideInMember 0.5s ease forwards;
|
||||
}
|
||||
|
||||
.memberCard:nth-child(1) { animation-delay: 0s; }
|
||||
.memberCard:nth-child(2) { animation-delay: 0.1s; }
|
||||
.memberCard:nth-child(3) { animation-delay: 0.2s; }
|
||||
|
||||
.memberCard p {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.buttonGroup {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
@keyframes slideInMember {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@media screen and (max-width: 760px) {
|
||||
.membersContainer {
|
||||
max-height: 200px;
|
||||
}
|
||||
.rightBlock > h2{
|
||||
font-size:18px;
|
||||
}
|
||||
|
||||
.memberCard p{
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import {Link} from "react-router";
|
||||
import {useEffect, useState} from "react";
|
||||
import searchEvents from "../../utils/searchEvents.js";
|
||||
import searchUsers from "../../utils/searchUsers.js";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useLocation } from "react-router";
|
||||
|
||||
|
||||
function SearchBar() {
|
||||
@@ -47,10 +47,7 @@ function SearchBar() {
|
||||
return (
|
||||
<div className={`${styles.searchBarContainer} glassCard`}>
|
||||
<form className={styles.searchBarForm} onSubmit={(element) => element.preventDefault()}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24px" viewBox="0 -960 960 960" fill="#currentColor">
|
||||
<path
|
||||
d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/>
|
||||
</svg>
|
||||
<img src="search.svg" alt="Rechercher"/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Rechercher un événement"
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
.searchedEvent{
|
||||
display: flex;
|
||||
padding: 0.5rem;
|
||||
border-radius: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import styles from "./ToolBar.module.css"
|
||||
import {Link} from "react-router";
|
||||
import Filter from "../Filter/Filter.jsx";
|
||||
import {useEffect, useState} from "react";
|
||||
import SearchBar from "../SearchBar/SearchBar.jsx";
|
||||
import CreateEventBtn from "../createEventBtn/CreateEventBtn.jsx";
|
||||
|
||||
|
||||
function ToolBar({setFilters, filters}) {
|
||||
function ToolBar({setFilters, filters, showCreate = true}) {
|
||||
const [isFilterVisible, setIsFilterVisible] = useState(false);
|
||||
const [isSearchVisible, setIsSearchVisible] = useState(false);
|
||||
const toggleFilters = () => {
|
||||
@@ -36,23 +36,17 @@ function ToolBar({setFilters, filters}) {
|
||||
</div>
|
||||
) : null}
|
||||
<div className={styles.eventsButtons}>
|
||||
<Link
|
||||
to="/event/create"
|
||||
className={`${styles.createButton} glassCard`}>
|
||||
+
|
||||
</Link>
|
||||
{showCreate ? (
|
||||
<CreateEventBtn />
|
||||
) : null}
|
||||
|
||||
<button className={`${styles.searchButton} glassCard`} onClick={() => setIsSearchVisible(true)}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#currentColor">
|
||||
<path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/>
|
||||
</svg>
|
||||
<img src="search.svg" alt="Rechercher"/>
|
||||
</button>
|
||||
|
||||
<div className={`${styles.filterContainer} ${isFilterVisible ? "glassCard" : ""}`}>
|
||||
<button className={`${isFilterVisible ? "" : styles.mobileFilter} ${styles.sortButton} glassCard`} onClick={toggleFilters}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||
<path d="M440-160q-17 0-28.5-11.5T400-200v-240L168-736q-15-20-4.5-42t36.5-22h560q26 0 36.5 22t-4.5 42L560-440v240q0 17-11.5 28.5T520-160h-80Zm40-308 198-252H282l198 252Zm0 0Z"/>
|
||||
</svg>
|
||||
<img src="filter.svg" alt="Filtrer"/>
|
||||
</button>
|
||||
<div className={styles.filterMenu}>
|
||||
<Filter isFilterVisible={isFilterVisible} filters={filters} setFilters={setFilters}/>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
gap: 1rem;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +17,9 @@
|
||||
padding: 0;
|
||||
border-radius: 30px;
|
||||
overflow: visible;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filterMenu{
|
||||
@@ -34,11 +38,6 @@
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.createButton {
|
||||
font-size: 2rem;
|
||||
color: #019AFF;
|
||||
}
|
||||
|
||||
.createButton:hover, .searchButton:hover, .mobileFilter:hover {
|
||||
transform: translateY(-5px);
|
||||
cursor: pointer;
|
||||
@@ -60,11 +59,6 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sortButton svg, .searchButton svg {
|
||||
transform: scale(0.5);
|
||||
fill: #019AFF;
|
||||
}
|
||||
|
||||
|
||||
.sortButton:focus, .searchButton:focus {
|
||||
outline: none;
|
||||
@@ -93,8 +87,8 @@
|
||||
}
|
||||
|
||||
.filterContainer{
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.filterMenu{
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import React, { useState } from "react";
|
||||
import { Doughnut } from "react-chartjs-2";
|
||||
import { Chart, ArcElement } from "chart.js";
|
||||
import { MenuItem, Select, FormControl } from "@mui/material";
|
||||
import styles from "./ParticipationChart.module.css";
|
||||
|
||||
Chart.register(ArcElement);
|
||||
|
||||
function participationChart() {
|
||||
const [selected, setSelected] = useState("3");
|
||||
const [participationRate, setParticipationRate] = useState(78);
|
||||
|
||||
const chartData = {
|
||||
labels: ["Progress", "Background"],
|
||||
datasets: [
|
||||
{
|
||||
data: [participationRate, 100 - participationRate],
|
||||
backgroundColor: ["#1e60f9ff", "#ffffffff"],
|
||||
borderWidth: 0,
|
||||
weight: 10,
|
||||
cutout: "93%",
|
||||
circumference: 360,
|
||||
rotation: -90,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const chartOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: { enabled: false },
|
||||
},
|
||||
animation: {
|
||||
animateScale: true,
|
||||
animateRotate: true,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.leftBlock} glassCard`}>
|
||||
<div className={styles.participationHeader}>
|
||||
<div className={`${styles.partiText} glassCard`}>
|
||||
<label htmlFor={styles.months}>Taux de participation depuis :</label>
|
||||
<div className={styles.formControl}>
|
||||
<FormControl className={styles.formDesign}>
|
||||
<Select
|
||||
value={selected}
|
||||
onChange={(e) => setSelected(e.target.value)}
|
||||
sx={{
|
||||
width: "100px",
|
||||
height: "35px",
|
||||
fontSize: "19px",
|
||||
paddingBottom: "-2px",
|
||||
"& .MuiOutlinedInput-notchedOutline": { border: "none" },
|
||||
"& .MuiSelect-select": { padding: "8px", paddingRight: "24px !important" },
|
||||
"& .MuiSvgIcon-root": { fontSize: "20px" },
|
||||
}}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
sx: {
|
||||
className: "glassCard",
|
||||
borderRadius: "7px",
|
||||
width: "100px",
|
||||
maxWidth: "100px",
|
||||
},
|
||||
},
|
||||
}}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value="1" sx={{ fontSize: "18px" }}>1 mois</MenuItem>
|
||||
<MenuItem value="3" sx={{ fontSize: "18px" }}>3 mois</MenuItem>
|
||||
<MenuItem value="6" sx={{ fontSize: "18px" }}>6 mois</MenuItem>
|
||||
<MenuItem value="12" sx={{ fontSize: "18px" }}>12 mois</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.chartContainer}>
|
||||
<div className={styles.chartWrapper}>
|
||||
<Doughnut data={chartData} options={chartOptions} />
|
||||
<div className={styles.chartPercentage}>{participationRate}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default participationChart;
|
||||
@@ -0,0 +1,120 @@
|
||||
.leftBlock {
|
||||
width: 40%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: calc(100vh - 260px);
|
||||
/* Exemple: calc(100vh - 60px) */
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.participationHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.partiText {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 2%;
|
||||
margin-top: 2%;
|
||||
animation: slideInEvent 2s;
|
||||
}
|
||||
|
||||
.partiText label {
|
||||
font-size: 20px;
|
||||
text-align: right;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.chartContainer {
|
||||
max-width: 80%;
|
||||
width: 400px;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
animation: slideEvent2 2s;
|
||||
}
|
||||
|
||||
.chartWrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chartPercentage {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
text-align: center;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 60px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@keyframes slideEvent2 {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInEvent {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.formControl {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.formDesign {
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 760px) {
|
||||
.partiText {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.partiText label {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.leftBlock {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: fit-content;
|
||||
margin-bottom: auto;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.chartContainer{
|
||||
height:fit-content;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import styles from "./createEventBtn.module.css"
|
||||
import { useState } from "react";
|
||||
import Modal from "../ui/modal/modal.jsx";
|
||||
import Button from "../ui/button/button.jsx";
|
||||
import TextInput from "../ui/input/input.jsx";
|
||||
import createEvent from "../../utils/event/createEvent.js"
|
||||
|
||||
|
||||
export default function CreateEventBtn() {
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [name, setName] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
|
||||
const handleCreateEvent = async () => {
|
||||
|
||||
if(name !== "" || description !== "") await createEvent(name, description);
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
return <>
|
||||
<Button className={`${styles.createButton} glassCard`} variant={"default"} onClick={() => setIsOpen(true)}> + </Button>
|
||||
|
||||
<Modal title={"Créer un événement"} open={isOpen} onClose={() => setIsOpen(false)}>
|
||||
|
||||
<section className={styles.section}>
|
||||
<div className={styles.inputContainer}>
|
||||
<h2>Titre</h2>
|
||||
<TextInput placeholder={"Nom de l'événement"} value={name} onChange={e => setName(e.target.value)} />
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<h2>Description</h2>
|
||||
<TextInput placeholder={"Description de l'événement"} value={description} onChange={e => setDescription(e.target.value)} />
|
||||
</div>
|
||||
|
||||
<Button onClick={handleCreateEvent} className={styles.create}>Créer</Button>
|
||||
|
||||
</section>
|
||||
</Modal>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
.createButton {
|
||||
font-size: 1.5rem;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.createButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
border-radius: 50% !important;
|
||||
width: 55px !important;
|
||||
height: 55px !important;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.createButton:hover {
|
||||
transform: translateY(-5px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inputContainer {
|
||||
|
||||
h2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.create {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
@@ -2,20 +2,25 @@ import { useState, useContext } from "react";
|
||||
import styles from "./loginForm.module.css";
|
||||
import Button from "../ui/button/button.jsx";
|
||||
import Input from "../ui/input/input.jsx";
|
||||
import Modal from "../ui/modal/modal.jsx";
|
||||
import { useNavigate } from "react-router";
|
||||
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||
|
||||
|
||||
function LoginForm({ className }) {
|
||||
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { login } = useContext(AuthContext);
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
let navigate = useNavigate();
|
||||
const { login } = useContext(AuthContext);
|
||||
const [open , setOpen] = useState(false);
|
||||
|
||||
async function handleSubmit() {
|
||||
const res = await login(email, password);
|
||||
if (res.status === 401 || res.status === 422) setOpen(true);
|
||||
else navigate("/");
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
await login(email, password);
|
||||
navigate("/");
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -37,6 +42,10 @@ function LoginForm({ className }) {
|
||||
<Button variant={"transparent"} onClick={() => navigate("/register")}>Vous n'avez pas de compte ?</Button>
|
||||
<Button onClick={handleSubmit} variant={"default"}> Se connecter </Button>
|
||||
</div>
|
||||
|
||||
<Modal title={"Impossible de se connecter"} open={open} onClose={() => setOpen(false)}>
|
||||
<p>L'Email ou le mot de passe est incorrect</p>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function ThemeSwitcher() {
|
||||
};
|
||||
|
||||
return <Button variant={"default"} onClick={switchTheme} className={styles.themeBtn}>
|
||||
<img src={`/public/icons/theme/${theme}.svg`} alt={`${theme} icon`} className={styles.themeIcon} />
|
||||
<img src={`/icons/theme/${theme}.svg`} alt={`${theme} icon`} className={styles.themeIcon} />
|
||||
<p>Changer de thème </p>
|
||||
</Button>
|
||||
}
|
||||
Reference in New Issue
Block a user