Compare commits

..

12 Commits

14 changed files with 364 additions and 203 deletions

View File

@ -48,7 +48,7 @@ To start the development server:
npm run dev npm run dev
``` ```
The app will be available at [http://localhost:3000](http://localhost:3000). The app will be available at [http://localhost:5173](http://localhost:5173).
### Building for Production ### Building for Production

View File

@ -27,6 +27,7 @@
"@codemirror/lang-yaml": "^6.1.2", "@codemirror/lang-yaml": "^6.1.2",
"@codemirror/theme-one-dark": "^6.1.3", "@codemirror/theme-one-dark": "^6.1.3",
"@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/browser": "^13.1.2",
"@tanstack/react-query": "^5.90.10",
"@tsparticles/react": "^3.0.0", "@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.9.1", "@tsparticles/slim": "^3.9.1",
"@uiw/react-codemirror": "^4.25.1", "@uiw/react-codemirror": "^4.25.1",
@ -71,10 +72,10 @@
"description": "3D Printer ERP and Control Software.", "description": "3D Printer ERP and Control Software.",
"scripts": { "scripts": {
"dev": "cross-env NODE_ENV=development vite", "dev": "cross-env NODE_ENV=development vite",
"electron": "cross-env ELECTRON_START_URL=http://0.0.0.0:3000 && cross-env NODE_ENV=development && electron .", "electron": "cross-env ELECTRON_START_URL=http://0.0.0.0:5173 && cross-env NODE_ENV=development && electron .",
"start": "serve -s build", "start": "serve -s build",
"build": "vite build", "build": "vite build",
"dev:electron": "concurrently \"cross-env NODE_ENV=development vite --no-open\" \"cross-env ELECTRON_START_URL=http://localhost:3000 cross-env NODE_ENV=development electron public/electron.js\"", "dev:electron": "concurrently \"cross-env NODE_ENV=development vite --no-open\" \"cross-env ELECTRON_START_URL=http://localhost:5173 cross-env NODE_ENV=development electron public/electron.js\"",
"build:electron": "vite build && electron-builder" "build:electron": "vite build && electron-builder"
}, },
"eslintConfig": { "eslintConfig": {

View File

@ -15,7 +15,7 @@ const NewFilamentStock = ({ onOk, reset }) => {
return ( return (
<NewObjectForm <NewObjectForm
type={'filamentstock'} type={'filamentStock'}
reset={reset} reset={reset}
defaultValues={{ state: { type: 'unconsumed' } }} defaultValues={{ state: { type: 'unconsumed' } }}
> >

View File

@ -1,4 +1,4 @@
import { useState, useRef, useEffect } from 'react' import { useState, useRef, useEffect, useContext } from 'react'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import { Space, Flex, Card, Splitter, Divider } from 'antd' import { Space, Flex, Card, Splitter, Divider } from 'antd'
import loglevel from 'loglevel' import loglevel from 'loglevel'
@ -26,6 +26,7 @@ import FilamentStockIcon from '../../../Icons/FilamentStockIcon.jsx'
import MissingPlaceholder from '../../common/MissingPlaceholder.jsx' import MissingPlaceholder from '../../common/MissingPlaceholder.jsx'
import { useMediaQuery } from 'react-responsive' import { useMediaQuery } from 'react-responsive'
import AlertsDisplay from '../../common/AlertsDisplay.jsx' import AlertsDisplay from '../../common/AlertsDisplay.jsx'
import { ApiServerContext } from '../../context/ApiServerContext.jsx'
const log = loglevel.getLogger('ControlPrinter') const log = loglevel.getLogger('ControlPrinter')
log.setLevel(config.logLevel) log.setLevel(config.logLevel)
@ -49,6 +50,7 @@ const ControlPrinter = () => {
} }
) )
const { connected, sendObjectAction } = useContext(ApiServerContext)
const [sideBarVisible, setSideBarVisible] = useState( const [sideBarVisible, setSideBarVisible] = useState(
collapseState.temperature || collapseState.temperature ||
collapseState.position || collapseState.position ||
@ -88,6 +90,38 @@ const ControlPrinter = () => {
finishEdit: () => { finishEdit: () => {
objectFormRef?.current.handleUpdate() objectFormRef?.current.handleUpdate()
return true return true
},
restartFirmware: () => {
if (connected == true) {
sendObjectAction(printerId, 'printer', {
type: 'restartPrinterFirmware'
})
}
return true
},
restartMoonraker: () => {
if (connected == true) {
sendObjectAction(printerId, 'printer', {
type: 'restartMoonraker'
})
}
return true
},
restart: () => {
if (connected == true) {
sendObjectAction(printerId, 'printer', {
type: 'restartPrinter'
})
}
return true
},
startQueue: () => {
if (connected == true) {
sendObjectAction(printerId, 'printer', {
type: 'startQueue'
})
}
return true
} }
} }
@ -128,6 +162,7 @@ const ControlPrinter = () => {
id={printerId} id={printerId}
disabled={objectFormState.loading} disabled={objectFormState.loading}
visibleActions={{ edit: false }} visibleActions={{ edit: false }}
objectData={objectFormState.objectData}
/> />
<ViewButton <ViewButton
disabled={objectFormState.loading} disabled={objectFormState.loading}
@ -195,33 +230,33 @@ const ControlPrinter = () => {
loading={objectFormState.loading} loading={objectFormState.loading}
ref={actionHandlerRef} ref={actionHandlerRef}
> >
<ObjectForm <Flex vertical>
id={printerId} <Splitter className={'farmcontrol-splitter'}>
type='printer' <Splitter.Panel>
ref={objectFormRef} <Flex vertical gap={'large'}>
onStateChange={(state) => { <InfoCollapse
console.log('Got edit form state change', state) title={'Printer'}
setEditFormState((prev) => ({ ...prev, ...state })) icon={<PrinterIcon />}
}} collapseKey='printer'
> active={collapseState.printer}
{({ onToggle={(expanded) =>
loading: printerObjectLoading, updateCollapseState('printer', expanded)
objectData: printerObjectData }
}) => { >
return ( <ObjectForm
<Flex vertical> id={printerId}
<Splitter className={'farmcontrol-splitter'}> type='printer'
<Splitter.Panel> ref={objectFormRef}
<Flex vertical gap={'large'}> onStateChange={(state) => {
<InfoCollapse console.log('Got edit form state change', state)
title={'Printer'} setEditFormState((prev) => ({ ...prev, ...state }))
icon={<PrinterIcon />} }}
collapseKey='printer' >
active={collapseState.printer} {({
onToggle={(expanded) => loading: printerObjectLoading,
updateCollapseState('printer', expanded) objectData: printerObjectData
} }) => {
> return (
<ObjectInfo <ObjectInfo
loading={printerObjectLoading} loading={printerObjectLoading}
column={sideBarVisible ? 1 : undefined} column={sideBarVisible ? 1 : undefined}
@ -236,155 +271,158 @@ const ControlPrinter = () => {
'moonraker.protocol': false, 'moonraker.protocol': false,
'moonraker.host': false, 'moonraker.host': false,
tags: false, tags: false,
firmware: false firmware: false,
alerts: false
}} }}
objectData={printerObjectData} objectData={printerObjectData}
type='printer' type='printer'
/> />
</InfoCollapse> )
<InfoCollapse }}
title={'Job'} </ObjectForm>
icon={<JobIcon />} </InfoCollapse>
collapseKey='job' <InfoCollapse
active={collapseState.job} title={'Job'}
onToggle={(expanded) => icon={<JobIcon />}
updateCollapseState('job', expanded) collapseKey='job'
} active={collapseState.job}
> onToggle={(expanded) =>
{printerObjectData?.currentJob?._id ? ( updateCollapseState('job', expanded)
<ObjectForm }
id={printerObjectData.currentJob._id} >
type='job' {objectFormState.objectData?.currentJob?._id ? (
onStateChange={() => {}} <ObjectForm
> id={objectFormState.objectData.currentJob._id}
{({ type='job'
loading: jobObjectLoading, onStateChange={() => {}}
objectData: jobObjectData
}) => {
return (
<ObjectInfo
loading={jobObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
printers: false,
createdAt: false
}}
objectData={jobObjectData}
type='job'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No job.'}
hasBackground={false}
/>
)}
</InfoCollapse>
<InfoCollapse
title={'Sub Job'}
icon={<SubJobIcon />}
collapseKey='subJob'
active={collapseState.subJob}
onToggle={(expanded) =>
updateCollapseState('subJob', expanded)
}
>
{printerObjectData?.currentSubJob?._id ? (
<ObjectForm
id={printerObjectData.currentSubJob._id}
type='subjob'
onStateChange={() => {}}
>
{({
loading: subJobObjectLoading,
objectData: subJobObjectData
}) => {
return (
<ObjectInfo
loading={subJobObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
printers: false,
createdAt: false
}}
objectData={subJobObjectData}
type='subJob'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No sub job.'}
hasBackground={false}
/>
)}
</InfoCollapse>
<InfoCollapse
title={'Filament Stock'}
icon={<FilamentStockIcon />}
collapseKey='filamentStock'
active={collapseState.filamentStock}
onToggle={(expanded) =>
updateCollapseState('filamentStock', expanded)
}
>
{printerObjectData?.currentFilamentStock?._id ? (
<ObjectForm
id={printerObjectData.currentFilamentStock._id}
type='filamentStock'
onStateChange={() => {}}
>
{({
loading: filamentStockObjectLoading,
objectData: filamentStockObjectData
}) => {
return (
<ObjectInfo
loading={filamentStockObjectLoading}
column={sideBarVisible ? 1 : undefined}
showHyperlink={true}
visibleProperties={{
updatedAt: false,
createdAt: false
}}
objectData={filamentStockObjectData}
type='filamentStock'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No filament stock.'}
hasBackground={false}
/>
)}
</InfoCollapse>
</Flex>
</Splitter.Panel>
{sideBarVisible && !isMobile ? (
<Splitter.Panel
style={{ minWidth: '325px' }}
defaultSize='20%'
max='35%'
> >
{sideBarItems} {({
</Splitter.Panel> loading: jobObjectLoading,
) : null} objectData: jobObjectData
</Splitter> }) => {
{isMobile ? ( return (
<> <ObjectInfo
<Divider /> loading={jobObjectLoading}
{sideBarItems} column={sideBarVisible ? 1 : undefined}
</> visibleProperties={{
) : null} printers: false,
createdAt: false
}}
objectData={jobObjectData}
type='job'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No job.'}
hasBackground={false}
/>
)}
</InfoCollapse>
<InfoCollapse
title={'Sub Job'}
icon={<SubJobIcon />}
collapseKey='subJob'
active={collapseState.subJob}
onToggle={(expanded) =>
updateCollapseState('subJob', expanded)
}
>
{objectFormState.objectData?.currentSubJob?._id ? (
<ObjectForm
id={objectFormState.objectData.currentSubJob._id}
type='subjob'
onStateChange={() => {}}
>
{({
loading: subJobObjectLoading,
objectData: subJobObjectData
}) => {
return (
<ObjectInfo
loading={subJobObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
printers: false,
createdAt: false
}}
objectData={subJobObjectData}
type='subJob'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No sub job.'}
hasBackground={false}
/>
)}
</InfoCollapse>
<InfoCollapse
title={'Filament Stock'}
icon={<FilamentStockIcon />}
collapseKey='filamentStock'
active={collapseState.filamentStock}
onToggle={(expanded) =>
updateCollapseState('filamentStock', expanded)
}
>
{objectFormState.objectData?.currentFilamentStock?._id ? (
<ObjectForm
id={
objectFormState.objectData.currentFilamentStock._id
}
type='filamentStock'
onStateChange={() => {}}
>
{({
loading: filamentStockObjectLoading,
objectData: filamentStockObjectData
}) => {
return (
<ObjectInfo
loading={filamentStockObjectLoading}
column={sideBarVisible ? 1 : undefined}
showHyperlink={true}
visibleProperties={{
updatedAt: false,
createdAt: false
}}
objectData={filamentStockObjectData}
type='filamentStock'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No filament stock.'}
hasBackground={false}
/>
)}
</InfoCollapse>
</Flex> </Flex>
) </Splitter.Panel>
}} {sideBarVisible && !isMobile ? (
</ObjectForm> <Splitter.Panel
style={{ minWidth: '325px' }}
defaultSize='20%'
max='35%'
>
{sideBarItems}
</Splitter.Panel>
) : null}
</Splitter>
{isMobile ? (
<>
<Divider />
{sideBarItems}
</>
) : null}
</Flex>
</ActionHandler> </ActionHandler>
<InfoCollapse <InfoCollapse
title='Notes' title='Notes'

View File

@ -61,9 +61,10 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
// Calculate computed values for initial data // Calculate computed values for initial data
const computedValues = calculateComputedValues(defaultValues, model) const computedValues = calculateComputedValues(defaultValues, model)
const initialFormData = { ...defaultValues, ...computedValues } const initialFormData = { ...defaultValues, ...computedValues }
form.setFieldsValue(initialFormData) form.setFieldsValue(initialFormData)
setObjectData(initialFormData) setObjectData((prev) => {
return merge({}, prev, initialFormData)
})
} }
}, [form, defaultValues, calculateComputedValues, model]) }, [form, defaultValues, calculateComputedValues, model])
@ -113,7 +114,6 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
// Merge all values (user input + computed values) // Merge all values (user input + computed values)
const allValues = { ...values, ...computedValues } const allValues = { ...values, ...computedValues }
setObjectData((prev) => { setObjectData((prev) => {
return merge({}, prev, allValues) return merge({}, prev, allValues)
}) })

View File

@ -63,7 +63,8 @@ function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
item.children = mapActionsToMenuItems( item.children = mapActionsToMenuItems(
action.children, action.children,
currentUrlWithActions, currentUrlWithActions,
id id,
objectData
) )
} }
return item return item

View File

@ -127,7 +127,7 @@ const ObjectProperty = ({
formItemName = name ? name.split('.') : undefined formItemName = name ? name.split('.') : undefined
} }
var textParams = { style: { whiteSpace: 'nowrap' } } var textParams = { style: { whiteSpace: 'nowrap', minWidth: '0' } }
if (disabled == true) { if (disabled == true) {
textParams = { ...textParams, delete: true, type: 'secondary' } textParams = { ...textParams, delete: true, type: 'secondary' }

View File

@ -10,19 +10,30 @@ const UrlDisplay = ({ url, showCopy = true, showLink = false }) => {
return ( return (
<> <>
<Flex> <Flex style={{ minWidth: 0 }}>
{showLink ? ( {showLink ? (
<Link <Link
href={url} href={url}
target='_blank' target='_blank'
rel='noopener noreferrer' rel='noopener noreferrer'
style={{ marginRight: 8 }} style={{
marginRight: 8,
minWidth: 0,
flex: 1,
overflow: 'hidden',
width: 0
}}
> >
<Text ellipsis>{url}</Text> <Text ellipsis style={{ display: 'block' }}>
{url}
</Text>
</Link> </Link>
) : ( ) : (
<> <>
<Text style={{ marginRight: 8 }} ellipsis> <Text
style={{ marginRight: 8, minWidth: 0, flex: 1, width: 0 }}
ellipsis
>
{url} {url}
</Text> </Text>
<Tooltip title='Open URL' arrow={false}> <Tooltip title='Open URL' arrow={false}>
@ -30,7 +41,7 @@ const UrlDisplay = ({ url, showCopy = true, showLink = false }) => {
icon={<LinkIcon />} icon={<LinkIcon />}
type='text' type='text'
size='small' size='small'
style={{ minWidth: 25 }} style={{ minWidth: 25, flexShrink: 0 }}
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()
window.open(url, '_blank', 'noopener,noreferrer') window.open(url, '_blank', 'noopener,noreferrer')

View File

@ -83,6 +83,11 @@ const AuthProvider = ({ children }) => {
// Read token from cookies if present // Read token from cookies if present
useEffect(() => { useEffect(() => {
try { try {
console.log(
'Retreiving token from cookies...',
getAuthCookies(),
validateAuthCookies()
)
// First validate existing cookies to clean up expired ones // First validate existing cookies to clean up expired ones
if (validateAuthCookies()) { if (validateAuthCookies()) {
const { const {
@ -210,7 +215,11 @@ const AuthProvider = ({ children }) => {
setUserProfile(authData) setUserProfile(authData)
// Store in cookies for persistence between tabs // Store in cookies for persistence between tabs
const cookieSuccess = setAuthCookies(authData) const cookieSuccess = setAuthCookies({
user: authData,
access_token: authData.access_token,
expires_at: authData.expires_at
})
if (!cookieSuccess) { if (!cookieSuccess) {
messageApi.warning( messageApi.warning(
'Authentication successful but failed to save login state. You may need to log in again if you close this tab.' 'Authentication successful but failed to save login state. You may need to log in again if you close this tab.'

View File

@ -1,8 +1,8 @@
const config = { const config = {
development: { development: {
backendUrl: 'http://192.168.68.53:8080', backendUrl: 'https://dev.tombutcher.work/api',
printServerUrl: 'ws://192.168.68.53:8081', printServerUrl: 'ws://192.168.68.53:8081',
apiServerUrl: 'ws://192.168.68.53:9090', apiServerUrl: 'https://dev-wss.tombutcher.work',
logLevel: 'trace' logLevel: 'trace'
}, },
production: { production: {

View File

@ -84,7 +84,7 @@ export const FilamentStock = {
required: true, required: true,
columnWidth: 300, columnWidth: 300,
value: (objectData) => { value: (objectData) => {
if (!objectData.currentWeight) { if (objectData?.state?.type === 'unconsumed') {
return objectData?.startingWeight return objectData?.startingWeight
} else { } else {
return objectData.currentWeight return objectData.currentWeight

View File

@ -3,6 +3,8 @@ import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
import ReloadIcon from '../../components/Icons/ReloadIcon' import ReloadIcon from '../../components/Icons/ReloadIcon'
import EditIcon from '../../components/Icons/EditIcon' import EditIcon from '../../components/Icons/EditIcon'
import PlayCircleIcon from '../../components/Icons/PlayCircleIcon' import PlayCircleIcon from '../../components/Icons/PlayCircleIcon'
import PauseCircleIcon from '../../components/Icons/PauseCircleIcon'
import StopCircleIcon from '../../components/Icons/StopCircleIcon'
export const Printer = { export const Printer = {
name: 'printer', name: 'printer',
@ -40,6 +42,97 @@ export const Printer = {
icon: EditIcon, icon: EditIcon,
url: (_id) => url: (_id) =>
`/dashboard/production/printers/info?printerId=${_id}&action=edit` `/dashboard/production/printers/info?printerId=${_id}&action=edit`
},
{ type: 'divider' },
{
name: 'restartSubmenu',
label: 'Restart',
icon: ReloadIcon,
disabled: (objectData) => {
return objectData?.online == false
},
children: [
{
name: 'restart',
label: 'Restart',
icon: ReloadIcon,
disabled: (objectData) => {
return objectData?.online == false
},
url: (_id) =>
`/dashboard/production/printers/control?printerId=${_id}&action=restart`
},
{
name: 'restartFirmware',
label: 'Restart Firmware',
icon: ReloadIcon,
disabled: (objectData) => {
return objectData?.online == false
},
url: (_id) =>
`/dashboard/production/printers/control?printerId=${_id}&action=restartFirmware`
},
{ type: 'divider' },
{
name: 'restartMoonraker',
label: 'Restart Moonraker',
icon: ReloadIcon,
disabled: (objectData) => {
return objectData?.online == false
},
url: (_id) =>
`/dashboard/production/printers/control?printerId=${_id}&action=restartMoonraker`
}
]
},
{
name: 'queue',
label: 'Queue',
icon: PlayCircleIcon,
disabled: (objectData) => {
return objectData?.online == false
},
children: [
{
name: 'Start',
label: 'Start',
icon: PlayCircleIcon,
disabled: (objectData) => {
console.log(objectData?.subJobs?.length)
return (
objectData?.state?.type == 'error' ||
objectData?.state?.type == 'printing' ||
objectData?.subJobs?.length == 0 ||
objectData?.subJobs?.length == undefined
)
},
url: (_id) =>
`/dashboard/production/printers/control?printerId=${_id}&action=startQueue`
},
{
name: 'pause',
label: 'Pause',
icon: PauseCircleIcon,
disabled: (objectData) => {
return objectData?.state?.type != 'printing'
},
url: (_id) =>
`/dashboard/production/printers/control?printerId=${_id}&action=pauseQueue`
},
{
name: 'Stop',
label: 'Stop',
icon: StopCircleIcon,
disabled: (objectData) => {
return (
objectData?.state?.type != 'printing' ||
objectData?.state?.type != 'error'
)
},
url: (_id) =>
`/dashboard/production/printers/control?printerId=${_id}&action=stopQueue`
}
]
} }
], ],
columns: ['name', '_id', 'state', 'tags', 'connectedAt'], columns: ['name', '_id', 'state', 'tags', 'connectedAt'],

View File

@ -11,8 +11,9 @@ export default defineConfig({
outDir: 'build' outDir: 'build'
}, },
server: { server: {
allowedHosts: ['dev.tombutcher.work'],
host: '0.0.0.0', host: '0.0.0.0',
open: false, port: 5173,
port: 3000 open: false
} }
}) })

View File

@ -1289,10 +1289,10 @@
resolved "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz" resolved "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz"
integrity sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA== integrity sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==
"@esbuild/darwin-arm64@0.25.9": "@esbuild/darwin-x64@0.25.9":
version "0.25.9" version "0.25.9"
resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz" resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz"
integrity sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg== integrity sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==
"@eslint-community/eslint-utils@^4.2.0": "@eslint-community/eslint-utils@^4.2.0":
version "4.7.0" version "4.7.0"
@ -1812,10 +1812,10 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
picomatch "^4.0.2" picomatch "^4.0.2"
"@rollup/rollup-darwin-arm64@4.48.0": "@rollup/rollup-darwin-x64@4.48.0":
version "4.48.0" version "4.48.0"
resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.0.tgz" resolved "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.0.tgz"
integrity sha512-QhR2KA18fPlJWFefySJPDYZELaVqIUVnYgAOdtJ+B/uH96CFg2l1TQpX19XpUMWUqMyIiyY45wje8K6F4w4/CA== integrity sha512-Q9RMXnQVJ5S1SYpNSTwXDpoQLgJ/fbInWOyjbCnnqTElEyeNvLAB3QvG5xmMQMhFN74bB5ZZJYkKaFPcOG8sGg==
"@rtsao/scc@^1.1.0": "@rtsao/scc@^1.1.0":
version "1.1.0" version "1.1.0"
@ -1940,6 +1940,18 @@
dependencies: dependencies:
defer-to-connect "^2.0.0" defer-to-connect "^2.0.0"
"@tanstack/query-core@5.90.10":
version "5.90.10"
resolved "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.10.tgz"
integrity sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==
"@tanstack/react-query@^5.90.10":
version "5.90.10"
resolved "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.10.tgz"
integrity sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==
dependencies:
"@tanstack/query-core" "5.90.10"
"@tootallnate/once@2": "@tootallnate/once@2":
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz"
@ -8768,7 +8780,7 @@ react-router@7.8.2:
cookie "^1.0.1" cookie "^1.0.1"
set-cookie-parser "^2.6.0" set-cookie-parser "^2.6.0"
react@*, "react@^18.0.0 || ^19.1.0", react@^19.1.1, "react@>= 16.8.0", "react@>= 16.x", react@>=16, react@>=16.0.0, react@>=16.11.0, react@>=16.8, react@>=16.8.0, react@>=16.8.4, react@>=16.9.0, react@>=17.0.0, react@>=18: react@*, "react@^18 || ^19", "react@^18.0.0 || ^19.1.0", react@^19.1.1, "react@>= 16.8.0", "react@>= 16.x", react@>=16, react@>=16.0.0, react@>=16.11.0, react@>=16.8, react@>=16.8.0, react@>=16.8.4, react@>=16.9.0, react@>=17.0.0, react@>=18:
version "19.1.1" version "19.1.1"
resolved "https://registry.npmjs.org/react/-/react-19.1.1.tgz" resolved "https://registry.npmjs.org/react/-/react-19.1.1.tgz"
integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ== integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==
@ -10592,11 +10604,6 @@ yaml@^1.10.0:
resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.4.2:
version "2.8.1"
resolved "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz"
integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==
yargs-parser@^21.1.1: yargs-parser@^21.1.1:
version "21.1.1" version "21.1.1"
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz"