diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 5edddfece2..c9b7e19def 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -15,14 +15,8 @@ export function setApiDefaults() { const token = useSessionState.getState().token; api.defaults.baseURL = host; - - if (token) { - api.defaults.headers.common['Authorization'] = `Token ${token}`; - } else { - api.defaults.headers.common['Authorization'] = null; - } + api.defaults.headers.common['Authorization'] = `Token ${token}`; } - export const queryClient = new QueryClient(); function checkMobile() { diff --git a/src/frontend/src/components/nav/PanelGroup.tsx b/src/frontend/src/components/nav/PanelGroup.tsx index 8ac898da8d..a56d9bb5ef 100644 --- a/src/frontend/src/components/nav/PanelGroup.tsx +++ b/src/frontend/src/components/nav/PanelGroup.tsx @@ -10,8 +10,15 @@ import { IconLayoutSidebarLeftCollapse, IconLayoutSidebarRightCollapse } from '@tabler/icons-react'; -import { ReactNode, useCallback, useMemo } from 'react'; +import { ReactNode, useMemo } from 'react'; import { useEffect, useState } from 'react'; +import { + Navigate, + Route, + Routes, + useNavigate, + useParams +} from 'react-router-dom'; import { useLocalState } from '../../states/LocalState'; import { PlaceholderPanel } from '../items/Placeholder'; @@ -37,57 +44,61 @@ export type PanelProps = { collapsible?: boolean; }; -export function PanelGroup({ +function BasePanelGroup({ pageKey, panels, onPanelChange, selectedPanel, collapsible = true }: PanelProps): ReactNode { - const localState = useLocalState(); + const navigate = useNavigate(); + const { panel } = useParams(); - const [panel, setPanel] = useState(selectedPanel ?? ''); - - // Keep a list of active panels (hidden and disabled panels are not active) const activePanels = useMemo( () => panels.filter((panel) => !panel.hidden && !panel.disabled), [panels] ); - // Set selected panel when component is initially loaded, or when the selected panel changes + const setLastUsedPanel = useLocalState((state) => + state.setLastUsedPanel(pageKey) + ); + useEffect(() => { - let first_panel: string = activePanels[0]?.name ?? ''; - let active_panel: string = - useLocalState.getState().getLastUsedPanel(pageKey)() ?? ''; - - let panelName = selectedPanel || active_panel || first_panel; - - if (panelName != panel) { - setPanel(panelName); + if (panel) { + setLastUsedPanel(panel); } - - if (panelName != active_panel) { - useLocalState.getState().setLastUsedPanel(pageKey)(panelName); - } - }, [activePanels, panels, selectedPanel]); + // panel is intentionally no dependency as this should only run on initial render + }, [setLastUsedPanel]); // Callback when the active panel changes - const handlePanelChange = useCallback( - (panelName: string) => { - // Ensure that the panel name is valid - if (!activePanels.some((panel) => panel.name == panelName)) { - return; - } + function handlePanelChange(panel: string) { + if (activePanels.findIndex((p) => p.name === panel) === -1) { + setLastUsedPanel(''); + return navigate('../'); + } - setPanel(panelName); - localState.setLastUsedPanel(pageKey)(panelName); + navigate(`../${panel}`); - if (onPanelChange) { - onPanelChange(panelName); - } - }, - [onPanelChange, pageKey] - ); + // Optionally call external callback hook + if (onPanelChange) { + onPanelChange(panel); + } + } + + // if the selected panel state changes update the current panel + useEffect(() => { + if (selectedPanel && selectedPanel !== panel) { + handlePanelChange(selectedPanel); + } + }, [selectedPanel, panel]); + + // Update the active panel when panels changes and the active is no longer available + useEffect(() => { + if (activePanels.findIndex((p) => p.name === panel) === -1) { + setLastUsedPanel(''); + return navigate('../'); + } + }, [activePanels, panel]); const [expanded, setExpanded] = useState(true); @@ -158,3 +169,38 @@ export function PanelGroup({ ); } + +function IndexPanelComponent({ pageKey, selectedPanel, panels }: PanelProps) { + const lastUsedPanel = useLocalState((state) => { + const panelName = + selectedPanel || state.lastUsedPanels[pageKey] || panels[0]?.name; + + if ( + panels.findIndex( + (p) => p.name === panelName && !p.disabled && !p.hidden + ) === -1 + ) { + return panels[0]?.name; + } + + return panelName; + }); + + return ; +} + +/** + * Render a panel group. The current panel will be appended to the current url. + * The last opened panel will be stored in local storage and opened if no panel is provided via url param + * @param panels - The list of panels to display + * @param onPanelChange - Callback when the active panel changes + * @param collapsible - If true, the panel group can be collapsed (defaults to true) + */ +export function PanelGroup(props: PanelProps) { + return ( + + } /> + } /> + + ); +} diff --git a/src/frontend/src/functions/auth.tsx b/src/frontend/src/functions/auth.tsx index 561d883e00..58e9d8e063 100644 --- a/src/frontend/src/functions/auth.tsx +++ b/src/frontend/src/functions/auth.tsx @@ -27,9 +27,7 @@ export const doClassicLogin = async (username: string, password: string) => { name: 'inventree-web-app' } }) - .then((response) => { - return response.status == 200 ? response.data?.token : undefined; - }) + .then((response) => response.data.token) .catch((error) => { showNotification({ title: t`Login failed`, @@ -39,7 +37,7 @@ export const doClassicLogin = async (username: string, password: string) => { return false; }); - if (!token) return false; + if (token === false) return token; // log in with token doTokenLogin(token); @@ -53,7 +51,6 @@ export const doClassicLogout = async () => { // TODO @matmair - logout from the server session // Set token in context const { setToken } = useSessionState.getState(); - setToken(undefined); notifications.show({ diff --git a/src/frontend/src/pages/part/CategoryDetail.tsx b/src/frontend/src/pages/part/CategoryDetail.tsx index 291c230979..30f63b6ba4 100644 --- a/src/frontend/src/pages/part/CategoryDetail.tsx +++ b/src/frontend/src/pages/part/CategoryDetail.tsx @@ -22,11 +22,9 @@ import { useInstance } from '../../hooks/UseInstance'; * * Note: If no category ID is supplied, this acts as the top-level part category page */ -export function CategoryPage({ - categoryId -}: { - categoryId?: string | undefined; -}) { +export default function CategoryDetail({}: {}) { + const { id } = useParams(); + const [treeOpen, setTreeOpen] = useState(false); const { @@ -35,8 +33,7 @@ export function CategoryPage({ instanceQuery } = useInstance({ endpoint: ApiPaths.category_list, - pk: categoryId, - hasPrimaryKey: true, + pk: id, params: { path_detail: true } @@ -52,7 +49,7 @@ export function CategoryPage({ @@ -65,7 +62,7 @@ export function CategoryPage({ content: ( ) @@ -77,7 +74,7 @@ export function CategoryPage({ content: } ], - [categoryId] + [category, id] ); const breadcrumbs = useMemo( @@ -113,13 +110,3 @@ export function CategoryPage({ ); } - -/** - * Detail page for a specific Part Category instance. - * Uses the :id parameter in the URL to determine which category to display.admin in - */ -export default function CategoryDetail({}: {}) { - const { id } = useParams(); - - return ; -} diff --git a/src/frontend/src/pages/part/PartIndex.tsx b/src/frontend/src/pages/part/PartIndex.tsx deleted file mode 100644 index cd60d701fd..0000000000 --- a/src/frontend/src/pages/part/PartIndex.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { CategoryPage } from './CategoryDetail'; - -export default function PartIndex({}: {}) { - return ; -} diff --git a/src/frontend/src/pages/stock/LocationDetail.tsx b/src/frontend/src/pages/stock/LocationDetail.tsx index e28544e245..af55680e8a 100644 --- a/src/frontend/src/pages/stock/LocationDetail.tsx +++ b/src/frontend/src/pages/stock/LocationDetail.tsx @@ -12,11 +12,9 @@ import { StockLocationTable } from '../../components/tables/stock/StockLocationT import { ApiPaths } from '../../enums/ApiEndpoints'; import { useInstance } from '../../hooks/UseInstance'; -export function LocationPage({ - locationId -}: { - locationId?: string | undefined; -}) { +export default function Stock() { + const { id } = useParams(); + const [treeOpen, setTreeOpen] = useState(false); const { @@ -25,8 +23,7 @@ export function LocationPage({ instanceQuery } = useInstance({ endpoint: ApiPaths.stock_location_list, - pk: locationId, - hasPrimaryKey: true, + pk: id, params: { path_detail: true } @@ -41,7 +38,7 @@ export function LocationPage({ content: ( ) @@ -53,13 +50,13 @@ export function LocationPage({ content: ( ) } ]; - }, [locationId]); + }, [location, id]); const breadcrumbs = useMemo( () => [ @@ -94,13 +91,3 @@ export function LocationPage({ ); } - -/** - * Detail page for a specific Stock Location instance - * Uses the :id parameter in the URL to determine which location to display - */ -export default function LocationDetail({}: {}) { - const { id } = useParams(); - - return ; -} diff --git a/src/frontend/src/pages/stock/StockIndex.tsx b/src/frontend/src/pages/stock/StockIndex.tsx deleted file mode 100644 index c548d65b38..0000000000 --- a/src/frontend/src/pages/stock/StockIndex.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { LocationPage } from './LocationDetail'; - -export default function StockIndex({}: {}) { - return ; -} diff --git a/src/frontend/src/router.tsx b/src/frontend/src/router.tsx index fc10016f06..45713dbe38 100644 --- a/src/frontend/src/router.tsx +++ b/src/frontend/src/router.tsx @@ -28,12 +28,9 @@ export const ManufacturerDetail = Loadable( lazy(() => import('./pages/company/ManufacturerDetail')) ); -export const PartIndex = Loadable(lazy(() => import('./pages/part/PartIndex'))); - export const CategoryDetail = Loadable( lazy(() => import('./pages/part/CategoryDetail')) ); - export const PartDetail = Loadable( lazy(() => import('./pages/part/PartDetail')) ); @@ -42,10 +39,6 @@ export const LocationDetail = Loadable( lazy(() => import('./pages/stock/LocationDetail')) ); -export const StockIndex = Loadable( - lazy(() => import('./pages/stock/StockIndex')) -); - export const StockDetail = Loadable( lazy(() => import('./pages/stock/StockDetail')) ); @@ -126,13 +119,13 @@ export const routes = ( } /> - } /> - } /> + } /> + } /> } /> - } /> - } /> + } /> + } /> } /> diff --git a/src/frontend/src/states/LocalState.tsx b/src/frontend/src/states/LocalState.tsx index a4640de5df..1eda3fcceb 100644 --- a/src/frontend/src/states/LocalState.tsx +++ b/src/frontend/src/states/LocalState.tsx @@ -24,7 +24,6 @@ interface LocalStateProps { loader: LoaderType; lastUsedPanels: Record; setLastUsedPanel: (panelKey: string) => (value: string) => void; - getLastUsedPanel: (panelKey: string) => () => string | undefined; } export const useLocalState = create()( @@ -56,9 +55,6 @@ export const useLocalState = create()( lastUsedPanels: { ...get().lastUsedPanels, [panelKey]: value } }); } - }, - getLastUsedPanel(panelKey) { - return () => get().lastUsedPanels[panelKey]; } }), {