Skip to content

Commit

Permalink
added summary dialog
Browse files Browse the repository at this point in the history
summary as button
  • Loading branch information
voiddp committed Mar 7, 2025
1 parent 9859fb6 commit 0a5a5f1
Show file tree
Hide file tree
Showing 2 changed files with 249 additions and 7 deletions.
36 changes: 29 additions & 7 deletions src/components/planner/depot/MaterialsNeeded.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ItemNeeded from "./ItemNeeded";
import getGoalIngredients from "util/fns/depot/getGoalIngredients";
import DepotItem from "types/depotItem";
import ExportImportDialog from "./ExportImportDialog";
import MaterialsSummaryDialog from "./MaterialsSummaryDialog";
import Board from "components/base/Board";
import canCompleteByCrafting from "util/fns/depot/canCompleteByCrafting";
import { LocalStorageSettings } from "types/localStorageSettings";
Expand Down Expand Up @@ -43,6 +44,8 @@ const MaterialsNeeded = React.memo((props: Props) => {

const [exportImportOpen, setExportImportOpen] = useState<boolean>(false);

const [summaryOpen, setSummaryOpen] = useState<boolean>(false);

const {setAnchorEl, menuProps, menuButtonProps} = useMenu();

const craftToggleTooltips = ["Toggle only craftable materials ON - use with Goals and Filters","Toggle all crafting states ON","Reset all crafting states"];
Expand Down Expand Up @@ -78,6 +81,7 @@ const MaterialsNeeded = React.memo((props: Props) => {
const [ rawValues, setRawValues ] = useState({} as Record<string, DepotItem>);
//states to keep data beetwen renders
const [ savedStates, setSavedStates ] = useState({
goalsMaterials: {} as Record<string, number>,
materialsNeeded: {} as Record<string, number>,
craftableItems: {} as Record<string, boolean>,
sortedMaterialsNeeded: [] as [string, number][],
Expand All @@ -100,6 +104,8 @@ const MaterialsNeeded = React.memo((props: Props) => {
});
}

const goalsMaterials = {...materialsNeeded};

// 3. calculate what ingredients can be fulfilled by crafting
const _depot = { ...depot, ..._rawValues }; // need to hypothetically deduct from stock
const { craftableItems, ingredientToCraftedItemsMapping } = canCompleteByCrafting(
Expand Down Expand Up @@ -132,6 +138,7 @@ const MaterialsNeeded = React.memo((props: Props) => {
});

setSavedStates({
goalsMaterials,
materialsNeeded,
craftableItems,
sortedMaterialsNeeded,
Expand Down Expand Up @@ -359,22 +366,27 @@ const MaterialsNeeded = React.memo((props: Props) => {
<>
<Board
title={
!depotIsUnsaved ?
<Box display="flex" alignItems="center">
<Typography variant="h2">Depot</Typography>
!depotIsUnsaved
? <Box display="flex" alignItems="center" paddingRight={1}>
<Typography variant="h2">Depot</Typography>
</Box>
: (<Box display="flex"
alignItems="center"
: (<Box display="flex" alignItems="center" paddingRight={1}
sx={{ flexFlow: "row nowrap", gap: { xs: 1, md: 3 } }}>
<Typography variant="h2">Depot</Typography>
<Typography variant="h2" sx={{ color: { xs: "primary.main", lg: "unset" } }}>Depot</Typography>
<Tooltip title="on 5s idle: ↑ upload changes to DB ↑">
<Typography variant="h3"
sx={{ color: "#FFD440" }}>unsaved</Typography>
sx={{ color: "primary.main", display: { xs: "none", lg: "unset" } }}>unsaved</Typography>
</Tooltip>
</Box>)
}
TitleAction={
<Box display="flex" gap={1}>
<Button
onClick={() => setSummaryOpen(true)}
variant="contained"
color="primary">
Summary
</Button>
<Tooltip arrow title={craftToggleTooltips[craftToggle]}>
<Button
onClick={handleCraftingToggleAll}
Expand Down Expand Up @@ -442,6 +454,7 @@ const MaterialsNeeded = React.memo((props: Props) => {
xs: "repeat(auto-fill, minmax(96px, 1fr))",
sm: "repeat(auto-fill, minmax(108px, 1fr))",
},
position: "relative",
}}
>
{savedStates.sortedMaterialsNeeded.map(([itemId, needed]) => (
Expand Down Expand Up @@ -481,6 +494,15 @@ const MaterialsNeeded = React.memo((props: Props) => {
}}
goals={goalData}
/>
<MaterialsSummaryDialog
depot={depot}
expOwned={savedStates.expOwned}
goalsMaterials={savedStates.goalsMaterials}
open={summaryOpen}
onClose={() => {
setSummaryOpen(false);
}}
/>
</>
);
});
Expand Down
220 changes: 220 additions & 0 deletions src/components/planner/depot/MaterialsSummaryDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import React, { useCallback, useMemo } from "react";
import {
Dialog,
DialogContent,
DialogTitle,
IconButton,
Slide,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { TransitionProps } from '@mui/material/transitions';
import { Close } from "@mui/icons-material";
import itemsJson from "data/items.json";
import ItemBase from "../depot/ItemBase";
import DepotItem from "types/depotItem";
import { Item } from "types/item";
import canCompleteByCrafting from "util/fns/depot/canCompleteByCrafting";


interface Props {
open: boolean;
onClose: () => void;
depot: Record<string, DepotItem>;
expOwned: number;
goalsMaterials: Record<string, number>;
}

const Transition = React.forwardRef(function Transition(
props: TransitionProps & {
children: React.ReactElement<any, any>;
},
ref: React.Ref<unknown>,
) {
return <Slide direction="up" ref={ref} {...props} />;
});

const MaterialsSummaryDialog = React.memo((props: Props) => {
const { open, onClose, depot, expOwned, goalsMaterials } = props;
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
const isMdUp = useMediaQuery(theme.breakpoints.up("md"));

const calculateSummaryMaterials = useCallback(() => {

//dont do background calculcation
if (!open) return { sortedNeedToFarm: [], sortedNeedToCraft: [] };

const craftTier = 4;
//specific craftables of wrong tiers
const includeCraftIds: string[] = [
"30013", //Orirock Cluster
];
const excludeCraftIds: string[] = [
//"3302" //skill summary 2
];
const excludeCraftNames: string[] = [
" Chip", //any " Chips" and " Chip Packs"
"Data", //module ingredients
];

const localSortId: [string, number][] = [
["LMD", -3],
["EXP", -2],
["Certificate", -1],
//all mats= 0
["Summary", 1],
["Catalyst", 3],
["chip", 2],
["Chip", 2],
["Data", 4],
];

const _depot = { ...depot, EXP: { material_id: "EXP", stock: expOwned } };

const craftingList = Object.keys(depot)
.filter((id) => {
const item: Item = itemsJson[id as keyof typeof itemsJson];
return (
item.tier >= craftTier
&& item.ingredients
&& !excludeCraftIds.includes(id)
&& !excludeCraftNames.some(keyword => item.name.includes(keyword))
)
})
.concat(includeCraftIds);

const _materialsNeeded = { ...goalsMaterials };

//mutates _materialsNeeded
canCompleteByCrafting(_materialsNeeded, depot, craftingList);

const sortedNeedToCraft = Object.entries(_materialsNeeded)
.filter(([id, need]) => craftingList.includes(id) && need - depot[id].stock > 0)
.sort(([itemIdA], [itemIdB]) => {
const itemA = itemsJson[itemIdA as keyof typeof itemsJson];
const itemB = itemsJson[itemIdB as keyof typeof itemsJson];
const itemAlocalSortID = localSortId.find(keyword => itemA.name.includes(keyword[0]))?.[1] ?? 0;
const itemBlocalSortID = localSortId.find(keyword => itemB.name.includes(keyword[0]))?.[1] ?? 0;
return (
(itemAlocalSortID - itemBlocalSortID) ||
(itemA.tier - itemB.tier) ||
(itemB.sortId - itemA.sortId)
)
});

const sortedNeedToFarm = Object.entries(_materialsNeeded)
.filter(([id, need]) => !craftingList.includes(id) && need - depot[id].stock > 0)
.sort(([itemIdA], [itemIdB]) => {
const itemA = itemsJson[itemIdA as keyof typeof itemsJson];
const itemB = itemsJson[itemIdB as keyof typeof itemsJson];
const itemAlocalSortID = localSortId.find(keyword => itemA.name.includes(keyword[0]))?.[1] ?? 0;
const itemBlocalSortID = localSortId.find(keyword => itemB.name.includes(keyword[0]))?.[1] ?? 0;
return (
(itemAlocalSortID - itemBlocalSortID) ||
(_materialsNeeded[itemIdB] - depot[itemIdB].stock) - (_materialsNeeded[itemIdA] - depot[itemIdA].stock)
);
});

return { sortedNeedToFarm, sortedNeedToCraft }
}, [open, goalsMaterials, depot, expOwned]
);

const { sortedNeedToFarm, sortedNeedToCraft } = useMemo(calculateSummaryMaterials, [calculateSummaryMaterials]);

const formatNumber = (num: number) => {
return num < 1000
? num
: num < 1000000
? `${num % 1000 === 0 ? `${num / 1000}` : (num / 1000).toFixed(1)}K`
: `${num % 1000000 === 0 ? `${num / 1000000}` : (num / 1000000).toFixed(2)}M`;
};

const itemBaseSize = isMdUp ? 64 : 56;

const numberCSS = {
component: "span",
sx: {
display: "inline-block",
py: 0.25,
px: 0.5,
lineHeight: 1,
mr: `${itemBaseSize / 16}px`,
mb: `${itemBaseSize / 16}px`,
alignSelf: "end",
justifySelf: "end",
backgroundColor: "background.paper",
zIndex: 1,
fontSize: `${itemBaseSize / 24 + 12}px`,
},
};

return (
<>
<Dialog
open={open}
onClose={onClose}
TransitionComponent={Transition}
fullScreen={fullScreen}
keepMounted fullWidth maxWidth="md">
<DialogTitle
sx={{
display: "flex",
justifyContent: "space-between",
paddingBottom: "12px",
}}
>
<Typography
component="div"
variant="h2"
sx={{
marginLeft: "8px",
paddingTop: "12px",
}}
>
Active goals require:
</Typography>
<IconButton onClick={() => onClose()} sx={{ display: { sm: "none" } }}>
<Close />
</IconButton>
</DialogTitle>
<DialogContent>
{sortedNeedToFarm.length === 0 && sortedNeedToCraft.length === 0 ? (
<>
<Typography variant="h3" p={2} fontWeight="bold">All requirements are met</Typography>
</>
) : null}
{sortedNeedToFarm.length > 0 ? (
<>
<Typography variant="h3" p={1} fontWeight="bold">Farm missing low tier materials (or craft from lower tier) </Typography>
{sortedNeedToFarm.map(([id, need]) => (
<ItemBase key={id} itemId={id} size={itemBaseSize}>
<Typography {...numberCSS}>
{formatNumber(need - depot[id].stock)}
</Typography>
</ItemBase>
))}
</>
) : null}
{sortedNeedToCraft.length > 0 ? (
<>
<Typography variant="h3" p={1} fontWeight="bold">Craft high tier materials</Typography>
{sortedNeedToCraft
.map(([id, need]) => (
<ItemBase key={id} itemId={id} size={itemBaseSize}>
<Typography {...numberCSS}>
{formatNumber(need - depot[id].stock)}
</Typography>
</ItemBase>
))}
</>
) : null}
</DialogContent>
</Dialog>
</>)

});

MaterialsSummaryDialog.displayName = "MaterialsSummaryDialog";
export default MaterialsSummaryDialog;

0 comments on commit 0a5a5f1

Please sign in to comment.