Skip to content

Commit

Permalink
feat: add questionings filters (#98)
Browse files Browse the repository at this point in the history
* feat: add questionings filters

* fix: inputProps type in search fields props

* feat: add collect filter (#99)

* feat: add blank target on questioning button
  • Loading branch information
RenauxLeaInsee authored Jan 7, 2025
1 parent 602f3e4 commit a97978d
Show file tree
Hide file tree
Showing 20 changed files with 455 additions and 167 deletions.
17 changes: 16 additions & 1 deletion src/hooks/useSearchFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import { FormEventHandler, useState } from "react";
import { create } from "zustand";
import { combine } from "zustand/middleware";

export type QuestioningsBaseType = {
searchParam: string;
state: string;
page: number;
pageSize: number;
lastEvent?: string[];
lastCommunication?: string[];
campaignId?: string;
};

export const base = {
contacts: {
searchParam: "",
Expand All @@ -13,9 +23,13 @@ export const base = {
},
questionings: {
searchParam: "",
state: "all",
page: 0,
pageSize: 10,
},
lastEvent: [],
lastCommunication: [],
campaignId: "",
} as QuestioningsBaseType,
surveys: {
idSource: "",
year: undefined as undefined | number,
Expand Down Expand Up @@ -88,6 +102,7 @@ export function useSearchForm<K extends Key>(key: K, initialValue: State[K]) {
value,
onSubmit,
onReset,
setValue,
inputProps: (name: keyof State[K]) => ({
id: name,
name: name,
Expand Down
15 changes: 11 additions & 4 deletions src/pages/ContactPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { theme } from "../theme.tsx";
import { Button, Typography } from "@mui/material";
import { ContactDetailsCard } from "../ui/Contact/ContactDetailsCard.tsx";
import { ContactCampaignsCard } from "../ui/Contact/ContactCampaignsCard.tsx";
import { Link } from "../ui/Link.tsx";
import { useSetSearchFilter } from "../hooks/useSearchFilter.ts";
import { useGetSearchFilter, useSetSearchFilter } from "../hooks/useSearchFilter.ts";
import { LinkWithForwardRef } from "../ui/Link.tsx";

export const ContactPage = () => {
const setFilter = useSetSearchFilter();
Expand All @@ -22,6 +22,8 @@ export const ContactPage = () => {
},
});

const { questionings: questioningFilter } = useGetSearchFilter();

if (!contact) {
return (
<Row justifyContent="center" py={10}>
Expand Down Expand Up @@ -53,10 +55,15 @@ export const ContactPage = () => {
<Button
variant="contained"
size="large"
component={Link}
component={LinkWithForwardRef}
to={`/questionings`}
onClick={() => {
return setFilter("questionings", { searchParam: contactName, page: 0, pageSize: 10 });
return setFilter("questionings", {
...questioningFilter,
page: 0,
pageSize: 10,
searchParam: contact.identifier,
});
}}
disabled={contact.listCampaigns?.length === 0}
>
Expand Down
55 changes: 22 additions & 33 deletions src/pages/Logout.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,33 @@
import { Stack, Typography, Box, Link } from "@mui/material";
import { Stack, Typography, Box, Card, Button } from "@mui/material";
import { Link } from "../ui/Link.tsx";
import { Row } from "../ui/Row.tsx";
import { PropsWithChildren } from "react";
import { Link as RouterLink } from "react-router-dom";

export function LogoutPage() {
return (
<>
<Stack
position="relative"
sx={{
background: "linear-gradient(270deg, #21005D 0%, #9A82DB 0%, #E12358 90%)",
}}
justifyContent="center"
alignItems="center"
minHeight={500}
height="calc(100vh - 230px)"
>
<Typography variant="displaySmall" fontWeight={400} color="white">
{"Vous avez été deconnecté,"}
</Typography>
<Row spacing={2}>
<Typography variant="displaySmall" fontWeight={400} color="white">
{"pour revenir sur "}
</Typography>
<Row typography="headlineMedium" gap={0.25} color="red.main" component="span">
<Box
sx={{
width: "90vw",
height: "90vh",
display: "flex",
justifyContent: "center",
alignItems: "center",
margin: "auto",
}}
>
<Card sx={{ p: 5, width: "500px" }} elevation={2}>
<Stack gap={5} alignItems={"center"}>
<Row typography="headlineLarge" gap={0.5} color="red.main" component="span">
<Box component="span" color="black.main" fontWeight={600}>
Platine
</Box>
Gestion
</Row>
,
</Row>
<Typography variant="displaySmall" fontWeight={400} color="white" component={HomeLink}>
{"cliquez ici"}
</Typography>
</Stack>
</>
<Typography variant="headlineLarge">Vous avez été déconnecté.</Typography>
<Button variant="contained" sx={{ py: 1 }} component={Link} to={"/"}>
Se reconnecter
</Button>
</Stack>
</Card>
</Box>
);
}

const HomeLink = (props: PropsWithChildren) => {
return <Link component={RouterLink} underline="none" to="/" {...props} />;
};
5 changes: 3 additions & 2 deletions src/pages/QuestioningPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { SyntheticEvent, useState } from "react";
import { theme } from "../theme.tsx";
import { Breadcrumbs } from "../ui/Breadcrumbs.tsx";
import { Row } from "../ui/Row.tsx";
import { Link } from "../ui/Link.tsx";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import { PageTab } from "../ui/PageTab.tsx";
import { QuestioningInfos } from "../ui/Questioning/QuestioningInfos.tsx";
import { collectStatus } from "../constants/collectStatus.ts";
import { getCollectStateChipColor } from "../ui/Questioning/SearchQuestioningTable.tsx";
import { useParams } from "react-router-dom";
import { useFetchQuery } from "../hooks/useFetchQuery.ts";
import { LinkWithForwardRef } from "../ui/Link.tsx";

enum Tab {
Infos = "Infos",
Expand Down Expand Up @@ -83,8 +83,9 @@ export const QuestioningPage = () => {
variant="contained"
disabled={hasNoQuestioningUrl}
size="large"
component={Link}
component={LinkWithForwardRef}
to={questioning.readOnlyUrl ?? ""}
target="_blank"
endIcon={<OpenInNewIcon />}
>
Voir le questionnaire miroir
Expand Down
97 changes: 72 additions & 25 deletions src/pages/Search/SearchQuestionings.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FormEventHandler, useState } from "react";
import { useState } from "react";
import {
QuestioningsBaseType,
useGetSearchFilter,
useSearchFilterParams,
useSearchForm,
Expand All @@ -12,41 +13,58 @@ import { Breadcrumbs } from "../../ui/Breadcrumbs.tsx";
import { Divider, ToggleButton, ToggleButtonGroup, Typography } from "@mui/material";
import { SearchQuestioningTable } from "../../ui/Questioning/SearchQuestioningTable.tsx";
import { EmptyState } from "../../ui/TableComponents.tsx";
import { SearchTextField } from "../../ui/SearchTextField.tsx";
import { useFetchQuery } from "../../hooks/useFetchQuery.ts";
import { MultipleSearchSelect } from "../../ui/Search/MultipleSearchSelect.tsx";
import { collectStatus } from "../../constants/collectStatus.ts";
import { FilterSelect } from "../../ui/FilterSelect.tsx";
import { communicationsList } from "../../constants/communications.ts";
import { SearchQuestioningTextField } from "../../ui/Search/SearchQuestioningTextField.tsx";

const endpoint = "/api/questionings/search";

export const SearchQuestionings = () => {
const breadcrumbs = [{ href: "/", title: "Accueil" }, "Interrogations"];

const [tab, setTab] = useState("me");
const [stateFilter, setStateFilter] = useState("all");

const { questionings: questioningFilter } = useGetSearchFilter();
const setFilter = useSetSearchFilter();

// filter to remove empty parameters from the query
const searchFilterParams = Object.fromEntries(
Object.entries(useSearchFilterParams("questionings"))
.map(([key, value]) => {
if (Array.isArray(value)) {
value = value.filter(v => v !== undefined);
}
return [key, value];
})
.filter(([, value]) => value !== undefined && !(Array.isArray(value) && value.length === 0)),
);

const { data, isLoading } = useFetchQuery(endpoint, {
query: {
...useSearchFilterParams("questionings"),
...searchFilterParams,
},
});

const questionings = data?.content ?? [];

const { onSubmit, onReset, inputProps, value } = useSearchForm("questionings", questioningFilter);

const handleSubmit: FormEventHandler = e => {
onSubmit(e);
};

const handleReset: FormEventHandler = e => {
onReset(e);
};
const { onReset, inputProps, value, setValue } = useSearchForm("questionings", questioningFilter);

const hasResetButton = value.searchParam !== "";

const hasNoQuestioning = !isLoading && questionings.length === 0;

const onResetSelect = (name: keyof QuestioningsBaseType) => {
setValue({ ...value, [name]: [] });
setFilter("questionings", { ...questioningFilter, [name]: [] });
};

const onSubmit = (name: keyof QuestioningsBaseType) => {
setFilter("questionings", { ...questioningFilter, [name]: inputProps(name).value });
};

return (
<Stack>
<Row
Expand All @@ -73,13 +91,13 @@ export const SearchQuestionings = () => {
</Row>
</Row>
<Divider variant="fullWidth" />
<form onSubmit={handleSubmit} onReset={handleReset}>
<form>
<Stack sx={{ my: 3, px: 5 }} gap={3}>
<Row justifyContent={"space-between"}>
<ToggleButtonGroup
value={stateFilter}
value={questioningFilter.state}
exclusive
onChange={(_, v) => setStateFilter(v)}
onChange={(_, value) => setFilter("questionings", { ...questioningFilter, state: value })}
sx={{
boxShadow: "none",
".MuiToggleButtonGroup-grouped:not(:first-of-type)": {
Expand All @@ -105,17 +123,46 @@ export const SearchQuestionings = () => {
</ToggleButton>
</ToggleButtonGroup>
</Row>
<SearchTextField
<SearchQuestioningTextField
hasResetButton={hasResetButton}
onReset={() => {
setFilter("questionings", { ...questioningFilter, "searchParam": "" });
setValue({ ...value, "searchParam": "" });
}}
onSubmit={() => onSubmit("searchParam")}
label={"Rechercher par unité enquêtée ou identifiant de connexion"}
inputProps={inputProps as any}
inputProps={{
...inputProps("searchParam"),
value: inputProps("searchParam").value as QuestioningsBaseType["searchParam"],
}}
/>
{/* TODO: use it later
<Row gap={3}>
<FilterSelect options={[]} label={"Collecte"} name={"campaignId"} />
<SearchSelectStatus />
<FilterSelect options={[]} label={"Dernière communication"} name={"lastCommunication"} />
</Row> */}
<Row gap={3}>
<FilterSelect
label={"Collecte"}
questioningFilter={questioningFilter}
setFilter={setFilter}
/>
<MultipleSearchSelect
options={collectStatus}
inputProps={{
...inputProps("lastEvent"),
value: inputProps("lastEvent").value as string[],
}}
label={"Statut"}
onReset={() => onResetSelect("lastEvent")}
onSubmit={() => onSubmit("lastEvent")}
/>
<MultipleSearchSelect
options={communicationsList}
inputProps={{
...inputProps("lastCommunication"),
value: inputProps("lastCommunication").value as string[],
}}
label={"Dernière communication"}
onReset={() => onResetSelect("lastCommunication")}
onSubmit={() => onSubmit("lastCommunication")}
/>
</Row>
{hasNoQuestioning && (
<EmptyState
isFiltered={hasResetButton}
Expand All @@ -126,7 +173,7 @@ export const SearchQuestionings = () => {
{!hasNoQuestioning && (
<SearchQuestioningTable
questionings={questionings}
stateFilter={stateFilter}
stateFilter={questioningFilter.state}
isLoading={isLoading}
totalCount={data?.totalElements ?? 0}
questioningFilter={questioningFilter}
Expand Down
28 changes: 28 additions & 0 deletions src/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,34 @@ export const theme = createTheme({
},
],
},
MuiAutocomplete: {
styleOverrides: {
clearIndicator: {
color: palette.primary.main,
visibility: "visible",
},
popupIndicator: {
color: palette.primary.main,
},
groupLabel: {
...typography.titleSmall,
color: palette.black.main,
},
option: {
"&[aria-selected='true']": {
backgroundColor: "white !important",
},
},
inputRoot: {
".MuiOutlinedInput-notchedOutline": {
borderColor: palette.border.default,
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
border: `1px solid ${palette.primary.main}`,
},
},
},
},
MuiIconButton: {
variants: [
{
Expand Down
Loading

0 comments on commit a97978d

Please sign in to comment.