Skip to content
Snippets Groups Projects
Users.tsx 6.12 KiB
Newer Older
Valentino's avatar
Valentino committed
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useCallback, useEffect, useMemo } from 'react';
Valentino's avatar
Valentino committed
import { SearchIcon, PlusIcon } from '@heroicons/react/solid';
Valentino's avatar
Valentino committed
import { CogIcon, TrashIcon } from '@heroicons/react/outline';
Luka's avatar
Luka committed
import { useUsers } from 'src/services/users';
import { Table } from 'src/components';
import { ConfirmationModal } from 'src/components/Modal';
Valentino's avatar
Valentino committed
import { debounce } from 'lodash';
Luka's avatar
Luka committed
import { UserModal } from './components/UserModal';

Valentino's avatar
Valentino committed
export const Users: React.FC = () => {
Luka's avatar
Luka committed
  const [selectedRowsIds, setSelectedRowsIds] = useState({});
  const [deleteModal, setDeleteModal] = useState(false);
  const [configureModal, setConfigureModal] = useState(false);
Valentino's avatar
Valentino committed
  const [userId, setUserId] = useState(null);
Valentino's avatar
Valentino committed
  const [search, setSearch] = useState('');
Valentino's avatar
Valentino committed
  const { users, loadUsers } = useUsers();
Luka's avatar
Luka committed

Valentino's avatar
Valentino committed
  const handleSearch = (event: any) => {
Valentino's avatar
Valentino committed
    setSearch(event.target.value);
Valentino's avatar
Valentino committed
  };
Valentino's avatar
Valentino committed

Valentino's avatar
Valentino committed
  const debouncedSearch = useCallback(debounce(handleSearch, 250), []);
Luka's avatar
Luka committed

  useEffect(() => {
Valentino's avatar
Valentino committed
    loadUsers();
Luka's avatar
Luka committed

Valentino's avatar
Valentino committed
    return () => {
      debouncedSearch.cancel();
    };
Luka's avatar
Luka committed
  }, []);

Valentino's avatar
Valentino committed
  const filterSearch = useMemo(() => {
Valentino's avatar
Valentino committed
    return users.filter((item: any) => item.email?.toLowerCase().includes(search.toLowerCase()));
  }, [search, users]);
Valentino's avatar
Valentino committed

Luka's avatar
Luka committed
  const deleteModalOpen = () => setDeleteModal(true);
  const deleteModalClose = () => setDeleteModal(false);

Valentino's avatar
Valentino committed
  const configureModalOpen = (id: any) => {
    setUserId(id);
    setConfigureModal(true);
  };
Luka's avatar
Luka committed
  const configureModalClose = () => setConfigureModal(false);

  const columns: any = React.useMemo(
    () => [
      {
        Header: 'Name',
        accessor: 'name',
        width: 'auto',
      },
      {
        Header: 'Email',
        accessor: 'email',
        width: 'auto',
      },
      {
        Header: 'Status',
        accessor: 'status',
        width: 'auto',
      },
      {
        Header: ' ',
Valentino's avatar
Valentino committed
        Cell: (props: any) => {
          const { row } = props;

Luka's avatar
Luka committed
          return (
            <div className="text-right opacity-0 group-hover:opacity-100 transition-opacity">
              <button
Valentino's avatar
Valentino committed
                onClick={() => configureModalOpen(row.original.id)}
Luka's avatar
Luka committed
                type="button"
                className="inline-flex items-center px-4 py-2 border border-gray-200 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
              >
                <CogIcon className="-ml-0.5 mr-2 h-4 w-4" aria-hidden="true" />
                Configure
              </button>
            </div>
          );
        },
        width: 'auto',
      },
    ],
    [],
  );

  const selectedRows = useCallback((rows: Record<string, boolean>) => {
    setSelectedRowsIds(rows);
  }, []);

  return (
Valentino's avatar
Valentino committed
    <div className="relative">
      <div className="max-w-7xl mx-auto py-4 sm:px-6 lg:px-8 h-full flex-grow">
        <div className="pb-5 mt-6 border-b border-gray-200 sm:flex sm:items-center sm:justify-between">
          <h1 className="text-3xl leading-6 font-bold text-gray-900">Users</h1>
          <div className="mt-3 sm:mt-0 sm:ml-4">
            <button
              onClick={() => configureModalOpen(null)}
              type="button"
              className="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary-700 hover:bg-primary-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-800"
            >
              <PlusIcon className="-ml-0.5 mr-2 h-4 w-4" aria-hidden="true" />
              Add new user
            </button>
          </div>
Luka's avatar
Luka committed
        </div>

Valentino's avatar
Valentino committed
        <div className="flex justify-between w-100 my-3 items-center mb-5 ">
          <div className="flex items-center">
            <div className="inline-block">
              <label htmlFor="email" className="block text-sm font-medium text-gray-700 sr-only">
                Search candidates
              </label>
              <div className="mt-1 flex rounded-md shadow-sm">
                <div className="relative flex items-stretch flex-grow focus-within:z-10">
                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
                    <SearchIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
                  </div>
                  <input
                    type="text"
                    name="email"
                    id="email"
                    className="focus:ring-primary-500 focus:border-primary-500 block w-full rounded-md pl-10 sm:text-sm border-gray-200"
                    placeholder="Search Users"
                    onChange={debouncedSearch}
                  />
Luka's avatar
Luka committed
                </div>
              </div>
            </div>
          </div>

Valentino's avatar
Valentino committed
          {selectedRowsIds && Object.keys(selectedRowsIds).length !== 0 && (
            <div className="flex items-center">
              <button
                onClick={deleteModalOpen}
                type="button"
                className="inline-flex items-center px-4 py-2 text-sm font-medium rounded-md text-red-700 bg-red-50 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
              >
                <TrashIcon className="-ml-0.5 mr-2 h-4 w-4" aria-hidden="true" />
                Delete
              </button>
            </div>
          )}
        </div>
Luka's avatar
Luka committed

Valentino's avatar
Valentino committed
        <div className="flex flex-col">
          <div className="-my-2 sm:-mx-6 lg:-mx-8">
            <div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
              <div className="shadow border-b border-gray-200 sm:rounded-lg">
                <Table data={filterSearch as any} columns={columns} getSelectedRowIds={selectedRows} selectable />
              </div>
Luka's avatar
Luka committed
            </div>
          </div>
        </div>

Valentino's avatar
Valentino committed
        <ConfirmationModal
          open={deleteModal}
          onClose={deleteModalClose}
          title="Delete user"
          body="Are you sure you want to delete this user? All of your data will be permanently removed. This action cannot be undone."
        />
Luka's avatar
Luka committed

Valentino's avatar
Valentino committed
        <UserModal open={configureModal} onClose={configureModalClose} userId={userId} />
      </div>
Luka's avatar
Luka committed
    </div>
  );
};