sparkletown/sparkle

View on GitHub
src/pages/SpacesDashboard/SpacesDashboard.tsx

Summary

Maintainability
D
2 days
Test Coverage
import React, { useMemo, useState } from "react";
import classNames from "classnames";

import {
  ADMIN_IA_SPACE_CREATE_PARAM_URL,
  ADMIN_IA_WORLD_BASE_URL,
  ADMIN_IA_WORLD_EDIT_PARAM_URL,
  ADMIN_IA_WORLD_TOOLS_PARAM_URL,
  SPACE_TAXON,
  SPACES_TAXON,
} from "settings";

import { isNotPartyMapVenue, isPartyMapVenue } from "types/venues";

import { generateUrl } from "utils/url";
import { SortingOptions, sortVenues } from "utils/venue";

import { useRelatedVenues } from "hooks/useRelatedVenues";
import { useUser } from "hooks/useUser";
import { useWorldBySlug } from "hooks/worlds/useWorldBySlug";
import { useWorldParams } from "hooks/worlds/useWorldParams";

import WithNavigationBar from "components/organisms/WithNavigationBar";

import { AdminSpaceCard } from "components/molecules/AdminSpaceCard";
import { AdminTitle } from "components/molecules/AdminTitle";
import { AdminTitleBar } from "components/molecules/AdminTitleBar";
import { LoadingPage } from "components/molecules/LoadingPage";

import { AdminRestricted } from "components/atoms/AdminRestricted";
import { ButtonNG } from "components/atoms/ButtonNG";
import { SortDropDown } from "components/atoms/SortDropDown";
import { TesterRestricted } from "components/atoms/TesterRestricted";

import "./SpacesDashboard.scss";

export const SpacesDashboard: React.FC = () => {
  const { relatedVenues, isLoading: isLoadingSpaces } = useRelatedVenues({});

  const { userId } = useUser();

  const { worldSlug } = useWorldParams();

  const { world, isLoaded: isWorldLoaded } = useWorldBySlug(worldSlug);

  const isWorldAdmin = userId ? world?.owners.includes(userId) : undefined;

  const venues = useMemo(
    () =>
      world ? relatedVenues.filter((venue) => venue.worldId === world.id) : [],
    [relatedVenues, world]
  );

  const [
    currentSortingOption,
    setCurrentSortingOption,
  ] = useState<SortingOptions>(SortingOptions.az);

  const sortedVenues = useMemo(
    () => sortVenues(venues, currentSortingOption) ?? [],
    [currentSortingOption, venues]
  );

  const renderedPartyVenues = useMemo(
    () =>
      sortedVenues?.filter(isPartyMapVenue).map((venue) => {
        const isSpaceAdmin = userId
          ? venue.owners?.includes(userId)
          : undefined;

        return (
          <AdminSpaceCard
            key={venue.id}
            venue={venue}
            worldSlug={worldSlug}
            isEditable={isWorldAdmin || isSpaceAdmin}
          />
        );
      }),
    [sortedVenues, worldSlug, isWorldAdmin, userId]
  );

  const renderedOtherVenues = useMemo(
    () =>
      sortedVenues?.filter(isNotPartyMapVenue).map((venue) => {
        const isSpaceAdmin = userId
          ? venue.owners?.includes(userId)
          : undefined;

        return (
          <AdminSpaceCard
            key={venue.id}
            venue={venue}
            worldSlug={worldSlug}
            isEditable={isWorldAdmin || isSpaceAdmin}
          />
        );
      }),
    [sortedVenues, worldSlug, isWorldAdmin, userId]
  );

  const hasPartyVenues = renderedPartyVenues.length > 0;
  const hasOtherVenues = renderedOtherVenues.length > 0;

  if (isLoadingSpaces || !isWorldLoaded) {
    return <LoadingPage />;
  }

  return (
    <div className="SpacesDashboard">
      <WithNavigationBar
        variant="internal-scroll"
        title={`${world?.name ?? ""}`}
      >
        <AdminRestricted>
          <AdminTitleBar variant="grid-with-tools">
            <ButtonNG
              variant="secondary"
              isLink
              linkTo={ADMIN_IA_WORLD_BASE_URL}
            >
              Change world
            </ButtonNG>
            <AdminTitle>{world?.name} dashboard</AdminTitle>
            <div>
              {isWorldAdmin && (
                <ButtonNG
                  variant="secondary"
                  isLink
                  linkTo={generateUrl({
                    route: ADMIN_IA_WORLD_EDIT_PARAM_URL,
                    required: ["worldSlug"],
                    params: { worldSlug },
                  })}
                >
                  Settings
                </ButtonNG>
              )}
            </div>
          </AdminTitleBar>

          <TesterRestricted>
            <div className="SpacesDashboard__tools">
              <ButtonNG
                variant="secondary"
                isLink
                linkTo={generateUrl({
                  route: ADMIN_IA_WORLD_TOOLS_PARAM_URL,
                  required: ["worldSlug"],
                  params: { worldSlug },
                })}
              >
                Tools
              </ButtonNG>
            </div>
          </TesterRestricted>

          {hasPartyVenues && <AdminTitle>My map spaces</AdminTitle>}
          <main
            className={classNames("SpacesDashboard__main", {
              "SpacesDashboard__main--empty": !hasPartyVenues,
            })}
          >
            <div className="SpacesDashboard__cards">
              {!hasPartyVenues && (
                <div className="SpacesDashboard__welcome-message">
                  <p>Welcome!</p>
                  <p>Create your first Sparkle {SPACE_TAXON.lower}</p>
                </div>
              )}
              {hasPartyVenues && renderedPartyVenues}
            </div>

            <aside className="SpacesDashboard__aside">
              <SortDropDown
                onClick={setCurrentSortingOption}
                title={`Sort ${SPACES_TAXON.lower}`}
              />
              <ButtonNG
                variant="primary"
                isLink
                linkTo={generateUrl({
                  route: ADMIN_IA_SPACE_CREATE_PARAM_URL,
                  required: ["worldSlug"],
                  params: { worldSlug: world?.slug },
                })}
                disabled={!world?.slug}
              >
                Create a new {SPACE_TAXON.lower}
              </ButtonNG>
            </aside>
          </main>

          {hasOtherVenues && <AdminTitle>My other spaces</AdminTitle>}
          <div
            className={classNames("SpacesDashboard__cards", {
              "SpacesDashboard__cards--empty": !hasOtherVenues,
            })}
          >
            {hasOtherVenues && renderedOtherVenues}
          </div>
        </AdminRestricted>
      </WithNavigationBar>
    </div>
  );
};