From 84f689555b600e7aba85340c4a2c8626fdd2fa21 Mon Sep 17 00:00:00 2001 From: David Graham Date: Wed, 29 Nov 2023 13:29:29 -0500 Subject: [PATCH] remove default margins from HtCard UI component the responsibility of layout should not be a part of the card component --- .../src/components/Manifest/ManifestForm.tsx | 742 +++++++++--------- .../SignBtn/HandlerSignBtn.spec.tsx | 120 +-- .../{HandlerSignBtn.tsx => QuickSignBtn.tsx} | 2 +- .../components/Manifest/QuickerSign/index.ts | 4 +- .../Manifest/Transporter/TransporterTable.tsx | 4 +- client/src/components/Org/UserOrg.tsx | 2 - .../SyncManifestBtn/SyncManifestBtn.tsx | 1 - client/src/components/UI/HtCard/HtCard.tsx | 37 +- client/src/features/Dashboard/Dashboard.tsx | 138 ++-- .../src/features/SiteDetails/SiteDetails.tsx | 42 +- client/src/features/SiteList/SiteList.tsx | 8 +- .../features/manifestList/ManifestList.tsx | 60 +- .../src/features/newManifest/NewManifest.tsx | 40 +- client/src/features/profile/Profile.tsx | 24 +- 14 files changed, 634 insertions(+), 590 deletions(-) rename client/src/components/Manifest/QuickerSign/SignBtn/{HandlerSignBtn.tsx => QuickSignBtn.tsx} (97%) diff --git a/client/src/components/Manifest/ManifestForm.tsx b/client/src/components/Manifest/ManifestForm.tsx index 0e81b67d8..cf0b328e4 100644 --- a/client/src/components/Manifest/ManifestForm.tsx +++ b/client/src/components/Manifest/ManifestForm.tsx @@ -7,7 +7,7 @@ import { WasteLine } from 'components/Manifest/WasteLine/wasteLineSchema'; import { RcraSiteDetails } from 'components/RcraSite'; import { HtButton, HtCard, HtForm, InfoIconTooltip } from 'components/UI'; import React, { createContext, useState } from 'react'; -import { Alert, Button, Col, Form, Row } from 'react-bootstrap'; +import { Alert, Button, Col, Form, Row, Stack } from 'react-bootstrap'; import { FormProvider, SubmitHandler, useFieldArray, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify'; @@ -15,7 +15,7 @@ import { manifest, manifestApi } from 'services'; import { ContactForm, PhoneForm } from './Contact'; import { AddHandler, GeneratorForm, Handler } from './Handler'; import { Manifest, manifestSchema, ManifestStatus } from './manifestSchema'; -import { HandlerSignBtn, QuickerSignData, QuickerSignModal } from './QuickerSign'; +import { QuickerSignData, QuickerSignModal, QuickSignBtn } from './QuickerSign'; import { Transporter, TransporterTable } from './Transporter'; import { EditWasteModal, WasteLineTable } from './WasteLine'; @@ -193,388 +193,388 @@ export function ManifestForm({ )} - - - - - - - MTN - -
- {errors.manifestTrackingNumber?.message} -
-
- - - - - {'Status '} - {!isDraft && ( - - )} - - - setManifestStatus(event.target.value as ManifestStatus | undefined) - } - > - - - - - - - - - - - - - - - - Manifest Type - - - - - - - - - -
- - - - - {'Created Date '} - - - -
{errors.createdDate?.message}
-
- - - - - {'Last Update Date '} - - - -
{errors.updatedDate?.message}
-
- - - - - {'Shipped Date '} - - - + + + + + + MTN + +
+ {errors.manifestTrackingNumber?.message} +
+
+ + + + + {'Status '} + {!isDraft && ( + + )} + + + setManifestStatus(event.target.value as ManifestStatus | undefined) + } + > + + + + + + + + + + + + + + + + Type + + + + + + + + + +
+ + + + + {'Created Date '} + + + +
{errors.createdDate?.message}
+
+ + + + + {'Last Update Date '} + + + +
{errors.updatedDate?.message}
+
+ + + + + {'Shipped Date '} + + + +
+ {errors.shippedDate?.message?.toString()} +
+
+ +
+ + + -
- {errors.shippedDate?.message?.toString()} -
-
- -
- - - -
{errors.import?.message}
- -
{errors.rejection?.message}
- - - - Potential Ship Date - {errors.import?.message} + -
{errors.potentialShipDate?.message}
-
- -
-
-
- - - - {readOnly ? ( - // if readOnly is true, show the generator in a nice read only way and display - // the button to sign for the generator. - <> - -

Emergency Contact Information

- -
- - {errors.rejection?.message}
+ + + + Potential Ship Date + - - - - ) : generator && !showGeneratorForm ? ( - <> - - -
- -
- - ) : showGeneratorForm ? ( - <> - -

Emergency Contact Information

- - - ) : ( - <> - - - - - - - - )} - { - if (!message) return null; - return ( +
{errors.potentialShipDate?.message}
+
+ + +
+
+ + + {readOnly ? ( + // if readOnly is true, show the generator in a nice read only way and display + // the button to sign for the generator. + <> + +

Emergency Contact Information

+ +
+ + + +
+ + ) : generator && !showGeneratorForm ? ( + <> + + +
+ +
+ + ) : showGeneratorForm ? ( + <> + +

Emergency Contact Information

+ + + ) : ( + <> + + + + + + + + )} + { + if (!message) return null; + return ( + + {message} + + ); + }} + /> +
+
+ + + + {readOnly ? ( + <> + ) : ( + + )} + ( {message} - ); - }} - /> - - - - - - - {readOnly ? ( - <> - ) : ( - - )} - ( - - {message} - - )} - /> - - - - - - - {readOnly ? ( - <> - ) : ( - - )} - ( - - {message} - + + + + + + {readOnly ? ( + <> + ) : ( + )} - /> - - - - - - {tsdf ? ( - <> - - -
- {/* Button to bring up the Quicker Sign modal*/} - - - -
- - ) : ( - <> - )} - {readOnly || tsdf ? ( - <> - ) : ( - - )} - { - if (!message) return null; - return ( + ( {message} - ); + )} + /> +
+
+ + + {tsdf ? ( + <> + + +
+ {/* Button to bring up the Quicker Sign modal*/} + + + +
+ + ) : ( + <> + )} + {readOnly || tsdf ? ( + <> + ) : ( + + )} + { + if (!message) return null; + return ( + + {message} + + ); + }} + /> +
+
+ + + + + +
+ + - - -
+ > + Cancel + + + + {/*If taking action that involves updating a manifest in RCRAInfo*/} {taskId && updatingRcrainfo ? : <>} diff --git a/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.spec.tsx b/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.spec.tsx index 5fa445284..135ff85f9 100644 --- a/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.spec.tsx +++ b/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.spec.tsx @@ -1,79 +1,103 @@ import '@testing-library/jest-dom'; -import { HandlerSignBtn } from 'components/Manifest/QuickerSign/index'; +import { ManifestContext } from 'components/Manifest/ManifestForm'; +import { Handler, RcraSiteType } from 'components/Manifest/manifestSchema'; +import { QuickSignBtn } from 'components/Manifest/QuickerSign/index'; import React from 'react'; import { cleanup, renderWithProviders, screen } from 'test-utils'; import { createMockMTNHandler } from 'test-utils/fixtures'; import { afterEach, describe, expect, test } from 'vitest'; +import { undefined } from 'zod'; afterEach(() => { cleanup(); }); -describe('QuickerSignModalBtn', () => { +function TestComponent({ + siteType, + handler, + signingSite, + status = 'NotAssigned', +}: { + siteType?: RcraSiteType; + handler?: Handler; + signingSite?: string; + status?: 'NotAssigned' | 'Pending' | 'Scheduled' | 'InTransit' | 'ReadyForSignature'; +}) { + if (!siteType) siteType = 'Generator'; + + return ( +
+ {/*@ts-ignore*/} + + undefined} /> + + , +
+ ); +} + +describe('QuickSignBtn', () => { test('renders', () => { - const handler = createMockMTNHandler(); - renderWithProviders( - undefined} /> - ); - expect(screen.getByRole('button')).toBeInTheDocument(); - }); - test('is disabled when already signed', () => { - const signed_handler = createMockMTNHandler({ - signed: true, - }); - renderWithProviders( - undefined} - /> - ); - expect(screen.getByRole('button')).toBeDisabled(); - }); - test('is not disabled when user org is rcrainfo integrated', () => { - const unsigned_handler = createMockMTNHandler({ - signed: false, - paperSignatureInfo: undefined, - electronicSignaturesInfo: undefined, - }); + const handlerId = 'TXD987654321'; + const handler = createMockMTNHandler({ siteType: 'Generator', epaSiteId: handlerId }); renderWithProviders( - undefined} - />, + , { - // Redux store state with an API user is required for this button to be active preloadedState: { profile: { + user: 'testuser1', org: { - name: 'Test Org', - id: '123', rcrainfoIntegrated: true, + id: '123', + name: 'Test Org', }, - user: 'username', - rcrainfoProfile: { - user: 'username', - phoneNumber: '1231231234', - apiUser: true, + sites: { + TXD987654321: { + name: 'Test Site', + handler: handler, + permissions: { eManifest: 'signer' }, + }, }, }, }, } ); - expect(screen.getByRole('button')).not.toBeDisabled(); + expect(screen.getByRole('button')).toBeInTheDocument(); + }); + test('is not disabled when user org is rcrainfo integrated', () => { + const unsigned_handler = createMockMTNHandler({ + signed: false, + electronicSignaturesInfo: [], + }); + renderWithProviders(, { + // Redux store state with an API user is required for this button to be active + preloadedState: { + profile: { + org: { + name: 'Test Org', + id: '123', + rcrainfoIntegrated: true, + }, + user: 'username', + rcrainfoProfile: { + user: 'username', + phoneNumber: '1231231234', + apiUser: true, + }, + }, + }, + }); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); }); test('is disabled when API user but already signed', () => { - // A handler that has not signed the manifest to be rendered + const epaSiteId = 'TXD987654321'; const unsigned_handler = createMockMTNHandler({ signed: true, + siteType: 'Generator', + epaSiteId, }); renderWithProviders( - undefined} - />, + , { // Redux store state with an API user is required for this button to be active preloadedState: { @@ -88,6 +112,6 @@ describe('QuickerSignModalBtn', () => { }, } ); - expect(screen.getByRole('button')).toBeDisabled(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); }); }); diff --git a/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.tsx b/client/src/components/Manifest/QuickerSign/SignBtn/QuickSignBtn.tsx similarity index 97% rename from client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.tsx rename to client/src/components/Manifest/QuickerSign/SignBtn/QuickSignBtn.tsx index b44bd64c4..32e953122 100644 --- a/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.tsx +++ b/client/src/components/Manifest/QuickerSign/SignBtn/QuickSignBtn.tsx @@ -24,7 +24,7 @@ interface QuickerSignModalBtnProps extends ButtonProps { * The button will be disabled if siteId (the EPA ID number) is not provided * @constructor */ -export function HandlerSignBtn({ +export function QuickSignBtn({ siteType, mtnHandler, handleClick, diff --git a/client/src/components/Manifest/QuickerSign/index.ts b/client/src/components/Manifest/QuickerSign/index.ts index 8fef51e2b..3ce4b25a8 100644 --- a/client/src/components/Manifest/QuickerSign/index.ts +++ b/client/src/components/Manifest/QuickerSign/index.ts @@ -1,6 +1,6 @@ -import { HandlerSignBtn } from 'components/Manifest/QuickerSign/SignBtn/HandlerSignBtn'; +import { QuickSignBtn } from 'components/Manifest/QuickerSign/SignBtn/QuickSignBtn'; import { QuickerSignature, QuickerSignData, QuickerSignForm } from './QuickerSignForm'; import { QuickerSignModal } from './QuickerSignModal'; -export { QuickerSignForm, QuickerSignModal, HandlerSignBtn }; +export { QuickerSignForm, QuickerSignModal, QuickSignBtn }; export type { QuickerSignature, QuickerSignData }; diff --git a/client/src/components/Manifest/Transporter/TransporterTable.tsx b/client/src/components/Manifest/Transporter/TransporterTable.tsx index 5b23a73a5..bc1450bc9 100644 --- a/client/src/components/Manifest/Transporter/TransporterTable.tsx +++ b/client/src/components/Manifest/Transporter/TransporterTable.tsx @@ -3,7 +3,7 @@ import { faAngleRight, faCheck, faSignature } from '@fortawesome/free-solid-svg- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Transporter } from 'components/Manifest'; import { Manifest } from 'components/Manifest/manifestSchema'; -import { HandlerSignBtn, QuickerSignData } from 'components/Manifest/QuickerSign'; +import { QuickerSignData, QuickSignBtn } from 'components/Manifest/QuickerSign'; import React, { useState } from 'react'; import { Accordion, Button, Card, Col, Row, Table, useAccordionButton } from 'react-bootstrap'; import { UseFieldArrayReturn } from 'react-hook-form'; @@ -67,7 +67,7 @@ function TransporterTable({ {readOnly ? ( - -
-

Organization

Name diff --git a/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.tsx b/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.tsx index 6299349d8..11dc4f8bf 100644 --- a/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.tsx +++ b/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.tsx @@ -37,7 +37,6 @@ export function SyncManifestBtn({ { if (siteId) manifestApi diff --git a/client/src/components/UI/HtCard/HtCard.tsx b/client/src/components/UI/HtCard/HtCard.tsx index 27ece8c6c..b8a5a4363 100644 --- a/client/src/components/UI/HtCard/HtCard.tsx +++ b/client/src/components/UI/HtCard/HtCard.tsx @@ -1,7 +1,7 @@ import { ErrorBoundary } from 'components/Error'; import { HtSpinner } from 'components/UI'; import React, { ReactElement } from 'react'; -import { Card, CardHeaderProps, CardProps } from 'react-bootstrap'; +import { Card, CardHeaderProps, CardProps, Container } from 'react-bootstrap'; interface HeaderProps extends CardHeaderProps { title?: string; @@ -14,8 +14,6 @@ interface SpinnerProps extends CardProps { /** * Card with haztrak styling, yeah baby - * @param {{children: ReactElement }} props react props - * @constructor * @example * * {top right dropdown button} @@ -23,18 +21,21 @@ interface SpinnerProps extends CardProps { * submit button here * */ -export function HtCard(props: CardProps): ReactElement { - const baseAttributes = `my-3 shadow-lg bg-white rounded px-0 ${ - props.className ? props.className : '' +export function HtCard({ className, title, children, ...props }: CardProps): ReactElement { + const classAttributes = `card shadow-lg bg-white rounded px-0 border-0 ${ + className ? className : '' }`; - const classAttributes = - props.border || props.className?.includes('border') - ? baseAttributes - : `border-0 ${baseAttributes}`; return ( - - {props.children} - +
+ {title ? ( +
+

{title}

+
+ ) : ( + <> + )} + {children} +
); } @@ -84,17 +85,17 @@ HtCard.Footer = function (props: CardProps): ReactElement { /** * Card body with Haztrak styling - * @param {{children: ReactElement}} props react props - * @constructor * @example * * Hello World! * */ -HtCard.Body = function (props: CardProps): ReactElement { +HtCard.Body = function ({ className, children, ...props }: CardProps): ReactElement { return ( - - {props.children} + + + {children} + ); }; diff --git a/client/src/features/Dashboard/Dashboard.tsx b/client/src/features/Dashboard/Dashboard.tsx index 8ec30b76f..88e6e18bd 100644 --- a/client/src/features/Dashboard/Dashboard.tsx +++ b/client/src/features/Dashboard/Dashboard.tsx @@ -42,13 +42,15 @@ export function Dashboard(): ReactElement { }; return ( - - - + + + Welcome, let's get started +

Features

+

@@ -72,70 +74,78 @@ export function Dashboard(): ReactElement {

View your site's information, check that EPA has the right information.

+

Alerts

+
+ + + { + launchExampleTask(); + }} + > + Long Running Tasks + +

Check out what a long running task will look like

+ + + { + dispatch( + addAlert({ + message: 'Oh No!', + autoClose: 1000, + type: 'warning', + }) + ); + }} + > + Show Warning + +

Check out what an alert will look like

+ +
-
- - - - - -

- - - -

-

Manifests in transit

- - -

- - - -

-

Ready for Signature

- - -

- - - -

-

Received

- -
-
-
- - { - launchExampleTask(); - }} - > - Click me - - { - dispatch( - addAlert({ - message: 'Oh No! (v2)', - id: 'home-page-test-id', - autoClose: 500, - type: 'warning', - }) - ); - }} - > - Show Alert - + + + + + + +

+ + + +

+

Manifests in transit

+ + +

+ + + +

+

Ready for Signature

+ + +

+ + + +

+

Received

+ +
+
+
); diff --git a/client/src/features/SiteDetails/SiteDetails.tsx b/client/src/features/SiteDetails/SiteDetails.tsx index 6c61086bc..508d50cb8 100644 --- a/client/src/features/SiteDetails/SiteDetails.tsx +++ b/client/src/features/SiteDetails/SiteDetails.tsx @@ -4,7 +4,7 @@ import { RcraSiteDetails } from 'components/RcraSite'; import { HtCard } from 'components/UI'; import { useHtApi } from 'hooks'; import React, { ReactElement } from 'react'; -import { Button, Container } from 'react-bootstrap'; +import { Button, Container, Stack } from 'react-bootstrap'; import { useNavigate, useParams } from 'react-router-dom'; /** @@ -22,25 +22,27 @@ export function SiteDetails(): ReactElement { if (error) throw error; return ( - -
- - -
- - - - {loading ? ( - - ) : siteData ? ( - - ) : ( - <> - )} - - + + +
+ +
+ +
+
+ + + + {loading ? ( + + ) : ( + siteData && + )} + + +
); } diff --git a/client/src/features/SiteList/SiteList.tsx b/client/src/features/SiteList/SiteList.tsx index 29b74805c..caaae5621 100644 --- a/client/src/features/SiteList/SiteList.tsx +++ b/client/src/features/SiteList/SiteList.tsx @@ -2,6 +2,7 @@ import { HaztrakSite, HtSiteTable } from 'components/HaztrakSite'; import { HtCard, HtModal } from 'components/UI'; import { useHtApi } from 'hooks'; import React, { useEffect, useState } from 'react'; +import { Container } from 'react-bootstrap'; import { Link } from 'react-router-dom'; /** @@ -21,9 +22,8 @@ export function SiteList() { }; return ( - <> - - + + {loading && !error ? ( @@ -54,6 +54,6 @@ export function SiteList() { )} - + ); } diff --git a/client/src/features/manifestList/ManifestList.tsx b/client/src/features/manifestList/ManifestList.tsx index 43c7bfcc9..1875ee27d 100644 --- a/client/src/features/manifestList/ManifestList.tsx +++ b/client/src/features/manifestList/ManifestList.tsx @@ -4,7 +4,7 @@ import { SyncManifestBtn } from 'components/Rcrainfo'; import { HtCard } from 'components/UI'; import { useTitle } from 'hooks'; import React, { ReactElement, useState } from 'react'; -import { Col, Container, Row } from 'react-bootstrap'; +import { Container, Row, Stack } from 'react-bootstrap'; import { useParams } from 'react-router-dom'; import { useGetMTNQuery } from 'store'; @@ -24,36 +24,34 @@ export function ManifestList(): ReactElement { return ( - - - -

{siteId}

- - - - - -
- - - - - {isLoading ? ( - - ) : data ? ( - - ) : ( - <> - )} - - - -
+ +

{siteId}

+
+ + + + + + + + + + + {isLoading ? ( + + ) : data ? ( + + ) : ( + <> + )} + + +
); } diff --git a/client/src/features/newManifest/NewManifest.tsx b/client/src/features/newManifest/NewManifest.tsx index 0c5d3d9f6..ddfe74583 100644 --- a/client/src/features/newManifest/NewManifest.tsx +++ b/client/src/features/newManifest/NewManifest.tsx @@ -5,7 +5,7 @@ import { RcraSite } from 'components/RcraSite'; import { HtCard } from 'components/UI'; import { useTitle } from 'hooks'; import React, { useState } from 'react'; -import { Form } from 'react-bootstrap'; +import { Col, Container, Form } from 'react-bootstrap'; import { useForm } from 'react-hook-form'; import { useParams } from 'react-router-dom'; import { siteByEpaIdSelector, useAppSelector } from 'store'; @@ -41,21 +41,29 @@ export function NewManifest() { if (!selectedSiteType || !manifestingSite) { // If the manifesting site's role on the manifest can't be automatically determined, ask the user. return ( - - -
-

Which site are you starting a manifest as?

- - - -
-
+ + + + +
+

Which site are you starting a manifest as?

+ + + +
+
+ +
); } // pass the site as the selected handler type as an initial value to the new Manifest diff --git a/client/src/features/profile/Profile.tsx b/client/src/features/profile/Profile.tsx index e473d6b2e..19fd19004 100644 --- a/client/src/features/profile/Profile.tsx +++ b/client/src/features/profile/Profile.tsx @@ -40,19 +40,23 @@ export function Profile(): ReactElement {

Profile

- - - - + + + - - - {profile.org && } - + - - + + + + {profile.org && } + + + + + + {profile.rcrainfoProfile && }