Skip to content

Commit

Permalink
fix: amélioration de la page users (#3259)
Browse files Browse the repository at this point in the history
  • Loading branch information
rap2hpoutre authored Oct 2, 2023
1 parent d9d6f1a commit b911fa1
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 126 deletions.
72 changes: 0 additions & 72 deletions ui/modules/admin/UsersList.tsx

This file was deleted.

215 changes: 161 additions & 54 deletions ui/pages/admin/users/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
Box,
Button,
Heading,
HStack,
Input,
Expand All @@ -9,47 +8,169 @@ import {
ModalContent,
ModalHeader,
ModalOverlay,
Spinner,
Stack,
Text,
VStack,
} from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { AccessorKeyColumnDef, SortingState } from "@tanstack/react-table";
import Head from "next/head";
import NavLink from "next/link";
import { useRouter } from "next/router";
import { useMemo, useState } from "react";

import { USER_STATUS_LABELS } from "@/common/constants/usersConstants";
import { _get } from "@/common/httpClient";
import { getAuthServerSideProps } from "@/common/SSR/getAuthServerSideProps";
import { formatDateNumericDayMonthYear } from "@/common/utils/dateUtils";
import Page from "@/components/Page/Page";
import withAuth from "@/components/withAuth";
import UserForm from "@/modules/admin/UserForm";
import UsersList from "@/modules/admin/UsersList";
import NewTable from "@/modules/indicateurs/NewTable";
import { ArrowRightLine } from "@/theme/components/icons";

export const getServerSideProps = async (context) => ({ props: { ...(await getAuthServerSideProps(context)) } });
const DEFAULT_LIMIT = 100;

const NO_LIMIT = 10_000;

type UserNormalized = {
_id: string;
normalizedNomPrenom: string;
normalizedEmail: string;
normalizedOrganismeNom: string;
organisationType: string;
organismeId: string;
organismeNom: string;
nom: string;
prenom: string;
account_status: string;
created_at: string;
email: string;
fonction: string;
last_connection: string;
};

const UsersColumns: AccessorKeyColumnDef<UserNormalized>[] = [
{
header: () => "Nom et prénom",
accessorKey: "normalizedNomPrenom",
cell: ({ row }) => (
<>
<Text fontSize="md">
{row.original?.nom} {row.original?.prenom}
</Text>
<Text fontSize="xs" color="#777">
{row.original?.email}
{row.original?.fonction ? ` - ${row.original?.fonction}` : ""}
</Text>
</>
),
},
{
header: () => "Etablissement",
accessorKey: "normalizedOrganismeNom",
cell: ({ row }) => {
return (
<Box
{...(row.original?.organismeId
? { as: NavLink, href: `/admin/organismes/${row.original?.organismeId}` }
: {})}
flexGrow={1}
>
<Text isTruncated maxWidth={400}>
{row.original?.organismeNom}
</Text>
{row.original?.organismeNom !== row.original?.organisationType && row.original?.organisationType ? (
<Text fontSize="xs" color="#777">
{row.original?.organisationType}
</Text>
) : null}
</Box>
);
},
},
{
header: () => "Date de création",
accessorKey: "created_at",
cell: ({ row }) => (
<>
<Text fontSize="sm">Créé le {formatDateNumericDayMonthYear(row.original?.created_at)}</Text>
<Text fontSize="xs" color="#777">
Dernière connexion le {formatDateNumericDayMonthYear(row.original?.last_connection)}
</Text>
</>
),
},
{
header: () => "Statut du compte",
accessorKey: "account_status",
cell: ({ row }) => (
<>
<Text fontSize="sm">{USER_STATUS_LABELS[row.original?.account_status] ?? row.original?.account_status}</Text>
</>
),
},
{
header: () => "",
accessorKey: "_id",
cell: ({ row }) => (
<NavLink href={`/admin/users/${row.original?._id}`}>
<ArrowRightLine w="1w" />
</NavLink>
),
},
];

const Users = () => {
const title = "Gestion des utilisateurs";
const router = useRouter();
const { page: _page, limit: _limit, q: searchValue, ...filter } = router.query;
const page = parseInt(router.query.page as string, 10) || 1;
const limit = parseInt(router.query.limit as string, 10) || DEFAULT_LIMIT;
const [search, setSearch] = useState<string>("");

const {
data: users,
data,
refetch: refetchUsers,
isLoading,
error,
} = useQuery<any, any>(["admin/users", page, limit, filter, searchValue], () =>
_get("/api/v1/admin/users/", { params: { page, q: searchValue, filter } })
);
// prefetch next page
useQuery(
["users", page + 1, limit, filter, searchValue],
() => _get("/api/v1/admin/users/", { params: { page: page + 1, limit, q: searchValue, filter } }),
{ enabled: !!(users?.pagination && page + 1 < users?.pagination?.lastPage) }
} = useQuery(["admin/users"], () =>
_get<{ data: any[] }>("/api/v1/admin/users/", {
params: {
page: 1,
limit: NO_LIMIT,
},
})
);

const users = useMemo(() => {
if (!data) return [];
return data.data.map((user) => {
const organismeId = user?.organisation?.organisme?._id;
const organismeNom = user?.organisation?.organisme?.nom || user?.organisation?.label || "";
return {
...user,
organismeId,
organismeNom,
organisationType: user?.organisation?.label || "",
normalizedOrganismeNom: organismeNom.toLowerCase(),
normalizedNomPrenom: user.nom.toLowerCase() + " " + user.prenom.toLowerCase(),
normalizedEmail: user.email.toLowerCase(),
};
});
}, [data]);

const filteredUsers = useMemo(() => {
if (!search) return users;
return users?.filter((user) => {
const searchLower = search.toLowerCase();
return (
user.normalizedNomPrenom.includes(searchLower) ||
user.normalizedEmail.includes(searchLower) ||
user.normalizedOrganismeNom.toLowerCase().includes(searchLower)
);
});
}, [search, users]);

const defaultSort: SortingState = [{ desc: false, id: "normalizedNomPrenom" }];
const [sort, setSort] = useState<SortingState>(defaultSort);

const closeModal = () => router.push("/admin/users", undefined, { shallow: true });

return (
Expand Down Expand Up @@ -94,43 +215,29 @@ const Users = () => {
</Button>
)} */}
</HStack>
{isLoading && !(users as any)?.data ? (
<Spinner alignSelf="center" />
) : error ? (
<Box>Une erreur est survenue : {(error as any).message}</Box>
) : (
<Stack spacing={2} width="100%">
<form
method="get"
onSubmit={(e: any) => {
e.preventDefault();
const formData = new FormData(e.target);
const { q } = Object.fromEntries(formData);
router.push(
{
pathname: "/admin/users",
query: (q ? { q } : {}) as any,
},
undefined,
{ shallow: true }
);
}}
>
<HStack gap={0} width={500}>
<Input type="search" name="q" defaultValue={searchValue} />
<Button type="submit" title="Rechercher" m={0} marginInlineStart={0}>
<i className="ri-search" />
Rechercher
</Button>
</HStack>
</form>
<Text>
{Intl.NumberFormat().format(users?.pagination?.total || 0)}{" "}
{users?.pagination?.total > 1 ? "comptes utilisateurs" : "compte utilisateur"}
</Text>
<UsersList data={users?.data || []} pagination={users?.pagination} searchValue={searchValue} />
</Stack>
)}
<Stack spacing={2} width="100%">
<HStack gap={0}>
<Input
placeholder="Rechercher un utilisateur par nom, prénom, email, siret, établissement, etc."
type="search"
name="q"
defaultValue={search}
onChange={(e) => setSearch(e.target.value)}
/>
</HStack>
<Text py="6" color="#777">
{Intl.NumberFormat().format(filteredUsers.length || 0)}{" "}
{filteredUsers.length > 1 ? "comptes utilisateurs" : "compte utilisateur"}
{filteredUsers.length < users.length ? ` (${users.length} au total)` : ""}
</Text>
<NewTable
data={filteredUsers || []}
loading={isLoading}
sortingState={sort}
onSortingChange={(state) => setSort(state)}
columns={UsersColumns}
/>
</Stack>
</VStack>
</Page>
);
Expand Down

0 comments on commit b911fa1

Please sign in to comment.