From 8e168ba1479107683ac6e4937c1389ae86c581e0 Mon Sep 17 00:00:00 2001 From: David Paul Graham Date: Thu, 14 Dec 2023 19:19:54 -0500 Subject: [PATCH] A number of misc minor polishings and fixes for the demo today, including... use TrakBaseSerializer for removing null fields before sending to RCRAInfo for additional Info serializer Add a new Group called Org Admin that has permission to see all haztrak specific model instances sidebar html structure fixes misc aria fixes update HtSpinner props to allow for automatic centering adjust logic for manifest Floating Action Buttons --- client/src/App.tsx | 2 +- client/src/components/Layout/Nav/NavItem.tsx | 8 +- .../src/components/Layout/Nav/NavSection.tsx | 26 +--- client/src/components/Layout/Root.tsx | 8 +- .../Manifest/Buttons/ManifestFABs.tsx | 2 +- .../Handler/Search/HandlerSearchForm.tsx | 10 +- .../Search/RcrainfoSiteSearchBadge.tsx | 4 +- .../src/components/Manifest/ManifestForm.tsx | 15 +- .../Manifest/UpdateRcra/UpdateRcra.tsx | 2 +- .../components/RcraProfile/RcraProfile.tsx | 10 +- .../buttons/RcraApiUserBtn/RcraApiUserBtn.tsx | 8 +- client/src/components/UI/HtCard/HtCard.tsx | 2 +- client/src/components/UI/HtSpinner.tsx | 17 ++- client/src/components/User/UserInfoForm.tsx | 9 +- .../features/ManifestList/ManifestList.tsx | 2 +- .../MmanifestDetails/ManifestDetails.tsx | 2 +- .../src/features/SiteDetails/SiteDetails.tsx | 2 +- client/src/features/SiteList/SiteList.tsx | 6 +- server/apps/core/admin.py | 7 +- .../trak/serializers/manifest_serializer.py | 2 +- server/apps/trak/urls.py | 6 +- server/apps/trak/views/manifest_view.py | 5 +- server/fixtures/dev_data.yaml | 137 +++++++++++++++++- 23 files changed, 198 insertions(+), 94 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 226ccb3da..c821e71d9 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -45,7 +45,7 @@ function App(): ReactElement { + } diff --git a/client/src/components/Layout/Nav/NavItem.tsx b/client/src/components/Layout/Nav/NavItem.tsx index 17f8f4770..ed1cd9af7 100644 --- a/client/src/components/Layout/Nav/NavItem.tsx +++ b/client/src/components/Layout/Nav/NavItem.tsx @@ -4,6 +4,7 @@ import { NavContext, NavContextProps } from 'components/Layout/Root'; import { Route } from 'components/Layout/Sidebar/SidebarRoutes'; import React, { useContext } from 'react'; import { Link } from 'react-router-dom'; +import { NavLink } from 'react-bootstrap'; interface NavItemProps { route: Route; @@ -18,13 +19,14 @@ export function NavItem({ route, targetBlank }: NavItemProps) { }; return ( - - + {route.text} {route.external && ( )} - + ); } diff --git a/client/src/components/Layout/Nav/NavSection.tsx b/client/src/components/Layout/Nav/NavSection.tsx index 8bba73f7d..0d1089682 100644 --- a/client/src/components/Layout/Nav/NavSection.tsx +++ b/client/src/components/Layout/Nav/NavSection.tsx @@ -10,30 +10,10 @@ interface SidebarSectionProps { } export function NavSection({ section }: SidebarSectionProps) { - const [collapsed, setCollapsed] = useState(true); return ( <> - {/* setCollapsed(!collapsed)}*/} - {/* aria-controls="collapseSite"*/} - {/* aria-expanded={collapsed}*/} - {/*>*/} - {/*

{section.name}

*/} - {/* */} - {/* */} - {/* */} - {/**/} - {/**/} - {/*
*/} -

- {section.name} -


-

+
+

{section.name}

{section.routes.map((route) => { return (
@@ -41,8 +21,6 @@ export function NavSection({ section }: SidebarSectionProps) {
); })} - {/*
*/} - {/*
*/} ); } diff --git a/client/src/components/Layout/Root.tsx b/client/src/components/Layout/Root.tsx index 48a7b2309..c601682ea 100644 --- a/client/src/components/Layout/Root.tsx +++ b/client/src/components/Layout/Root.tsx @@ -26,13 +26,7 @@ export function Root() { - - - - } - > + }> diff --git a/client/src/components/Manifest/Buttons/ManifestFABs.tsx b/client/src/components/Manifest/Buttons/ManifestFABs.tsx index a323a9c0e..593f27d0e 100644 --- a/client/src/components/Manifest/Buttons/ManifestFABs.tsx +++ b/client/src/components/Manifest/Buttons/ManifestFABs.tsx @@ -14,7 +14,7 @@ export function ManifestFABs({ onSignClick }: ManifestActionBtnsProps) { const { nextSigningSite, readOnly, status, signAble } = useContext(ManifestContext); const rcraSiteType = manifest.siteTypeToRcraSiteType(nextSigningSite?.siteType); let component: ReactElement | undefined = undefined; - if (!readOnly || status === 'NotAssigned') { + if (!readOnly) { component = ; } else if (signAble) { component = ; diff --git a/client/src/components/Manifest/Handler/Search/HandlerSearchForm.tsx b/client/src/components/Manifest/Handler/Search/HandlerSearchForm.tsx index b12b0448e..35b37dd7c 100644 --- a/client/src/components/Manifest/Handler/Search/HandlerSearchForm.tsx +++ b/client/src/components/Manifest/Handler/Search/HandlerSearchForm.tsx @@ -1,9 +1,9 @@ import { ManifestContext, ManifestContextType } from 'components/Manifest/ManifestForm'; import { Manifest, SiteType, Transporter } from 'components/Manifest/manifestSchema'; import { RcraSite } from 'components/RcraSite'; -import { HtForm, HtSpinner, HtTooltip } from 'components/UI'; +import { HtForm } from 'components/UI'; import React, { useContext, useEffect, useState } from 'react'; -import { Badge, Button, Col, Row } from 'react-bootstrap'; +import { Button, Col, Row } from 'react-bootstrap'; import { Controller, SubmitHandler, @@ -104,10 +104,6 @@ export function HandlerSearchForm({ setOptions([...allOptions]); }, [data, rcrainfoData]); - useEffect(() => { - setRcrainfoSitesLoading(isLoading || fetchingFromRcrainfo); - }, [isLoading, fetchingFromRcrainfo]); - const handleInputChange = async (value: string) => { setInputValue(value); }; @@ -126,7 +122,7 @@ export function HandlerSearchForm({ - + {message} {isFetching ? ( diff --git a/client/src/components/Manifest/ManifestForm.tsx b/client/src/components/Manifest/ManifestForm.tsx index be486baaf..9861bbe71 100644 --- a/client/src/components/Manifest/ManifestForm.tsx +++ b/client/src/components/Manifest/ManifestForm.tsx @@ -117,12 +117,9 @@ export function ManifestForm({ resolver: zodResolver(manifestSchema), }); const { - watch, formState: { errors }, } = manifestForm; - console.log(watch('designatedFacility')); - useEffect(() => { if (createData) { if ('manifestTrackingNumber' in createData) { @@ -270,12 +267,12 @@ export function ManifestForm({
-

+

{`${manifestData?.manifestTrackingNumber || 'Draft Manifest'}`} {manifestData?.manifestTrackingNumber?.endsWith('DFT') && ( (draft) )} -

+
@@ -505,7 +502,7 @@ export function ManifestForm({ horizontalAlign onClick={toggleShowAddGenerator} children={'Add Generator'} - variant="outline-info" + variant="outline-primary" /> @@ -545,7 +542,7 @@ export function ManifestForm({ )} @@ -574,7 +571,7 @@ export function ManifestForm({ )} @@ -615,7 +612,7 @@ export function ManifestForm({ )} diff --git a/client/src/components/Manifest/UpdateRcra/UpdateRcra.tsx b/client/src/components/Manifest/UpdateRcra/UpdateRcra.tsx index 1d7ae1b57..779bbf7db 100644 --- a/client/src/components/Manifest/UpdateRcra/UpdateRcra.tsx +++ b/client/src/components/Manifest/UpdateRcra/UpdateRcra.tsx @@ -46,7 +46,7 @@ export function UpdateRcra({ taskId }: UpdateRcraProps) { } else { return (
- +
); } diff --git a/client/src/components/RcraProfile/RcraProfile.tsx b/client/src/components/RcraProfile/RcraProfile.tsx index 889a2ed07..a143c5e08 100644 --- a/client/src/components/RcraProfile/RcraProfile.tsx +++ b/client/src/components/RcraProfile/RcraProfile.tsx @@ -67,13 +67,7 @@ export function RcraProfile({ profile }: ProfileViewProps) { .catch((error: AxiosError) => toast.error(error.message)); }; - if (profile.isLoading) { - return ( - - - - ); - } + if (profile.isLoading) return ; return ( <> @@ -166,7 +160,7 @@ export function RcraProfile({ profile }: ProfileViewProps) {

RCRAInfo Sites

{inProgress ? ( - + ) : ( diff --git a/client/src/components/Rcrainfo/buttons/RcraApiUserBtn/RcraApiUserBtn.tsx b/client/src/components/Rcrainfo/buttons/RcraApiUserBtn/RcraApiUserBtn.tsx index 435ff64fa..9b0f8d2cc 100644 --- a/client/src/components/Rcrainfo/buttons/RcraApiUserBtn/RcraApiUserBtn.tsx +++ b/client/src/components/Rcrainfo/buttons/RcraApiUserBtn/RcraApiUserBtn.tsx @@ -13,8 +13,10 @@ export function RcraApiUserBtn({ children, ...props }: HtApiUserBtnProps) { const active = !props.disabled && profile.org?.rcrainfoIntegrated; return ( - + <> + + ); } diff --git a/client/src/components/UI/HtCard/HtCard.tsx b/client/src/components/UI/HtCard/HtCard.tsx index bec14b0ad..d99f44722 100644 --- a/client/src/components/UI/HtCard/HtCard.tsx +++ b/client/src/components/UI/HtCard/HtCard.tsx @@ -24,7 +24,7 @@ export function HtCard({ className, title, children, ...props }: CardProps): Rea
{title ? (
-

{title}

+

{title}

) : ( <> diff --git a/client/src/components/UI/HtSpinner.tsx b/client/src/components/UI/HtSpinner.tsx index 8c812c25c..c8eb3a6ae 100644 --- a/client/src/components/UI/HtSpinner.tsx +++ b/client/src/components/UI/HtSpinner.tsx @@ -1,21 +1,28 @@ +import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { faGear } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome'; import React, { ReactElement } from 'react'; +import { Container } from 'react-bootstrap'; interface HtSpinnerProps extends Omit { - className?: string; + center?: boolean; + icon?: IconProp; } -export function HtSpinner({ className, size }: HtSpinnerProps): ReactElement { +export function HtSpinner({ className, size, center, ...props }: HtSpinnerProps): ReactElement { const defaultCLasses = 'text-muted bg-transparent'; return ( - <> + - + ); } diff --git a/client/src/components/User/UserInfoForm.tsx b/client/src/components/User/UserInfoForm.tsx index 748b15dfb..2dd90871e 100644 --- a/client/src/components/User/UserInfoForm.tsx +++ b/client/src/components/User/UserInfoForm.tsx @@ -45,13 +45,7 @@ export function UserInfoForm({ user, profile }: UserProfileProps) { .catch((error: AxiosError) => toast.error(error.message)); }; - if (user?.isLoading || profile?.loading) { - return ( - - - - ); - } + if (user?.isLoading || profile?.loading) return ; return ( @@ -59,6 +53,7 @@ export function UserInfoForm({ user, profile }: UserProfileProps) { diff --git a/client/src/features/ManifestList/ManifestList.tsx b/client/src/features/ManifestList/ManifestList.tsx index 3c238199f..86bc39575 100644 --- a/client/src/features/ManifestList/ManifestList.tsx +++ b/client/src/features/ManifestList/ManifestList.tsx @@ -40,7 +40,7 @@ export function ManifestList(): ReactElement { {isLoading ? ( - + ) : data ? ( ) : ( diff --git a/client/src/features/MmanifestDetails/ManifestDetails.tsx b/client/src/features/MmanifestDetails/ManifestDetails.tsx index 73666e14d..e2be40c90 100644 --- a/client/src/features/MmanifestDetails/ManifestDetails.tsx +++ b/client/src/features/MmanifestDetails/ManifestDetails.tsx @@ -26,7 +26,7 @@ export function ManifestDetails() { } return loading ? ( - + ) : manifestData ? ( ; + if (isLoading) return ; if (data) return ( diff --git a/client/src/features/SiteList/SiteList.tsx b/client/src/features/SiteList/SiteList.tsx index e9f093f36..0d429231f 100644 --- a/client/src/features/SiteList/SiteList.tsx +++ b/client/src/features/SiteList/SiteList.tsx @@ -11,13 +11,13 @@ export function SiteList() { useTitle('Sites'); const { data, isLoading, error } = useGetUserHaztrakSitesQuery(); // ToDO global error handling + if (isLoading) return ; + return ( - {isLoading && !error ? ( - - ) : data ? ( + {data ? ( ) : (
diff --git a/server/apps/core/admin.py b/server/apps/core/admin.py index ebb3b976d..ed604ba09 100644 --- a/server/apps/core/admin.py +++ b/server/apps/core/admin.py @@ -25,7 +25,7 @@ class HaztrakUserAdmin(UserAdmin): @admin.display(description="Profile") def related_profile(self, user: HaztrakUser) -> str: url = ( - reverse("admin:core_haztrakprofile_changelist") + "?" + urlencode({"user": str(user)}) + reverse("admin:core_haztrakprofile_changelist") + "?" + urlencode({"user": str(user)}) # noqa E501 ) return format_html("{}", url, user.haztrak_profile) @@ -54,6 +54,7 @@ class HaztrakProfileAdmin(admin.ModelAdmin): list_display = ["__str__", "number_of_sites", "rcrainfo_integrated_org"] search_fields = ["user__username"] inlines = [SitePermissionsInline] + raw_id_fields = ["user", "rcrainfo_profile"] readonly_fields = ["rcrainfo_integrated_org"] def rcrainfo_integrated_org(self, profile: HaztrakProfile) -> bool: @@ -75,6 +76,10 @@ class RcraProfileAdmin(admin.ModelAdmin): search_fields = ["haztrak_profile__user__username", "rcra_username"] inlines = [RcraSitePermissionInline] + def get_model_perms(self, request): + """Hide from the Side Navigation""" + return {} + def related_user(self, user): url = reverse("admin:core_haztrakuser_changelist") + "?" + urlencode({"q": str(user.id)}) return format_html("{}", url, user) diff --git a/server/apps/trak/serializers/manifest_serializer.py b/server/apps/trak/serializers/manifest_serializer.py index d195cca05..0b87aafe7 100644 --- a/server/apps/trak/serializers/manifest_serializer.py +++ b/server/apps/trak/serializers/manifest_serializer.py @@ -22,7 +22,7 @@ logger = logging.getLogger(__name__) -class AdditionalInfoSerializer(serializers.ModelSerializer): +class AdditionalInfoSerializer(TrakBaseSerializer): originalManifestTrackingNumbers = serializers.JSONField( allow_null=True, required=False, diff --git a/server/apps/trak/urls.py b/server/apps/trak/urls.py index 72fa3ca70..10a9a0f68 100644 --- a/server/apps/trak/urls.py +++ b/server/apps/trak/urls.py @@ -1,5 +1,5 @@ -from django.urls import include, path, re_path -from rest_framework.routers import DefaultRouter, SimpleRouter +from django.urls import include, path +from rest_framework.routers import SimpleRouter from apps.trak.views import ( # type: ignore DotHazardClassView, @@ -23,10 +23,10 @@ include( [ # Manifest - path("", include(manifest_router.urls)), path("manifest/emanifest", SaveElectronicManifestView.as_view()), path("manifest/emanifest/sign", SignManifestView.as_view()), path("manifest/emanifest/sync", SyncSiteManifestView.as_view()), + path("", include(manifest_router.urls)), # MT path("mtn", MtnListView.as_view()), path("mtn/", MtnListView.as_view()), diff --git a/server/apps/trak/views/manifest_view.py b/server/apps/trak/views/manifest_view.py index d6086345e..ac43b449b 100644 --- a/server/apps/trak/views/manifest_view.py +++ b/server/apps/trak/views/manifest_view.py @@ -2,7 +2,7 @@ from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer from rest_framework import mixins, serializers, status, viewsets -from rest_framework.generics import GenericAPIView, ListAPIView, RetrieveAPIView +from rest_framework.generics import GenericAPIView, ListAPIView from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView @@ -22,10 +22,11 @@ logger = logging.getLogger(__name__) -class ManifestViewSet(viewsets.GenericViewSet, mixins.RetrieveModelMixin): +class ManifestViewSet(viewsets.GenericViewSet, mixins.RetrieveModelMixin, mixins.CreateModelMixin): """Local CRUD operations for HazTrak manifests""" lookup_field = "mtn" + allowed_methods = ["GET", "POST", "PUT"] queryset = Manifest.objects.all() serializer_class = ManifestSerializer lookup_regex = "[0-9]{9}[a-zA-Z]{3}" diff --git a/server/fixtures/dev_data.yaml b/server/fixtures/dev_data.yaml index 07d2d5eea..da36978c2 100644 --- a/server/fixtures/dev_data.yaml +++ b/server/fixtures/dev_data.yaml @@ -1,3 +1,122 @@ +- model: auth.group + pk: 1 + fields: + name: Org admin + permissions: + - 161 + - 162 + - 163 + - 164 + - 157 + - 158 + - 159 + - 160 + - 165 + - 166 + - 167 + - 168 + - 126 + - 127 + - 128 + - 129 + - 130 + - 131 + - 132 + - 133 + - 134 + - 135 + - 136 + - 137 + - 138 + - 139 + - 140 + - 141 + - 142 + - 143 + - 144 + - 145 + - 146 + - 147 + - 148 + - 153 + - 154 + - 155 + - 156 + - 149 + - 150 + - 151 + - 152 + - 69 + - 70 + - 71 + - 72 + - 117 + - 118 + - 119 + - 120 + - 73 + - 74 + - 75 + - 76 + - 113 + - 114 + - 115 + - 116 + - 77 + - 78 + - 79 + - 80 + - 109 + - 110 + - 111 + - 112 + - 81 + - 82 + - 83 + - 84 + - 85 + - 86 + - 87 + - 88 + - 89 + - 90 + - 91 + - 92 + - 93 + - 94 + - 95 + - 96 + - 105 + - 106 + - 107 + - 108 + - 121 + - 122 + - 123 + - 124 + - 97 + - 98 + - 99 + - 100 + - 101 + - 102 + - 103 + - 104 +- model: core.haztrakuser + pk: 8062d496-15f1-485d-961c-a8e5fa118dde + fields: + password: pbkdf2_sha256$390000$iVFyQyb6N3g06Wan8YLnTv$MgFhKBvwPSwctgxWSviA/OXClEKtDXg87iPU+g9+Zjs= + last_login: 2023-03-19 00:29:46.249589+00:00 + is_superuser: false + username: orgadmin + first_name: 'org' + last_name: 'admin' + email: foo@generator.com + is_staff: true + is_active: true + date_joined: 2022-12-17 19:14:17.239000+00:00 + groups: [1] + user_permissions: [] - model: core.haztrakuser pk: a0ba4966-aa53-44c8-be1a-0a0d2d6b6acd fields: @@ -127,11 +246,17 @@ limited_esign: true registered_emanifest_user: true - model: core.rcraprofile - pk: 1fd27bec-8743-4eb3-a44c-fd063ea62021 + pk: d74f904a-7843-4a60-862c-3b94b6051359 fields: rcra_username: dpgraham4401 phone_number: null - email: dpgraham4401@haztrak.net + email: orgadmin@generator.com +- model: core.rcraprofile + pk: 1fd27bec-8743-4eb3-a44c-fd063ea62021 + fields: + rcra_username: '' + phone_number: null + email: superadmin@gmail.com - model: core.rcraprofile pk: 192c73f4-24f1-4f21-8239-dee2da43c547 fields: @@ -142,7 +267,13 @@ pk: efb9e104-7f61-4365-a9af-9d7b55c854c4 fields: name: Generators Org LLC - admin: a0ba4966-aa53-44c8-be1a-0a0d2d6b6acd + admin: 8062d496-15f1-485d-961c-a8e5fa118dde +- model: core.haztrakprofile + pk: c65dbee9-b6bf-400e-93e0-90a749cc2939 + fields: + user: 8062d496-15f1-485d-961c-a8e5fa118dde + rcrainfo_profile: d74f904a-7843-4a60-862c-3b94b6051359 + org: efb9e104-7f61-4365-a9af-9d7b55c854c4 - model: core.haztrakprofile pk: 186642a9-7b5f-4bcb-b328-0fdf8b43f191 fields: