import React from "react"; import { matchPath } from "react-router-dom"; import { rulesEngineSelectors } from "@dndbeyond/character-rules-engine/es"; // export interface RouteDefinition { // key: string, // type: string, // path: string, // Component: any, // checkCanAccess: (routeDef:any, state:any) => boolean, // } import config from "~/config"; import { BuilderMethod, NavigationType, RouteKey, SectionKey, } from "~/subApps/builder/constants"; import { ClassChoose } from "~/subApps/builder/routes/ClassChoose"; import { Home } from "~/subApps/builder/routes/Home"; import { SpeciesChoose } from "~/subApps/builder/routes/SpeciesChoose"; import { WhatsNext } from "../../../../subApps/builder/routes/WhatsNext"; import AbilityScoresHelp from "../containers/pages/AbilityScoresHelp"; import AbilityScoresManage from "../containers/pages/AbilityScoresManage"; import ClassHelp from "../containers/pages/ClassHelp"; import ClassesManage from "../containers/pages/ClassesManage"; import DescriptionHelp from "../containers/pages/DescriptionHelp"; import DescriptionManage from "../containers/pages/DescriptionManage"; import EquipmentHelp from "../containers/pages/EquipmentHelp"; import EquipmentManage from "../containers/pages/EquipmentManage"; import HomeHelp from "../containers/pages/HomeHelp"; import SpeciesHelp from "../containers/pages/SpeciesHelp"; import SpeciesManage from "../containers/pages/SpeciesManage"; import { NavigationUtils } from "../utils"; // TODO: remove the use of either `Component` or `getComponent` on the ROUTE_DEFINITIONS and we can get rid of the no-dd-sa rule.. const BASE_PATHNAME = config.basePathname; export const ROUTE_DEFINITIONS = { [RouteKey.HOME_BASIC_INFO]: { key: RouteKey.HOME_BASIC_INFO, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/home/basic`, Component: Home, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.RACE_CHOOSE]: { key: RouteKey.RACE_CHOOSE, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/species/choose`, Component: SpeciesChoose, getComponent: () => , // no-dd-sa:typescript-best-practices/no-dupe-keys checkCanAccess: (routeDef, state) => { if (!NavigationUtils.checkStdBuilderPageRequirements(routeDef, state)) { return false; } if (rulesEngineSelectors.getRace(state) !== null) { return false; } return true; }, }, [RouteKey.RACE_MANAGE]: { key: RouteKey.RACE_MANAGE, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/species/manage`, Component: SpeciesManage, getComponent: () => , // no-dd-sa:typescript-best-practices/no-dupe-keys checkCanAccess: (routeDef, state) => { if (!NavigationUtils.checkStdBuilderPageRequirements(routeDef, state)) { return false; } if (rulesEngineSelectors.getRace(state) === null) { return false; } return true; }, }, [RouteKey.CLASS_CHOOSE]: { key: RouteKey.CLASS_CHOOSE, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/class/choose`, Component: ClassChoose, getComponent: () => , // no-dd-sa:typescript-best-practices/no-dupe-keys checkCanAccess: (routeDef, state) => { if (!NavigationUtils.checkStdBuilderPageRequirements(routeDef, state)) { return false; } if (rulesEngineSelectors.getClasses(state).length > 0) { return false; } return true; }, }, [RouteKey.CLASS_MANAGE]: { key: RouteKey.CLASS_MANAGE, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/class/manage`, Component: ClassesManage, getComponent: () => , // no-dd-sa:typescript-best-practices/no-dupe-keys checkCanAccess: (routeDef, state) => { if (!NavigationUtils.checkStdBuilderPageRequirements(routeDef, state)) { return false; } if (rulesEngineSelectors.getClasses(state).length === 0) { return false; } return true; }, }, [RouteKey.ABILITY_SCORES_MANAGE]: { key: RouteKey.ABILITY_SCORES_MANAGE, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/ability-scores/manage`, Component: AbilityScoresManage, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.DESCRIPTION_MANAGE]: { key: RouteKey.DESCRIPTION_MANAGE, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/description/manage`, Component: DescriptionManage, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.EQUIPMENT_MANAGE]: { key: RouteKey.EQUIPMENT_MANAGE, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/equipment/manage`, Component: EquipmentManage, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.WHATS_NEXT]: { key: RouteKey.WHATS_NEXT, type: NavigationType.Page, path: `${BASE_PATHNAME}/:characterId/builder/whats-next`, Component: WhatsNext, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.HOME_HELP]: { key: RouteKey.HOME_HELP, type: NavigationType.HelpPage, path: `${BASE_PATHNAME}/:characterId/builder/home/help`, Component: HomeHelp, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.RACE_HELP]: { key: RouteKey.RACE_HELP, type: NavigationType.HelpPage, path: `${BASE_PATHNAME}/:characterId/builder/species/help`, Component: SpeciesHelp, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.CLASS_HELP]: { key: RouteKey.CLASS_HELP, type: NavigationType.HelpPage, path: `${BASE_PATHNAME}/:characterId/builder/class/help`, Component: ClassHelp, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.ABILITY_SCORES_HELP]: { key: RouteKey.ABILITY_SCORES_HELP, type: NavigationType.HelpPage, path: `${BASE_PATHNAME}/:characterId/builder/ability-scores/help`, Component: AbilityScoresHelp, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.DESCRIPTION_HELP]: { key: RouteKey.DESCRIPTION_HELP, type: NavigationType.HelpPage, path: `${BASE_PATHNAME}/:characterId/builder/description/help`, Component: DescriptionHelp, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, [RouteKey.EQUIPMENT_HELP]: { key: RouteKey.EQUIPMENT_HELP, type: NavigationType.HelpPage, path: `${BASE_PATHNAME}/:characterId/builder/equipment/help`, Component: EquipmentHelp, // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => , checkCanAccess: (routeDef, state) => NavigationUtils.checkStdBuilderPageRequirements(routeDef, state), }, }; export interface RouteDef { key: string; // get the thing as an enum type: string; path: string; Component: React.ComponentType; // no-dd-sa:typescript-best-practices/no-dupe-keys getComponent: () => React.ReactElement; checkCanAccess?: (routeDef: RouteDef, state: any) => boolean; } export const getAllRouterRoutes = () => { return (Object.values(ROUTE_DEFINITIONS) as RouteDef[]).map((routeDef) => ({ element: , path: routeDef.path, routeDef, })); }; const addSearchParamsToRouteDef = (routeDef: RouteDef): RouteDef => { let params = ""; if (typeof window !== "undefined") { // If we ever need to know specific search params, we can use URLSearchParams here instead. params = window.location.search; } return { ...routeDef, path: `${routeDef.path}${params}`, }; }; export const getRouteDef = (routeKey) => ROUTE_DEFINITIONS[routeKey]; export const getRouteDefPath = (routeKey) => { const basePath = getRouteDef(routeKey); return addSearchParamsToRouteDef(basePath).path; }; export const getRouteDefType = (routeKey) => getRouteDef(routeKey).type; export const NAVIGATION_DEFINITIONS = { [BuilderMethod.STEP_BY_STEP]: [ { type: NavigationType.Section, key: SectionKey.HOME, getLabel: () => "Home", routes: [ getRouteDef(RouteKey.HOME_HELP), getRouteDef(RouteKey.HOME_BASIC_INFO), ], }, { type: NavigationType.Section, key: SectionKey.CLASS, getLabel: () => "1. Class", routes: [ getRouteDef(RouteKey.CLASS_HELP), getRouteDef(RouteKey.CLASS_CHOOSE), getRouteDef(RouteKey.CLASS_MANAGE), ], }, { type: NavigationType.Section, key: SectionKey.DESCRIPTION, getLabel: () => "2. Background", routes: [ getRouteDef(RouteKey.DESCRIPTION_HELP), getRouteDef(RouteKey.DESCRIPTION_MANAGE), ], }, { type: NavigationType.Section, key: SectionKey.RACE, getLabel: () => "3. Species", routes: [ getRouteDef(RouteKey.RACE_HELP), getRouteDef(RouteKey.RACE_CHOOSE), getRouteDef(RouteKey.RACE_MANAGE), ], }, { type: NavigationType.Section, key: SectionKey.ABILITY_SCORES, getLabel: () => "4. Abilities", routes: [ getRouteDef(RouteKey.ABILITY_SCORES_HELP), getRouteDef(RouteKey.ABILITY_SCORES_MANAGE), ], }, { type: NavigationType.Section, key: SectionKey.EQUIPMENT, getLabel: () => "5. Equipment", routes: [ getRouteDef(RouteKey.EQUIPMENT_HELP), getRouteDef(RouteKey.EQUIPMENT_MANAGE), ], }, { type: NavigationType.Section, key: SectionKey.WHATS_NEXT, getLabel: () => "What's Next", routes: [getRouteDef(RouteKey.WHATS_NEXT)], }, ], }; export function checkIsRouteInSection(testRouteDef, sectionRoutes) { if (testRouteDef && sectionRoutes) { return !!sectionRoutes.filter( (routeDef) => routeDef.key === testRouteDef.key ).length; } return false; } export function getNavigationSections(builderMethod, currentRouteDef) { const navConfig = getNavBuilderConfig(builderMethod); return navConfig.map((section) => ({ ...section, active: checkIsRouteInSection(currentRouteDef, section.routes), })); } export function getNavBuilderConfig(builderMethod) { if (builderMethod) { const config = NAVIGATION_DEFINITIONS[builderMethod]; // Add search params to all route paths in the config return config.map((section) => ({ ...section, routes: section.routes.map((routeDef) => addSearchParamsToRouteDef(routeDef) ), })); } else { return []; } } export function getCurrentRouteDef(currentPath) { const routes = getAllRouterRoutes(); let matchedRouteDef: RouteDef | null = null; routes.forEach((route) => { const match = matchPath(route.path, currentPath); if (match) { matchedRouteDef = route.routeDef; } }); return matchedRouteDef; }