New source found from dndbeyond.com
This commit is contained in:
+265
@@ -0,0 +1,265 @@
|
||||
import clsx from "clsx";
|
||||
import { sortBy } from "lodash";
|
||||
import { FC, HTMLAttributes, useContext, useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
characterActions,
|
||||
CharClass,
|
||||
ClassFeature,
|
||||
ClassFeatureUtils,
|
||||
ClassUtils,
|
||||
ContainerManager,
|
||||
HelperUtils,
|
||||
} from "@dndbeyond/character-rules-engine";
|
||||
import ArrowRightIcon from "@dndbeyond/fontawesome-cache/svgs/solid/arrow-right.svg";
|
||||
|
||||
import { Button } from "~/components/Button";
|
||||
import { FeatureChoices } from "~/components/FeatureChoices";
|
||||
import { useSidebar } from "~/contexts/Sidebar";
|
||||
import { useCharacterEngine } from "~/hooks/useCharacterEngine";
|
||||
import { useDispatch } from "~/hooks/useDispatch";
|
||||
import DataLoadingStatusEnum from "~/tools/js/Shared/constants/DataLoadingStatusEnum";
|
||||
import { InventoryManagerContext } from "~/tools/js/Shared/managers/InventoryManagerContext";
|
||||
import { AppLoggerUtils, PaneIdentifierUtils } from "~/tools/js/Shared/utils";
|
||||
import Collapsible from "~/tools/js/smartComponents/Collapsible";
|
||||
import LoadingPlaceholder from "~/tools/js/smartComponents/LoadingPlaceholder";
|
||||
|
||||
import { Header } from "../../components/Header";
|
||||
import { Heading } from "../../components/Heading";
|
||||
import { PaneComponentEnum } from "../../types";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
export interface ItemPlansManagePaneProps
|
||||
extends HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const ItemPlansManagePane: FC<ItemPlansManagePaneProps> = ({
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { classes } = useCharacterEngine();
|
||||
const {
|
||||
pane: { paneHistoryPush },
|
||||
} = useSidebar();
|
||||
const { inventoryManager } = useContext(InventoryManagerContext);
|
||||
|
||||
const [itemShoppe, setItemShoppe] = useState<ContainerManager | null>(null);
|
||||
const [itemsLoadingStatus, setItemsLoadingStatus] =
|
||||
useState<DataLoadingStatusEnum>(DataLoadingStatusEnum.NOT_INITIALIZED);
|
||||
|
||||
useEffect(() => {
|
||||
const abortController = new AbortController();
|
||||
|
||||
if (
|
||||
itemShoppe === null &&
|
||||
itemsLoadingStatus !== DataLoadingStatusEnum.LOADED
|
||||
) {
|
||||
if (inventoryManager) {
|
||||
setItemsLoadingStatus(DataLoadingStatusEnum.LOADING);
|
||||
|
||||
inventoryManager
|
||||
.getInventoryShoppe({
|
||||
onSuccess: (shoppeContainer: ContainerManager) => {
|
||||
setItemShoppe(shoppeContainer);
|
||||
setItemsLoadingStatus(DataLoadingStatusEnum.LOADED);
|
||||
},
|
||||
additionalApiConfig: {
|
||||
signal: abortController.signal,
|
||||
},
|
||||
})
|
||||
.catch((e) => {
|
||||
if (abortController.signal.aborted) return;
|
||||
AppLoggerUtils.handleAdhocApiError(e);
|
||||
});
|
||||
} else {
|
||||
setItemsLoadingStatus(DataLoadingStatusEnum.LOADED);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [itemsLoadingStatus, inventoryManager, itemShoppe]);
|
||||
|
||||
const handleClassFeatureClick = (
|
||||
charClass: CharClass,
|
||||
classFeature: ClassFeature
|
||||
): void => {
|
||||
paneHistoryPush(
|
||||
PaneComponentEnum.CLASS_FEATURE_DETAIL,
|
||||
PaneIdentifierUtils.generateClassFeature(
|
||||
ClassFeatureUtils.getId(classFeature),
|
||||
ClassUtils.getMappingId(charClass)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleChoiceChange = (
|
||||
charClass: CharClass,
|
||||
classFeature: ClassFeature,
|
||||
id: string,
|
||||
type: number,
|
||||
value: any,
|
||||
parentChoiceId: string | null
|
||||
): void => {
|
||||
if (!charClass || !classFeature) {
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
characterActions.classFeatureChoiceSetRequest(
|
||||
ClassUtils.getActiveId(charClass),
|
||||
ClassFeatureUtils.getId(classFeature),
|
||||
type,
|
||||
id,
|
||||
HelperUtils.parseInputInt(value),
|
||||
parentChoiceId,
|
||||
ClassFeatureUtils.getHasItemMappings(classFeature)
|
||||
? inventoryManager.handleAcceptOnSuccess(true)
|
||||
: undefined
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx([styles.itemPlansManagePane, className])} {...props}>
|
||||
<Header>Manage Magic Item Plans</Header>
|
||||
{classes.map((charClass) => {
|
||||
const subclass = ClassUtils.getSubclass(charClass);
|
||||
const subclassId = subclass?.id;
|
||||
const subclassName = subclass?.name;
|
||||
const baseClassId = ClassUtils.getId(charClass);
|
||||
|
||||
const activeFeaturesWithItemMappings =
|
||||
ClassUtils.getActiveClassFeatures(charClass).filter(
|
||||
ClassFeatureUtils.getHasItemMappings
|
||||
);
|
||||
|
||||
const baseClassFeatures: ClassFeature[] = [];
|
||||
const subClassFeatures: ClassFeature[] = [];
|
||||
activeFeaturesWithItemMappings.forEach((feature) => {
|
||||
const featureClassId = ClassFeatureUtils.getClassId(feature);
|
||||
if (featureClassId === baseClassId) {
|
||||
baseClassFeatures.push(feature);
|
||||
} else if (featureClassId === subclassId) {
|
||||
subClassFeatures.push(feature);
|
||||
}
|
||||
});
|
||||
|
||||
const sortedBaseFeatures = sortBy(baseClassFeatures, [
|
||||
(feature) => ClassFeatureUtils.getName(feature),
|
||||
]);
|
||||
|
||||
const sortedSubclassFeatures = sortBy(subClassFeatures, [
|
||||
(feature) => ClassFeatureUtils.getName(feature),
|
||||
]);
|
||||
|
||||
return (
|
||||
<div key={ClassUtils.getMappingId(charClass)}>
|
||||
<Heading>{ClassUtils.getName(charClass)}</Heading>
|
||||
{itemsLoadingStatus !== DataLoadingStatusEnum.LOADED && (
|
||||
<LoadingPlaceholder />
|
||||
)}
|
||||
{itemsLoadingStatus === DataLoadingStatusEnum.LOADED &&
|
||||
sortedBaseFeatures.map((feature) => {
|
||||
return (
|
||||
<Collapsible
|
||||
className={styles.classFeature}
|
||||
key={ClassFeatureUtils.getUniqueKey(feature)}
|
||||
header={`${ClassFeatureUtils.getName(feature)} (${
|
||||
ClassFeatureUtils.getChoices(feature).length
|
||||
})`}
|
||||
initiallyCollapsed={false}
|
||||
>
|
||||
<div className={styles.buttonContainer}>
|
||||
<Button
|
||||
themed
|
||||
size="xx-small"
|
||||
className={styles.viewDetailsButton}
|
||||
onClick={() =>
|
||||
handleClassFeatureClick(charClass, feature)
|
||||
}
|
||||
>
|
||||
View {ClassFeatureUtils.getName(feature)} Details
|
||||
<ArrowRightIcon className={styles.arrowIcon} />
|
||||
</Button>
|
||||
</div>
|
||||
<FeatureChoices
|
||||
className={styles.featureChoices}
|
||||
choices={ClassFeatureUtils.getChoices(feature)}
|
||||
charClass={charClass}
|
||||
classFeature={feature}
|
||||
onChoiceChange={(id, type, value, parentChoiceId) =>
|
||||
handleChoiceChange(
|
||||
charClass,
|
||||
feature,
|
||||
id,
|
||||
type,
|
||||
value,
|
||||
parentChoiceId
|
||||
)
|
||||
}
|
||||
collapseDescription={true}
|
||||
shouldFetch={false}
|
||||
itemShoppe={itemShoppe}
|
||||
/>
|
||||
</Collapsible>
|
||||
);
|
||||
})}
|
||||
{itemsLoadingStatus === DataLoadingStatusEnum.LOADED &&
|
||||
sortedSubclassFeatures.length > 0 && (
|
||||
<>
|
||||
<Heading>{subclassName}</Heading>
|
||||
{sortedSubclassFeatures.map((feature) => {
|
||||
return (
|
||||
<Collapsible
|
||||
className={styles.classFeature}
|
||||
key={ClassFeatureUtils.getUniqueKey(feature)}
|
||||
header={`${ClassFeatureUtils.getName(feature)} (${
|
||||
ClassFeatureUtils.getChoices(feature).length
|
||||
})`}
|
||||
initiallyCollapsed={false}
|
||||
>
|
||||
<div className={styles.buttonContainer}>
|
||||
<Button
|
||||
themed
|
||||
size="xx-small"
|
||||
className={styles.viewDetailsButton}
|
||||
onClick={() =>
|
||||
handleClassFeatureClick(charClass, feature)
|
||||
}
|
||||
>
|
||||
View {ClassFeatureUtils.getName(feature)} Details
|
||||
<ArrowRightIcon className={styles.arrowIcon} />
|
||||
</Button>
|
||||
</div>
|
||||
<FeatureChoices
|
||||
className={styles.featureChoices}
|
||||
choices={ClassFeatureUtils.getChoices(feature)}
|
||||
charClass={charClass}
|
||||
classFeature={feature}
|
||||
onChoiceChange={(id, type, value, parentChoiceId) =>
|
||||
handleChoiceChange(
|
||||
charClass,
|
||||
feature,
|
||||
id,
|
||||
type,
|
||||
value,
|
||||
parentChoiceId
|
||||
)
|
||||
}
|
||||
collapseDescription={true}
|
||||
shouldFetch={false}
|
||||
itemShoppe={itemShoppe}
|
||||
/>
|
||||
</Collapsible>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user