Skip to content
Snippets Groups Projects
Users.tsx 7.03 KiB
Newer Older
Valentino's avatar
Valentino committed
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useCallback, useEffect, useMemo } from 'react';
Luka's avatar
Luka committed
import { RouteComponentProps, Link } from '@reach/router';
import { ChevronRightIcon, 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';

const pages = [{ name: 'Users', href: '#', current: true }];

export const Users: React.FC<RouteComponentProps> = () => {
  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 (
    <div className="max-w-7xl mx-auto py-4 sm:px-6 lg:px-8 h-full flex-grow">
      <nav className="flex mb-8" aria-label="Breadcrumb">
        <ol className="flex items-center space-x-4">
          <li>
            <div className="flex items-center">
              <Link to="/dashboard" className="text-sm font-medium text-gray-500 hover:text-gray-700">
                <span>Dashboard</span>
              </Link>
            </div>
          </li>
          {pages.map((page) => (
            <li key={page.name}>
              <div className="flex items-center">
                <ChevronRightIcon className="flex-shrink-0 h-5 w-5 text-gray-400" aria-hidden="true" />
                <a
                  href={page.href}
                  className="ml-4 text-sm font-medium text-gray-500 hover:text-gray-700"
                  aria-current={page.current ? 'page' : undefined}
                >
                  {page.name}
                </a>
              </div>
            </li>
          ))}
        </ol>
      </nav>

      <div className="pb-5 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
Valentino's avatar
Valentino committed
            onClick={() => configureModalOpen(null)}
Luka's avatar
Luka committed
            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>
      </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">
Luka's avatar
Luka committed
            <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"
Valentino's avatar
Valentino committed
                  onChange={debouncedSearch}
Luka's avatar
Luka committed
                />
              </div>
            </div>
          </div>
        </div>

        {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>

      <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">
Valentino's avatar
Valentino committed
              <Table data={filterSearch as any} columns={columns} getSelectedRowIds={selectedRows} selectable />
Luka's avatar
Luka committed
            </div>
          </div>
        </div>
      </div>

      <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."
      />

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