Skip to content
Snippets Groups Projects
Commit 631c2d22 authored by YAHYAOUI Firas's avatar YAHYAOUI Firas
Browse files

Merge branch 'dev/client' into 'main'

add: event filters

See merge request !22
parents 26efc50a 154c852b
No related branches found
No related tags found
1 merge request!22add: event filters
Pipeline #20327 passed
import { Filter } from "../components/events/FiltersSection";
import { EventsResponse } from "../models/Event.model";
import { API } from "./utils";
export const getEvents = async (limit: number, page: number) => {
export const getEvents = async (limit: number, page: number, filters: Filter[]) => {
const offset = (page - 1) * limit;
const eventFilters = filters.map(filter => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const eventFilter: any = {};
eventFilter[filter.label] = filter.options;
return eventFilter
});
const result = await API.post<EventsResponse>(`/get_filtered_events/?limit=${limit}&offset=${offset}`, {
"order_by": "date",
"only_upcoming_events": true
"only_upcoming_events": true,
filters: eventFilters
});
const hasNext = offset + limit < result.data.total_count;
return {
......@@ -19,3 +27,23 @@ export const getEventById = async (eventId: string) => {
const result = await API.get<EventsResponse>(`/get_event_by_id/${eventId}`);
return result.data.results[0]
}
export const getThemes = async () => {
const result = await API.get<string[]>('/get_theme');
return result.data;
}
export const getCities = async () => {
const result = await API.get<string[]>('/get_city');
return result.data;
}
export const getOrganizers = async () => {
const result = await API.get<string[]>('/get_organizer');
return result.data;
}
export const getHoods = async () => {
const result = await API.get<string[]>('/get_hood');
return result.data;
}
\ No newline at end of file
import { Button, Figure } from "react-bootstrap";
import { Event } from "../../models/Event.model";
import { useState } from "react";
import EventParticipationModal from "./EventParticipationModal";
type EventDetailProps = {
event: Event;
};
export default function EventDetail({ event }: EventDetailProps) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div className="d-flex flex-column align-items-center px-5">
<h1>{event.nom}</h1>
<Figure.Image src={event.media_url} />
<Button className="mb-1">Voir participants</Button>
<Button>Je participe</Button>
<Button onClick={handleShow}>Je participe</Button>
<EventParticipationModal
show={show}
handleClose={handleClose}
event={event}
/>
<p>
<b>Description: </b>
{event.description}
......
import { Button, FloatingLabel, Form, Modal } from "react-bootstrap";
import { Event } from "../../models/Event.model";
import { useState } from "react";
type EventParticipationModalProps = {
show: boolean;
handleClose: () => void;
event: Event;
};
export default function EventParticipationModal({
show,
handleClose,
event,
}: EventParticipationModalProps) {
const [accept, setAccept] = useState(true);
const [choice, setChoice] = useState(2);
const radioLabels = [
"Je prévois de conduire et d'offrir des places dans ma voiture",
"Je prévois de participer et je recherche un covoiturage",
"Je vais m'arranger par moi-même",
];
return (
<Modal show={show} onHide={handleClose} size="lg" centered>
<Modal.Header closeButton>
<Modal.Title>{event.nom}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Check
type="switch"
id="custom-switch"
label="Je partage ma participation"
checked={accept}
onChange={() => setAccept(!accept)}
/>
</Form>
{accept && (
<>
<Form>
<div className="p-3">
{radioLabels.map((label, index) => (
<Form.Check
label={label}
name={`group1`}
type="radio"
id={`radio-${index}`}
key={`radio-${index}`}
checked={index === choice}
onChange={() => setChoice(index)}
/>
))}
</div>
</Form>
{choice === 0 && (
<>
<FloatingLabel
controlId="inputNbPlaces"
label="Nombre de places (entre 1 et 4)"
>
<Form.Control
type="number"
id="inputNbPlaces"
pattern="[1-4]"
placeholder="Nombre de places (entre 1 et 4)"
/>
</FloatingLabel>
<Form.Select multiple>
<option disabled>En liste d'attente</option>
<option value="1">Alex</option>
<option value="2">Chris</option>
<option value="3">Fantine</option>
</Form.Select>
</>
)}
{choice === 1 && (
<Form.Select>
<option disabled>Sélectionner co-voiturage</option>
<option value="1">Alex - places disponibles: 3</option>
<option value="2">Chris - places disponibles: 1</option>
<option value="3">Fantine - places disponibles: 2</option>
</Form.Select>
)}
</>
)}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Fermer
</Button>
<Button variant="primary" onClick={handleClose}>
Soumettre
</Button>
</Modal.Footer>
</Modal>
);
}
import { ButtonGroup, Dropdown } from "react-bootstrap";
export type Filter = {
label: string;
options: string[];
};
type FiltersSectionProps = {
filters: Filter[];
selectedFilters: Filter[];
onFilterChange: (index: number, option: string) => void;
};
export default function FiltersSection({
filters,
selectedFilters,
onFilterChange,
}: FiltersSectionProps) {
return (
<ButtonGroup className="mb-1">
{filters.map((filter, index) => (
<Dropdown key={index} className="me-1">
<Dropdown.Toggle
variant="primary"
id={`dropdown-${index}`}
className={selectedFilters[index] ? "selected-filter" : ""}
>
{filter.label}
</Dropdown.Toggle>
<Dropdown.Menu>
{filter.options.map((option, optionIndex) => (
<Dropdown.Item
key={optionIndex}
className={selectedFilters[index].options.includes(option) ? "bg-success-subtle" : ""}
onClick={() => onFilterChange(index, option)}
>
{option}
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
))}
</ButtonGroup>
);
}
import { useQuery } from "@tanstack/react-query";
import { getCities } from "../api/eventsAPI";
export const useGetCities = () => useQuery({
queryKey: ['cities'],
queryFn: getCities
})
\ No newline at end of file
import { useQuery } from "@tanstack/react-query";
import { getHoods } from "../api/eventsAPI";
export const useGetHoods = () => useQuery({
queryKey: ['hoods'],
queryFn: getHoods
})
\ No newline at end of file
import { useInfiniteQuery } from "@tanstack/react-query";
import { getEvents } from "../api/eventsAPI";
import { Filter } from "../components/events/FiltersSection";
export const useGetInfiniteEvents = () => {
export const useGetInfiniteEvents = (filters: Filter[]) => {
return useInfiniteQuery({
queryKey: ["events", "infinite"],
queryFn: async ({ pageParam }) => await getEvents(12, pageParam),
queryKey: ["events", ["infinite", filters]],
queryFn: async ({ pageParam }) => await getEvents(12, pageParam, filters),
initialPageParam: 1,
getNextPageParam: (lastPage) => lastPage.nextPage,
getPreviousPageParam: (firstPage) => firstPage.previousPage,
......
import { useQuery } from "@tanstack/react-query"
import { getThemes } from "../api/eventsAPI"
export const useGetThemes = () => {
return useQuery({
queryKey: ['themes'],
queryFn: getThemes
})
}
\ No newline at end of file
import { useQuery } from "@tanstack/react-query";
import { getOrganizers } from "../api/eventsAPI";
export const useGetOrganizers = () => useQuery({
queryKey: ['organizers'],
queryFn: getOrganizers
})
\ No newline at end of file
import EventsList from "../components/events/EventsList";
import { useInView } from "react-intersection-observer";
import { Button } from "react-bootstrap";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useGetInfiniteEvents } from "../hooks/useGetInfiniteEventsHook";
import LoadingDisplay from "../components/loading/LoadingDisplay";
import ErrorDisplay from "../components/error/ErrorDisplay";
import FiltersSection, { Filter } from "../components/events/FiltersSection";
import { useGetThemes } from "../hooks/useGetThemesHook";
import { useGetCities } from "../hooks/useGetCitiesHook";
import { useGetHoods } from "../hooks/useGetHoodsHook";
import { useGetOrganizers } from "../hooks/userGetOrganizersHook";
export default function EventsPage() {
const [ref, inView] = useInView();
const { data: themes } = useGetThemes();
const { data: cities } = useGetCities();
const { data: organizers } = useGetOrganizers();
const { data: hoods } = useGetHoods();
const filters = [
{ label: "rubrique", options: themes ?? [] },
{ label: "emetteur", options: organizers ?? [] },
{ label: "ville", options: cities ?? [] },
{ label: "lieu_quartier", options: hoods ?? [] },
];
const initialFilters = filters.map((filter) => {
return { label: filter.label, options: [] };
});
console.log(initialFilters);
const [selectedFilters, setSelectedFilters] =
useState<Filter[]>(initialFilters);
const {
data,
fetchNextPage,
......@@ -17,12 +43,22 @@ export default function EventsPage() {
isLoading,
isError,
error,
} = useGetInfiniteEvents();
} = useGetInfiniteEvents(selectedFilters);
useEffect(() => {
if (inView) fetchNextPage();
}, [fetchNextPage, inView]);
const handleFilterChange = (index: number, selectedOption: string) => {
const updatedFilters = [...selectedFilters];
if (updatedFilters[index].options.includes(selectedOption))
updatedFilters[index].options = updatedFilters[index].options.filter(
(option) => option !== selectedOption
);
else updatedFilters[index].options.push(selectedOption);
setSelectedFilters(updatedFilters);
};
return (
<>
{isError && <ErrorDisplay error={error} />}
......@@ -30,6 +66,11 @@ export default function EventsPage() {
{data && (
<>
<h1>Evènements</h1>
<FiltersSection
filters={filters}
onFilterChange={handleFilterChange}
selectedFilters={selectedFilters}
/>
<EventsList events={data.pages.map((data) => data.events).flat()} />
<Button
ref={ref}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment