import clsx from "clsx"; import { FC, HTMLAttributes, useEffect, useState } from "react"; import { useSelector } from "react-redux"; import { ApiAdapterUtils, apiCreatorSelectors, CharClass, Choice, ChoiceUtils, ClassDefinitionContract, ClassUtils, Constants, Feat, FeatUtils, } from "@dndbeyond/character-rules-engine"; import { Heading } from "~/subApps/sheet/components/Sidebar/components/Heading"; import { appEnvSelectors } from "~/tools/js/Shared/selectors"; import { AppLoggerUtils } from "~/tools/js/Shared/utils"; import LoadingPlaceholder from "~/tools/js/smartComponents/LoadingPlaceholder"; import { DataLoadingStatusEnum } from "~/tools/js/smartComponents/componentConstants"; import { FeatureChoice } from "./FeatureChoice"; import styles from "./styles.module.css"; export interface FeatureChoicesProps extends HTMLAttributes { choices: Array; showHeading?: boolean; onChoiceChange: ( choiceId: string, type: number, value: any, parentChoiceId: string | null ) => void; collapseDescription?: boolean; charClass?: CharClass; shouldFetch?: boolean; // Optional prop to control fetching behavior } export const FeatureChoices: FC = ({ className, choices, showHeading, onChoiceChange, collapseDescription, charClass, shouldFetch = true, // Default to true if not provided ...props }) => { const isReadonly = useSelector(appEnvSelectors.getIsReadonly); const loadAvailableFeats = useSelector( apiCreatorSelectors.makeLoadAvailableFeats ); const loadAvailableSubclasses = useSelector( apiCreatorSelectors.makeLoadAvailableSubclasses ); const [subclassData, setSubclassData] = useState< Array >([]); const [featsData, setFeatsData] = useState>([]); const [featLoadingStatus, setFeatLoadingStatus] = useState(DataLoadingStatusEnum.NOT_INITIALIZED); const [subclassLoadingStatus, setSubclassLoadingStatus] = useState(DataLoadingStatusEnum.NOT_INITIALIZED); const hasFeatChoice = choices.some( (choice) => ChoiceUtils.getType(choice) === Constants.BuilderChoiceTypeEnum.FEAT_CHOICE_OPTION ); const hasSubclassChoice = choices.some( (choice) => ChoiceUtils.getType(choice) === Constants.BuilderChoiceTypeEnum.SUB_CLASS_OPTION ); useEffect(() => { if ( hasFeatChoice && shouldFetch && featLoadingStatus === DataLoadingStatusEnum.NOT_INITIALIZED ) { setFeatLoadingStatus(DataLoadingStatusEnum.LOADING); loadAvailableFeats() .then((response) => { let featsData: Array = []; const data = ApiAdapterUtils.getResponseData(response); if (data !== null) { featsData = data.map((definition) => FeatUtils.simulateFeat(definition) ); } setFeatsData(featsData); setFeatLoadingStatus(DataLoadingStatusEnum.LOADED); }) .catch(AppLoggerUtils.handleAdhocApiError); } if ( charClass && hasSubclassChoice && shouldFetch && subclassLoadingStatus === DataLoadingStatusEnum.NOT_INITIALIZED ) { setSubclassLoadingStatus(DataLoadingStatusEnum.LOADING); loadAvailableSubclasses(ClassUtils.getId(charClass)) .then((response) => { let subclassData: Array = []; const data = ApiAdapterUtils.getResponseData(response); if (data !== null) { subclassData = data; } setSubclassData(subclassData); setSubclassLoadingStatus(DataLoadingStatusEnum.LOADED); }) .catch(AppLoggerUtils.handleAdhocApiError); } }, [ hasFeatChoice, hasSubclassChoice, subclassLoadingStatus, featLoadingStatus, charClass, loadAvailableFeats, loadAvailableSubclasses, shouldFetch, ]); const isDataLoaded = (): boolean => { if (hasSubclassChoice) { if (subclassLoadingStatus !== DataLoadingStatusEnum.LOADED) { return false; } } if (hasFeatChoice) { if (featLoadingStatus !== DataLoadingStatusEnum.LOADED) { return false; } } return true; }; return !isReadonly && choices.length > 0 ? (
<> {showHeading && ( {`Option${ choices.length > 1 ? "s" : "" }`} )} {isDataLoaded() ? ( choices.map((choice) => ( )) ) : ( )}
) : null; };