Create a chapter
When we need to implement a new chapter, in 90% of the cases, we have to create a List view and a Detail view.
Let's create a chapter for PurchaseOrderShipments
.
#
1. Create the overview pagesrc/pages/purchase-order-shipments/PurchaseOrderShipmentsOverviewPage.tsx
import { Pane, Box, Breadcrumbs, ErrorBoundary,} from '@springtree/eva-suite-ui';import { useIntl } from 'react-intl';import { TitleHelmet } from '@springtree/eva-suite-composite';import PurchaseOrderShipmentsOverview from 'components/purchase-order-shipments/overview/PurchaseOrderShipmentsOverview';import PurchaseOrderShipmentsSidebar from 'components/purchase-order-shipments/overview/PurchaseOrderShipmentsSidebar';
const PurchaseOrderShipmentsOverviewPage = () => { const intl = useIntl();
const title = intl.formatMessage({ id: 'purchase-order-shipments.overview.title', });
return ( <> <TitleHelmet title={title} /> <Pane sideBarContent={<PurchaseOrderShipmentsSidebar />}> <ErrorBoundary> <Box> <Breadcrumbs showBackButton={false} breadcrumbs={[ { name: title, url: '/purchase-order-shipments', }, ]} /> <ErrorBoundary> <PurchaseOrderShipmentsOverview /> </ErrorBoundary> <div /> </Box> </ErrorBoundary> </Pane> </> );};
export default PurchaseOrderShipmentsOverviewPage;
#
2. Create the detail pagesrc/pages/purchase-order-shipments/PurchaseOrderShipmentsDetailPage.tsx
import { Box, BreadCrumbs, Card, ErrorBoundary, Pane,} from '@springtree/eva-suite-ui';import { useLocation, useRouteMatch } from 'react-router-dom';import { useIntl } from 'react-intl';import { TitleHelmet } from '@springtree/eva-suite-composite';import { useParseStringToInteger } from '@springtree/eva-suite-react-hooks';import PurchaseOrderShipmentDetail from 'components/purchase-order-shipments/detail/PurchaseOrderShipmentDetail';
const PurchaseOrderShipmentDetailPage = () => { const intl = useIntl(); const match = useRouteMatch<{ id: string }>(); const location = useLocation(); const purchaseOrderShipmentID = useParseStringToInteger(match.params.id);
const title = intl.formatMessage( { id: 'purchase-order-shipment.detail.title', }, { id: purchaseOrderShipmentID, } );
return ( <> <TitleHelmet title={title} /> <Pane> <ErrorBoundary> <Box> <Breadcrumbs showBackButton={false} breadcrumbs={[ { name: title, url: `/purchase-order-shipments/${purchaseOrderShipmentID}`, }, ]} /> <ErrorBoundary> <PurchaseOrderShipmentDetail /> </ErrorBoundary> </Box> </ErrorBoundary> </Pane> </> );};
export default PurchaseOrderShipmentDetailPage;
#
3. Create the wrapper component that manages routingsrc/pages/purchase-order-shipments/PurchaseOrderShipments.tsx
import { ErrorBoundary } from '@springtree/eva-suite-ui';import { useRouteMatch, Switch, Route } from 'react-router-dom';import PurchaseOrderShipmentsOverviewPage from './PurchaseOrderShipmentsOverviewPage';import PurchaseOrderShipmentDetailPage from './PurchaseOrderShipmentDetailPage';
const PurchaseOrderShipments = () => { const match = useRouteMatch(); return ( <ErrorBoundary> <Switch> <Route path={match.path} exact> <PurchaseOrderShipmentsOverviewPage /> </Route> <Route path={`${match.path}/:id`}> <PurchaseOrderShipmentDetailPage /> </Route> </Switch> </ErrorBoundary> );};
export default PurchaseOrderShipments;
#
4. Add the wrapper to the router componentsrc/router/Router.tsx
import { AuthenticatedSwitch, AuthenticatedRoute, SpotlightShortcut,} from '@springtree/eva-suite-composite';import { EIntentAction } from '@springtree/eva-suite-uri-parser';import { useIntl } from 'react-intl';import { Redirect, Route } from 'react-router-dom';import Orchestration from 'pages/orchestration/Orchestration';import EvaIntentHandler from './EvaIntentHandler';import LoginPage from '../pages/login/Login';import ForgotPasswordPage from '../pages/forgot-password/ForgotPassword';import UnAuthorisedPage from '../pages/401/UnAuthorised';import NotFoundPage from '../pages/404/NotFound';import Orders from '../pages/orders/Orders';import PurchaseOrderShipments from '../pages/purchase-order-shipments/PurchaseOrderShipments';
/** * Configure the admin routes here */const Routes = () => { const intl = useIntl(); return ( <> <AuthenticatedSwitch loginTranslations={{ // This is an example on how to translate login translations // 'credentials.failure.not.authorized': intl.formatMessage({ id: 'credentials.failure.not.authorized', }), }} > <Route exact path='/login' component={LoginPage} /> <Route exact path='/forgot-password' component={ForgotPasswordPage} /> <Route path='/link' component={EvaIntentHandler} /> <Route path='/401' component={UnAuthorisedPage} /> <AuthenticatedRoute path='/orchestration'> <Orchestration /> </AuthenticatedRoute> <AuthenticatedRoute path='/orders'> <Orders /> </AuthenticatedRoute> <AuthenticatedRoute path='/purchase-order-shipments'> <PurchaseOrderShipments /> </AuthenticatedRoute> <Redirect from='/' exact to='/orders' /> {/* Leave this the last route */} <Route component={NotFoundPage} /> </AuthenticatedSwitch> </> );};
export default Routes;
#
5. Add the route to the SideNav componentsrc/components/side-nav/SideNav
import { useMemo } from 'react';import { IRoute } from '@springtree/eva-suite-ui/dist/components/sidenav/SideNav';import { SideNav as EvaSideNav } from '@springtree/eva-suite-ui';import { ReactComponent as OrchestrationIcon } from 'assets/icons/orchestration_icon.svg';import FileCopyIcon from '@material-ui/icons/FileCopy';import { ReactComponent as PurchaseOrderShipmentsIcon } from 'assets/icons/purchase-order-shipments.svg';import { useIntl } from 'react-intl';import { useHasFunctionality, FunctionalityScope,} from '@springtree/eva-suite-react-hooks';import { hooks, state } from '@springtree/eva-sdk-react-recoil';
const SideNav = () => { const intl = useIntl(); const ouID = hooks.useGetState(state.currentUser.currentUserState.response) ?.User.CurrentOrganizationID;
const { functionalityAllowed: ordersViewAllowed } = useHasFunctionality( 'Orders', FunctionalityScope.View, ouID );
const { functionalityAllowed: orchestrationViewAllowed } = useHasFunctionality('SymphonySheets', FunctionalityScope.View, ouID);
const { functionalityAllowed: PurchaseOrdersViewAllowed } = useHasFunctionality('PurchaseOrders', FunctionalityScope.View, ouID);
const routes = useMemo<IRoute[]>(() => { const ordersRoute: IRoute = { name: intl.formatMessage({ id: 'orders.chapter.title' }), url: '/orders', icon: <FileCopyIcon />, };
const orchestrationRoute: IRoute = { name: intl.formatMessage({ id: 'orchestration.chapter.title' }), url: '/orchestration', icon: <OrchestrationIcon />, };
const purchaseOrderShipmentsRoute: IRoute = { name: intl.formatMessage({ id: 'purchase-order-shipments.chapter.title', }), url: '/purchase-order-shipments', icon: <PurcaseOrderShipmentsIcon />, };
const authorizedRoutes: IRoute[] = [];
if (ordersViewAllowed) { authorizedRoutes.push(ordersRoute); }
if (orchestrationViewAllowed) { authorizedRoutes.push(orchestrationRoute); }
if (purchaseOrderShipmentsViewAllowed) { authorizedRoutes.push(purchaseOrderShipmentsRoute); }
return authorizedRoutes; }, [ intl, orchestrationViewAllowed, ordersViewAllowed, purchaseOrderShipmentsViewAllowed, ]);
return ( <EvaSideNav closeSidebarText={intl.formatMessage({ id: 'sidebar.close' })} routes={routes} /> );};
export default SideNav;