Skip to main content

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 page#

src/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 page#

src/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 routing#

src/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 component#

src/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 component#

src/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;