Vizzuality/landgriffon

View on GitHub
client/src/pages/profile/users.tsx

Summary

Maintainability
C
1 day
Test Coverage
import { useMemo, useState } from 'react';
import Head from 'next/head';
import { PlusIcon } from '@heroicons/react/solid';
import { useRouter } from 'next/router';
import { GetServerSideProps } from 'next';
import { dehydrate } from '@tanstack/react-query';

import { auth } from '@/pages/api/auth/[...nextauth]';
import { useUsers } from 'hooks/users';
import ProfileLayout from 'layouts/profile';
import Button from 'components/button';
import Search from 'components/search';
import Table from 'components/table';
import { DEFAULT_PAGE_SIZES } from 'components/table/pagination/constants';
import UserAvatar from 'containers/user-avatar';
import { useProfile } from 'hooks/profile';
import EditUser from 'containers/edit-user';
import getUserFullName from 'utils/user-full-name';
import { usePermissions } from 'hooks/permissions';
import { RoleName } from 'hooks/permissions/enums';
import Modal from 'components/modal';
import UserForm from 'containers/edit-user/user-form';
import getQueryClient from '@/lib/react-query';

import type { User } from 'types';
import type { TableProps } from 'components/table/component';
import type { PaginationState, SortingState } from '@tanstack/react-table';

const AdminUsersPage: React.FC = () => {
  const { query } = useRouter();
  const [sorting, setSorting] = useState<SortingState>([]);
  const [pagination, setPaginationState] = useState<PaginationState>({
    pageIndex: 1,
    pageSize: DEFAULT_PAGE_SIZES[0],
  });
  const [openModal, setOpenModal] = useState(false);
  const closeModal = () => setOpenModal(false);

  const sortStr = useMemo(() => {
    return sorting.map((sort) => (sort.desc ? `-${sort.id}` : sort.id)).join(',') || null;
  }, [sorting]);

  const { data: user, isFetching: isFetchingUser } = useProfile();

  const searchTerm = useMemo(
    () => (typeof query.search === 'string' ? query.search : null),
    [query.search],
  );

  const { data, isFetching } = useUsers({
    'page[size]': pagination.pageSize,
    'page[number]': pagination.pageIndex,
    sort: sortStr,
    ...(searchTerm && {
      'filter[email]': searchTerm,
    }),
  });

  const { hasRole } = usePermissions();
  const isAdmin = hasRole(RoleName.ADMIN);

  const tableData = useMemo(() => {
    if (data?.data && user) {
      return data?.data?.filter(({ id }) => id !== user?.id);
    }
    return [];
  }, [data, user]);

  const tableProps = useMemo<TableProps<User>>(
    () => ({
      state: { pagination, sorting },
      columns: [
        {
          id: 'fname',
          header: 'Name',
          size: 110,
          align: 'left',
          enableSorting: true,
          cell: ({ row }) => (
            <div className="name my-6 flex items-center gap-x-4">
              <UserAvatar
                userFullName={getUserFullName(row.original, { replaceByEmail: true })}
                user={row.original}
                className="h-10 w-10"
              />
              {getUserFullName(row.original)}
            </div>
          ),
        },
        {
          id: 'email',
          header: 'Email',
          align: 'left',
          enableSorting: true,
        },
        {
          id: 'title',
          header: 'Title',
          align: 'left',
          enableSorting: true,
        },
        {
          id: 'roles',
          header: 'Role',
          cell: ({ row }) => (
            <div>
              {row.original.roles.map((role) => (
                <p className="capitalize" key={role.name}>
                  {role.name}
                </p>
              ))}
            </div>
          ),
          align: 'left',
          enableSorting: true,
        },
        {
          id: 'id',
          header: '',
          size: 100,
          align: 'right',
          cell: ({ row }) => (
            <div className="py-4 pr-6">
              <EditUser user={row.original} />
            </div>
          ),
        },
      ],
      data: tableData,
      headerTheme: 'clean',
      onPaginationChange: setPaginationState,
      onSortingChange: setSorting,
      paginationProps: {
        pageSizes: DEFAULT_PAGE_SIZES,
        pageSize: pagination.pageSize,
        currentPage: pagination.pageIndex,
        totalPages: data?.meta?.totalPages,
        totalItems: data?.meta?.totalItems,
      },
    }),
    [data, tableData, pagination, sorting],
  );

  return (
    <ProfileLayout title="Users" searchSection={<Search placeholder="Search user..." disabled />}>
      <Head>
        <title>Admin users | Landgriffon</title>
      </Head>
      <div className="flex  h-full flex-1 flex-col">
        <div className="flex-0 mb-6 flex justify-end">
          <Button
            variant="primary"
            icon={
              <div
                aria-hidden="true"
                className="flex h-5 w-5 items-center justify-center rounded-full bg-white"
              >
                <PlusIcon className="h-4 w-4 text-navy-400" />
              </div>
            }
            onClick={() => setOpenModal(true)}
            disabled={!isAdmin}
          >
            Add user
          </Button>
          <Modal size="narrow" open={openModal} title="Add user" onDismiss={closeModal}>
            <UserForm onSubmit={closeModal}>
              <div className="mr-2.5 flex w-full justify-end">
                <Button size="base" variant="white" onClick={closeModal}>
                  Cancel
                </Button>
              </div>
            </UserForm>
          </Modal>
        </div>
        <div className="h-full flex-1">
          <Table {...tableProps} isLoading={isFetching || isFetchingUser} />
        </div>
      </div>
    </ProfileLayout>
  );
};

export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const session = await auth(ctx.req, ctx.res);
  const queryClient = getQueryClient();

  queryClient.setQueryData(['profile', session.accessToken], session.user);

  return {
    props: {
      session,
      dehydratedState: dehydrate(queryClient),
    },
  };
};

export default AdminUsersPage;