Source: components/dashboard.js

"use client";

import React, { cloneElement, useContext } from "react";
import {
  Avatar,
  Button,
  Box,
  Nav,
  Text,
  Sidebar as SidebarBase,
  Header,
  Page,
  PageContent,
  ResponsiveContext,
  Grommet,
  grommet,
} from "grommet";
import { Logout, DocumentTest, Contract } from "grommet-icons";
import { BRAND_HEX } from "../lib/config";
import { Logo } from "./logo";
import { usePathname, useRouter } from "next/navigation";
import { signOut } from "next-auth/react";

const MOBILE_SIDEBAR_WIDTH = "4.5rem";
const MOBILE_HEADER_HEIGHT = "3.95rem";
const DESKTOP_SIDEBAR_WIDTH = "15rem";
const DESKTOP_HEADER_HEIGHT = "4.688rem";

const SidebarHeader = (props) => {
  const { serverSession, size, deviceType } = props;

  const isSmall = deviceType === "mobile" || size === "small";

  return (
    <Box
      align="center"
      gap="small"
      direction="row"
      margin={{ bottom: "xxsmall" }}
      justify="center"
    >
      {serverSession?.user?.image ? (
        <Avatar src={serverSession.user.image} />
      ) : null}
      {isSmall ? null : <Text>{serverSession?.user?.name}</Text>}
    </Box>
  );
};

const SidebarButton = ({ icon, label, selected, ...rest }) => (
  <Button
    gap="medium"
    justify="start"
    fill
    icon={cloneElement(icon, {
      color: selected ? "white" : undefined,
    })}
    label={label}
    plain
    {...rest}
    style={{
      ...rest.style,
      whiteSpace: "nowrap",
      height: "3rem",
      paddingLeft: "2rem",
      flex: "unset",
      background: selected ? BRAND_HEX : "transparent",
      color: selected ? "white" : "unset",
    }}
  />
);

const SidebarFooter = (props) => {
  const { size, deviceType } = props;

  const { push } = useRouter();
  const pathname = usePathname();

  const apiManagement = "/api-management";

  if (deviceType === "mobile" || size === "small") {
    return (
      <Nav gap="small">
        <Button
          icon={<Contract />}
          hoverIndicator={pathname !== apiManagement}
          primary={pathname === apiManagement}
          onClick={() => push(apiManagement)}
        />
        <Button
          icon={<Logout />}
          hoverIndicator
          onClick={async () => {
            await signOut({
              redirect: true,
            });
          }}
        />
      </Nav>
    );
  }

  return (
    <Nav>
      <SidebarButton
        icon={<Contract />}
        label={"API Management"}
        selected={pathname === apiManagement}
        onClick={() => push(apiManagement)}
      />
      <SidebarButton
        icon={<Logout />}
        label={"Logout"}
        onClick={async () => {
          await signOut({
            redirect: true,
          });
        }}
      />
    </Nav>
  );
};

/**
 * Navigation items are organized by
 * usage order (data from G.A.)
 */
const MainNavigation = (props) => {
  const { size, deviceType } = props;
  const { push } = useRouter();
  const pathname = usePathname();

  const completions = "/completions/";

  const matchCompletions = pathname.includes(completions);

  if (deviceType === "mobile" || size === "small") {
    return (
      <Nav gap="small">
        <Button
          icon={<DocumentTest />}
          hoverIndicator={!matchCompletions}
          primary={matchCompletions}
          onClick={() => push(completions + "pending")}
        />
      </Nav>
    );
  }

  return (
    <Nav gap="medium" fill="vertical" responsive={false}>
      <SidebarButton
        icon={<DocumentTest />}
        label={"Completions"}
        selected={matchCompletions}
        onClick={() => push(completions + "pending")}
      />
    </Nav>
  );
};

function MobileSidebar(props) {
  const { serverSession, size } = props;

  return (
    <SidebarBase
      elevation="large"
      responsive={false}
      background="light-1"
      header={
        <SidebarHeader
          serverSession={serverSession}
          size={size}
          deviceType={"mobile"}
        />
      }
      footer={<SidebarFooter size={size} deviceType="mobile" />}
      style={{
        top: MOBILE_HEADER_HEIGHT,
        height: `calc(100vh - ${MOBILE_HEADER_HEIGHT})`,
        minHeight: `calc(100vh - ${MOBILE_HEADER_HEIGHT})`,
        position: "fixed",
        minWidth: MOBILE_SIDEBAR_WIDTH,
        maxWidth: MOBILE_SIDEBAR_WIDTH,
        borderRight: `1px solid ${BRAND_HEX}`,
        // Trick to make the box-shadow from the sidebar and header look good
        zIndex: "11",
      }}
    >
      <MainNavigation
        size={size}
        serverSession={serverSession}
        deviceType={"mobile"}
      />
    </SidebarBase>
  );
}

function DesktopSidebar(props) {
  const { serverSession, size } = props;

  return (
    <SidebarBase
      responsive={false}
      elevation="large"
      header={
        <SidebarHeader
          serverSession={serverSession}
          size={size}
          deviceType={"desktop"}
        />
      }
      footer={<SidebarFooter deviceType="desktop" />}
      pad={{ left: "unset", right: "unset", vertical: "large" }}
      background="light-1"
      style={{
        position: "fixed",
        top: DESKTOP_HEADER_HEIGHT,
        height: `calc(100vh - ${DESKTOP_HEADER_HEIGHT})`,
        minHeight: `calc(100vh - ${DESKTOP_HEADER_HEIGHT})`,
        minWidth: DESKTOP_SIDEBAR_WIDTH,
        maxWidth: DESKTOP_SIDEBAR_WIDTH,
        borderRight: `1px solid ${BRAND_HEX}`,
        // Trick to make the box-shadow from the sidebar and header look good
        zIndex: "11",
      }}
    >
      <MainNavigation size={size} serverSession={serverSession} />
    </SidebarBase>
  );
}

function Sidebar(props) {
  const { serverSession, size, deviceType } = props;

  if (deviceType === "mobile" || size === "small") {
    return (
      <MobileSidebar
        serverSession={serverSession}
        size={size}
        deviceType={deviceType}
      />
    );
  }

  return (
    <DesktopSidebar
      serverSession={serverSession}
      size={size}
      deviceType={deviceType}
    />
  );
}

export default function Dashboard(props) {
  const { serverSession, children, deviceType } = props;
  const size = useContext(ResponsiveContext);

  const isSmall = deviceType === "mobile" || size === "small";

  return (
    <>
      <Grommet
        full
        theme={grommet}
        style={{
          overflow: "hidden",
        }}
      >
        <Header
          pad="small"
          style={{
            position: "fixed",
            width: "100vw",
            borderBottom: `1px solid ${BRAND_HEX}`,
            zIndex: "10",
          }}
          background="light-1"
          elevation="large"
        >
          <Box
            style={{
              display: "flex",
              width: "100%",
              flexDirection: "row",
              justifyContent: "space-between",
              margin: "auto",
              maxWidth: "96rem",
              // Trick to make the box-shadow from the sidebar and header look good
              zIndex: "9",
            }}
          >
            <Logo noTitle />
          </Box>
        </Header>
        <Page background="background-front" kind="full">
          <Box direction="row" height={{ min: "100%" }}>
            <Sidebar
              serverSession={serverSession}
              size={size}
              deviceType={deviceType}
            />
            <PageContent
              pad="medium"
              style={{
                width: isSmall
                  ? `calc(100vw - ${MOBILE_SIDEBAR_WIDTH})`
                  : `calc(100vw - ${DESKTOP_SIDEBAR_WIDTH})`,
                minHeight: isSmall
                  ? `calc(100vh - ${MOBILE_HEADER_HEIGHT})`
                  : `calc(100vh - ${DESKTOP_HEADER_HEIGHT})`,
                maxHeight: isSmall
                  ? `calc(100vh - ${MOBILE_HEADER_HEIGHT})`
                  : `calc(100vh - ${DESKTOP_HEADER_HEIGHT})`,
                minWidth: "0px",
                marginTop: isSmall
                  ? MOBILE_HEADER_HEIGHT
                  : DESKTOP_HEADER_HEIGHT,
                marginLeft: isSmall
                  ? MOBILE_SIDEBAR_WIDTH
                  : DESKTOP_SIDEBAR_WIDTH,
                overflow: "auto",
              }}
            >
              {children}
            </PageContent>
          </Box>
        </Page>
      </Grommet>
    </>
  );
}