From f1d55bccaa27c60c144930bdee1ed77541de3ab4 Mon Sep 17 00:00:00 2001 From: David Graham Date: Tue, 28 Nov 2023 18:43:38 -0500 Subject: [PATCH 1/4] rename ProfileState interface to ProfileSlice --- client/src/components/Org/UserOrg.tsx | 4 ++-- .../components/RcraProfile/RcraProfile.tsx | 2 +- .../src/components/User/UserInfoForm.spec.tsx | 6 ++--- client/src/components/User/UserInfoForm.tsx | 4 ++-- client/src/features/profile/Profile.tsx | 4 ++-- client/src/store/index.ts | 2 +- .../src/store/profileSlice/profile.slice.ts | 22 +++++++++---------- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/client/src/components/Org/UserOrg.tsx b/client/src/components/Org/UserOrg.tsx index aa6da5660..ef94eb19b 100644 --- a/client/src/components/Org/UserOrg.tsx +++ b/client/src/components/Org/UserOrg.tsx @@ -3,11 +3,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { SiteAccess } from 'components/User/SiteAccess'; import React from 'react'; import { Col, Row, Tab, Tabs } from 'react-bootstrap'; -import { ProfileState } from 'store'; +import { ProfileSlice } from 'store'; import { OrgSitesTable } from './OrgSitesTable'; interface UserOrgProps { - profile: ProfileState; + profile: ProfileSlice; } export function UserOrg({ profile }: UserOrgProps) { diff --git a/client/src/components/RcraProfile/RcraProfile.tsx b/client/src/components/RcraProfile/RcraProfile.tsx index da678a74e..f64068bca 100644 --- a/client/src/components/RcraProfile/RcraProfile.tsx +++ b/client/src/components/RcraProfile/RcraProfile.tsx @@ -53,7 +53,7 @@ export function RcraProfile({ profile }: ProfileViewProps) { /** * submitting the RcraProfile form (RCRAInfo API ID, Key, username, etc.) - * @param data {ProfileState} + * @param data {ProfileSlice} */ const onSubmit = (data: RcraProfileForm) => { setProfileLoading(!profileLoading); diff --git a/client/src/components/User/UserInfoForm.spec.tsx b/client/src/components/User/UserInfoForm.spec.tsx index 3a34ae53f..53f99f1f7 100644 --- a/client/src/components/User/UserInfoForm.spec.tsx +++ b/client/src/components/User/UserInfoForm.spec.tsx @@ -5,7 +5,7 @@ import { UserInfoForm } from 'components/User/UserInfoForm'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; import React from 'react'; -import { HaztrakUser, ProfileState } from 'store'; +import { HaztrakUser, ProfileSlice } from 'store'; import { renderWithProviders, screen } from 'test-utils'; import { API_BASE_URL } from 'test-utils/mock/handlers'; import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from 'vitest'; @@ -41,7 +41,7 @@ describe('UserProfile', () => { username: 'test', firstName: 'David', }; - const profile: ProfileState = { + const profile: ProfileSlice = { user: 'test', }; renderWithProviders(, {}); @@ -54,7 +54,7 @@ describe('UserProfile', () => { const user: HaztrakUser = { ...DEFAULT_USER, }; - const profile: ProfileState = { + const profile: ProfileSlice = { user: 'test', }; renderWithProviders(, {}); diff --git a/client/src/components/User/UserInfoForm.tsx b/client/src/components/User/UserInfoForm.tsx index a25a032ec..748b15dfb 100644 --- a/client/src/components/User/UserInfoForm.tsx +++ b/client/src/components/User/UserInfoForm.tsx @@ -8,12 +8,12 @@ import { Button, Col, Container, Form, Row } from 'react-bootstrap'; import { useForm } from 'react-hook-form'; import { toast } from 'react-toastify'; import { UserApi } from 'services'; -import { HaztrakUser, ProfileState, updateUserProfile, useAppDispatch } from 'store'; +import { HaztrakUser, ProfileSlice, updateUserProfile, useAppDispatch } from 'store'; import { z } from 'zod'; interface UserProfileProps { user: HaztrakUser; - profile: ProfileState; + profile: ProfileSlice; } const haztrakUserForm = z.object({ diff --git a/client/src/features/profile/Profile.tsx b/client/src/features/profile/Profile.tsx index 822f9e2c4..e473d6b2e 100644 --- a/client/src/features/profile/Profile.tsx +++ b/client/src/features/profile/Profile.tsx @@ -8,7 +8,7 @@ import { Col, Container, Row } from 'react-bootstrap'; import { getRcraProfile, HaztrakUser, - ProfileState, + ProfileSlice, selectRcraProfile, selectUser, useAppDispatch, @@ -22,7 +22,7 @@ import { */ export function Profile(): ReactElement { const dispatch = useAppDispatch(); - const profile: ProfileState | undefined = useAppSelector(selectRcraProfile); + const profile: ProfileSlice | undefined = useAppSelector(selectRcraProfile); const user: HaztrakUser | undefined = useAppSelector(selectUser); useTitle('Profile'); diff --git a/client/src/store/index.ts b/client/src/store/index.ts index 36eb83ca5..6a60b86a8 100644 --- a/client/src/store/index.ts +++ b/client/src/store/index.ts @@ -65,7 +65,7 @@ export type { HaztrakError } from './errorSlice/error.slice'; export type { TaskStatus } from './haztrakApiSlice'; export type { LongRunningTask, HaztrakAlert } from './notificationSlice/notification.slice'; export type { - ProfileState, + ProfileSlice, RcrainfoProfileState, HaztrakProfileSite, HaztrakSitePermissions, diff --git a/client/src/store/profileSlice/profile.slice.ts b/client/src/store/profileSlice/profile.slice.ts index 24ba4b37f..fba8880df 100644 --- a/client/src/store/profileSlice/profile.slice.ts +++ b/client/src/store/profileSlice/profile.slice.ts @@ -8,7 +8,7 @@ import { UserApi } from 'services'; import { RootState } from 'store'; /**The user's RCRAInfo account data stored in the Redux store*/ -export interface ProfileState { +export interface ProfileSlice { user: string | undefined; rcrainfoProfile?: RcrainfoProfile>; sites?: Record; @@ -64,7 +64,7 @@ export interface RcrainfoSitePermissions { } /**initial, state of a user's RcraProfile.*/ -const initialState: ProfileState = { +const initialState: ProfileSlice = { user: undefined, rcrainfoProfile: undefined, sites: undefined, @@ -88,11 +88,11 @@ export const getHaztrakProfile = createAsyncThunk('profile/getHaztrakProfile', a user: data.user, org: data.org, sites: sites, - } as ProfileState; + } as ProfileSlice; }); /**Retrieves a user's RcrainfoProfile, if it exists, from the server.*/ -export const getRcraProfile = createAsyncThunk( +export const getRcraProfile = createAsyncThunk( 'profile/getRcrainfoProfile', async (arg, thunkAPI) => { const state = thunkAPI.getState() as RootState; @@ -112,7 +112,7 @@ export const getRcraProfile = createAsyncThunk( }; }, {}), }, - } as ProfileState; + } as ProfileSlice; } ); @@ -120,7 +120,7 @@ const profileSlice = createSlice({ name: 'profile', initialState, reducers: { - updateProfile: (state: ProfileState, action: PayloadAction) => { + updateProfile: (state: ProfileSlice, action: PayloadAction) => { return { ...state, ...action.payload, @@ -176,7 +176,7 @@ const profileSlice = createSlice({ /** Retrieve a Haztrak site from the users Profile by the site's EPA ID number */ export const siteByEpaIdSelector = (epaId: string | undefined) => createSelector( - (state: { profile: ProfileState }) => state.profile.sites, + (state: { profile: ProfileSlice }) => state.profile.sites, (sites: Record | undefined) => { if (!sites) return undefined; @@ -192,7 +192,7 @@ export const siteByEpaIdSelector = (epaId: string | undefined) => /** Get all sites a user has access to their Haztrak Profile*/ export const selectHaztrakSites = createSelector( - (state: { profile: ProfileState }) => state.profile.sites, + (state: { profile: ProfileSlice }) => state.profile.sites, (sites: Record | undefined) => { if (!sites) return undefined; @@ -202,7 +202,7 @@ export const selectHaztrakSites = createSelector( /** select all RCRAInfo sites a user has access to from their RCRAInfo Profile if they're updated it*/ export const selectRcrainfoSites = createSelector( - (state: { profile: ProfileState }) => state.profile.rcrainfoProfile?.rcraSites, + (state: { profile: ProfileSlice }) => state.profile.rcrainfoProfile?.rcraSites, (rcraSites: Record | undefined) => { if (!rcraSites) return undefined; @@ -213,13 +213,13 @@ export const selectRcrainfoSites = createSelector( /** Retrieve a user's RcraProfile from the Redux store. */ export const selectRcraProfile = createSelector( (state: RootState) => state.profile, - (rcraProfile: ProfileState) => rcraProfile + (rcraProfile: ProfileSlice) => rcraProfile ); /** Retrieve a user's HaztrakProfile from the Redux store. */ export const selectHaztrakProfile = createSelector( (state: RootState) => state.profile, - (haztrakProfile: ProfileState) => haztrakProfile + (haztrakProfile: ProfileSlice) => haztrakProfile ); export default profileSlice.reducer; From 9c2331cd62f67476cf29e079129e9cb8bc6c00e1 Mon Sep 17 00:00:00 2001 From: David Graham Date: Tue, 28 Nov 2023 19:06:48 -0500 Subject: [PATCH 2/4] do not show quicker sign button if the site is not in the user's list of Haztrak Sites they have access to --- .../components/Manifest/QuickerSign/QuickerSignModalBtn.tsx | 5 +++-- client/src/store/profileSlice/profile.slice.ts | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.tsx b/client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.tsx index a54ac711d..d8163c638 100644 --- a/client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.tsx +++ b/client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.tsx @@ -4,6 +4,7 @@ import { Handler, RcraSiteType } from 'components/Manifest/manifestSchema'; import { RcraApiUserBtn } from 'components/Rcrainfo'; import React from 'react'; import { ButtonProps } from 'react-bootstrap'; +import { siteByEpaIdSelector, useAppSelector } from 'store'; interface QuickerSignData { handler: Handler | undefined; @@ -29,8 +30,8 @@ export function QuickerSignModalBtn({ disabled, iconOnly = false, }: QuickerSignModalBtnProps) { - if (mtnHandler === undefined) { - return null; + if (!useAppSelector(siteByEpaIdSelector(mtnHandler?.epaSiteId))) { + return <>; } return ( +export const siteByEpaIdSelector = ( + epaId: string | undefined +): ((state: RootState) => RcraSite | undefined) => createSelector( (state: { profile: ProfileSlice }) => state.profile.sites, (sites: Record | undefined) => { From c8c576c5115222db485cba5379feddd6dc8fc215 Mon Sep 17 00:00:00 2001 From: David Graham Date: Tue, 28 Nov 2023 19:22:03 -0500 Subject: [PATCH 3/4] move quicker sign buttons to SignBtn folder add manifest service module for business logic related to the uniform hazardous waste manifest move api related web service modules to a subdirectory called APIs --- .../src/components/Manifest/ManifestForm.tsx | 23 ++---- .../Manifest/QuickerSign/QuickerSignForm.tsx | 25 +++++- .../HandlerSignBtn.spec.tsx} | 14 ++-- .../HandlerSignBtn.tsx} | 15 ++-- .../components/Manifest/QuickerSign/index.ts | 7 +- .../Manifest/QuickerSign/quickerSignSchema.ts | 22 ------ .../Manifest/Transporter/TransporterTable.tsx | 4 +- .../src/components/Manifest/manifestSchema.ts | 75 +++++------------- .../SyncManifestBtn/SyncManifestBtn.tsx | 2 +- client/src/services/{ => APIs}/UserApi.ts | 2 +- client/src/services/{ => APIs}/htApi.ts | 0 client/src/services/{ => APIs}/manifestApi.ts | 2 +- client/src/services/index.ts | 8 +- client/src/services/manifest/manifest.spec.ts | 76 +++++++++++++++++++ client/src/services/manifest/manifest.ts | 31 ++++++++ 15 files changed, 181 insertions(+), 125 deletions(-) rename client/src/components/Manifest/QuickerSign/{QuickerSignModalBtn.spec.tsx => SignBtn/HandlerSignBtn.spec.tsx} (89%) rename client/src/components/Manifest/QuickerSign/{QuickerSignModalBtn.tsx => SignBtn/HandlerSignBtn.tsx} (79%) delete mode 100644 client/src/components/Manifest/QuickerSign/quickerSignSchema.ts rename client/src/services/{ => APIs}/UserApi.ts (97%) rename client/src/services/{ => APIs}/htApi.ts (100%) rename client/src/services/{ => APIs}/manifestApi.ts (95%) create mode 100644 client/src/services/manifest/manifest.spec.ts create mode 100644 client/src/services/manifest/manifest.ts diff --git a/client/src/components/Manifest/ManifestForm.tsx b/client/src/components/Manifest/ManifestForm.tsx index 0229526b0..0e81b67d8 100644 --- a/client/src/components/Manifest/ManifestForm.tsx +++ b/client/src/components/Manifest/ManifestForm.tsx @@ -11,11 +11,11 @@ import { Alert, Button, Col, Form, Row } from 'react-bootstrap'; import { FormProvider, SubmitHandler, useFieldArray, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify'; -import { manifestApi } from 'services/manifestApi'; +import { manifest, manifestApi } from 'services'; import { ContactForm, PhoneForm } from './Contact'; import { AddHandler, GeneratorForm, Handler } from './Handler'; import { Manifest, manifestSchema, ManifestStatus } from './manifestSchema'; -import { QuickerSignData, QuickerSignModal, QuickerSignModalBtn } from './QuickerSign'; +import { HandlerSignBtn, QuickerSignData, QuickerSignModal } from './QuickerSign'; import { Transporter, TransporterTable } from './Transporter'; import { EditWasteModal, WasteLineTable } from './WasteLine'; @@ -34,6 +34,7 @@ export interface ManifestContextType { setTsdfStateCode: React.Dispatch>; editWasteLineIndex?: number; setEditWasteLineIndex: React.Dispatch>; + signingSite?: string | undefined; } interface ManifestFormProps { @@ -51,6 +52,7 @@ export const ManifestContext = createContext({ setTsdfStateCode: () => {}, editWasteLineIndex: undefined, setEditWasteLineIndex: () => {}, + signingSite: undefined, }); /** @@ -138,7 +140,7 @@ export function ManifestForm({ const [showSignForm, setShowSignForm] = useState(false); const [quickerSignHandler, setQuickerSignHandler] = useState({ handler: undefined, - siteType: 'Generator', // ToDo initialize to undefined + siteType: 'Generator', }); const toggleQuickerSignShow = () => setShowSignForm(!showSignForm); const setupSign = (signContext: QuickerSignData) => { @@ -178,6 +180,7 @@ export function ManifestForm({ setTsdfStateCode: setTsdfStateCode, editWasteLineIndex: editWasteLine, setEditWasteLineIndex: setEditWasteLine, + signingSite: manifest.getNextSigner(manifestData), }} > @@ -389,7 +392,7 @@ export function ManifestForm({
- ) : generator && !showGeneratorForm ? ( - // If the form holds a value for generator, but they don't need to edit the - // generators values (allowed) then display the site details in a nice read only way <> @@ -409,16 +410,12 @@ export function ManifestForm({
) : showGeneratorForm ? ( - // Show the Handler form with current value for the generator - // The HandlerForm allows for fine-grained control over the handler inputs <>

Emergency Contact Information

) : ( - // default on a blank manifest, ask if they'd like to search for a generator to - // add, or if the user would like to manually enter the generator's info. <> - {/* List transporters */} - {/* Table Showing current Waste Lines included on the manifest */} - {/* Where The Tsdf information is added and displayed */} @@ -517,7 +511,7 @@ export function ManifestForm({
{/* Button to bring up the Quicker Sign modal*/} - - {/* Additional information for the manifest, such as reference information*/} diff --git a/client/src/components/Manifest/QuickerSign/QuickerSignForm.tsx b/client/src/components/Manifest/QuickerSign/QuickerSignForm.tsx index f5e83f4ca..8eb8f9231 100644 --- a/client/src/components/Manifest/QuickerSign/QuickerSignForm.tsx +++ b/client/src/components/Manifest/QuickerSign/QuickerSignForm.tsx @@ -2,7 +2,6 @@ import { faFileSignature } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { AxiosError } from 'axios'; import { Handler, RcraSiteType } from 'components/Manifest/manifestSchema'; -import { QuickerSignature } from 'components/Manifest/QuickerSign/quickerSignSchema'; import { Transporter } from 'components/Manifest/Transporter'; import { HtForm } from 'components/UI'; import React from 'react'; @@ -10,8 +9,30 @@ import { Button, Col, Container, Form, ListGroup, Row } from 'react-bootstrap'; import { SubmitHandler, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify'; -import { manifestApi } from 'services/manifestApi'; +import { manifestApi } from 'services'; import { selectUserName, useAppSelector } from 'store'; +import { z } from 'zod'; + +const siteType = z.enum(['Transporter', 'Generator', 'Tsdf', 'Broker']); +/** + * The EPA Quicker Sign schema + */ +const quickerSignatureSchema = z.object({ + siteId: z.string(), + siteType: siteType, + transporterOrder: z.number().optional(), + printedSignatureName: z.string(), + printedSignatureDate: z.string(), + manifestTrackingNumbers: z.string().array(), +}); + +const quickerSignDataSchema = z.object({ + handler: z.any(), + siteType: z.enum(['Generator', 'Transporter', 'Tsdf']), +}); + +export type QuickerSignData = z.infer; +export type QuickerSignature = z.infer; interface QuickerSignProps { mtn: Array; diff --git a/client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.spec.tsx b/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.spec.tsx similarity index 89% rename from client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.spec.tsx rename to client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.spec.tsx index fb26128cb..5fa445284 100644 --- a/client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.spec.tsx +++ b/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.spec.tsx @@ -1,5 +1,5 @@ import '@testing-library/jest-dom'; -import { QuickerSignModalBtn } from 'components/Manifest/QuickerSign/index'; +import { HandlerSignBtn } from 'components/Manifest/QuickerSign/index'; import React from 'react'; import { cleanup, renderWithProviders, screen } from 'test-utils'; import { createMockMTNHandler } from 'test-utils/fixtures'; @@ -13,11 +13,7 @@ describe('QuickerSignModalBtn', () => { test('renders', () => { const handler = createMockMTNHandler(); renderWithProviders( - undefined} - /> + undefined} /> ); expect(screen.getByRole('button')).toBeInTheDocument(); }); @@ -26,7 +22,7 @@ describe('QuickerSignModalBtn', () => { signed: true, }); renderWithProviders( - undefined} @@ -41,7 +37,7 @@ describe('QuickerSignModalBtn', () => { electronicSignaturesInfo: undefined, }); renderWithProviders( - undefined} @@ -73,7 +69,7 @@ describe('QuickerSignModalBtn', () => { signed: true, }); renderWithProviders( - undefined} diff --git a/client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.tsx b/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.tsx similarity index 79% rename from client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.tsx rename to client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.tsx index d8163c638..b44bd64c4 100644 --- a/client/src/components/Manifest/QuickerSign/QuickerSignModalBtn.tsx +++ b/client/src/components/Manifest/QuickerSign/SignBtn/HandlerSignBtn.tsx @@ -1,8 +1,9 @@ import { faFeather } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { ManifestContext } from 'components/Manifest/ManifestForm'; import { Handler, RcraSiteType } from 'components/Manifest/manifestSchema'; import { RcraApiUserBtn } from 'components/Rcrainfo'; -import React from 'react'; +import React, { useContext } from 'react'; import { ButtonProps } from 'react-bootstrap'; import { siteByEpaIdSelector, useAppSelector } from 'store'; @@ -23,16 +24,18 @@ interface QuickerSignModalBtnProps extends ButtonProps { * The button will be disabled if siteId (the EPA ID number) is not provided * @constructor */ -export function QuickerSignModalBtn({ +export function HandlerSignBtn({ siteType, mtnHandler, handleClick, disabled, iconOnly = false, }: QuickerSignModalBtnProps) { - if (!useAppSelector(siteByEpaIdSelector(mtnHandler?.epaSiteId))) { - return <>; - } + const { signingSite } = useContext(ManifestContext); + if (!useAppSelector(siteByEpaIdSelector(mtnHandler?.epaSiteId))) return <>; + + if (mtnHandler?.epaSiteId !== signingSite) return <>; + return ( { @@ -40,7 +43,7 @@ export function QuickerSignModalBtn({ }} disabled={disabled} > - {iconOnly ? '' : 'Quicker Sign '} + {iconOnly ? '' : 'Sign '} ); diff --git a/client/src/components/Manifest/QuickerSign/index.ts b/client/src/components/Manifest/QuickerSign/index.ts index 90aef679f..8fef51e2b 100644 --- a/client/src/components/Manifest/QuickerSign/index.ts +++ b/client/src/components/Manifest/QuickerSign/index.ts @@ -1,7 +1,6 @@ -import { QuickerSignForm } from './QuickerSignForm'; +import { HandlerSignBtn } from 'components/Manifest/QuickerSign/SignBtn/HandlerSignBtn'; +import { QuickerSignature, QuickerSignData, QuickerSignForm } from './QuickerSignForm'; import { QuickerSignModal } from './QuickerSignModal'; -import { QuickerSignModalBtn } from './QuickerSignModalBtn'; -import { QuickerSignature, QuickerSignData } from './quickerSignSchema'; -export { QuickerSignForm, QuickerSignModal, QuickerSignModalBtn }; +export { QuickerSignForm, QuickerSignModal, HandlerSignBtn }; export type { QuickerSignature, QuickerSignData }; diff --git a/client/src/components/Manifest/QuickerSign/quickerSignSchema.ts b/client/src/components/Manifest/QuickerSign/quickerSignSchema.ts deleted file mode 100644 index 0744cb6ae..000000000 --- a/client/src/components/Manifest/QuickerSign/quickerSignSchema.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { z } from 'zod'; - -const siteType = z.enum(['Transporter', 'Generator', 'Tsdf', 'Broker']); -/** - * The EPA Quicker Sign schema - */ -const quickerSignatureSchema = z.object({ - siteId: z.string(), - siteType: siteType, - transporterOrder: z.number().optional(), - printedSignatureName: z.string(), - printedSignatureDate: z.string(), - manifestTrackingNumbers: z.string().array(), -}); - -const quickerSignDataSchema = z.object({ - handler: z.any(), - siteType: z.enum(['Generator', 'Transporter', 'Tsdf']), -}); - -export type QuickerSignData = z.infer; -export type QuickerSignature = z.infer; diff --git a/client/src/components/Manifest/Transporter/TransporterTable.tsx b/client/src/components/Manifest/Transporter/TransporterTable.tsx index d2e8007a6..5b23a73a5 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 { QuickerSignData, QuickerSignModalBtn } from 'components/Manifest/QuickerSign'; +import { HandlerSignBtn, QuickerSignData } 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 ? ( - ; export const rcraSiteType = z.enum(['Generator', 'Tsdf', 'Transporter']); -/** - * The RCRAInfo/e-Manifest enum used to indicate site type - */ +/** The RCRAInfo/e-Manifest enum used to indicate site type*/ export type RcraSiteType = z.infer; export const paperSignatureSchema = z.object({ @@ -22,13 +17,9 @@ export const paperSignatureSchema = z.object({ export type PaperSignature = z.infer; -/** - * Schema for signer of a hazardous waste manifest - */ +/** Schema for signer of a hazardous waste manifest*/ const signerSchema = z.object({ - /** - * User's RCRAInfo username - */ + /** User's RCRAInfo username*/ userId: z.string().optional(), firstName: z.string().optional(), middleInitial: z.string().optional(), @@ -38,33 +29,22 @@ const signerSchema = z.object({ companyName: z.string().optional(), contactType: z.enum(['Email', 'Text', 'Voice']), }); -/** - * EPA's RCRAInfo electronic signature schema - */ +/** EPA's RCRAInfo electronic signature schema*/ export const electronicSignatureSchema = z.object({ signer: signerSchema.optional(), signatureDate: z.date().optional(), - /** - * Object representing metadata on a printable, human friendly, version of the manifest - * ToDo: see the USEPA/e-Manifest documentation on GitHub - */ + /**Object representing metadata on a printable, human friendly, version of the manifest */ humanReadableDocument: z.any().optional(), }); export const handlerSchema = rcraSite.extend({ paperSignatureInfo: paperSignatureSchema.optional(), electronicSignaturesInfo: electronicSignatureSchema.array().optional(), - /** - * Property on by back end to signify whether the handler has signed - */ + /**Haztrak specific Property to signify whether the handler has signed*/ signed: z.boolean().optional(), }); -/** - * The Handler extends the RcraSite schema and adds manifest specific data - */ + +/**The Handler extends the RcraSite schema and adds manifest specific data*/ export type Handler = z.infer; -/** - * The Signature that appears on paper versions of the manifest - */ export const transporterSchema = handlerSchema.extend({ order: z.number(), @@ -83,42 +63,24 @@ const manifestStatusEnum = z.enum([ 'MtnValidationFailed', ]); -/** - * Available statuses for a manifest as defined by EPA - */ +/** Available statuses for a manifest as defined by EPA*/ export type ManifestStatus = z.infer; -/** - * The Transporter type extends the Handler schema and adds transporter - * specific data, such as their order on the manifest - */ +/** The Manifest Transporter type extends the Handler schema*/ export type Transporter = z.infer; -/** - * A signer of a hazardous waste manifest - */ +/** A signer of a hazardous waste manifest*/ export type Signer = z.infer; -/** - * RCRAInfo electronic signature definition - */ +/** RCRAInfo electronic signature definition*/ export type ElectronicSignature = z.infer; export const manifestSchema = z .object({ manifestTrackingNumber: z.string().optional(), - /** - * The date-time the manifest was created in the e-Manifest system. - * Managed by EPA's systems. - */ + /** The date-time the manifest was created in the e-Manifest system, managed by EPA's systems.*/ createdDate: z.string().optional(), - /** - * The last date-time manifest was updated in the e-Manifest system. - * Managed by EPA's systems. - */ + /** The last date-time manifest was updated in the e-Manifest system, managed by EPA's systems.*/ updatedDate: z.string().optional(), - /** - * The date-time the hazardous waste shipment departed (was signed by) the generator. - * Managed by EPA's systems, but not present on pre-shipment manifests. - */ + /** The date-time the waste shipment departed the generator. Managed by EPA's systems*/ shippedDate: z.string().optional(), import: z.boolean().optional(), rejection: z.boolean().optional(), @@ -127,16 +89,13 @@ export const manifestSchema = z .optional() .transform((val) => { if (val === '' || val === undefined) { - // If empty string or undefined, return undefined return undefined; } else { return new Date(val).toISOString(); } }), status: manifestStatusEnum, - /** - * Whether the manifest is publicly available through EPA - */ + /** Whether the manifest is publicly available through EPA*/ isPublic: z.boolean().optional(), generator: handlerSchema.optional(), transporters: z.array(transporterSchema), diff --git a/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.tsx b/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.tsx index 052b12a37..6299349d8 100644 --- a/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.tsx +++ b/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.tsx @@ -4,7 +4,7 @@ import { AxiosError } from 'axios'; import { RcraApiUserBtn } from 'components/Rcrainfo/buttons/RcraApiUserBtn/RcraApiUserBtn'; import { useProgressTracker } from 'hooks'; import React, { useEffect, useState } from 'react'; -import { manifestApi } from 'services/manifestApi'; +import { manifestApi } from 'services'; import { addTask, updateTask, useAppDispatch } from 'store'; interface SyncManifestProps { diff --git a/client/src/services/UserApi.ts b/client/src/services/APIs/UserApi.ts similarity index 97% rename from client/src/services/UserApi.ts rename to client/src/services/APIs/UserApi.ts index 7656a27fa..67804a049 100644 --- a/client/src/services/UserApi.ts +++ b/client/src/services/APIs/UserApi.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { HaztrakSite } from 'components/HaztrakSite'; -import { htApi } from 'services/htApi'; import { HaztrakModulePermissions, HaztrakUser, RcrainfoProfile, RcrainfoProfileSite } from 'store'; +import { htApi } from './htApi'; interface HaztrakOrgResponse { id: string; diff --git a/client/src/services/htApi.ts b/client/src/services/APIs/htApi.ts similarity index 100% rename from client/src/services/htApi.ts rename to client/src/services/APIs/htApi.ts diff --git a/client/src/services/manifestApi.ts b/client/src/services/APIs/manifestApi.ts similarity index 95% rename from client/src/services/manifestApi.ts rename to client/src/services/APIs/manifestApi.ts index 60eddd6a6..3ad145a67 100644 --- a/client/src/services/manifestApi.ts +++ b/client/src/services/APIs/manifestApi.ts @@ -1,8 +1,8 @@ import { AxiosResponse } from 'axios'; import { Manifest } from 'components/Manifest'; import { QuickerSignature } from 'components/Manifest/QuickerSign'; -import { htApi } from 'services/htApi'; import { TaskStatus } from 'store'; +import { htApi } from './htApi'; export const manifestApi = { /** Sign a manifest through the Haztrak Proxy endpoint */ diff --git a/client/src/services/index.ts b/client/src/services/index.ts index 9f7f7fb86..0f76efe72 100644 --- a/client/src/services/index.ts +++ b/client/src/services/index.ts @@ -1,4 +1,4 @@ -import { htApi } from 'services/htApi'; -import { UserApi } from 'services/UserApi'; - -export { htApi, UserApi }; +export { htApi } from 'services/APIs/htApi'; +export { UserApi } from 'services/APIs/UserApi'; +export { manifestApi } from 'services/APIs/manifestApi'; +export { manifest } from 'services/manifest/manifest'; diff --git a/client/src/services/manifest/manifest.spec.ts b/client/src/services/manifest/manifest.spec.ts new file mode 100644 index 000000000..0255c805f --- /dev/null +++ b/client/src/services/manifest/manifest.spec.ts @@ -0,0 +1,76 @@ +import { manifest } from 'services/manifest/manifest'; +import { + createMockManifest, + createMockMTNHandler, + createMockTransporter, +} from 'test-utils/fixtures'; +import { describe, expect, test } from 'vitest'; + +describe('manifest.getNextSigner', () => { + test('returns the the generator if scheduled and no generator signature present', () => { + // Arrange + const mockId = 'VATESTGEN001'; + const generator = createMockMTNHandler({ + epaSiteId: mockId, + electronicSignaturesInfo: [], + siteType: 'Generator', + }); + const mockManifest = createMockManifest({ + status: 'Scheduled', + generator, + }); + // Act + const result = manifest.getNextSigner(mockManifest); + // Assert + expect(result).toBe(mockId); + }); + test('returns the TSDF if ReadyForSignature', () => { + const mockId = 'VATESTTSDF001'; + const designatedFacility = createMockMTNHandler({ + epaSiteId: mockId, + electronicSignaturesInfo: [], + siteType: 'Tsdf', + }); + const mockManifest = createMockManifest({ + status: 'ReadyForSignature', + designatedFacility, + }); + const result = manifest.getNextSigner(mockManifest); + expect(result).toBe(mockId); + }); + test('returns the first transporter if "Scheduled" status and generator signed', () => { + const mockId = 'VATESTTRANS1'; + const mockTransporter = createMockTransporter({ + epaSiteId: mockId, + electronicSignaturesInfo: [], + siteType: 'Transporter', + }); + const generator = createMockMTNHandler({ + epaSiteId: mockId, + electronicSignaturesInfo: [ + { + signer: { firstName: 'John', lastName: 'Doe', contactType: 'Email' }, + }, + ], + siteType: 'Generator', + }); + const mockManifest = createMockManifest({ + status: 'Scheduled', + transporters: [mockTransporter], + generator, + }); + const result = manifest.getNextSigner(mockManifest); + expect(result).toBe(mockId); + }); + test.each(['NotAssigned', 'Pending', 'Corrected', 'Signed', 'UnderCorrection'])( + `returns undefined if status is %s`, + (status: string) => { + const mockManifest = createMockManifest({ + // @ts-ignore + status, + }); + const result = manifest.getNextSigner(mockManifest); + expect(result).toBe(undefined); + } + ); +}); diff --git a/client/src/services/manifest/manifest.ts b/client/src/services/manifest/manifest.ts new file mode 100644 index 000000000..f7f2d038f --- /dev/null +++ b/client/src/services/manifest/manifest.ts @@ -0,0 +1,31 @@ +import { Manifest } from 'components/Manifest'; + +export const manifest = { + /** Returns EPA ID of the next site that can sign on a manifest or undefined if not applicable. */ + getNextSigner(manifest: Partial | undefined): string | undefined { + if (manifest === undefined || manifest === null || manifest.status === undefined) { + return undefined; + } + if ( + manifest.status === 'Scheduled' && + !manifest.generator?.electronicSignaturesInfo?.[0]?.signer && + manifest.generator + ) { + return manifest.generator.epaSiteId; + } + if (manifest.status === 'Scheduled' && manifest.transporters?.length === 1) { + return manifest.transporters[0].epaSiteId; + } + if (manifest.status === 'InTransit' && manifest.transporters) { + for (const transporter of manifest.transporters) { + if (!transporter.electronicSignaturesInfo?.[0].signer) { + return transporter.epaSiteId; + } + } + } + if (manifest.status === 'ReadyForSignature' && manifest.designatedFacility) { + return manifest.designatedFacility.epaSiteId; + } + return undefined; + }, +}; From 0ddfca5c33e270faccaac3332308a70fe9b195db Mon Sep 17 00:00:00 2001 From: David Graham Date: Wed, 29 Nov 2023 13:29:29 -0500 Subject: [PATCH 4/4] 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 && }