mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-11 12:30:25 +08:00
Merge pull request #801 from porink0424/fix/recoil-to-reactquery
Partially replace recoil with react-query
This commit is contained in:
@@ -3,7 +3,6 @@ import { useSnackbar } from "notistack"
|
||||
import {
|
||||
getStudyDetailAPI,
|
||||
getStudySummariesAPI,
|
||||
getParamImportances,
|
||||
createNewStudyAPI,
|
||||
deleteStudyAPI,
|
||||
saveStudyNoteAPI,
|
||||
@@ -25,7 +24,6 @@ import {
|
||||
import {
|
||||
studyDetailsState,
|
||||
studySummariesState,
|
||||
paramImportanceState,
|
||||
isFileUploading,
|
||||
artifactIsAvailable,
|
||||
plotlypyIsAvailableState,
|
||||
@@ -43,8 +41,6 @@ export const actionCreator = () => {
|
||||
const [studyDetails, setStudyDetails] =
|
||||
useRecoilState<StudyDetails>(studyDetailsState)
|
||||
const setReloadInterval = useSetRecoilState<number>(reloadIntervalState)
|
||||
const [paramImportance, setParamImportance] =
|
||||
useRecoilState<StudyParamImportance>(paramImportanceState)
|
||||
const setUploading = useSetRecoilState<boolean>(isFileUploading)
|
||||
const setTrialsUpdating = useSetRecoilState(trialsUpdatingState)
|
||||
const setArtifactIsAvailable = useSetRecoilState<boolean>(artifactIsAvailable)
|
||||
@@ -207,15 +203,6 @@ export const actionCreator = () => {
|
||||
setStudyDetailState(studyId, newStudy)
|
||||
}
|
||||
|
||||
const setStudyParamImportanceState = (
|
||||
studyId: number,
|
||||
importance: ParamImportance[][]
|
||||
) => {
|
||||
const newVal = Object.assign({}, paramImportance)
|
||||
newVal[studyId] = importance
|
||||
setParamImportance(newVal)
|
||||
}
|
||||
|
||||
const updateAPIMeta = () => {
|
||||
getMetaInfoAPI().then((r) => {
|
||||
setArtifactIsAvailable(r.artifact_is_available)
|
||||
@@ -273,22 +260,6 @@ export const actionCreator = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const updateParamImportance = (studyId: number) => {
|
||||
getParamImportances(studyId)
|
||||
.then((importance) => {
|
||||
setStudyParamImportanceState(studyId, importance)
|
||||
})
|
||||
.catch((err) => {
|
||||
const reason = err.response?.data.reason
|
||||
enqueueSnackbar(
|
||||
`Failed to load hyperparameter importance (reason=${reason})`,
|
||||
{
|
||||
variant: "error",
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const createNewStudy = (studyName: string, directions: StudyDirection[]) => {
|
||||
createNewStudyAPI(studyName, directions)
|
||||
.then((study_summary) => {
|
||||
@@ -714,7 +685,6 @@ export const actionCreator = () => {
|
||||
updateAPIMeta,
|
||||
updateStudyDetail,
|
||||
updateStudySummaries,
|
||||
updateParamImportance,
|
||||
createNewStudy,
|
||||
deleteStudy,
|
||||
renameStudy,
|
||||
|
||||
@@ -15,6 +15,18 @@ import {
|
||||
import { CompareStudies } from "./CompareStudies"
|
||||
import { StudyDetail } from "./StudyDetail"
|
||||
import { StudyList } from "./StudyList"
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const App: FC = () => {
|
||||
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)")
|
||||
@@ -38,95 +50,99 @@ export const App: FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: colorMode === "dark" ? "#121212" : "#ffffff",
|
||||
width: "100%",
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
>
|
||||
<SnackbarProvider maxSnack={3}>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/analytics"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"analytics"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/trials"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"trialList"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/trialTable"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"trialTable"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/note"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"note"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/graph"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"graph"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"top"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/preference-history"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"preferenceHistory"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/compare-studies"}
|
||||
element={<CompareStudies toggleColorMode={toggleColorMode} />}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/"}
|
||||
element={<StudyList toggleColorMode={toggleColorMode} />}
|
||||
/>
|
||||
</Routes>
|
||||
</Router>
|
||||
</SnackbarProvider>
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
</RecoilRoot>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RecoilRoot>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: colorMode === "dark" ? "#121212" : "#ffffff",
|
||||
width: "100%",
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
>
|
||||
<SnackbarProvider maxSnack={3}>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/analytics"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"analytics"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/trials"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"trialList"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/trialTable"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"trialTable"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/note"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"note"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/graph"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"graph"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"top"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/preference-history"}
|
||||
element={
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"preferenceHistory"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/compare-studies"}
|
||||
element={
|
||||
<CompareStudies toggleColorMode={toggleColorMode} />
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/"}
|
||||
element={<StudyList toggleColorMode={toggleColorMode} />}
|
||||
/>
|
||||
</Routes>
|
||||
</Router>
|
||||
</SnackbarProvider>
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
</RecoilRoot>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ import blue from "@mui/material/colors/blue"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { usePlotlyColorTheme } from "../state"
|
||||
import { getAxisInfo } from "../graphUtil"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { PlotType } from "../apiClient"
|
||||
import { useBackendRender } from "../state"
|
||||
import { usePlot } from "../hooks/usePlot"
|
||||
|
||||
const plotDomId = "graph-contour"
|
||||
|
||||
@@ -36,18 +37,23 @@ const ContourBackend: FC<{
|
||||
const studyId = study?.id
|
||||
const numCompletedTrials =
|
||||
study?.trials.filter((t) => t.state === "Complete").length || 0
|
||||
const { data, layout, error } = usePlot({
|
||||
numCompletedTrials,
|
||||
studyId,
|
||||
plotType: PlotType.Contour,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (studyId === undefined) {
|
||||
return
|
||||
if (data && layout) {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
}
|
||||
getPlotAPI(studyId, PlotType.Contour)
|
||||
.then(({ data, layout }) => {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}, [studyId, numCompletedTrials])
|
||||
}, [data, layout])
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}, [error])
|
||||
|
||||
return <Box id={plotDomId} sx={{ height: "450px" }} />
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,8 @@ import * as plotly from "plotly.js-dist-min"
|
||||
import React, { FC, useEffect } from "react"
|
||||
import { Typography, useTheme, Box, Card, CardContent } from "@mui/material"
|
||||
|
||||
import { actionCreator } from "../action"
|
||||
import { useParamImportanceValue, useStudyDirections } from "../state"
|
||||
import { usePlotlyColorTheme } from "../state"
|
||||
import { useParamImportance } from "../hooks/useParamImportance"
|
||||
import { useStudyDirections, usePlotlyColorTheme } from "../state"
|
||||
|
||||
const plotDomId = "graph-hyperparameter-importances"
|
||||
|
||||
@@ -16,10 +15,12 @@ export const GraphHyperparameterImportance: FC<{
|
||||
const theme = useTheme()
|
||||
const colorTheme = usePlotlyColorTheme(theme.palette.mode)
|
||||
|
||||
const action = actionCreator()
|
||||
const importances = useParamImportanceValue(studyId)
|
||||
const numCompletedTrials =
|
||||
study?.trials.filter((t) => t.state === "Complete").length || 0
|
||||
const { importances } = useParamImportance({
|
||||
numCompletedTrials,
|
||||
studyId,
|
||||
})
|
||||
const nObjectives = useStudyDirections(studyId)?.length
|
||||
const objectiveNames: string[] =
|
||||
study?.objective_names ||
|
||||
@@ -27,11 +28,7 @@ export const GraphHyperparameterImportance: FC<{
|
||||
[]
|
||||
|
||||
useEffect(() => {
|
||||
action.updateParamImportance(studyId)
|
||||
}, [numCompletedTrials])
|
||||
|
||||
useEffect(() => {
|
||||
if (importances !== null && nObjectives === importances.length) {
|
||||
if (importances !== undefined && nObjectives === importances.length) {
|
||||
plotParamImportance(importances, objectiveNames, colorTheme)
|
||||
}
|
||||
}, [nObjectives, importances, colorTheme])
|
||||
|
||||
@@ -17,8 +17,9 @@ import {
|
||||
useParamTargets,
|
||||
} from "../trialFilter"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { PlotType } from "../apiClient"
|
||||
import { useBackendRender } from "../state"
|
||||
import { usePlot } from "../hooks/usePlot"
|
||||
|
||||
const plotDomId = "graph-parallel-coordinate"
|
||||
|
||||
@@ -102,18 +103,24 @@ const GraphParallelCoordinateBackend: FC<{
|
||||
const studyId = study?.id
|
||||
const numCompletedTrials =
|
||||
study?.trials.filter((t) => t.state === "Complete").length || 0
|
||||
|
||||
const { data, layout, error } = usePlot({
|
||||
numCompletedTrials,
|
||||
studyId,
|
||||
plotType: PlotType.ParallelCoordinate,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (studyId === undefined) {
|
||||
return
|
||||
if (data && layout) {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
}
|
||||
getPlotAPI(studyId, PlotType.ParallelCoordinate)
|
||||
.then(({ data, layout }) => {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}, [studyId, numCompletedTrials])
|
||||
}, [data, layout])
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}, [error])
|
||||
|
||||
return <Box id={plotDomId} sx={{ height: "450px" }} />
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,9 @@ import {
|
||||
} from "@mui/material"
|
||||
import { getAxisInfo, makeHovertext } from "../graphUtil"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { PlotType } from "../apiClient"
|
||||
import { usePlotlyColorTheme, useBackendRender } from "../state"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { usePlot } from "../hooks/usePlot"
|
||||
|
||||
const plotDomId = "graph-rank"
|
||||
|
||||
@@ -46,18 +47,24 @@ const GraphRankBackend: FC<{
|
||||
const studyId = study?.id
|
||||
const numCompletedTrials =
|
||||
study?.trials.filter((t) => t.state === "Complete").length || 0
|
||||
|
||||
const { data, layout, error } = usePlot({
|
||||
numCompletedTrials,
|
||||
studyId,
|
||||
plotType: PlotType.Rank,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (studyId === undefined) {
|
||||
return
|
||||
if (data && layout) {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
}
|
||||
getPlotAPI(studyId, PlotType.Rank)
|
||||
.then(({ data, layout }) => {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}, [studyId, numCompletedTrials])
|
||||
}, [data, layout])
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}, [error])
|
||||
|
||||
return <Box id={plotDomId} sx={{ height: "450px" }} />
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,9 @@ import {
|
||||
useParamTargets,
|
||||
} from "../trialFilter"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { PlotType } from "../apiClient"
|
||||
import { usePlotlyColorTheme, useBackendRender } from "../state"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { usePlot } from "../hooks/usePlot"
|
||||
|
||||
const plotDomId = "graph-slice"
|
||||
|
||||
@@ -47,18 +48,24 @@ const GraphSliceBackend: FC<{
|
||||
const studyId = study?.id
|
||||
const numCompletedTrials =
|
||||
study?.trials.filter((t) => t.state === "Complete").length || 0
|
||||
|
||||
const { data, layout, error } = usePlot({
|
||||
numCompletedTrials,
|
||||
studyId,
|
||||
plotType: PlotType.Slice,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (studyId === undefined) {
|
||||
return
|
||||
if (data && layout) {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
}
|
||||
getPlotAPI(studyId, PlotType.Slice)
|
||||
.then(({ data, layout }) => {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}, [studyId, numCompletedTrials])
|
||||
}, [data, layout])
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}, [error])
|
||||
|
||||
return <Box id={plotDomId} sx={{ height: "450px" }} />
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { useEffect } from "react"
|
||||
import { useSnackbar } from "notistack"
|
||||
import { getParamImportances } from "../../ts/apiClient"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { AxiosError } from "axios"
|
||||
|
||||
export const useParamImportance = ({
|
||||
numCompletedTrials,
|
||||
studyId,
|
||||
}: { numCompletedTrials: number; studyId: number }) => {
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
const { data, isLoading, error } = useQuery<
|
||||
ParamImportance[][],
|
||||
AxiosError<{ reason: string }>
|
||||
>({
|
||||
queryKey: ["paramImportance", studyId, numCompletedTrials],
|
||||
queryFn: () => getParamImportances(studyId),
|
||||
staleTime: Infinity,
|
||||
gcTime: 30 * 60 * 1000, // 30 minutes
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
const reason = error.response?.data.reason
|
||||
enqueueSnackbar(
|
||||
`Failed to load hyperparameter importance (reason=${reason})`,
|
||||
{
|
||||
variant: "error",
|
||||
}
|
||||
)
|
||||
}
|
||||
}, [error])
|
||||
|
||||
return {
|
||||
importances: data,
|
||||
isLoading,
|
||||
error,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import * as plotly from "plotly.js-dist-min"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { AxiosError } from "axios"
|
||||
import { PlotType, getPlotAPI } from "../apiClient"
|
||||
|
||||
export const usePlot = ({
|
||||
numCompletedTrials,
|
||||
studyId,
|
||||
plotType,
|
||||
}: {
|
||||
numCompletedTrials: number
|
||||
studyId: number | undefined
|
||||
plotType: PlotType
|
||||
}) => {
|
||||
const { data, isLoading, error } = useQuery<
|
||||
{ data: plotly.Data[]; layout: plotly.Layout },
|
||||
AxiosError
|
||||
>({
|
||||
enabled: studyId !== undefined,
|
||||
queryKey: ["plot", studyId, numCompletedTrials, plotType],
|
||||
queryFn: () => {
|
||||
if (studyId === undefined) {
|
||||
return Promise.reject(new Error("Invalid studyId"))
|
||||
}
|
||||
return getPlotAPI(studyId, plotType)
|
||||
},
|
||||
staleTime: Infinity,
|
||||
gcTime: 30 * 60 * 1000, // 30 minutes
|
||||
})
|
||||
|
||||
return {
|
||||
data: data?.data,
|
||||
layout: data?.layout,
|
||||
isLoading,
|
||||
error,
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,6 @@ export const trialsUpdatingState = atom<{
|
||||
default: {},
|
||||
})
|
||||
|
||||
export const paramImportanceState = atom<StudyParamImportance>({
|
||||
key: "paramImportance",
|
||||
default: {},
|
||||
})
|
||||
|
||||
// TODO(c-bata): Consider representing the state as boolean.
|
||||
export const reloadIntervalState = atom<number>({
|
||||
key: "reloadInterval",
|
||||
@@ -81,14 +76,6 @@ export const useTrialUpdatingValue = (trialId: number): boolean => {
|
||||
return updating[trialId] || false
|
||||
}
|
||||
|
||||
export const useParamImportanceValue = (
|
||||
studyId: number
|
||||
): ParamImportance[][] | null => {
|
||||
const studyParamImportance =
|
||||
useRecoilValue<StudyParamImportance>(paramImportanceState)
|
||||
return studyParamImportance[studyId] || null
|
||||
}
|
||||
|
||||
export const useStudyDirections = (
|
||||
studyId: number
|
||||
): StudyDirection[] | null => {
|
||||
|
||||
Vendored
-4
@@ -226,10 +226,6 @@ type StudyDetails = {
|
||||
[study_id: string]: StudyDetail
|
||||
}
|
||||
|
||||
type StudyParamImportance = {
|
||||
[study_id: string]: ParamImportance[][]
|
||||
}
|
||||
|
||||
type PreferenceHistory = {
|
||||
id: string
|
||||
candidates: number[]
|
||||
|
||||
Generated
+38
@@ -16,6 +16,7 @@
|
||||
"@mui/material": "^5.15.6",
|
||||
"@react-three/drei": "^9.96.4",
|
||||
"@react-three/fiber": "^8.15.15",
|
||||
"@tanstack/react-query": "^5.18.1",
|
||||
"@types/three": "^0.160.0",
|
||||
"axios": "^1.6.7",
|
||||
"elkjs": "^0.9.1",
|
||||
@@ -4247,6 +4248,30 @@
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.18.1.tgz",
|
||||
"integrity": "sha512-fYhrG7bHgSNbnkIJF2R4VUXb4lF7EBiQjKkDc5wOlB7usdQOIN4LxxHpDxyE3qjqIst1WBGvDtL48T0sHJGKCw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.18.1.tgz",
|
||||
"integrity": "sha512-PdI07BbsahZ+04PxSuDQsQvBWe008eWFk/YYWzt8fvzt2sALUM0TpAJa/DFpqa7+SSo7j1EQR6Jx6znXNHyaXw==",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.18.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "9.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
|
||||
@@ -18499,6 +18524,19 @@
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@tanstack/query-core": {
|
||||
"version": "5.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.18.1.tgz",
|
||||
"integrity": "sha512-fYhrG7bHgSNbnkIJF2R4VUXb4lF7EBiQjKkDc5wOlB7usdQOIN4LxxHpDxyE3qjqIst1WBGvDtL48T0sHJGKCw=="
|
||||
},
|
||||
"@tanstack/react-query": {
|
||||
"version": "5.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.18.1.tgz",
|
||||
"integrity": "sha512-PdI07BbsahZ+04PxSuDQsQvBWe008eWFk/YYWzt8fvzt2sALUM0TpAJa/DFpqa7+SSo7j1EQR6Jx6znXNHyaXw==",
|
||||
"requires": {
|
||||
"@tanstack/query-core": "5.18.1"
|
||||
}
|
||||
},
|
||||
"@testing-library/dom": {
|
||||
"version": "9.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"@mui/material": "^5.15.6",
|
||||
"@react-three/drei": "^9.96.4",
|
||||
"@react-three/fiber": "^8.15.15",
|
||||
"@tanstack/react-query": "^5.18.1",
|
||||
"@types/three": "^0.160.0",
|
||||
"axios": "^1.6.7",
|
||||
"elkjs": "^0.9.1",
|
||||
|
||||
Reference in New Issue
Block a user