Skip to content
Snippets Groups Projects
Header.tsx 7.58 KiB
Newer Older
Valentino's avatar
Valentino committed
import React, { Fragment, useState } from 'react';
Luka's avatar
Luka committed
import { Disclosure, Menu, Transition } from '@headlessui/react';
import { MenuIcon, XIcon } from '@heroicons/react/outline';
import { useAuth } from 'src/services/auth';
Valentino's avatar
Valentino committed
import Gravatar from 'react-gravatar';
import { Link, useLocation } from 'react-router-dom';
import clsx from 'clsx';
Valentino's avatar
Valentino committed
import { CurrentUserModal } from './components/CurrentUserModal';
Luka's avatar
Luka committed

const navigation = [
Davor's avatar
Davor committed
  { name: 'Dashboard', to: '/dashboard', requiresAdmin: false },
  { name: 'Users', to: '/users', requiresAdmin: true },
Luka's avatar
Luka committed
];

function classNames(...classes: any[]) {
  return classes.filter(Boolean).join(' ');
}

Davor's avatar
Davor committed
function filterNavigationByDashboardRole(isAdmin: boolean) {
  if (isAdmin) {
    return navigation;
Davor's avatar
Davor committed
  return navigation.filter((item) => !item.requiresAdmin);
const HYDRA_URL = process.env.REACT_APP_HYDRA_PUBLIC_URL;

Valentino's avatar
Valentino committed
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface HeaderProps {}
Luka's avatar
Luka committed

const Header: React.FC<HeaderProps> = () => {
Valentino's avatar
Valentino committed
  const [currentUserModal, setCurrentUserModal] = useState(false);
Davor's avatar
Davor committed
  const { logOut, currentUser, isAdmin } = useAuth();
Luka's avatar
Luka committed

Valentino's avatar
Valentino committed
  const { pathname } = useLocation();

Valentino's avatar
Valentino committed
  const currentUserModalOpen = () => {
    setCurrentUserModal(true);
  };
  const currentUserModalClose = () => setCurrentUserModal(false);

Davor's avatar
Davor committed
  const navigationItems = filterNavigationByDashboardRole(isAdmin);
  const singOutUrl = `${HYDRA_URL}/oauth2/sessions/logout`;

Luka's avatar
Luka committed
  return (
Valentino's avatar
Valentino committed
    <>
      <Disclosure as="nav" className="bg-white shadow relative z-10">
        {({ open }) => (
          <div className="relative">
            <div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
              <div className="relative flex justify-between h-16">
                <div className="absolute inset-y-0 left-0 flex items-center sm:hidden">
                  {/* Mobile menu button */}
                  <Disclosure.Button className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500">
                    <span className="sr-only">Open main menu</span>
                    {open ? (
                      <XIcon className="block h-6 w-6" aria-hidden="true" />
                    ) : (
                      <MenuIcon className="block h-6 w-6" aria-hidden="true" />
                    )}
                  </Disclosure.Button>
                </div>
                <div className="flex-1 flex items-center justify-center sm:items-stretch sm:justify-start">
                  <Link to="/" className="flex-shrink-0 flex items-center">
                    <img className="block lg:hidden" src="/assets/logo-small.svg" alt="Stackspin" />
                    <img className="hidden lg:block" src="/assets/logo.svg" alt="Stackspin" />
                  </Link>
                  <div className="hidden sm:ml-6 sm:flex sm:space-x-8">
                    {/* Current: "border-primary-500 text-gray-900", Default: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700" */}
                    {navigationItems.map((item) => (
Valentino's avatar
Valentino committed
                      <Link
                        key={item.name}
                        to={item.to}
                        className={clsx(
                          'border-primary-50 text-gray-900 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium',
                          {
                            'border-primary-500 text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 text-sm font-medium':
                              pathname.includes(item.to),
                          },
                        )}
                      >
                        {item.name}
                      </Link>
                    ))}
                  </div>
Luka's avatar
Luka committed
                </div>
Valentino's avatar
Valentino committed
                <div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
                  {/* Profile dropdown */}
                  <Menu as="div" className="ml-3 relative">
                    <div>
                      <Menu.Button className="bg-white rounded-full flex text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
                        <span className="sr-only">Open user menu</span>
                        <span className="inline-flex items-center justify-center h-8 w-8 rounded-full bg-gray-500 overflow-hidden">
Valentino Kozinec's avatar
Valentino Kozinec committed
                          <Gravatar email={currentUser?.email || undefined} size={32} rating="pg" protocol="https://" />
Valentino's avatar
Valentino committed
                        </span>
                      </Menu.Button>
                    </div>
                    <Transition
                      as={Fragment}
                      enter="transition ease-out duration-200"
                      enterFrom="transform opacity-0 scale-95"
                      enterTo="transform opacity-100 scale-100"
                      leave="transition ease-in duration-75"
                      leaveFrom="transform opacity-100 scale-100"
                      leaveTo="transform opacity-0 scale-95"
Luka's avatar
Luka committed
                    >
Valentino's avatar
Valentino committed
                      <Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
                        <Menu.Item>
                          {({ active }) => (
                            <a
                              onClick={() => currentUserModalOpen()}
                              className={classNames(
                                active ? 'bg-gray-100 cursor-pointer' : '',
                                'block px-4 py-2 text-sm text-gray-700 cursor-pointer',
                              )}
                            >
                              Configure profile
                            </a>
                          )}
                        </Menu.Item>
                        <Menu.Item>
                          {({ active }) => (
                            <a
                              onClick={() => logOut()}
                              href={singOutUrl}
Valentino's avatar
Valentino committed
                              className={classNames(
                                active ? 'bg-gray-100 cursor-pointer' : '',
                                'block px-4 py-2 text-sm text-gray-700 cursor-pointer',
                              )}
                            >
                              Sign out
                            </a>
                          )}
                        </Menu.Item>
                      </Menu.Items>
                    </Transition>
                  </Menu>
Luka's avatar
Luka committed
                </div>
              </div>
Valentino's avatar
Valentino committed
            </div>

            <Disclosure.Panel className="sm:hidden">
              <div className="pt-2 pb-4 space-y-1">
                {navigation.map((item) => (
                  <Link
                    key={item.name}
                    to={item.to}
                    className={clsx(
                      'border-transparent text-gray-500 hover:text-gray-700 block pl-3 pr-4 py-2 border-l-4 text-base font-medium',
                      {
                        'bg-primary-50 border-primary-400 text-primary-700 block pl-3 pr-4 py-2': pathname.includes(
                          item.to,
                        ),
                      },
                    )}
Luka's avatar
Luka committed
                  >
Valentino's avatar
Valentino committed
                    {item.name}
                  </Link>
                ))}
Luka's avatar
Luka committed
              </div>
Valentino's avatar
Valentino committed
            </Disclosure.Panel>
Luka's avatar
Luka committed
          </div>
Valentino's avatar
Valentino committed
        )}
      </Disclosure>
Luka's avatar
Luka committed

Valentino's avatar
Valentino committed
      <CurrentUserModal open={currentUserModal} onClose={currentUserModalClose} user={currentUser} />
    </>
Luka's avatar
Luka committed
  );
};

export default Header;