diff --git a/src/app/App.js b/src/app/App.js index 398dbf7..cc544d1 100644 --- a/src/app/App.js +++ b/src/app/App.js @@ -30,6 +30,9 @@ const ItemTradesSeekingPage = loadable(() => ); const ModelingPage = loadable(() => import("./ModelingPage")); const PrivacyPolicyPage = loadable(() => import("./PrivacyPolicyPage")); +const SupportPetAppearancesPage = loadable(() => + import("./SupportPetAppearancesPage") +); const UserItemsPage = loadable(() => import("./UserItemsPage")); const UserOutfitsPage = loadable(() => import("./UserOutfitsPage")); const WardrobePage = loadable(() => import("./WardrobePage"), { @@ -156,6 +159,11 @@ function App() { + + + + + diff --git a/src/app/SupportPetAppearancesPage.js b/src/app/SupportPetAppearancesPage.js new file mode 100644 index 0000000..a73ee21 --- /dev/null +++ b/src/app/SupportPetAppearancesPage.js @@ -0,0 +1,126 @@ +import { gql, useQuery } from "@apollo/client"; +import { Box, Flex, Wrap, WrapItem } from "@chakra-ui/layout"; +import { Link } from "react-router-dom"; +import HangerSpinner from "./components/HangerSpinner"; +import { + getValidPoses, + useAllValidPetPoses, +} from "./components/SpeciesColorPicker"; +import { ErrorMessage, Heading1 } from "./util"; +import useSupport from "./WardrobePage/support/useSupport"; + +function SupportPetAppearancesPage() { + const { isSupportUser } = useSupport(); + + if (!isSupportUser) { + return "Sorry, this page is only for Support users!"; + } + + return ( + <> + Support: Pet appearances + + + ); +} + +function UnlabeledPetAppearancesList() { + const { loading, error, speciesColorPairs } = useUnlabeledPetAppearances(); + + return ( + + + These pet appearances have some UNKNOWN poses that need + labeled! Please take a look! + + {loading && ( + + + + )} + {error && {error.message}} + {speciesColorPairs.length > 0 && ( + + {speciesColorPairs.map(({ species, color }) => ( + + + + ))} + + )} + + ); +} + +function SpeciesColorEditorLink({ species, color }) { + return ( + + {color.name} {species.name} + + ); +} + +function useUnlabeledPetAppearances() { + const { + loading: loadingValids, + error: errorValids, + valids, + } = useAllValidPetPoses({ headers: { "Cache-Control": "no-cache" } }); + const { loading: loadingGQL, error: errorGQL, data } = useQuery(gql` + query SupportUnlabeledPetAppearances { + allColors { + id + name + } + + allSpecies { + id + name + } + } + `); + + const loading = loadingValids || loadingGQL; + const error = errorValids || errorGQL; + const speciesColorPairs = + valids && data?.allColors && data?.allSpecies + ? data?.allSpecies + .map((species) => data.allColors.map((color) => ({ species, color }))) + .flat() + .filter(({ species, color }) => { + const poses = getValidPoses(valids, species.id, color.id); + const hasAllStandardPoses = + poses.has("HAPPY_MASC") && + poses.has("HAPPY_FEM") && + poses.has("SAD_MASC") && + poses.has("SAD_FEM") && + poses.has("SICK_MASC") && + poses.has("SICK_FEM"); + const hasAtLeastOneUnknownPose = poses.has("UNKNOWN"); + return !hasAllStandardPoses && hasAtLeastOneUnknownPose; + }) + .sort((a, b) => + `${a.species.name} ${a.color.name}`.localeCompare( + `${b.species.name} ${b.color.name}` + ) + ) + : []; + + return { + loading, + error, + speciesColorPairs, + }; +} + +export default SupportPetAppearancesPage; diff --git a/src/app/components/SpeciesColorPicker.js b/src/app/components/SpeciesColorPicker.js index 9e7a8f3..3cd4c1b 100644 --- a/src/app/components/SpeciesColorPicker.js +++ b/src/app/components/SpeciesColorPicker.js @@ -310,10 +310,10 @@ const SpeciesColorSelect = ({ ); }; -export function useAllValidPetPoses() { +export function useAllValidPetPoses(fetchOptions) { const { loading, error, data: validsBuffer } = useFetch( "/api/validPetPoses", - { responseType: "arrayBuffer" } + { ...fetchOptions, responseType: "arrayBuffer" } ); const valids = React.useMemo( diff --git a/src/app/util.js b/src/app/util.js index 9bc7b6b..b02d0b0 100644 --- a/src/app/util.js +++ b/src/app/util.js @@ -227,7 +227,7 @@ export function usePageTitle(title, { skip = false } = {}) { * * Our limited API is designed to match the `use-http` library! */ -export function useFetch(url, { responseType }) { +export function useFetch(url, { headers = {}, responseType }) { // Just trying to be clear about what you'll get back ^_^` If we want to // fetch non-binary data later, extend this and get something else from res! if (responseType !== "arrayBuffer") { @@ -241,7 +241,7 @@ export function useFetch(url, { responseType }) { React.useEffect(() => { let canceled = false; - fetch(url) + fetch(url, { headers }) .then(async (res) => { if (canceled) { return;