Skip to content

Commit

Permalink
feat: Parcourir la liste des guichets ref #313 avec autocomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
pprev94 committed Apr 24, 2024
1 parent abb2f05 commit c8bfebb
Show file tree
Hide file tree
Showing 23 changed files with 601 additions and 89 deletions.
2 changes: 2 additions & 0 deletions assets/@types/app_espaceco.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export type GetResponse<T> = {
previousPage: number;
nextPage: number;
};

export type CommunityListFilter = "public" | "iam_member" | "affiliation";
1 change: 1 addition & 0 deletions assets/@types/espaceco.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface CommunityResponseDTO {
id: number;
description: string | null;
detailed_description?: string | null;
name: string;
active: boolean;
shared_georem: "all" | "restrained" | "personal";
Expand Down
13 changes: 11 additions & 2 deletions assets/components/Utils/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,20 @@ type PaginationProps = {

const Pagination: FC<PaginationProps> = (props: PaginationProps) => {
const { page, count, shape = "rounded", size = "small", onChange } = props;
console.log("count : ", count);

return (
<MuiDsfrThemeProvider>
<MuiPagination page={page} count={count} size={size} shape={shape} variant={"outlined"} showFirstButton showLastButton onChange={onChange} />
<MuiPagination
className={fr.cx("fr-my-2v")}
page={page}
count={count}
size={size}
shape={shape}
variant={"outlined"}
showFirstButton
showLastButton
onChange={onChange}
/>
</MuiDsfrThemeProvider>
);
};
Expand Down
2 changes: 1 addition & 1 deletion assets/entrepot/pages/dashboard/DashboardPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const DashboardPro = () => {
</div>
{isApiEspaceCoDefined() && (
<div className={fr.cx("fr-grid-row", "fr-grid-row--left", "fr-mt-4w")}>
<Button /*linkProps={routes.home().link*}*/>{t("espaceco_frontoffice_list")}</Button>
<Button linkProps={routes.espaceco_community_list().link}>{t("espaceco_frontoffice_list")}</Button>
</div>
)}
</>
Expand Down
28 changes: 20 additions & 8 deletions assets/espaceco/api/community.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import SymfonyRouting from "../../modules/Routing";

import { jsonFetch } from "../../modules/jsonFetch";
import { CommunityListFilter, GetResponse } from "../../@types/app_espaceco";
import { type CommunityResponseDTO } from "../../@types/espaceco";
import { GetResponse } from "../../@types/app_espaceco";
import { jsonFetch } from "../../modules/jsonFetch";

const get = (name: string, page: number, limit: number, signal: AbortSignal) => {
const url = SymfonyRouting.generate("cartesgouvfr_api_espaceco_community_get", {
name: name,
page: page,
limit: limit,
const get = (queryParams: { page: number; limit: number }, signal: AbortSignal) => {
const params = { ...queryParams, sort: "name:ASC" };
const url = SymfonyRouting.generate("cartesgouvfr_api_espaceco_community_get", params);
return jsonFetch<GetResponse<CommunityResponseDTO>>(url, {
signal: signal,
});
};

const searchByName = (name: string, filter: CommunityListFilter, signal: AbortSignal) => {
const queryParams = { name: `%${name}%`, filter: filter, sort: "name:ASC" };
const url = SymfonyRouting.generate("cartesgouvfr_api_espaceco_community_search", queryParams);
return jsonFetch<CommunityResponseDTO[]>(url, {
signal: signal,
});
};

const getAsMember = (queryParams: Record<string, unknown>, signal: AbortSignal) => {
const params = { ...queryParams, sort: "name:ASC" };
const url = SymfonyRouting.generate("cartesgouvfr_api_espaceco_community_get_as_member", params);
return jsonFetch<GetResponse<CommunityResponseDTO>>(url, {
signal: signal,
});
};

const community = { get };
const community = { get, searchByName, getAsMember };

export default community;
163 changes: 163 additions & 0 deletions assets/espaceco/pages/communities/Communities.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { fr } from "@codegouvfr/react-dsfr";
import RadioButtons from "@codegouvfr/react-dsfr/RadioButtons";
import MuiDsfrThemeProvider from "@codegouvfr/react-dsfr/mui";
import { Skeleton } from "@mui/material";
import { useQuery } from "@tanstack/react-query";
import { FC, useState } from "react";
import { CommunityListFilter, GetResponse } from "../../../@types/app_espaceco";
import { CommunityResponseDTO } from "../../../@types/espaceco";
import Pagination from "../../../components/Utils/Pagination";
import RQKeys from "../../../modules/espaceco/RQKeys";
import { CartesApiException } from "../../../modules/jsonFetch";
import api from "../../api";
import CommunityList from "./CommunityList";
import { useTranslation } from "../../../i18n/i18n";
import Alert from "@codegouvfr/react-dsfr/Alert";
import SearchCommunity from "./SearchCommunity";

const defaultLimit = 10;

type commonParams = {
page: number;
limit: number;
};

type Pending = commonParams & { pending: boolean };

const Communities: FC = () => {
const { t } = useTranslation("EspaceCoCommunities");

const [filter, setFilter] = useState<CommunityListFilter>("public");

const [params, setParams] = useState<commonParams>({ page: 1, limit: defaultLimit });
const [communitiesAsMemberParams, setCommunitiesAsMemberParams] = useState<Pending>({ pending: false, page: 1, limit: defaultLimit });

const [community, setCommunity] = useState<CommunityResponseDTO | null>(null);

// const { data /*, isError, error*/ } = useInfiniteQuery<
// GetResponse<CommunityResponseDTO>,
// CartesApiException,
// InfiniteData<GetResponse<CommunityResponseDTO>, number>,
// string[],
// number
// >({
// queryKey: RQKeys.community_list("", page, limit),
// queryFn: ({ pageParam, signal }) => api.community.get(pageParam, limit, signal),
// initialPageParam: 1,
// getNextPageParam: (lastPage /*, allPages, lastPageParam, allPageParams*/) => lastPage.nextPage,
// getPreviousPageParam: (firstPage /*, allPages, firstPageParam, allPageParams*/) => firstPage.previousPage,
// });

const communityQuery = useQuery<GetResponse<CommunityResponseDTO>, CartesApiException>({
queryKey: RQKeys.community_list(params.page, params.limit),
queryFn: ({ signal }) => api.community.get(params, signal),
staleTime: 3600000,
retry: false,
enabled: filter === "public",
});

const communitiesAsMember = useQuery<GetResponse<CommunityResponseDTO>, CartesApiException>({
queryKey: RQKeys.communities_as_member(communitiesAsMemberParams.pending, communitiesAsMemberParams.page, communitiesAsMemberParams.limit),
queryFn: ({ signal }) => api.community.getAsMember(communitiesAsMemberParams, signal),
staleTime: 3600000,
retry: false,
enabled: filter === "iam_member" || filter === "affiliation",
});

const handleFilterChange = (filter: CommunityListFilter) => {
setFilter(filter);
setCommunity(null);
if (filter === "iam_member" || filter === "affiliation") {
// TODO A VOIR SI 2 REQUETES DIFFERENTES
setCommunitiesAsMemberParams({ pending: filter === "affiliation", page: 1, limit: defaultLimit });
}
};

return (
<div className={fr.cx("fr-container")}>
<h1>{t("title")}</h1>
<div className={fr.cx("fr-grid-row")}>
<RadioButtons
legend={t("filters")}
options={[
{
label: t("all_public_communities"),
nativeInputProps: {
checked: filter === "public",
onChange: () => handleFilterChange("public"),
},
},
{
label: t("communities_as_member"),
nativeInputProps: {
checked: filter === "iam_member",
onChange: () => handleFilterChange("iam_member"),
},
},
{
label: t("pending_membership"),
nativeInputProps: {
checked: filter === "affiliation",
onChange: () => handleFilterChange("affiliation"),
},
},
]}
orientation="horizontal"
/>
</div>
{communityQuery.isLoading || communitiesAsMember.isLoading ? (
<MuiDsfrThemeProvider>
{[...Array(10).keys()].map((n) => (
<Skeleton className={fr.cx("fr-my-2v")} key={n} variant="rectangular" height={50} />
))}
</MuiDsfrThemeProvider>
) : communityQuery.isError ? (
<Alert severity="error" closable={false} title={communityQuery.error?.message} />
) : communitiesAsMember.isError ? (
<Alert severity="error" closable={false} title={communitiesAsMember.error?.message} />
) : (
<div>
<SearchCommunity
filter={filter}
onChange={(community) => {
setCommunity(community);
}}
/>
{community ? (
<CommunityList communities={[community]} filter={filter} />
) : filter === "public" ? (
communityQuery.data && (
<div>
<CommunityList communities={communityQuery.data.content} filter={filter} />
<div className={fr.cx("fr-grid-row", "fr-grid-row--center")}>
<Pagination
size={"large"}
count={communityQuery.data.totalPages}
page={params.page}
onChange={(_, page) => setParams({ ...params, page: page, limit: defaultLimit })}
/>
</div>
</div>
)
) : (
communitiesAsMember.data && (
<div>
<CommunityList communities={communitiesAsMember.data.content} filter={filter} />
<div className={fr.cx("fr-grid-row", "fr-grid-row--center")}>
<Pagination
size={"large"}
count={communitiesAsMember.data.totalPages}
page={communitiesAsMemberParams.page}
onChange={(_, page) => setCommunitiesAsMemberParams({ ...communitiesAsMemberParams, page: page, limit: defaultLimit })}
/>
</div>
</div>
)
)}
</div>
)}
</div>
);
};

export default Communities;
16 changes: 16 additions & 0 deletions assets/espaceco/pages/communities/CommunitiesSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { fr } from "@codegouvfr/react-dsfr";
import MuiDsfrThemeProvider from "@codegouvfr/react-dsfr/mui";
import Skeleton from "@mui/material/Skeleton";
import { FC } from "react";

const CommunitiesSkeleton: FC = () => {
return (
<MuiDsfrThemeProvider>
{[...Array(10).keys()].map((n) => (
<Skeleton className={fr.cx("fr-my-2v")} key={n} variant="rectangular" height={50} />
))}
</MuiDsfrThemeProvider>
);
};

export default CommunitiesSkeleton;
90 changes: 19 additions & 71 deletions assets/espaceco/pages/communities/CommunityList.tsx
Original file line number Diff line number Diff line change
@@ -1,82 +1,30 @@
import { useQuery } from "@tanstack/react-query";
import { FC, useState } from "react";
import { GetResponse } from "../../../@types/app_espaceco";
import { CommunityResponseDTO } from "../../../@types/espaceco";
import api from "../../../espaceco/api";
import RQKeys from "../../../modules/espaceco/RQKeys";
import { CartesApiException } from "../../../modules/jsonFetch";
import Pagination from "../../../components/Utils/Pagination";
import { fr } from "@codegouvfr/react-dsfr";
import LoadingText from "../../../components/Utils/LoadingText";
import LoadingIcon from "../../../components/Utils/LoadingIcon";
import Wait from "../../../components/Utils/Wait";
import Alert from "@codegouvfr/react-dsfr/Alert";
import { FC } from "react";
import { CommunityResponseDTO } from "../../../@types/espaceco";
import CommunityListItem from "./CommunityListItem";
import { CommunityListFilter } from "../../../@types/app_espaceco";
import { useTranslation } from "../../../i18n/i18n";

const limit = 10;

type QueryParams = {
name: string;
currentPage: number;
type CommunityListProps = {
communities: CommunityResponseDTO[];
filter: CommunityListFilter;
};

const CommunityList: FC = () => {
const [params, setParams] = useState<QueryParams>({ name: "", currentPage: 1 });

const { t: tCommon } = useTranslation("Common");

// const { data /*, isError, error*/ } = useInfiniteQuery<
// GetResponse<CommunityResponseDTO>,
// CartesApiException,
// InfiniteData<GetResponse<CommunityResponseDTO>, number>,
// string[],
// number
// >({
// queryKey: RQKeys.community_list("", currentPage, limit),
// queryFn: ({ pageParam, signal }) => api.community.get(pageParam, limit, signal),
// initialPageParam: 1,
// getNextPageParam: (lastPage /*, allPages, lastPageParam, allPageParams*/) => lastPage.nextPage,
// getPreviousPageParam: (firstPage /*, allPages, firstPageParam, allPageParams*/) => firstPage.previousPage,
// });

const communityQuery = useQuery<GetResponse<CommunityResponseDTO>, CartesApiException>({
queryKey: RQKeys.community_list(params.name, params.currentPage, limit),
queryFn: ({ signal }) => api.community.get(params.name, params.currentPage, limit, signal),
staleTime: 20000,
retry: false,
enabled: params.name.length === 0 || params.name.length > 3,
});

console.log(communityQuery.data);
const CommunityList: FC<CommunityListProps> = ({ communities, filter }) => {
const { t } = useTranslation("EspaceCoCommunities");

return (
<div className={fr.cx("fr-container")}>
{communityQuery.isLoading ? (
<Wait>
<div>
<LoadingIcon className={fr.cx("fr-ml-2w")} largeIcon={true} />
<span className={fr.cx("fr-ml-1v")}>{tCommon("loading")}</span>
</div>
</Wait>
<div>
{communities.length === 0 ? (
<div className={fr.cx("fr-my-2v")}>
<Alert severity={"info"} title={t("no_result", { filter: filter })} closable />
</div>
) : (
// <LoadingText as={"h3"} />
communityQuery.data?.content.length && (
<>
<ul>{communityQuery.data?.content.map((community) => <li key={community.id}>{community.name}</li>)}</ul>
<div className={fr.cx("fr-grid-row", "fr-grid-row--center")}>
<Pagination
size={"large"}
count={communityQuery.data.totalPages}
page={params.currentPage}
onChange={(_, page) => setParams({ ...params, currentPage: page })}
/*getPageLinkProps={(pageNumber) => {
return {
onClick: () => setParams({ ...params, currentPage: pageNumber }),
};
}}*/
/>
</div>
</>
)
communities.map((community, index) => {
const className = index % 2 === 0 ? "frx-community-even" : "";
return <CommunityListItem key={community.id} className={className} community={community} />;
})
)}
</div>
);
Expand Down
Loading

0 comments on commit c8bfebb

Please sign in to comment.