Source: components/dashboard.js

import React, { cloneElement, useContext } from "react";
import {
  Avatar,
  Button,
  Box,
  Nav,
  Text,
  Sidebar as SidebarBase,
  Header,
  Page,
  PageContent,
  ResponsiveContext,
} from "grommet";
import {
  Book,
  Logout,
  Magic,
  Star,
  UserSettings,
  BarChart,
  Inbox,
} from "grommet-icons";
import { BRAND_HEX } from "../lib/config";
import { Logo } from "./logo";
import PageActions from "./page-actions";
import { useRouter } from "next/router";
import { signOut } from "next-auth/react";
import { useTranslation } from "next-i18next";

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"
    >
      <Avatar src={serverSession.user.image} />
      {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: "3rem",
      flex: "unset",
      background: selected ? BRAND_HEX : "transparent",
      color: selected ? "white" : "unset",
    }}
  />
);

const SidebarFooter = (props) => {
  const { size, deviceType, t } = props;
  const { pathname: rawPathname, push, locale } = useRouter();

  const account = `/${locale}/account`;
  const callback = `/${locale}`;
  const pathname = `/${locale}${rawPathname}`;

  if (deviceType === "mobile" || size === "small") {
    return (
      <Nav gap="small">
        <Button
          icon={<UserSettings />}
          hoverIndicator={pathname !== account}
          primary={pathname === account}
          onClick={() => push(account)}
        />
        <Button
          icon={<Logout />}
          hoverIndicator
          onClick={async () => {
            const data = await signOut({
              redirect: false,
              callbackUrl: callback,
            });

            push(data.url);
          }}
        />
      </Nav>
    );
  }

  return (
    <Nav>
      <SidebarButton
        icon={<UserSettings />}
        label={t("my-account")}
        selected={pathname === account}
        onClick={() => push(account)}
      />
      <SidebarButton
        icon={<Logout />}
        label={t("logout")}
        onClick={async () => {
          const data = await signOut({
            redirect: false,
            callbackUrl: callback,
          });

          push(data.url);
        }}
      />
    </Nav>
  );
};

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

  const dreams = `/${locale}/dreams`;
  const myDreams = `/${locale}/my-dreams`;
  const insights = `/${locale}/insights`;
  const inbox = `/${locale}/inbox`;
  const savedDreams = `/${locale}/saved-dreams`;
  const pathname = `/${locale}${rawPathname}`;

  if (deviceType === "mobile" || size === "small") {
    return (
      <Nav gap="small">
        <Button
          icon={<Magic />}
          hoverIndicator={pathname !== dreams}
          primary={pathname === dreams}
          onClick={() => push(dreams)}
        />
        <Button
          icon={<Book />}
          hoverIndicator={pathname !== myDreams}
          primary={pathname === myDreams}
          onClick={() => push(myDreams)}
        />
        <Button
          icon={<BarChart />}
          hoverIndicator={pathname !== insights}
          primary={pathname === insights}
          onClick={() => push(insights)}
        />
        <Button
          icon={<Inbox />}
          hoverIndicator={pathname !== inbox}
          primary={pathname === inbox}
          onClick={() => push(inbox)}
          badge={
            serverSession?.inboxCount
              ? {
                  value: serverSession?.inboxCount,
                  background: {
                    color: pathname === inbox ? "#6FFFB0" : BRAND_HEX,
                  },
                }
              : 0
          }
        />
        <Button
          icon={<Star />}
          hoverIndicator={pathname !== savedDreams}
          primary={pathname === savedDreams}
          onClick={() => push(savedDreams)}
        />
      </Nav>
    );
  }

  return (
    <Nav gap="medium" fill="vertical" responsive={false}>
      <SidebarButton
        icon={<Magic />}
        label={t("discover")}
        selected={pathname === dreams}
        onClick={() => push(dreams)}
      />
      <SidebarButton
        icon={<Book />}
        label={t("my-dreams")}
        selected={pathname === myDreams}
        onClick={() => push(myDreams)}
      />
      <SidebarButton
        icon={<BarChart />}
        label={t("insights")}
        selected={pathname === insights}
        onClick={() => push(insights)}
      />
      <SidebarButton
        icon={
          <Button
            as="span"
            plain
            icon={<Inbox color={pathname === inbox ? "white" : undefined} />}
            badge={
              serverSession?.inboxCount
                ? {
                    value: serverSession?.inboxCount,
                    background: {
                      color: pathname === inbox ? "#6FFFB0" : BRAND_HEX,
                    },
                  }
                : 0
            }
          />
        }
        label={t("inbox")}
        selected={pathname === inbox}
        onClick={() => push(inbox)}
      />
      <SidebarButton
        icon={<Star />}
        label={t("saved")}
        selected={pathname === savedDreams}
        onClick={() => push(savedDreams)}
      />
    </Nav>
  );
};

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

  return (
    <SidebarBase
      elevation="large"
      responsive={false}
      background="light-1"
      header={
        <SidebarHeader
          serverSession={serverSession}
          size={size}
          deviceType={"mobile"}
          t={t}
        />
      }
      footer={<SidebarFooter size={size} deviceType="mobile" t={t} />}
      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"}
        t={t}
      />
    </SidebarBase>
  );
}

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

  return (
    <SidebarBase
      responsive={false}
      elevation="large"
      header={
        <SidebarHeader
          serverSession={serverSession}
          size={size}
          deviceType={"desktop"}
          t={t}
        />
      }
      footer={<SidebarFooter deviceType="desktop" t={t} />}
      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} t={t} />
    </SidebarBase>
  );
}

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

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

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

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

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

  return (
    <>
      <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 />
          <PageActions serverSession={serverSession} />
        </Box>
      </Header>
      <Page background="background-front" kind="full">
        <Box direction="row" height={{ min: "100%" }}>
          <Sidebar
            serverSession={serverSession}
            size={size}
            deviceType={deviceType}
            t={t}
          />
          <PageContent
            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})`,
              minWidth: "0px",
              marginTop: isSmall ? MOBILE_HEADER_HEIGHT : DESKTOP_HEADER_HEIGHT,
              marginLeft: isSmall
                ? MOBILE_SIDEBAR_WIDTH
                : DESKTOP_SIDEBAR_WIDTH,
            }}
          >
            {children}
          </PageContent>
        </Box>
      </Page>
    </>
  );
}