All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
214 lines
5.8 KiB
JavaScript
214 lines
5.8 KiB
JavaScript
import { createContext, useContext, useState, useEffect } from 'react'
|
|
import { useNavigate, useLocation } from 'react-router-dom'
|
|
import PropTypes from 'prop-types'
|
|
|
|
const HistoryContext = createContext()
|
|
|
|
export const HistoryProvider = ({ children }) => {
|
|
const [navigationHistory, setNavigationHistory] = useState([])
|
|
const [currentPosition, setCurrentPosition] = useState(-1)
|
|
const navigate = useNavigate()
|
|
const location = useLocation()
|
|
|
|
// Base route names
|
|
const baseRouteNames = {
|
|
'/production': 'Production',
|
|
'/management': 'Management',
|
|
'/dashboard/production/gcodefiles': 'GCode Files',
|
|
'/dashboard/management/filaments': 'Filaments',
|
|
'/dashboard/management/parts': 'Parts',
|
|
'/dashboard/management/products': 'Products',
|
|
'/dashboard/management/vendors': 'Vendors',
|
|
'/dashboard/management/materials': 'Materials'
|
|
}
|
|
|
|
const getEntityDetails = (pathname, search) => {
|
|
const searchParams = new URLSearchParams(search)
|
|
|
|
// Handle different entity types
|
|
if (pathname.includes('/gcodefiles/info')) {
|
|
const gcodeFileId = searchParams.get('gcodeFileId')
|
|
return {
|
|
type: 'gcodefile',
|
|
id: gcodeFileId,
|
|
displayName: `GCode File Info${gcodeFileId ? ` (${gcodeFileId})` : ''}`
|
|
}
|
|
}
|
|
if (pathname.includes('/filaments/info')) {
|
|
const filamentId = searchParams.get('filamentId')
|
|
return {
|
|
type: 'filament',
|
|
id: filamentId,
|
|
displayName: `Filament Info${filamentId ? ` (${filamentId})` : ''}`
|
|
}
|
|
}
|
|
if (pathname.includes('/parts/info')) {
|
|
const partId = searchParams.get('partId')
|
|
return {
|
|
type: 'part',
|
|
id: partId,
|
|
displayName: `Part Info${partId ? ` (${partId})` : ''}`
|
|
}
|
|
}
|
|
if (pathname.includes('/products/info')) {
|
|
const productId = searchParams.get('productId')
|
|
return {
|
|
type: 'product',
|
|
id: productId,
|
|
displayName: `Product Info${productId ? ` (${productId})` : ''}`
|
|
}
|
|
}
|
|
if (pathname.includes('/vendors/info')) {
|
|
const vendorId = searchParams.get('vendorId')
|
|
return {
|
|
type: 'vendor',
|
|
id: vendorId,
|
|
displayName: `Vendor Info${vendorId ? ` (${vendorId})` : ''}`
|
|
}
|
|
}
|
|
if (pathname.includes('/materials/info')) {
|
|
const materialId = searchParams.get('materialId')
|
|
return {
|
|
type: 'material',
|
|
id: materialId,
|
|
displayName: `Material Info${materialId ? ` (${materialId})` : ''}`
|
|
}
|
|
}
|
|
|
|
// For base routes, return the simple name
|
|
const baseName = baseRouteNames[pathname]
|
|
if (baseName) {
|
|
return {
|
|
type: 'base',
|
|
displayName: baseName
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
// Track location changes
|
|
useEffect(() => {
|
|
const newPath = location.pathname
|
|
const details = getEntityDetails(location.pathname, location.search)
|
|
|
|
if (
|
|
newPath === '/dashboard/production/gcodefiles' ||
|
|
newPath === '/dashboard/management/filaments' ||
|
|
newPath === '/dashboard/management/materials'
|
|
) {
|
|
setNavigationHistory([
|
|
{
|
|
path: newPath + location.search,
|
|
details,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
])
|
|
setCurrentPosition(0)
|
|
} else if (details) {
|
|
setNavigationHistory((prev) => {
|
|
const newHistory = prev.slice(0, currentPosition + 1)
|
|
return [
|
|
...newHistory,
|
|
{
|
|
path: newPath + location.search,
|
|
details,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
]
|
|
})
|
|
setCurrentPosition((prev) => prev + 1)
|
|
}
|
|
}, [location])
|
|
|
|
const goBack = () => {
|
|
if (currentPosition > 0) {
|
|
setCurrentPosition((prev) => prev - 1)
|
|
navigate(-1)
|
|
}
|
|
}
|
|
|
|
const goForward = () => {
|
|
if (currentPosition < navigationHistory.length - 1) {
|
|
setCurrentPosition((prev) => prev + 1)
|
|
navigate(1)
|
|
}
|
|
}
|
|
|
|
const getBreadcrumbItems = () => {
|
|
// If there's no history, return empty array
|
|
if (navigationHistory.length === 0) return []
|
|
|
|
// Get current location
|
|
const currentItem = navigationHistory[currentPosition]
|
|
if (!currentItem) return []
|
|
|
|
const pathParts = currentItem.path.split('/')
|
|
const breadcrumbs = []
|
|
|
|
// Build up breadcrumbs including parent routes
|
|
if (pathParts[1]) {
|
|
// First level (e.g. /production or /management)
|
|
const firstLevelPath = '/' + pathParts[1]
|
|
breadcrumbs.push({
|
|
path: firstLevelPath,
|
|
title: baseRouteNames[firstLevelPath]
|
|
})
|
|
}
|
|
|
|
if (pathParts[2]) {
|
|
// Second level (e.g. /dashboard/production/gcodefiles)
|
|
const secondLevelPath =
|
|
'/' + pathParts[1] + '/' + pathParts[2].split('?')[0]
|
|
breadcrumbs.push({
|
|
path: currentItem.path,
|
|
title:
|
|
baseRouteNames[secondLevelPath] || currentItem.details.displayName
|
|
})
|
|
}
|
|
|
|
// Add the entity detail level if it exists (e.g. specific gcodefile, filament, etc)
|
|
if (currentItem.details.type !== 'base') {
|
|
breadcrumbs.push({
|
|
path: currentItem.path,
|
|
title: currentItem.details.displayName
|
|
})
|
|
}
|
|
|
|
return breadcrumbs
|
|
}
|
|
|
|
const canGoBack = currentPosition > 0
|
|
const canGoForward = currentPosition < navigationHistory.length - 1
|
|
|
|
return (
|
|
<HistoryContext.Provider
|
|
value={{
|
|
navigationHistory,
|
|
currentPosition,
|
|
goBack,
|
|
goForward,
|
|
canGoBack,
|
|
canGoForward,
|
|
getBreadcrumbItems
|
|
}}
|
|
>
|
|
{children}
|
|
</HistoryContext.Provider>
|
|
)
|
|
}
|
|
|
|
export const useHistory = () => {
|
|
const context = useContext(HistoryContext)
|
|
if (!context) {
|
|
throw new Error('useHistory must be used within a HistoryProvider')
|
|
}
|
|
return context
|
|
}
|
|
|
|
HistoryProvider.propTypes = {
|
|
children: PropTypes.node.isRequired
|
|
}
|
|
|
|
export default HistoryContext
|