From a2de8131c1cb287f7d6cae6815a62d1a38d5598b Mon Sep 17 00:00:00 2001 From: Tyler Adam Martinez Date: Fri, 6 Feb 2026 13:30:05 -0600 Subject: [PATCH 1/7] refactor --- .gitignore | 3 ++- frontend/src/App.tsx | 17 ++++++++++---- frontend/src/views/Activities/index.ts | 1 + .../src/views/Chlorides/ChloridesView.tsx | 23 +++++++++++-------- frontend/src/views/Chlorides/index.ts | 1 + frontend/src/views/Meters/index.ts | 1 + frontend/src/views/MonitoringWells/index.ts | 1 + frontend/src/views/Parts/index.ts | 1 + frontend/src/views/UserManagement/index.ts | 1 + frontend/src/views/WellManagement/index.ts | 1 + frontend/src/views/WorkOrders/index.ts | 1 + frontend/src/views/index.ts | 19 +++++++++++---- 12 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 frontend/src/views/Activities/index.ts create mode 100644 frontend/src/views/Chlorides/index.ts create mode 100644 frontend/src/views/Meters/index.ts create mode 100644 frontend/src/views/MonitoringWells/index.ts create mode 100644 frontend/src/views/Parts/index.ts create mode 100644 frontend/src/views/UserManagement/index.ts create mode 100644 frontend/src/views/WellManagement/index.ts create mode 100644 frontend/src/views/WorkOrders/index.ts diff --git a/.gitignore b/.gitignore index 483a01fe..f768459b 100644 --- a/.gitignore +++ b/.gitignore @@ -100,6 +100,7 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST +.python-version # PyInstaller # Usually these files are written by a python script from a template @@ -227,4 +228,4 @@ cython_debug/ /api/backupdb/*.sql # dependencies /node_modules -/frontend/node_modules \ No newline at end of file +/frontend/node_modules diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b453bc47..3bdf0079 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -5,9 +5,18 @@ import { QueryClient, QueryClientProvider } from "react-query"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { LocalizationProvider } from "@mui/x-date-pickers"; import { SnackbarProvider, enqueueSnackbar } from "notistack"; -import { BackupsView, Home, Login, Settings } from "./views"; -import { MonitoringWellsView } from "./views/MonitoringWells/MonitoringWellsView"; -import { ActivitiesView } from "./views/Activities/ActivitiesView"; + +import { + + + ActivitiesView, + MonitoringWellsView, + BackupsView, + Home, + Login, + Settings, + NotFound +} from "./views"; import { ActivityPhotoView } from "./views/Activities/ActivityPhotoView"; import { MetersView } from "./views/Meters/MetersView"; import { PartsView } from "./views/Parts/PartsView"; @@ -22,8 +31,8 @@ import { MaintenanceReportView } from "./views/Reports/Maintenance"; import { PartsUsedReportView } from "./views/Reports/PartsUsed"; import { BoardReportView } from "./views/Reports/Board"; import { ChloridesReportView } from "./views/Reports/Chlorides"; + import { AppLayout } from "./AppLayout"; -import { NotFound } from "./views/NotFound"; import { ProtectedRoute } from "./ProtectedRoute"; export const App = () => { diff --git a/frontend/src/views/Activities/index.ts b/frontend/src/views/Activities/index.ts new file mode 100644 index 00000000..5e3365f9 --- /dev/null +++ b/frontend/src/views/Activities/index.ts @@ -0,0 +1 @@ +export * from './ActivitiesView' diff --git a/frontend/src/views/Chlorides/ChloridesView.tsx b/frontend/src/views/Chlorides/ChloridesView.tsx index 09232ec7..1a8d0e78 100644 --- a/frontend/src/views/Chlorides/ChloridesView.tsx +++ b/frontend/src/views/Chlorides/ChloridesView.tsx @@ -11,24 +11,27 @@ import { AlertTitle, Grid, } from "@mui/material"; +import { Science } from "@mui/icons-material"; import { useMutation, useQuery } from "react-query"; import { useAuthUser } from "react-auth-kit"; import { useSnackbar } from "notistack"; -import { ChloridesTable } from "./ChloridesTable"; -import { ChloridesPlot } from "./ChloridesPlot"; -import { CreateModal, UpdateModal } from "../../components/Modals/Region"; +import dayjs, { Dayjs } from "dayjs"; + +import { CreateModal, UpdateModal } from "@/components/Modals/Region"; import { NewRegionMeasurement, PatchRegionMeasurement, SecurityScope, RegionMeasurementDTO, -} from "../../interfaces"; -import dayjs, { Dayjs } from "dayjs"; -import { useFetchWithAuth } from "../../hooks"; -import { Science } from "@mui/icons-material"; -import { BackgroundBox } from "../../components/BackgroundBox"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; -import { emptyToNull } from "../../utils"; +} from "@/interfaces"; +import { useFetchWithAuth } from "@/hooks"; +import { + BackgroundBox, + CustomCardHeader +} from "@/components"; +import { emptyToNull } from "@/utils"; +import { ChloridesTable } from "./ChloridesTable"; +import { ChloridesPlot } from "./ChloridesPlot"; export const ChloridesView = () => { const { enqueueSnackbar } = useSnackbar(); diff --git a/frontend/src/views/Chlorides/index.ts b/frontend/src/views/Chlorides/index.ts new file mode 100644 index 00000000..499133d3 --- /dev/null +++ b/frontend/src/views/Chlorides/index.ts @@ -0,0 +1 @@ +export * from './ChloridesView' diff --git a/frontend/src/views/Meters/index.ts b/frontend/src/views/Meters/index.ts new file mode 100644 index 00000000..de3ffd87 --- /dev/null +++ b/frontend/src/views/Meters/index.ts @@ -0,0 +1 @@ +export * from './MetersView' diff --git a/frontend/src/views/MonitoringWells/index.ts b/frontend/src/views/MonitoringWells/index.ts new file mode 100644 index 00000000..43abef67 --- /dev/null +++ b/frontend/src/views/MonitoringWells/index.ts @@ -0,0 +1 @@ +export * from './MonitoringWellsView' diff --git a/frontend/src/views/Parts/index.ts b/frontend/src/views/Parts/index.ts new file mode 100644 index 00000000..e55dff06 --- /dev/null +++ b/frontend/src/views/Parts/index.ts @@ -0,0 +1 @@ +export * from './PartsView' diff --git a/frontend/src/views/UserManagement/index.ts b/frontend/src/views/UserManagement/index.ts new file mode 100644 index 00000000..8350cab9 --- /dev/null +++ b/frontend/src/views/UserManagement/index.ts @@ -0,0 +1 @@ +export * from './UserManagementView' diff --git a/frontend/src/views/WellManagement/index.ts b/frontend/src/views/WellManagement/index.ts new file mode 100644 index 00000000..db545593 --- /dev/null +++ b/frontend/src/views/WellManagement/index.ts @@ -0,0 +1 @@ +export * from './WellManagementView' diff --git a/frontend/src/views/WorkOrders/index.ts b/frontend/src/views/WorkOrders/index.ts new file mode 100644 index 00000000..33fc5515 --- /dev/null +++ b/frontend/src/views/WorkOrders/index.ts @@ -0,0 +1 @@ +export * from './WorkOrdersView' diff --git a/frontend/src/views/index.ts b/frontend/src/views/index.ts index 38f6ed6f..ae5a45e3 100644 --- a/frontend/src/views/index.ts +++ b/frontend/src/views/index.ts @@ -1,4 +1,15 @@ -export * from "./Backups"; -export * from "./Home"; -export * from "./Login"; -export * from "./Settings"; +export * from './Activities' +export * from './Backups' +export * from './Chlorides' +export * from './Home.ts' +export * from './InsufficientPermView.ts' +export * from './Login.ts' +export * from './Meters' +export * from './MonitoringWells' +export * from './NotFound.ts' +export * from './Parts' +export * from './Reports' +export * from './Settings.ts' +export * from './UserManagement' +export * from './WellManagement' +export * from './WorkOrders' From 2af038aa2e21bda50a4e488c88c2de94ba09fc0f Mon Sep 17 00:00:00 2001 From: Tyler Adam Martinez Date: Mon, 9 Feb 2026 22:17:33 -0600 Subject: [PATCH 2/7] chore(RHControlled): Update imports --- frontend/src/App.tsx | 49 +----- .../Modals/MonitoredWell/Create.tsx | 3 +- .../Modals/MonitoredWell/Update.tsx | 2 +- .../RHControlled/ControlledActivitySelect.tsx | 10 +- .../RHControlled/ControlledAutocomplete.tsx | 64 ++++--- .../RHControlled/ControlledCheckbox.tsx | 51 +++--- .../components/RHControlled/ControlledDMS.tsx | 34 ++-- .../RHControlled/ControlledDatepicker.tsx | 30 ++-- .../ControlledMeterRegisterSelect.tsx | 35 ++-- .../RHControlled/ControlledMeterSelection.tsx | 16 +- .../ControlledMeterStatusTypeSelect.tsx | 11 +- .../ControlledMeterTypeSelect.tsx | 11 +- .../RHControlled/ControlledPartTypeSelect.tsx | 11 +- .../RHControlled/ControlledTextbox.tsx | 42 ++--- .../RHControlled/ControlledTimepicker.tsx | 44 ++--- .../RHControlled/ControlledUserSelect.tsx | 13 +- .../RHControlled/ControlledWellSelection.tsx | 13 +- .../RHControlled/NotesChipSelect.tsx | 9 +- .../RHControlled/PartsChipSelect.tsx | 12 +- .../RHControlled/ServicesChipSelect.tsx | 12 +- .../MaintenanceRepairSelection.tsx | 5 +- .../MeterActivitySelection.tsx | 16 +- .../MeterActivityEntry/MeterInstallation.tsx | 15 +- .../ObservationsSelection.tsx | 12 +- frontend/src/views/Activities/index.ts | 3 +- .../src/views/Meters/MeterDetailsFields.tsx | 31 ++-- .../MeterHistory/SelectedActivityDetails.tsx | 54 +++--- .../SelectedObservationDetails.tsx | 34 ++-- .../src/views/Parts/MeterTypeDetailsCard.tsx | 23 ++- frontend/src/views/Parts/PartDetailsCard.tsx | 10 +- frontend/src/views/Reports/Board/index.tsx | 115 ------------ .../src/views/Reports/Chlorides/index.tsx | 147 ++++++++++------ .../src/views/Reports/Maintenance/index.tsx | 16 +- .../views/Reports/MonitoringWells/index.tsx | 9 +- .../src/views/Reports/PartsUsed/index.tsx | 29 +-- .../src/views/Reports/WorkOrders/index.tsx | 166 ------------------ frontend/src/views/Reports/index.tsx | 20 +-- .../views/UserManagement/RoleDetailsCard.tsx | 21 +-- .../views/UserManagement/UserDetailsCard.tsx | 56 +++--- .../views/WellManagement/WellDetailsCard.tsx | 39 ++-- .../WellManagement/WellManagementView.tsx | 16 +- .../src/views/WorkOrders/WorkOrdersView.tsx | 14 +- frontend/src/views/index.ts | 30 ++-- 43 files changed, 523 insertions(+), 830 deletions(-) delete mode 100644 frontend/src/views/Reports/Board/index.tsx delete mode 100644 frontend/src/views/Reports/WorkOrders/index.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3bdf0079..024d349e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -5,31 +5,26 @@ import { QueryClient, QueryClientProvider } from "react-query"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { LocalizationProvider } from "@mui/x-date-pickers"; import { SnackbarProvider, enqueueSnackbar } from "notistack"; - import { - - ActivitiesView, MonitoringWellsView, BackupsView, Home, Login, Settings, - NotFound + NotFound, + ActivityPhotoView, + MetersView, + PartsView, + UserManagementView, + WellManagementView, + WorkOrdersView, + ChloridesView, + ReportsView, } from "./views"; -import { ActivityPhotoView } from "./views/Activities/ActivityPhotoView"; -import { MetersView } from "./views/Meters/MetersView"; -import { PartsView } from "./views/Parts/PartsView"; -import { UserManagementView } from "./views/UserManagement/UserManagementView"; -import WellManagementView from "./views/WellManagement/WellManagementView"; -import WorkOrdersView from "./views/WorkOrders/WorkOrdersView"; -import { ChloridesView } from "./views/Chlorides/ChloridesView"; -import { ReportsView } from "./views/Reports"; -import { WorkOrdersReportView } from "./views/Reports/WorkOrders"; import { MonitoringWellsReportView } from "./views/Reports/MonitoringWells"; import { MaintenanceReportView } from "./views/Reports/Maintenance"; import { PartsUsedReportView } from "./views/Reports/PartsUsed"; -import { BoardReportView } from "./views/Reports/Board"; import { ChloridesReportView } from "./views/Reports/Chlorides"; import { AppLayout } from "./AppLayout"; @@ -184,19 +179,6 @@ export const App = () => { } /> - - - - - - } - /> { } /> - - - - - - } - /> { const activityTypeList = useGetActivityTypeList(); return ( @@ -21,4 +21,4 @@ export default function ControlledActivitySelect({ value={activityTypeList.isLoading ? "Loading..." : childProps.value} /> ); -} +}; diff --git a/frontend/src/components/RHControlled/ControlledAutocomplete.tsx b/frontend/src/components/RHControlled/ControlledAutocomplete.tsx index d474020e..ca0424d3 100644 --- a/frontend/src/components/RHControlled/ControlledAutocomplete.tsx +++ b/frontend/src/components/RHControlled/ControlledAutocomplete.tsx @@ -8,7 +8,7 @@ const disabledInputStyle = { cursor: "default", }; -export default function ControlledAutocomplete({ +export const ControlledAutocomplete = ({ control, name, options = [], @@ -17,37 +17,35 @@ export default function ControlledAutocomplete({ isOptionEqualToValue, multiple = false, ...childProps -}: any) { - return ( - { - const { value, onChange, ...restField } = field; +}: any) => ( + { + const { value, onChange, ...restField } = field; - const safeValue = multiple - ? Array.isArray(value) - ? value - : [] - : value ?? null; + const safeValue = multiple + ? Array.isArray(value) + ? value + : [] + : (value ?? null); - return ( - onChange(newValue)} - sx={disabledInputStyle} - {...childProps} - /> - ); - }} - /> - ); -} + return ( + onChange(newValue)} + sx={disabledInputStyle} + {...childProps} + /> + ); + }} + /> +); diff --git a/frontend/src/components/RHControlled/ControlledCheckbox.tsx b/frontend/src/components/RHControlled/ControlledCheckbox.tsx index da701cca..4d85b486 100644 --- a/frontend/src/components/RHControlled/ControlledCheckbox.tsx +++ b/frontend/src/components/RHControlled/ControlledCheckbox.tsx @@ -8,32 +8,25 @@ const disabledInputStyle = { cursor: "default", }; -export default function ControlledCheckbox({ - name, - control, - ...childProps -}: any) { - return ( - { - //console.log(field) - return ( - - } - {...childProps} - /> - ); - }} - /> - ); -} +export const ControlledCheckbox = ({ name, control, ...childProps }: any) => ( + { + return ( + + } + {...childProps} + /> + ); + }} + /> +); diff --git a/frontend/src/components/RHControlled/ControlledDMS.tsx b/frontend/src/components/RHControlled/ControlledDMS.tsx index 79c4afa9..93b84806 100644 --- a/frontend/src/components/RHControlled/ControlledDMS.tsx +++ b/frontend/src/components/RHControlled/ControlledDMS.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState, forwardRef } from "react"; import { TextField } from "@mui/material"; import { Controller } from "react-hook-form"; -import { GCSdimension } from "../../enums"; import { PatternFormat, PatternFormatProps } from "react-number-format"; +import { GCSdimension } from "@/enums"; interface DMSInputProps { dimension_type: GCSdimension; @@ -16,7 +16,7 @@ interface CustomProps { name: string; } -const DMSFormatCustom = React.forwardRef( +const DMSFormatCustom = forwardRef( function PatternFormatCustom(props, ref) { const { onChange, ...other } = props; @@ -122,18 +122,16 @@ function DMSInput({ dimension_type, value, onChange }: DMSInputProps) { ); } -export default function ControlledDMS({ name, control, ...childProps }: any) { - return ( - ( - field.onChange(newValue)} - /> - )} - /> - ); -} +export const ControlledDMS = ({ name, control, ...childProps }: any) => ( + ( + field.onChange(newValue)} + /> + )} + /> +); diff --git a/frontend/src/components/RHControlled/ControlledDatepicker.tsx b/frontend/src/components/RHControlled/ControlledDatepicker.tsx index 78055d85..1b0917bf 100644 --- a/frontend/src/components/RHControlled/ControlledDatepicker.tsx +++ b/frontend/src/components/RHControlled/ControlledDatepicker.tsx @@ -1,23 +1,21 @@ import { DatePicker } from "@mui/x-date-pickers"; import { Controller } from "react-hook-form"; -export default function ControlledDatepicker({ +export const ControlledDatepicker = ({ name, control, size = "small", ...childProps -}: any) { - return ( - ( - - )} - /> - ); -} +}: any) => ( + ( + + )} + /> +); diff --git a/frontend/src/components/RHControlled/ControlledMeterRegisterSelect.tsx b/frontend/src/components/RHControlled/ControlledMeterRegisterSelect.tsx index 696d6928..9fea17b5 100644 --- a/frontend/src/components/RHControlled/ControlledMeterRegisterSelect.tsx +++ b/frontend/src/components/RHControlled/ControlledMeterRegisterSelect.tsx @@ -1,23 +1,22 @@ -import MeterRegisterSelect from "../MeterRegisterSelect"; import { Controller } from "react-hook-form"; -export default function ControlledMeterRegisterSelect({ +import MeterRegisterSelect from "../MeterRegisterSelect"; + +export const ControlledMeterRegisterSelect = ({ control, name, ...childProps -}: any) { - return ( - ( - - )} - /> - ); -} +}: any) => ( + ( + + )} + /> +); diff --git a/frontend/src/components/RHControlled/ControlledMeterSelection.tsx b/frontend/src/components/RHControlled/ControlledMeterSelection.tsx index 42b34342..7c1e811d 100644 --- a/frontend/src/components/RHControlled/ControlledMeterSelection.tsx +++ b/frontend/src/components/RHControlled/ControlledMeterSelection.tsx @@ -1,16 +1,18 @@ import { useState } from "react"; import { TextField } from "@mui/material"; import { useDebounce } from "use-debounce"; -import { useGetMeterList } from "../../service/ApiServiceNew"; -import { MeterListDTO } from "../../interfaces"; -import ControlledAutocomplete from "./ControlledAutocomplete"; -import { MeterStatusNames } from "../../enums"; -export default function ControlledMeterSelection({ +import { useGetMeterList } from "@/service/ApiServiceNew"; +import { MeterListDTO } from "@/interfaces"; +import { MeterStatusNames } from "@/enums"; + +import { ControlledAutocomplete } from "./ControlledAutocomplete"; + +export const ControlledMeterSelection = ({ name, control, ...childProps -}: any) { +}: any) => { const [meterSearchQuery, setMeterSearchQuery] = useState(""); const [meterSearchQueryDebounced] = useDebounce(meterSearchQuery, 250); @@ -63,4 +65,4 @@ export default function ControlledMeterSelection({ }} /> ); -} +}; diff --git a/frontend/src/components/RHControlled/ControlledMeterStatusTypeSelect.tsx b/frontend/src/components/RHControlled/ControlledMeterStatusTypeSelect.tsx index 838fd6d6..73a8bc56 100644 --- a/frontend/src/components/RHControlled/ControlledMeterStatusTypeSelect.tsx +++ b/frontend/src/components/RHControlled/ControlledMeterStatusTypeSelect.tsx @@ -1,12 +1,13 @@ -import { useGetMeterStatusTypeList } from "../../service/ApiServiceNew"; -import { MeterStatus } from "../../interfaces"; +import { useGetMeterStatusTypeList } from "@/service/ApiServiceNew"; +import { MeterStatus } from "@/interfaces"; + import { ControlledSelect } from "./ControlledSelect"; -export default function ControlledMeterStatusTypeSelect({ +export const ControlledMeterStatusTypeSelect = ({ name, control, ...childProps -}: any) { +}: any) => { const statusTypeList = useGetMeterStatusTypeList(); return ( @@ -21,4 +22,4 @@ export default function ControlledMeterStatusTypeSelect({ value={statusTypeList.isLoading ? "Loading..." : childProps.value} /> ); -} +}; diff --git a/frontend/src/components/RHControlled/ControlledMeterTypeSelect.tsx b/frontend/src/components/RHControlled/ControlledMeterTypeSelect.tsx index 688eec03..23349722 100644 --- a/frontend/src/components/RHControlled/ControlledMeterTypeSelect.tsx +++ b/frontend/src/components/RHControlled/ControlledMeterTypeSelect.tsx @@ -1,12 +1,13 @@ -import { useGetMeterTypeList } from "../../service/ApiServiceNew"; -import { MeterTypeLU } from "../../interfaces"; +import { useGetMeterTypeList } from "@/service/ApiServiceNew"; +import { MeterTypeLU } from "@/interfaces"; + import { ControlledSelect } from "./ControlledSelect"; -export default function ControlledMeterTypeSelect({ +export const ControlledMeterTypeSelect = ({ name, control, ...childProps -}: any) { +}: any) => { const meterTypeList = useGetMeterTypeList(); return ( @@ -21,4 +22,4 @@ export default function ControlledMeterTypeSelect({ value={meterTypeList.isLoading ? "Loading..." : childProps.value} /> ); -} +}; diff --git a/frontend/src/components/RHControlled/ControlledPartTypeSelect.tsx b/frontend/src/components/RHControlled/ControlledPartTypeSelect.tsx index bbe8b0aa..8c6a491f 100644 --- a/frontend/src/components/RHControlled/ControlledPartTypeSelect.tsx +++ b/frontend/src/components/RHControlled/ControlledPartTypeSelect.tsx @@ -1,12 +1,13 @@ -import { useGetPartTypeList } from "../../service/ApiServiceNew"; -import { PartTypeLU } from "../../interfaces"; +import { useGetPartTypeList } from "@/service/ApiServiceNew"; +import { PartTypeLU } from "@/interfaces"; + import { ControlledSelect } from "./ControlledSelect"; -export default function ControlledPartTypeSelect({ +export const ControlledPartTypeSelect = ({ name, control, ...childProps -}: any) { +}: any) => { const partTypeList = useGetPartTypeList(); return ( @@ -21,4 +22,4 @@ export default function ControlledPartTypeSelect({ value={partTypeList.isLoading ? "Loading..." : childProps.value} /> ); -} +}; diff --git a/frontend/src/components/RHControlled/ControlledTextbox.tsx b/frontend/src/components/RHControlled/ControlledTextbox.tsx index a7dd467d..6c1ce009 100644 --- a/frontend/src/components/RHControlled/ControlledTextbox.tsx +++ b/frontend/src/components/RHControlled/ControlledTextbox.tsx @@ -8,27 +8,21 @@ const disabledInputStyle = { cursor: "default", }; -export default function ControlledTextbox({ - name, - control, - ...childProps -}: any) { - return ( - ( - - )} - /> - ); -} +export const ControlledTextbox = ({ name, control, ...childProps }: any) => ( + ( + + )} + /> +); diff --git a/frontend/src/components/RHControlled/ControlledTimepicker.tsx b/frontend/src/components/RHControlled/ControlledTimepicker.tsx index 53f2cd85..b8bd5323 100644 --- a/frontend/src/components/RHControlled/ControlledTimepicker.tsx +++ b/frontend/src/components/RHControlled/ControlledTimepicker.tsx @@ -1,28 +1,22 @@ import { TimePicker } from "@mui/x-date-pickers"; import { Controller } from "react-hook-form"; -export default function ControlledTimepicker({ - name, - control, - ...childProps -}: any) { - return ( - ( - - )} - /> - ); -} +export const ControlledTimepicker = ({ name, control, ...childProps }: any) => ( + ( + + )} + /> +); diff --git a/frontend/src/components/RHControlled/ControlledUserSelect.tsx b/frontend/src/components/RHControlled/ControlledUserSelect.tsx index d76b352f..d7cb63d2 100644 --- a/frontend/src/components/RHControlled/ControlledUserSelect.tsx +++ b/frontend/src/components/RHControlled/ControlledUserSelect.tsx @@ -1,16 +1,17 @@ import { useState } from "react"; -import { ControlledSelect } from "./ControlledSelect"; -import { User } from "../../interfaces"; -import { useGetUserList } from "../../service/ApiServiceNew"; import { useAuthUser } from "react-auth-kit"; +import { User } from "@/interfaces"; +import { useGetUserList } from "@/service/ApiServiceNew"; + +import { ControlledSelect } from "./ControlledSelect"; -export default function ControlledUserSelect({ +export const ControlledUserSelect = ({ name, control, hideAndSelectCurrentUser = false, setValue = null, ...childProps -}: any) { +}: any) => { const [isCurrentUserSet, setIsCurrentUserSet] = useState(false); if (!hideAndSelectCurrentUser) { @@ -36,4 +37,4 @@ export default function ControlledUserSelect({ } return null; } -} +}; diff --git a/frontend/src/components/RHControlled/ControlledWellSelection.tsx b/frontend/src/components/RHControlled/ControlledWellSelection.tsx index cb160284..a4ef1615 100644 --- a/frontend/src/components/RHControlled/ControlledWellSelection.tsx +++ b/frontend/src/components/RHControlled/ControlledWellSelection.tsx @@ -1,15 +1,16 @@ import { useState } from "react"; import { TextField } from "@mui/material"; import { useDebounce } from "use-debounce"; -import { useGetWells } from "../../service/ApiServiceNew"; -import { Well } from "../../interfaces"; -import ControlledAutocomplete from "./ControlledAutocomplete"; +import { useGetWells } from "@/service/ApiServiceNew"; +import { Well } from "@/interfaces"; -export default function ControlledWellSelection({ +import { ControlledAutocomplete } from "./ControlledAutocomplete"; + +export const ControlledWellSelection = ({ name, control, ...childProps -}: any) { +}: any) => { const [wellSearchQuery, setWellSearchQuery] = useState(""); const [wellSearchQueryDebounced] = useDebounce(wellSearchQuery, 250); @@ -45,4 +46,4 @@ export default function ControlledWellSelection({ }} /> ); -} +}; diff --git a/frontend/src/components/RHControlled/NotesChipSelect.tsx b/frontend/src/components/RHControlled/NotesChipSelect.tsx index b4bf883a..2fc7a097 100644 --- a/frontend/src/components/RHControlled/NotesChipSelect.tsx +++ b/frontend/src/components/RHControlled/NotesChipSelect.tsx @@ -1,9 +1,10 @@ import ChipSelect from "../ChipSelect"; -import { NoteTypeLU } from "../../interfaces"; -import { useGetNoteTypes } from "../../service/ApiServiceNew"; +import { NoteTypeLU } from "@/interfaces"; +import { useGetNoteTypes } from "@/service/ApiServiceNew"; + import { Controller } from "react-hook-form"; -export default function NotesChipSelect({ name, control }: any) { +export const NotesChipSelect = ({ name, control }: any) => { const notesList = useGetNoteTypes(); return ( @@ -44,4 +45,4 @@ export default function NotesChipSelect({ name, control }: any) { }} /> ); -} +}; diff --git a/frontend/src/components/RHControlled/PartsChipSelect.tsx b/frontend/src/components/RHControlled/PartsChipSelect.tsx index b7cccb61..5537cc10 100644 --- a/frontend/src/components/RHControlled/PartsChipSelect.tsx +++ b/frontend/src/components/RHControlled/PartsChipSelect.tsx @@ -1,9 +1,11 @@ -import ChipSelect from "../ChipSelect"; -import { Part } from "../../interfaces"; -import { useGetMeterPartsList } from "../../service/ApiServiceNew"; import { Controller } from "react-hook-form"; -export default function PartsChipSelect({ name, control, meterid }: any) { +import { Part } from "@/interfaces"; +import { useGetMeterPartsList } from "@/service/ApiServiceNew"; + +import ChipSelect from "../ChipSelect"; + +export const PartsChipSelect = ({ name, control, meterid }: any) => { const partsList = useGetMeterPartsList({ meter_id: meterid }); return ( @@ -42,4 +44,4 @@ export default function PartsChipSelect({ name, control, meterid }: any) { }} /> ); -} +}; diff --git a/frontend/src/components/RHControlled/ServicesChipSelect.tsx b/frontend/src/components/RHControlled/ServicesChipSelect.tsx index 4a970e19..7bb4e5f0 100644 --- a/frontend/src/components/RHControlled/ServicesChipSelect.tsx +++ b/frontend/src/components/RHControlled/ServicesChipSelect.tsx @@ -1,9 +1,11 @@ -import ChipSelect from "../ChipSelect"; -import { ServiceTypeLU } from "../../interfaces"; -import { useGetServiceTypes } from "../../service/ApiServiceNew"; import { Controller } from "react-hook-form"; -export default function ServicesChipSelect({ name, control }: any) { +import { ServiceTypeLU } from "@/interfaces"; +import { useGetServiceTypes } from "@/service/ApiServiceNew"; + +import ChipSelect from "../ChipSelect"; + +export const ServicesChipSelect = ({ name, control }: any) => { const servicesList = useGetServiceTypes(); return ( @@ -46,4 +48,4 @@ export default function ServicesChipSelect({ name, control }: any) { }} /> ); -} +}; diff --git a/frontend/src/views/Activities/MeterActivityEntry/MaintenanceRepairSelection.tsx b/frontend/src/views/Activities/MeterActivityEntry/MaintenanceRepairSelection.tsx index d8a7dfff..4457a3ca 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/MaintenanceRepairSelection.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/MaintenanceRepairSelection.tsx @@ -1,8 +1,7 @@ import { Box, Grid, Typography } from "@mui/material"; import { useFieldArray } from "react-hook-form"; -import ControlledTextbox from "../../../components/RHControlled/ControlledTextbox"; -import { StyledToggleButton } from "../../../components"; -import { useGetServiceTypes } from "../../../service/ApiServiceNew"; +import { ControlledTextbox, StyledToggleButton } from "@/components"; +import { useGetServiceTypes } from "@/service/ApiServiceNew"; export default function MaintenanceRepairSelection({ control, diff --git a/frontend/src/views/Activities/MeterActivityEntry/MeterActivitySelection.tsx b/frontend/src/views/Activities/MeterActivityEntry/MeterActivitySelection.tsx index a34539a6..988d1a7c 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/MeterActivitySelection.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/MeterActivitySelection.tsx @@ -1,11 +1,13 @@ import { Grid } from "@mui/material"; -import ControlledMeterSelection from "../../../components/RHControlled/ControlledMeterSelection"; -import ControlledActivitySelect from "../../../components/RHControlled/ControlledActivitySelect"; -import ControlledUserSelect from "../../../components/RHControlled/ControlledUserSelect"; -import ControlledDatepicker from "../../../components/RHControlled/ControlledDatepicker"; -import ControlledTimepicker from "../../../components/RHControlled/ControlledTimepicker"; -import ControlledCheckbox from "../../../components/RHControlled/ControlledCheckbox"; -import { ControlledWorkOrderSelect } from "../../../components/RHControlled/ControlledWorkOrderSelect"; +import { + ControlledActivitySelect, + ControlledCheckbox, + ControlledDatepicker, + ControlledMeterSelection, + ControlledTimepicker, + ControlledUserSelect, + ControlledWorkOrderSelect, +} from "@/components"; export function MeterActivitySelection({ control, errors, setValue }: any) { return ( diff --git a/frontend/src/views/Activities/MeterActivityEntry/MeterInstallation.tsx b/frontend/src/views/Activities/MeterActivityEntry/MeterInstallation.tsx index fda3fcd5..d60e8dad 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/MeterInstallation.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/MeterInstallation.tsx @@ -8,10 +8,9 @@ import { TableRow, Typography, } from "@mui/material"; -import { ActivityType } from "../../../enums"; -import ControlledTextbox from "../../../components/RHControlled/ControlledTextbox"; -import ControlledWellSelection from "../../../components/RHControlled/ControlledWellSelection"; -import { formatLatLong } from "../../../conversions"; +import { ActivityType } from "@/enums"; +import { ControlledTextbox, ControlledWellSelection } from "@/components"; +import { formatLatLong } from "@/conversions"; export default function MeterInstallation({ control, errors, watch }: any) { const isActivity = (activitiesList: ActivityType[]) => @@ -62,12 +61,12 @@ export default function MeterInstallation({ control, errors, watch }: any) { {watch("current_installation.well")?.location?.latitude == - null + null ? "--" : formatLatLong( - watch("current_installation.well")?.location?.latitude, - watch("current_installation.well")?.location?.longitude, - )} + watch("current_installation.well")?.location?.latitude, + watch("current_installation.well")?.location?.longitude, + )} {watch("current_installation.well")?.osetag ?? "--"} diff --git a/frontend/src/views/Activities/MeterActivityEntry/ObservationsSelection.tsx b/frontend/src/views/Activities/MeterActivityEntry/ObservationsSelection.tsx index 6cf8a7c4..6aa16506 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/ObservationsSelection.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/ObservationsSelection.tsx @@ -3,11 +3,13 @@ import { Box, Button, Grid, Typography, IconButton } from "@mui/material"; import { UseQueryResult } from "react-query"; import { Delete } from "@mui/icons-material"; import { useFieldArray, useWatch } from "react-hook-form"; -import { ObservedPropertyTypeLU } from "../../../interfaces"; -import { useGetPropertyTypes } from "../../../service/ApiServiceNew"; -import { ControlledSelectNonObject } from "../../../components/RHControlled/ControlledSelect"; -import ControlledTimepicker from "../../../components/RHControlled/ControlledTimepicker"; -import ControlledTextbox from "../../../components/RHControlled/ControlledTextbox"; +import { ObservedPropertyTypeLU } from "@/interfaces"; +import { useGetPropertyTypes } from "@/service/ApiServiceNew"; +import { + ControlledSelectNonObject, + ControlledTimepicker, + ControlledTextbox, +} from "@/components"; import dayjs from "dayjs"; const ObservationRow = ({ diff --git a/frontend/src/views/Activities/index.ts b/frontend/src/views/Activities/index.ts index 5e3365f9..b9f1c2f5 100644 --- a/frontend/src/views/Activities/index.ts +++ b/frontend/src/views/Activities/index.ts @@ -1 +1,2 @@ -export * from './ActivitiesView' +export * from "./ActivitiesView"; +export * from "./ActivityPhotoView"; diff --git a/frontend/src/views/Meters/MeterDetailsFields.tsx b/frontend/src/views/Meters/MeterDetailsFields.tsx index f9aba705..ee18d103 100644 --- a/frontend/src/views/Meters/MeterDetailsFields.tsx +++ b/frontend/src/views/Meters/MeterDetailsFields.tsx @@ -1,12 +1,9 @@ -import { useForm, SubmitHandler } from "react-hook-form"; import { useEffect, useState } from "react"; +import { useForm, SubmitHandler } from "react-hook-form"; import { enqueueSnackbar } from "notistack"; import { useAuthUser } from "react-auth-kit"; import { createSearchParams, useNavigate } from "react-router-dom"; -import GradingIcon from "@mui/icons-material/Grading"; -import AddIcon from "@mui/icons-material/Add"; -import SaveIcon from "@mui/icons-material/Save"; -import SaveAsIcon from "@mui/icons-material/SaveAs"; +import { Add, Grading, Save, SaveAs } from "@mui/icons-material"; import { Button, Grid, Card, CardContent, InputAdornment } from "@mui/material"; import { Table, @@ -16,7 +13,7 @@ import { TableHead, TableRow, } from "@mui/material"; -import { SecurityScope, Meter } from "../../interfaces"; +import { SecurityScope, Meter } from "@/interfaces"; import { useCreateMeter, useGetMeter, @@ -24,13 +21,15 @@ import { } from "../../service/ApiServiceNew"; import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; -import ControlledTextbox from "../../components/RHControlled/ControlledTextbox"; -import ControlledMeterTypeSelect from "../../components/RHControlled/ControlledMeterTypeSelect"; -import ControlledWellSelection from "../../components/RHControlled/ControlledWellSelection"; -import ControlledMeterStatusTypeSelect from "../../components/RHControlled/ControlledMeterStatusTypeSelect"; -import { formatLatLong } from "../../conversions"; -import ControlledMeterRegisterSelect from "../../components/RHControlled/ControlledMeterRegisterSelect"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; +import { + CustomCardHeader, + ControlledTextbox, + ControlledMeterTypeSelect, + ControlledWellSelection, + ControlledMeterStatusTypeSelect, + ControlledMeterRegisterSelect, +} from "@/components"; +import { formatLatLong } from "@/conversions"; const MeterResolverSchema: Yup.ObjectSchema = Yup.object().shape({ serial_number: Yup.string().required("Please enter a serial number."), @@ -124,7 +123,7 @@ export const MeterDetailsFields = ({ @@ -292,7 +291,7 @@ export const MeterDetailsFields = ({ variant="contained" onClick={handleSubmit(onAddMeter, onErr)} > - +   Save New Meter ) : ( @@ -301,7 +300,7 @@ export const MeterDetailsFields = ({ variant="contained" onClick={handleSubmit(onSaveChanges, onErr)} > - +   Save Changes )} diff --git a/frontend/src/views/Meters/MeterHistory/SelectedActivityDetails.tsx b/frontend/src/views/Meters/MeterHistory/SelectedActivityDetails.tsx index 821a685f..a355cd22 100644 --- a/frontend/src/views/Meters/MeterHistory/SelectedActivityDetails.tsx +++ b/frontend/src/views/Meters/MeterHistory/SelectedActivityDetails.tsx @@ -2,32 +2,28 @@ import { useEffect } from "react"; import { useForm, SubmitHandler } from "react-hook-form"; import { useAuthUser } from "react-auth-kit"; import { Grid, Card, CardContent, Stack, Button } from "@mui/material"; -import SaveIcon from "@mui/icons-material/Save"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; +import { Save, InfoOutlined } from "@mui/icons-material"; import { PatchActivityForm, PatchActivitySubmit, SecurityScope, -} from "../../../interfaces"; -import { - useUpdateActivity, - useDeleteActivity, -} from "../../../service/ApiServiceNew"; +} from "@/interfaces"; +import { useUpdateActivity, useDeleteActivity } from "@/service/ApiServiceNew"; import dayjs from "dayjs"; import { enqueueSnackbar } from "notistack"; - -import ControlledDatepicker from "../../../components/RHControlled/ControlledDatepicker"; -import ControlledTimepicker from "../../../components/RHControlled/ControlledTimepicker"; -import ControlledActivitySelect from "../../../components/RHControlled/ControlledActivitySelect"; -import ControlledUserSelect from "../../../components/RHControlled/ControlledUserSelect"; -import ControlledWellSelection from "../../../components/RHControlled/ControlledWellSelection"; -import ControlledTextbox from "../../../components/RHControlled/ControlledTextbox"; - -import NotesChipSelect from "../../../components/RHControlled/NotesChipSelect"; -import ServicesChipSelect from "../../../components/RHControlled/ServicesChipSelect"; -import PartsChipSelect from "../../../components/RHControlled/PartsChipSelect"; -import ControlledCheckbox from "../../../components/RHControlled/ControlledCheckbox"; -import { CustomCardHeader } from "../../../components/CustomCardHeader"; +import { + ControlledDatepicker, + ControlledTimepicker, + ControlledActivitySelect, + ControlledUserSelect, + ControlledWellSelection, + ControlledTextbox, + NotesChipSelect, + ServicesChipSelect, + PartsChipSelect, + ControlledCheckbox, + CustomCardHeader, +} from "@/components"; export const SelectedActivityDetails = ({ selectedActivity, @@ -120,15 +116,12 @@ export const SelectedActivityDetails = ({ - + - + @@ -214,7 +204,7 @@ export const SelectedActivityDetails = ({ onClick={handleSubmit(onSaveChanges)} disabled={!hasAdminScope} > - +   Save Changes ) : ( @@ -152,7 +151,7 @@ export const MeterTypeDetailsCard = ({ variant="contained" onClick={handleSubmit(onSaveChanges, onErr)} > - +   Save Changes )} diff --git a/frontend/src/views/Parts/PartDetailsCard.tsx b/frontend/src/views/Parts/PartDetailsCard.tsx index e0505ee6..f29e04a4 100644 --- a/frontend/src/views/Parts/PartDetailsCard.tsx +++ b/frontend/src/views/Parts/PartDetailsCard.tsx @@ -27,10 +27,12 @@ import { useGetPart, useUpdatePart, } from "@/service/ApiServiceNew"; -import ControlledTextbox from "@/components/RHControlled/ControlledTextbox"; -import ControlledPartTypeSelect from "@/components/RHControlled/ControlledPartTypeSelect"; -import { ControlledSelectNonObject } from "@/components/RHControlled/ControlledSelect"; -import { CustomCardHeader } from "@/components"; +import { + ControlledTextbox, + ControlledPartTypeSelect, + ControlledSelectNonObject, + CustomCardHeader, +} from "@/components"; import { MeterTypeLU, Part } from "@/interfaces"; const PartResolverSchema: Yup.ObjectSchema = Yup.object().shape({ diff --git a/frontend/src/views/Reports/Board/index.tsx b/frontend/src/views/Reports/Board/index.tsx deleted file mode 100644 index c7932f33..00000000 --- a/frontend/src/views/Reports/Board/index.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { ArrowBack, People, PictureAsPdf } from "@mui/icons-material"; -import { - Box, - Button, - Card, - CardContent, - CardHeader, - Grid, - IconButton, - Tooltip, -} from "@mui/material"; -import { Link } from "react-router-dom"; -import ControlledDatepicker from "../../../components/RHControlled/ControlledDatepicker"; -import { useForm } from "react-hook-form"; -import * as yup from "yup"; -import { yupResolver } from "@hookform/resolvers/yup"; -import dayjs from "dayjs"; - -const schema = yup.object().shape({ - from: yup.mixed().nullable().required("From date is required"), - to: yup.mixed().nullable().required("To date is required"), -}); - -const defaultSchema = { - from: dayjs(), - to: dayjs(), -}; - -export const BoardReportView = () => { - const { control, reset } = useForm({ - resolver: yupResolver(schema), - defaultValues: defaultSchema, - }); - - return ( - - - - Board Report - - - } - sx={{ mb: 0, pb: 0 }} - /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/frontend/src/views/Reports/Chlorides/index.tsx b/frontend/src/views/Reports/Chlorides/index.tsx index eeff2111..6bc80848 100644 --- a/frontend/src/views/Reports/Chlorides/index.tsx +++ b/frontend/src/views/Reports/Chlorides/index.tsx @@ -17,20 +17,34 @@ import { Divider, Box, } from "@mui/material"; -import { LayersControl, MapContainer, Marker, Tooltip as MapTooltip } from "react-leaflet"; +import { + LayersControl, + MapContainer, + Marker, + Tooltip as MapTooltip, +} from "react-leaflet"; import { Link } from "react-router-dom"; import { useForm } from "react-hook-form"; import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import L from "leaflet"; -import { API_URL } from "../../../config"; -import ControlledDatepicker from "../../../components/RHControlled/ControlledDatepicker"; -import { CustomCardHeader, BackgroundBox, DirectionCard, SoutheastGuideLayer, SatelliteLayer, OpenStreetMapLayer, WellMapLegend } from "../../../components"; -import { useFetchWithAuth } from "../../../hooks"; -import { useGetWellLocations } from "../../../service/ApiServiceNew"; -import { Well } from "../../../interfaces"; -import { RedMapIcon, BlackMapIcon } from "../../../components/MapIcons"; -import { WellStatus } from "../../../enums"; + +import { API_URL } from "@/config"; +import { + ControlledDatepicker, + CustomCardHeader, + BackgroundBox, + DirectionCard, + SoutheastGuideLayer, + SatelliteLayer, + OpenStreetMapLayer, + WellMapLegend, +} from "@/components"; +import { RedMapIcon, BlackMapIcon } from "@/components/MapIcons"; +import { useFetchWithAuth } from "@/hooks"; +import { useGetWellLocations } from "@/service/ApiServiceNew"; +import { Well } from "@/interfaces"; +import { WellStatus } from "@/enums"; // @ts-ignore import MarkerClusterGroup from "@changey/react-leaflet-markercluster"; @@ -43,15 +57,15 @@ const schema = yup.object().shape({ .mixed() .nullable() .required("To date is required") - .test("is-after", "'To' date must be after 'From'", function(value) { + .test("is-after", "'To' date must be after 'From'", function (value) { const { from } = this.parent; return !from || !value || dayjs(value).isAfter(dayjs(from)); }), }); const defaultSchema = { - from: dayjs().startOf('month'), - to: dayjs().endOf('month'), + from: dayjs().startOf("month"), + to: dayjs().endOf("month"), }; interface iMinMaxAvgMedCount { @@ -92,19 +106,13 @@ export const ChloridesReportView = () => { return fetchWithAuth({ method: "GET", route: `/chlorides/report?${searchParams.toString()}`, - }) + }); }, enabled: !!from && !!to, }); const downloadPDFMutation = useMutation({ - mutationFn: async ({ - from, - to, - }: { - from: Dayjs; - to: Dayjs; - }) => { + mutationFn: async ({ from, to }: { from: Dayjs; to: Dayjs }) => { const params = new URLSearchParams({ from_date: from?.format("YYYY-MM-DD"), to_date: to?.format("YYYY-MM-DD"), @@ -140,7 +148,7 @@ export const ChloridesReportView = () => { }); }; - const wellQuery = useGetWellLocations('', true); + const wellQuery = useGetWellLocations("", true); useEffect(() => { if (wellQuery.hasNextPage && !wellQuery.isFetchingNextPage) { @@ -153,10 +161,7 @@ export const ChloridesReportView = () => { return ( - + { sx={{ py: 3, px: 2 }} > - Chlorides Reading: + + Chlorides Reading: + {chloridesQuery.isLoading && ( {[0, 1, 2, 3].map((i) => ( - + - + @@ -247,7 +261,8 @@ export const ChloridesReportView = () => { )} {chloridesQuery.isError && ( - {chloridesQuery.error?.message || "Failed to load chloride readings."} + {chloridesQuery.error?.message || + "Failed to load chloride readings."} )} {!chloridesQuery.isLoading && !chloridesQuery.isError && ( @@ -296,17 +311,19 @@ export const ChloridesReportView = () => { )} - + @@ -350,7 +367,9 @@ export const ChloridesReportView = () => { well.location?.longitude, ]} icon={ - well.well_status_id === WellStatus.PLUGGED ? BlackMapIcon : RedMapIcon + well.well_status_id === WellStatus.PLUGGED + ? BlackMapIcon + : RedMapIcon } > @@ -367,27 +386,41 @@ export const ChloridesReportView = () => { {/* Loading first page */} {wellQuery.isLoading && ( - Loading well markers... + + Loading well markers... + )} {/* Loading additional pages */} {wellQuery.isFetchingNextPage && ( - Loading more wells... + + Loading more wells... + )} {wellQuery.isSuccess && wellMarkers.length === 0 && ( - + No wells found for that search. @@ -395,10 +428,14 @@ export const ChloridesReportView = () => { {/* Error */} {wellQuery.isError && ( - + Failed to load wells: {wellQuery.error.message} @@ -412,6 +449,6 @@ export const ChloridesReportView = () => { - + ); }; diff --git a/frontend/src/views/Reports/Maintenance/index.tsx b/frontend/src/views/Reports/Maintenance/index.tsx index a3fa4d18..4cf1441d 100644 --- a/frontend/src/views/Reports/Maintenance/index.tsx +++ b/frontend/src/views/Reports/Maintenance/index.tsx @@ -1,4 +1,5 @@ import { useMemo } from "react"; +import { useAuthHeader } from "react-auth-kit"; import { ArrowBack, PictureAsPdf, Plumbing } from "@mui/icons-material"; import { Box, @@ -13,18 +14,11 @@ import { Typography, } from "@mui/material"; import { Link } from "react-router-dom"; -import ControlledDatepicker from "../../../components/RHControlled/ControlledDatepicker"; -import ControlledAutocomplete from "../../../components/RHControlled/ControlledAutocomplete"; import { useForm } from "react-hook-form"; import { useMutation, useQuery } from "react-query"; import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import dayjs, { Dayjs } from "dayjs"; -import { CustomCardHeader } from "../../../components/CustomCardHeader"; -import { BackgroundBox } from "../../../components/BackgroundBox"; -import ControlledTextbox from "../../../components/RHControlled/ControlledTextbox"; -import { useAuthHeader } from "react-auth-kit"; -import { API_URL } from "../../../config"; import { PieChart } from "@mui/x-charts"; import { DataGrid, @@ -32,6 +26,14 @@ import { GridValueGetter, GridValueFormatter, } from "@mui/x-data-grid"; +import { + ControlledDatepicker, + ControlledAutocomplete, + BackgroundBox, + ControlledTextbox, + CustomCardHeader, +} from "@/components"; +import { API_URL } from "@/config"; interface User { full_name: string; diff --git a/frontend/src/views/Reports/MonitoringWells/index.tsx b/frontend/src/views/Reports/MonitoringWells/index.tsx index 0f92a639..c03bef5b 100644 --- a/frontend/src/views/Reports/MonitoringWells/index.tsx +++ b/frontend/src/views/Reports/MonitoringWells/index.tsx @@ -34,9 +34,12 @@ import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import dayjs, { Dayjs } from "dayjs"; -import { BackgroundBox, CustomCardHeader } from "@/components"; -import ControlledDatepicker from "@/components/RHControlled/ControlledDatepicker"; -import ControlledAutocomplete from "@/components/RHControlled/ControlledAutocomplete"; +import { + ControlledDatepicker, + ControlledAutocomplete, + BackgroundBox, + CustomCardHeader, +} from "@/components"; import { MonitoredWell, WellMeasurementDTO } from "@/interfaces"; import { ReportAveragesResponse } from "@/interfaces/ReportAveragesResponse"; import { useFetchWithAuth } from "@/hooks"; diff --git a/frontend/src/views/Reports/PartsUsed/index.tsx b/frontend/src/views/Reports/PartsUsed/index.tsx index d8b1afdf..369a0035 100644 --- a/frontend/src/views/Reports/PartsUsed/index.tsx +++ b/frontend/src/views/Reports/PartsUsed/index.tsx @@ -1,4 +1,5 @@ import { useEffect, useMemo } from "react"; +import { useAuthHeader } from "react-auth-kit"; import { ArrowBack, Build, PictureAsPdf } from "@mui/icons-material"; import { Autocomplete, @@ -13,18 +14,20 @@ import { Tooltip, } from "@mui/material"; import { Link } from "react-router-dom"; -import ControlledDatepicker from "../../../components/RHControlled/ControlledDatepicker"; import { Controller, useForm } from "react-hook-form"; import { useMutation, useQuery } from "react-query"; import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; -import dayjs, { Dayjs } from "dayjs"; -import { API_URL } from "../../../config"; -import { useAuthHeader } from "react-auth-kit"; import { DataGrid, GridColDef } from "@mui/x-data-grid"; -import { BackgroundBox } from "../../../components/BackgroundBox"; -import { CustomCardHeader } from "../../../components/CustomCardHeader"; -import { ControlledSelect } from "../../../components/RHControlled/ControlledSelect"; +import dayjs, { Dayjs } from "dayjs"; + +import { API_URL } from "@/config"; +import { + ControlledDatepicker, + BackgroundBox, + CustomCardHeader, + ControlledSelect, +} from "@/components"; export interface MeterType { id: number; @@ -63,7 +66,7 @@ const schema = yup.object().shape({ .mixed() .nullable() .required("To date is required") - .test("is-after", "'To' date must be after 'From'", function(value) { + .test("is-after", "'To' date must be after 'From'", function (value) { const { from } = this.parent; return !from || !value || dayjs(value).isAfter(dayjs(from)); }), @@ -87,15 +90,15 @@ const schema = yup.object().shape({ .array() .of(yup.number().required()) .min(1, "At least one Part is required"), - in_use: yup.bool().required() + in_use: yup.bool().required(), }); const defaultSchema = { - from: dayjs().startOf('month'), - to: dayjs().endOf('month'), + from: dayjs().startOf("month"), + to: dayjs().endOf("month"), part_types: [], parts: [], - in_use: true + in_use: true, }; export const PartsUsedReportView = () => { @@ -400,7 +403,7 @@ export const PartsUsedReportView = () => { }} /> - + { - const techiciansQuery = useQuery({ - queryKey: ["workorders", "report", "techicians"], - queryFn: async () => {}, - }); - const sourceQuery = useQuery({ - queryKey: ["workorders", "report", "source"], - queryFn: async () => {}, - }); - - const { control, reset } = useForm({ - resolver: yupResolver(schema), - defaultValues: defaultSchema, - }); - - return ( - - - - Work Orders Report - - - } - sx={{ mb: 0, pb: 0 }} - /> - - - - - - - - - - - - - - - - - - - - - - - - - { - if (techiciansQuery.isLoading) - params.inputProps.value = "Loading..."; - return ( - - ); - }} - /> - - - { - if (sourceQuery.isLoading) - params.inputProps.value = "Loading..."; - return ( - - ); - }} - /> - - - - - - - - - - - - ); -}; diff --git a/frontend/src/views/Reports/index.tsx b/frontend/src/views/Reports/index.tsx index a479c2e2..f2387922 100644 --- a/frontend/src/views/Reports/index.tsx +++ b/frontend/src/views/Reports/index.tsx @@ -6,9 +6,7 @@ import { Science, } from "@mui/icons-material"; import { Box, Card, CardContent } from "@mui/material"; -import { NavLink } from "../../components/NavLink"; -import { BackgroundBox } from "../../components/BackgroundBox"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; +import { BackgroundBox, CustomCardHeader, NavLink } from "@/components"; export const ReportsView = () => { return ( @@ -17,14 +15,6 @@ export const ReportsView = () => { - {/* - - */} { label="Parts Used" icon={Build} /> - {/* - - */} = Yup.object().shape({ name: Yup.string().required("Please enter a name."), @@ -113,7 +108,7 @@ export const RoleDetailsCard = ({ @@ -148,7 +143,7 @@ export const RoleDetailsCard = ({ label={value.scope_string} clickable deleteIcon={ - event.stopPropagation() } @@ -189,7 +184,7 @@ export const RoleDetailsCard = ({ variant="contained" onClick={handleSubmit(onAddPart, onErr)} > - +   Save New Role ) : ( @@ -198,7 +193,7 @@ export const RoleDetailsCard = ({ variant="contained" onClick={handleSubmit(onSaveChanges, onErr)} > - +   Save Changes )} diff --git a/frontend/src/views/UserManagement/UserDetailsCard.tsx b/frontend/src/views/UserManagement/UserDetailsCard.tsx index 1bcf638d..33a67a42 100644 --- a/frontend/src/views/UserManagement/UserDetailsCard.tsx +++ b/frontend/src/views/UserManagement/UserDetailsCard.tsx @@ -11,12 +11,14 @@ import { Grid, Typography, } from "@mui/material"; -import AddIcon from "@mui/icons-material/Add"; -import EditIcon from "@mui/icons-material/Edit"; -import SaveIcon from "@mui/icons-material/Save"; -import SaveAsIcon from "@mui/icons-material/SaveAs"; -import LockResetIcon from "@mui/icons-material/LockReset"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import { + Add, + Edit, + Save, + SaveAs, + LockReset, + ExpandMore, +} from "@mui/icons-material"; import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { enqueueSnackbar } from "notistack"; @@ -26,14 +28,14 @@ import { useUpdateUser, useGetRoles, useUpdateUserPassword, -} from "../../service/ApiServiceNew"; -import ControlledTextbox from "../../components/RHControlled/ControlledTextbox"; -import { UpdatedUserPassword, User, UserRole } from "../../interfaces"; +} from "@/service/ApiServiceNew"; import { + ControlledTextbox, ControlledSelect, ControlledSelectNonObject, -} from "../../components/RHControlled/ControlledSelect"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; + CustomCardHeader, +} from "@/components"; +import { UpdatedUserPassword, User, UserRole } from "@/interfaces"; const UserResolverSchema: Yup.ObjectSchema = Yup.object().shape({ full_name: Yup.string().required("Please enter a full name."), @@ -50,21 +52,20 @@ const formatSubmission = (user: User) => { formattedUser.user_role_id = user.user_role?.id; delete formattedUser.user_role; return formattedUser; -} +}; const SetNewPasswordAccordion = ({ control, errorMessage, - handleSubmit + handleSubmit, }: any) => { return ( } + expandIcon={} sx={{ m: 0, mx: 2, p: 0, color: "#595959" }} > - {" "} -   +   Set New Password for User @@ -81,7 +82,7 @@ const SetNewPasswordAccordion = ({ @@ -89,7 +90,7 @@ const SetNewPasswordAccordion = ({ ); -} +}; export const UserDetailsCard = ({ selectedUser, @@ -113,11 +114,13 @@ export const UserDetailsCard = ({ const onSuccessfulUpdate = () => enqueueSnackbar("Successfully Updated User!", { variant: "success" }); const onSuccessfulPasswordUpdate = () => - enqueueSnackbar("Successfully Updated User's Password!", { variant: "success" }); + enqueueSnackbar("Successfully Updated User's Password!", { + variant: "success", + }); const onSuccessfulCreate = () => { enqueueSnackbar("Successfully Created New User!", { variant: "success" }); reset(); - } + }; const onErr = (data: any) => console.error("ERR: ", data); @@ -125,7 +128,8 @@ export const UserDetailsCard = ({ const createUser = useCreateUser(onSuccessfulCreate); const updateUserPassword = useUpdateUserPassword(onSuccessfulPasswordUpdate); - const onSaveChanges = (user: User) => updateUser.mutate(formatSubmission(user)); + const onSaveChanges = (user: User) => + updateUser.mutate(formatSubmission(user)); const onCreateUser = (user: User) => { if (!user.password || user.password.length < 1) { @@ -133,7 +137,7 @@ export const UserDetailsCard = ({ return; } createUser.mutate(formatSubmission(user)); - } + }; const onUpdateUserPassword = ( userId: number, @@ -148,7 +152,7 @@ export const UserDetailsCard = ({ new_password: newPassword, }; updateUserPassword.mutate(updatedUserPassword); - } + }; useEffect(() => { if (selectedUser != undefined) { @@ -169,7 +173,7 @@ export const UserDetailsCard = ({ @@ -262,7 +266,7 @@ export const UserDetailsCard = ({ variant="contained" onClick={handleSubmit(onCreateUser, onErr)} > - +   Save New User ) : ( @@ -271,7 +275,7 @@ export const UserDetailsCard = ({ variant="contained" onClick={handleSubmit(onSaveChanges, onErr)} > - +   Save Changes )} diff --git a/frontend/src/views/WellManagement/WellDetailsCard.tsx b/frontend/src/views/WellManagement/WellDetailsCard.tsx index 6175a3b5..a8c3b54f 100644 --- a/frontend/src/views/WellManagement/WellDetailsCard.tsx +++ b/frontend/src/views/WellManagement/WellDetailsCard.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useForm, SubmitHandler } from "react-hook-form"; import { useQueryClient } from "react-query"; import { @@ -11,10 +11,8 @@ import { Grid, Stack, } from "@mui/material"; -import AddIcon from "@mui/icons-material/Add"; -import EditIcon from "@mui/icons-material/Edit"; -import SaveIcon from "@mui/icons-material/Save"; -import SaveAsIcon from "@mui/icons-material/SaveAs"; +import { Add, Edit, Save, SaveAs } from "@mui/icons-material"; +import { useAuthUser } from "react-auth-kit"; import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { enqueueSnackbar } from "notistack"; @@ -25,8 +23,7 @@ import { useGetWaterSources, useGetWellStatusTypes, useUpdateWell, -} from "../../service/ApiServiceNew"; -import ControlledTextbox from "../../components/RHControlled/ControlledTextbox"; +} from "@/service/ApiServiceNew"; import { SubmitWellCreate, WellUpdate, @@ -34,15 +31,17 @@ import { Well, WellStatus, WellUseLU, -} from "../../interfaces"; -import { ControlledSelect } from "../../components/RHControlled/ControlledSelect"; -import ControlledDMS from "../../components/RHControlled/ControlledDMS"; -import { GCSdimension } from "../../enums"; -import { MergeWellModal } from "../../components/MergeWellModal"; -import { useAuthUser } from "react-auth-kit"; -import { SecurityScope } from "../../interfaces"; -import ControlledCheckbox from "../../components/RHControlled/ControlledCheckbox"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; + SecurityScope, +} from "@/interfaces"; +import { + ControlledTextbox, + ControlledSelect, + ControlledDMS, + MergeWellModal, + ControlledCheckbox, + CustomCardHeader, +} from "@/components"; +import { GCSdimension } from "@/enums"; const WellResolverSchema: Yup.ObjectSchema = Yup.object().shape({ use_type: Yup.object().required("Please select a use type."), @@ -124,7 +123,7 @@ export const WellDetailsCard = ({ const hasErrors = () => Object.keys(errors).length > 0; // Modal related functions - const [isWellMergeModalOpen, setIsWellMergeModalOpen] = React.useState(false); + const [isWellMergeModalOpen, setIsWellMergeModalOpen] = useState(false); const handleOpenMergeModal = () => setIsWellMergeModalOpen(true); const handleCloseMergeModal = () => setIsWellMergeModalOpen(false); @@ -132,7 +131,7 @@ export const WellDetailsCard = ({ @@ -315,7 +314,7 @@ export const WellDetailsCard = ({ variant="contained" onClick={handleSubmit(onAddWell, onErr)} > - +   Save New Well ) : ( @@ -324,7 +323,7 @@ export const WellDetailsCard = ({ variant="contained" onClick={handleSubmit(onSaveChanges, onErr)} > - +   Save Changes )} diff --git a/frontend/src/views/WellManagement/WellManagementView.tsx b/frontend/src/views/WellManagement/WellManagementView.tsx index 9927a275..3da88baf 100644 --- a/frontend/src/views/WellManagement/WellManagementView.tsx +++ b/frontend/src/views/WellManagement/WellManagementView.tsx @@ -1,11 +1,12 @@ -import { Grid } from "@mui/material"; import { useEffect, useState } from "react"; +import { Grid } from "@mui/material"; +import { BackgroundBox } from "@/components"; +import { Well } from "@/interfaces"; + import { WellsTable } from "./WellsTable"; -import { Well } from "../../interfaces"; import { WellDetailsCard } from "./WellDetailsCard"; -import { BackgroundBox } from "../../components/BackgroundBox"; -export default function WellManagementView() { +export const WellManagementView = () => { const [wellAddMode, setWellAddMode] = useState(true); const [selectedWell, setSelectedWell] = useState(); @@ -15,10 +16,7 @@ export default function WellManagementView() { return ( - + ); -} +}; diff --git a/frontend/src/views/WorkOrders/WorkOrdersView.tsx b/frontend/src/views/WorkOrders/WorkOrdersView.tsx index c3ad9394..af89586f 100644 --- a/frontend/src/views/WorkOrders/WorkOrdersView.tsx +++ b/frontend/src/views/WorkOrders/WorkOrdersView.tsx @@ -1,16 +1,16 @@ import { Card, CardContent } from "@mui/material"; -import FormatListBulletedOutlinedIcon from "@mui/icons-material/FormatListBulletedOutlined"; +import { FormatListBulletedOutlined } from "@mui/icons-material"; +import { BackgroundBox, CustomCardHeader } from "@/components"; + import WorkOrdersTable from "./WorkOrdersTable"; -import { BackgroundBox } from "../../components/BackgroundBox"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; -export default function WorkOrdersView() { +export const WorkOrdersView = () => { return ( - + @@ -18,4 +18,4 @@ export default function WorkOrdersView() { ); -} +}; diff --git a/frontend/src/views/index.ts b/frontend/src/views/index.ts index ae5a45e3..1862084b 100644 --- a/frontend/src/views/index.ts +++ b/frontend/src/views/index.ts @@ -1,15 +1,15 @@ -export * from './Activities' -export * from './Backups' -export * from './Chlorides' -export * from './Home.ts' -export * from './InsufficientPermView.ts' -export * from './Login.ts' -export * from './Meters' -export * from './MonitoringWells' -export * from './NotFound.ts' -export * from './Parts' -export * from './Reports' -export * from './Settings.ts' -export * from './UserManagement' -export * from './WellManagement' -export * from './WorkOrders' +export * from "./Activities"; +export * from "./Backups"; +export * from "./Chlorides"; +export * from "./Home"; +export * from "./InsufficientPermView"; +export * from "./Login"; +export * from "./Meters"; +export * from "./MonitoringWells"; +export * from "./NotFound"; +export * from "./Parts"; +export * from "./Reports"; +export * from "./Settings"; +export * from "./UserManagement"; +export * from "./WellManagement"; +export * from "./WorkOrders"; From dc120046f1d40d600ea034a5fc41bdcf2df69607 Mon Sep 17 00:00:00 2001 From: Tyler Adam Martinez Date: Mon, 9 Feb 2026 22:37:34 -0600 Subject: [PATCH 3/7] chore(components): Update imports --- frontend/src/components/ChipSelect.tsx | 4 +- frontend/src/components/ImageDialog.tsx | 4 +- frontend/src/components/ImagePreviewGrid.tsx | 110 +++++++++-------- frontend/src/components/MergeWellModal.tsx | 5 +- .../src/components/MeterMapColorLegend.tsx | 7 +- .../src/components/MeterRegisterSelect.tsx | 4 +- frontend/src/components/MeterSelection.tsx | 6 +- frontend/src/components/MeterTypeSelect.tsx | 4 +- .../Modals/MonitoredWell/Create.tsx | 2 +- .../Modals/MonitoredWell/Update.tsx | 2 +- .../src/components/Modals/Region/Create.tsx | 2 +- .../src/components/Modals/Region/Update.tsx | 2 +- frontend/src/components/NavLink.tsx | 15 ++- .../RHControlled/ControlledActivitySelect.tsx | 2 +- .../RHControlled/ControlledMeterSelection.tsx | 2 +- .../ControlledMeterStatusTypeSelect.tsx | 2 +- .../ControlledMeterTypeSelect.tsx | 2 +- .../RHControlled/ControlledPartTypeSelect.tsx | 2 +- .../RHControlled/ControlledUserSelect.tsx | 2 +- .../RHControlled/ControlledWellSelection.tsx | 2 +- .../components/RHControlled/NSPChipSelect.tsx | 9 +- .../RHControlled/NotesChipSelect.tsx | 7 +- .../RHControlled/PartsChipSelect.tsx | 2 +- .../RHControlled/ServicesChipSelect.tsx | 2 +- frontend/src/components/ReportsNavItem.tsx | 17 +-- frontend/src/components/StatCell.tsx | 19 ++- frontend/src/components/TopbarUserButton.tsx | 28 +++-- frontend/src/components/UserSelection.tsx | 4 +- frontend/src/components/WellSelection.tsx | 4 +- frontend/src/components/WorkOrderSelect.tsx | 114 +++++++++++------- frontend/src/service/index.ts | 1 + 31 files changed, 220 insertions(+), 168 deletions(-) create mode 100644 frontend/src/service/index.ts diff --git a/frontend/src/components/ChipSelect.tsx b/frontend/src/components/ChipSelect.tsx index 4cf0bbfb..39b656e9 100644 --- a/frontend/src/components/ChipSelect.tsx +++ b/frontend/src/components/ChipSelect.tsx @@ -7,7 +7,7 @@ import { OutlinedInput, Select, } from "@mui/material"; -import CancelIcon from "@mui/icons-material/Cancel"; +import { Cancel } from "@mui/icons-material"; interface chipselectitem { id: number; @@ -45,7 +45,7 @@ export default function ChipSelect({ label={value.name} clickable deleteIcon={ - event.stopPropagation()} /> } diff --git a/frontend/src/components/ImageDialog.tsx b/frontend/src/components/ImageDialog.tsx index 11e4f2e3..7004534d 100644 --- a/frontend/src/components/ImageDialog.tsx +++ b/frontend/src/components/ImageDialog.tsx @@ -1,5 +1,5 @@ import { Dialog, DialogContent, IconButton, Box } from "@mui/material"; -import CloseIcon from "@mui/icons-material/Close"; +import { Close } from "@mui/icons-material"; export const ImageDialog = ({ open, @@ -25,7 +25,7 @@ export const ImageDialog = ({ "&:hover": { backgroundColor: "rgba(0,0,0,0.8)" }, }} > - + {src && ( void; - onOpen?: (src: string) => void; -}) => { - return ( - - {previews.map((src, i) => { - return ( - onOpen?.(src)} - > +export const ImagePreviewGrid = memo( + ({ + previews, + onRemove, + onOpen, + }: { + previews: string[]; + onRemove?: (index: number) => void; + onOpen?: (src: string) => void; + }) => { + return ( + + {previews.map((src, i) => { + return ( - {onRemove && ( - onRemove(i)} + onDoubleClick={() => onOpen?.(src)} + > + - - - )} - - ); - })} - - ); -}); + /> + {onRemove && ( + onRemove(i)} + sx={{ + position: "absolute", + top: 0, + right: 0, + backgroundColor: "rgba(255,255,255,0.7)", + border: "1px solid black", + "&:hover": { + backgroundColor: "rgba(255,0,0,0.8)", + color: "white", + }, + }} + > + + + )} + + ); + })} + + ); + }, +); diff --git a/frontend/src/components/MergeWellModal.tsx b/frontend/src/components/MergeWellModal.tsx index bc7bbfdc..0afaf310 100644 --- a/frontend/src/components/MergeWellModal.tsx +++ b/frontend/src/components/MergeWellModal.tsx @@ -1,8 +1,9 @@ import { Box, Modal, Button, Grid } from "@mui/material"; import { useState, useEffect } from "react"; -import { useMergeWells } from "../service/ApiServiceNew"; +import { useMergeWells } from "@/service"; +import { Well } from "@/interfaces"; + import WellSelection from "./WellSelection"; -import { Well } from "../interfaces"; export function MergeWellModal({ isWellMergeModalOpen, diff --git a/frontend/src/components/MeterMapColorLegend.tsx b/frontend/src/components/MeterMapColorLegend.tsx index fad8515b..ef75b7d3 100644 --- a/frontend/src/components/MeterMapColorLegend.tsx +++ b/frontend/src/components/MeterMapColorLegend.tsx @@ -1,7 +1,7 @@ import { useEffect } from "react"; import { useLeafletContext } from "@react-leaflet/core"; import L from "leaflet"; -import { PM_COLORS } from "../constants"; +import { PM_COLORS } from "@/constants"; export const MeterMapColorLegend = () => { const context = useLeafletContext(); @@ -9,7 +9,7 @@ export const MeterMapColorLegend = () => { useEffect(() => { const legend = new L.Control({ position: "bottomleft" }); - legend.onAdd = function() { + legend.onAdd = function () { const div = L.DomUtil.create("div", "info legend"); div.style.background = "white"; @@ -53,5 +53,4 @@ export const MeterMapColorLegend = () => { }, [context.map]); return null; -} - +}; diff --git a/frontend/src/components/MeterRegisterSelect.tsx b/frontend/src/components/MeterRegisterSelect.tsx index 3c1255d6..f9d2b803 100644 --- a/frontend/src/components/MeterRegisterSelect.tsx +++ b/frontend/src/components/MeterRegisterSelect.tsx @@ -1,5 +1,4 @@ import { useEffect, useMemo } from "react"; -import { useGetMeterRegisterList } from "../service/ApiServiceNew"; import { FormControl, InputLabel, @@ -7,7 +6,8 @@ import { Select, FormHelperText, } from "@mui/material"; -import { MeterRegister, MeterType } from "../interfaces"; +import { useGetMeterRegisterList } from "@/service"; +import { MeterRegister, MeterType } from "@/interfaces"; function getRegisterTitle(register: MeterRegister) { //Describing the register can be a bit complex, so this function will return a string that describes the register diff --git a/frontend/src/components/MeterSelection.tsx b/frontend/src/components/MeterSelection.tsx index 9df45ae4..0d5136b8 100644 --- a/frontend/src/components/MeterSelection.tsx +++ b/frontend/src/components/MeterSelection.tsx @@ -1,9 +1,9 @@ import { useState } from "react"; import { Autocomplete, TextField } from "@mui/material"; -import { useGetMeterList } from "../service/ApiServiceNew"; import { useDebounce } from "use-debounce"; -import { MeterListDTO } from "../interfaces"; -import { MeterStatusNames } from "../enums"; +import { useGetMeterList } from "@/service"; +import { MeterListDTO } from "@/interfaces"; +import { MeterStatusNames } from "@/enums"; interface MeterSelectionProps { selectedMeter: MeterListDTO | undefined; diff --git a/frontend/src/components/MeterTypeSelect.tsx b/frontend/src/components/MeterTypeSelect.tsx index 05246ac1..d2be1e64 100644 --- a/frontend/src/components/MeterTypeSelect.tsx +++ b/frontend/src/components/MeterTypeSelect.tsx @@ -1,6 +1,6 @@ -import { useGetMeterTypeList } from "../service/ApiServiceNew"; import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; -import { MeterTypeLU } from "../interfaces"; +import { useGetMeterTypeList } from "@/service"; +import { MeterTypeLU } from "@/interfaces"; export default function MeterTypeSelect({ selectedMeterTypeID, diff --git a/frontend/src/components/Modals/MonitoredWell/Create.tsx b/frontend/src/components/Modals/MonitoredWell/Create.tsx index 6510f277..cca993f8 100644 --- a/frontend/src/components/Modals/MonitoredWell/Create.tsx +++ b/frontend/src/components/Modals/MonitoredWell/Create.tsx @@ -22,7 +22,7 @@ dayjs.extend(timezone); import { DatePicker, TimePicker } from "@mui/x-date-pickers"; import { NewWellMeasurement, SecurityScope } from "@/interfaces"; -import { useGetUserList } from "@/service/ApiServiceNew"; +import { useGetUserList } from "@/service"; import { Save } from "@mui/icons-material"; export const CreateModal = ({ diff --git a/frontend/src/components/Modals/MonitoredWell/Update.tsx b/frontend/src/components/Modals/MonitoredWell/Update.tsx index 0a700c3a..5f4c7874 100644 --- a/frontend/src/components/Modals/MonitoredWell/Update.tsx +++ b/frontend/src/components/Modals/MonitoredWell/Update.tsx @@ -20,7 +20,7 @@ dayjs.extend(utc); dayjs.extend(timezone); import { DatePicker, TimePicker } from "@mui/x-date-pickers"; -import { useGetUserList } from "@/service/ApiServiceNew"; +import { useGetUserList } from "@/service"; import { PatchWellMeasurement } from "@/interfaces"; export function UpdateModal({ diff --git a/frontend/src/components/Modals/Region/Create.tsx b/frontend/src/components/Modals/Region/Create.tsx index 7ef6942b..5e5744ff 100644 --- a/frontend/src/components/Modals/Region/Create.tsx +++ b/frontend/src/components/Modals/Region/Create.tsx @@ -30,7 +30,7 @@ import timezone from "dayjs/plugin/timezone"; dayjs.extend(utc); dayjs.extend(timezone); -import { useGetUserList } from "@/service/ApiServiceNew"; +import { useGetUserList } from "@/service"; import { useFetchWithAuth } from "@/hooks"; export const CreateModal = ({ diff --git a/frontend/src/components/Modals/Region/Update.tsx b/frontend/src/components/Modals/Region/Update.tsx index 86357e74..64ab5d91 100644 --- a/frontend/src/components/Modals/Region/Update.tsx +++ b/frontend/src/components/Modals/Region/Update.tsx @@ -28,7 +28,7 @@ import { Delete, Save, } from "@mui/icons-material"; -import { useGetUserList } from "@/service/ApiServiceNew"; +import { useGetUserList } from "@/service"; import { useQuery } from "react-query"; import { useFetchWithAuth } from "@/hooks"; import { MonitoredWell, PatchRegionMeasurement } from "@/interfaces"; diff --git a/frontend/src/components/NavLink.tsx b/frontend/src/components/NavLink.tsx index f6541404..40d14ab8 100644 --- a/frontend/src/components/NavLink.tsx +++ b/frontend/src/components/NavLink.tsx @@ -1,7 +1,14 @@ -import { SvgIconProps, Badge, ListItem, ListItemButton, ListItemIcon, ListItemText } from "@mui/material"; -import TableViewIcon from "@mui/icons-material/TableView"; +import { + SvgIconProps, + Badge, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, +} from "@mui/material"; +import { TableView } from "@mui/icons-material"; import { Link, type LinkProps } from "react-router-dom"; -import { useIsActiveRoute } from "../hooks"; +import { useIsActiveRoute } from "@/hooks"; export const NavLink = ({ disabled = false, @@ -46,7 +53,7 @@ export const NavLink = ({ ) : ( - + )} { const notesList = useGetNoteTypes(); diff --git a/frontend/src/components/RHControlled/PartsChipSelect.tsx b/frontend/src/components/RHControlled/PartsChipSelect.tsx index 5537cc10..9a9751f6 100644 --- a/frontend/src/components/RHControlled/PartsChipSelect.tsx +++ b/frontend/src/components/RHControlled/PartsChipSelect.tsx @@ -1,7 +1,7 @@ import { Controller } from "react-hook-form"; import { Part } from "@/interfaces"; -import { useGetMeterPartsList } from "@/service/ApiServiceNew"; +import { useGetMeterPartsList } from "@/service"; import ChipSelect from "../ChipSelect"; diff --git a/frontend/src/components/RHControlled/ServicesChipSelect.tsx b/frontend/src/components/RHControlled/ServicesChipSelect.tsx index 7bb4e5f0..c3010108 100644 --- a/frontend/src/components/RHControlled/ServicesChipSelect.tsx +++ b/frontend/src/components/RHControlled/ServicesChipSelect.tsx @@ -1,7 +1,7 @@ import { Controller } from "react-hook-form"; import { ServiceTypeLU } from "@/interfaces"; -import { useGetServiceTypes } from "@/service/ApiServiceNew"; +import { useGetServiceTypes } from "@/service"; import ChipSelect from "../ChipSelect"; diff --git a/frontend/src/components/ReportsNavItem.tsx b/frontend/src/components/ReportsNavItem.tsx index 2de81665..28ac2a88 100644 --- a/frontend/src/components/ReportsNavItem.tsx +++ b/frontend/src/components/ReportsNavItem.tsx @@ -5,15 +5,17 @@ import { ListItemIcon, ListItemText, } from "@mui/material"; -import { - Assessment, - ExpandLess, - ExpandMore, -} from "@mui/icons-material"; +import { Assessment, ExpandLess, ExpandMore } from "@mui/icons-material"; import { useNavigate } from "react-router-dom"; -import { useIsActiveRoute } from "../hooks"; +import { useIsActiveRoute } from "@/hooks"; -export function ReportsNavItem({ open, setOpen }: { open: boolean, setOpen: Dispatch> }) { +export function ReportsNavItem({ + open, + setOpen, +}: { + open: boolean; + setOpen: Dispatch>; +}) { const navigate = useNavigate(); const [clickTimer, setClickTimer] = useState(null); const isActive = useIsActiveRoute("/reports"); @@ -69,4 +71,3 @@ export function ReportsNavItem({ open, setOpen }: { open: boolean, setOpen: Disp ); } - diff --git a/frontend/src/components/StatCell.tsx b/frontend/src/components/StatCell.tsx index 954ac962..1a6b2329 100644 --- a/frontend/src/components/StatCell.tsx +++ b/frontend/src/components/StatCell.tsx @@ -1,13 +1,24 @@ import { Stack, Typography } from "@mui/material"; -import { formatNumberData } from "../utils"; +import { formatNumberData } from "@/utils"; -export const StatCell = ({ label, value, isCount }: { label: string; value?: number, isCount?: boolean }) => { +export const StatCell = ({ + label, + value, + isCount, +}: { + label: string; + value?: number; + isCount?: boolean; +}) => { return ( {label} - {formatNumberData(value)}{isCount ? "" : " ppm"} + + {formatNumberData(value)} + {isCount ? "" : " ppm"} + ); -} +}; diff --git a/frontend/src/components/TopbarUserButton.tsx b/frontend/src/components/TopbarUserButton.tsx index 5cbd6fab..d0bf7eaa 100644 --- a/frontend/src/components/TopbarUserButton.tsx +++ b/frontend/src/components/TopbarUserButton.tsx @@ -1,8 +1,7 @@ import { Avatar, Button, ButtonProps } from "@mui/material"; -import { getRoleColor } from "../utils"; import { Badge, Engineering, Face } from "@mui/icons-material"; import { useTheme } from "@mui/material/styles"; - +import { getRoleColor } from "@/utils"; export const TopbarUserButton = ({ display_name, @@ -10,9 +9,9 @@ export const TopbarUserButton = ({ src, ...buttonProps }: { - display_name: string, - role: string, - src?: string + display_name: string; + role: string; + src?: string; } & ButtonProps) => { const theme = useTheme(); const buttonColor = getRoleColor(role); @@ -23,22 +22,25 @@ export const TopbarUserButton = ({ const roleIcons: Record = { Admin: , - Technician: , + Technician: ( + + ), }; - const renderRoleIcon = () => roleIcons[role] ?? ; + const renderRoleIcon = () => + roleIcons[role] ?? ; const roleBgColor: Record = { Admin: primary.dark, Technician: secondary.dark, - OSE: warning.dark - } + OSE: warning.dark, + }; const roleBorderColor: Record = { Admin: primary.contrastText, Technician: secondary.contrastText, - OSE: warning.contrastText - } + OSE: warning.contrastText, + }; return ( ); -} +}; diff --git a/frontend/src/components/UserSelection.tsx b/frontend/src/components/UserSelection.tsx index bf913158..814742ea 100644 --- a/frontend/src/components/UserSelection.tsx +++ b/frontend/src/components/UserSelection.tsx @@ -1,7 +1,7 @@ -import { User } from "../interfaces"; import { FormControl, InputLabel, Select, MenuItem } from "@mui/material"; -import { useGetUserList } from "../service/ApiServiceNew"; import { useAuthUser } from "react-auth-kit"; +import { useGetUserList } from "@/service"; +import { User } from "@/interfaces"; export default function UserSelection({ selectedUser, diff --git a/frontend/src/components/WellSelection.tsx b/frontend/src/components/WellSelection.tsx index e81c7880..03debe63 100644 --- a/frontend/src/components/WellSelection.tsx +++ b/frontend/src/components/WellSelection.tsx @@ -2,8 +2,8 @@ import { useState } from "react"; import { TextField } from "@mui/material"; import { useDebounce } from "use-debounce"; import { Autocomplete } from "@mui/material"; -import { useGetWells } from "../service/ApiServiceNew"; -import { Well } from "../interfaces"; +import { useGetWells } from "@/service"; +import { Well } from "@/interfaces"; export default function WellSelection({ selectedWell, diff --git a/frontend/src/components/WorkOrderSelect.tsx b/frontend/src/components/WorkOrderSelect.tsx index 3eaa7ea2..0e5ad26f 100644 --- a/frontend/src/components/WorkOrderSelect.tsx +++ b/frontend/src/components/WorkOrderSelect.tsx @@ -2,61 +2,83 @@ A simple select component that limits options based on filters. */ -import React, { useEffect } from 'react' -import { FormControl, InputLabel, MenuItem, Select } from '@mui/material' -import { useGetWorkOrders } from '../service/ApiServiceNew' -import { WorkOrderStatus } from '../enums' -import { WorkOrder } from '../interfaces' +import { useEffect, useState } from "react"; +import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; +import { useGetWorkOrders } from "@/service"; +import { WorkOrderStatus } from "@/enums"; +import { WorkOrder } from "@/interfaces"; interface WorkOrderSelectFilters { - meter_serial?: string - assigned_user_id?: number - date_created?: Date + meter_serial?: string; + assigned_user_id?: number; + date_created?: Date; } interface WorkOrderSelectProps { - selectedWorkOrderID: number | null - setSelectedWorkOrderID: (workOrderID: number | null) => void - option_filters?: WorkOrderSelectFilters + selectedWorkOrderID: number | null; + setSelectedWorkOrderID: (workOrderID: number | null) => void; + option_filters?: WorkOrderSelectFilters; } -function optionsFilter(workOrders: WorkOrder[], filters: WorkOrderSelectFilters) { - //Use the filter method for each component of the filter - if (filters.meter_serial && filters.meter_serial !== undefined) { - workOrders = workOrders.filter((workOrder) => workOrder.meter_serial === filters.meter_serial) - } - if (filters.assigned_user_id && filters.assigned_user_id !== undefined) { - workOrders = workOrders.filter((workOrder) => workOrder.assigned_user_id === filters.assigned_user_id) - } - if (filters.date_created && filters.date_created !== undefined) { - workOrders = workOrders.filter((workOrder) => workOrder.date_created === filters.date_created) - } - return workOrders +function optionsFilter( + workOrders: WorkOrder[], + filters: WorkOrderSelectFilters, +) { + //Use the filter method for each component of the filter + if (filters.meter_serial && filters.meter_serial !== undefined) { + workOrders = workOrders.filter( + (workOrder) => workOrder.meter_serial === filters.meter_serial, + ); + } + if (filters.assigned_user_id && filters.assigned_user_id !== undefined) { + workOrders = workOrders.filter( + (workOrder) => workOrder.assigned_user_id === filters.assigned_user_id, + ); + } + if (filters.date_created && filters.date_created !== undefined) { + workOrders = workOrders.filter( + (workOrder) => workOrder.date_created === filters.date_created, + ); + } + return workOrders; } -export default function WorkOrderSelect({selectedWorkOrderID, setSelectedWorkOrderID, option_filters}: WorkOrderSelectProps) { - const workOrderList = useGetWorkOrders([WorkOrderStatus['Open']]) - const [filteredWorkOrders, setFilteredWorkOrders] = React.useState([]) +export default function WorkOrderSelect({ + selectedWorkOrderID, + setSelectedWorkOrderID, + option_filters, +}: WorkOrderSelectProps) { + const workOrderList = useGetWorkOrders([WorkOrderStatus["Open"]]); + const [filteredWorkOrders, setFilteredWorkOrders] = useState([]); - useEffect(() => { - if (workOrderList.data) { - setFilteredWorkOrders(optionsFilter(workOrderList.data, option_filters ?? {})); - } - }, [workOrderList, option_filters]); + useEffect(() => { + if (workOrderList.data) { + setFilteredWorkOrders( + optionsFilter(workOrderList.data, option_filters ?? {}), + ); + } + }, [workOrderList, option_filters]); - return ( - - Work Order - setSelectedWorkOrderID(event.target.value)} + > + None + {filteredWorkOrders.map((workOrder: WorkOrder) => { + return ( + - None - {filteredWorkOrders.map((workOrder: WorkOrder) => { - return {workOrder.title} - })} - - - ) -} \ No newline at end of file + {workOrder.title} + + ); + })} + + + ); +} diff --git a/frontend/src/service/index.ts b/frontend/src/service/index.ts new file mode 100644 index 00000000..67ffb42e --- /dev/null +++ b/frontend/src/service/index.ts @@ -0,0 +1 @@ +export * from "./ApiServiceNew"; From cb6ba28d698b99b717459a67c98fcbd6c9c02a1a Mon Sep 17 00:00:00 2001 From: Tyler Adam Martinez Date: Tue, 10 Feb 2026 01:12:37 -0600 Subject: [PATCH 4/7] fix(Modals): Fix broken types in Create & Update --- frontend/src/AppLayout.tsx | 13 +- .../src/components/Modals/Region/Create.tsx | 48 ++- .../src/components/Modals/Region/Update.tsx | 79 ++-- frontend/src/components/TabPanel.tsx | 48 +-- frontend/src/components/Topbar.tsx | 90 +++-- frontend/src/hooks/useFetchST2.ts | 4 +- frontend/src/hooks/useFetchWithAuth.ts | 6 +- frontend/src/service/ApiServiceNew.ts | 4 +- frontend/src/utils/index.ts | 1 + .../src/views/Activities/ActivitiesView.tsx | 3 +- .../views/Activities/ActivityPhotoView.tsx | 4 +- .../MeterActivityEntry/ActivityFormConfig.ts | 6 +- .../MaintenanceRepairSelection.tsx | 2 +- .../MeterActivityEntry/MeterActivityEntry.tsx | 2 +- .../MeterActivityEntry/NotesSelection.tsx | 13 +- .../ObservationsSelection.tsx | 2 +- .../MeterActivityEntry/PartsSelection.tsx | 6 +- .../src/views/Chlorides/ChloridesView.tsx | 11 +- frontend/src/views/Home.tsx | 35 +- frontend/src/views/Login.tsx | 18 +- .../Meters/MeterHistory/MeterHistory.tsx | 12 +- .../Meters/MeterHistory/MeterHistoryTable.tsx | 6 +- .../Meters/MeterHistory/SelectedBlankCard.tsx | 2 +- .../MeterHistory/SelectedHistoryDetails.tsx | 8 +- .../SelectedObservationDetails.tsx | 2 +- .../Meters/MeterSelection/MeterSelection.tsx | 29 +- .../MeterSelection/MeterSelectionMap.tsx | 73 ++-- .../MonitoringWells/MonitoringWellsView.tsx | 65 ++-- frontend/src/views/NotFound.tsx | 12 +- .../src/views/Parts/MeterTypeDetailsCard.tsx | 5 +- frontend/src/views/Parts/MeterTypesTable.tsx | 2 +- frontend/src/views/Parts/PartDetailsCard.tsx | 3 +- frontend/src/views/Parts/PartsTable.tsx | 2 +- frontend/src/views/Parts/PartsView.tsx | 11 +- frontend/src/views/Settings.tsx | 362 ++++++++++++------ .../views/UserManagement/PermissionsTable.tsx | 2 +- .../views/UserManagement/RoleDetailsCard.tsx | 6 +- .../src/views/UserManagement/RolesTable.tsx | 2 +- .../views/UserManagement/UserDetailsCard.tsx | 2 +- .../UserManagement/UserManagementView.tsx | 7 +- .../src/views/UserManagement/UsersTable.tsx | 2 +- .../views/WellManagement/WellDetailsCard.tsx | 2 +- .../views/WellManagement/WellSelectionMap.tsx | 76 ++-- .../WellManagement/WellSelectionTable.tsx | 2 +- .../src/views/WellManagement/WellsTable.tsx | 34 +- 45 files changed, 696 insertions(+), 428 deletions(-) diff --git a/frontend/src/AppLayout.tsx b/frontend/src/AppLayout.tsx index 0a9aaf78..69630725 100644 --- a/frontend/src/AppLayout.tsx +++ b/frontend/src/AppLayout.tsx @@ -1,19 +1,15 @@ import { useState } from "react"; import { Box } from "@mui/material"; -import Topbar from "./components/Topbar"; +import { Topbar } from "@/components"; import Sidenav from "./sidenav"; const drawerWidth = 250; -export const AppLayout = ({ - children, -}: { - children: JSX.Element -}) => { +export const AppLayout = ({ children }: { children: JSX.Element }) => { const [drawerOpen, setDrawerOpen] = useState(false); return ( - + setDrawerOpen(!drawerOpen)} @@ -35,7 +31,6 @@ export const AppLayout = ({ > {children} - + ); }; - diff --git a/frontend/src/components/Modals/Region/Create.tsx b/frontend/src/components/Modals/Region/Create.tsx index 5e5744ff..2695a2f6 100644 --- a/frontend/src/components/Modals/Region/Create.tsx +++ b/frontend/src/components/Modals/Region/Create.tsx @@ -22,6 +22,7 @@ import { useQuery } from "react-query"; import { MonitoredWell, NewRegionMeasurement, + NewWellMeasurement, SecurityScope, } from "@/interfaces"; import dayjs, { Dayjs } from "dayjs"; @@ -33,19 +34,26 @@ dayjs.extend(timezone); import { useGetUserList } from "@/service"; import { useFetchWithAuth } from "@/hooks"; -export const CreateModal = ({ - region_id, //Used to filter wells - open, - onClose, - handleSubmitNewMeasurement, - title = "Create New Measurement", -}: { - region_id: number; //Used to filter wells - open: boolean; - onClose: () => void; - handleSubmitNewMeasurement: (newMeasurement: NewRegionMeasurement) => void; - title?: string; -}) => { +type CreateModalProps = + | { + mode: "region"; + region_id?: number; + open: boolean; + onClose: () => void; + handleSubmitNewMeasurement: (m: Partial) => void; + title?: string; + } + | { + mode: "well"; + open: boolean; + onClose: () => void; + handleSubmitNewMeasurement: (m: Partial) => void; + title?: string; + }; + +export const CreateModal = (props: CreateModalProps) => { + const { open, onClose, title = "Create New Measurement" } = props; + const authUser = useAuthUser(); const hasAdminScope = authUser() ?.user_role.security_scopes.map( @@ -54,12 +62,13 @@ export const CreateModal = ({ .includes("admin"); const fetchWithAuth = useFetchWithAuth(); + const regionId = props.mode === "region" ? props.region_id : undefined; const { data: wells, isLoading: isLoadingWells } = useQuery< { items: MonitoredWell[] }, Error, MonitoredWell[] >({ - queryKey: ["wells", "has_chloride_groups", region_id], + queryKey: ["wells", "has_chloride_groups", regionId], queryFn: () => fetchWithAuth({ method: "GET", @@ -68,11 +77,11 @@ export const CreateModal = ({ sort_by: "ra_number", sort_direction: "asc", has_chloride_group: true, - chloride_group_id: region_id, + chloride_group_id: regionId, limit: 100, }, }), - enabled: open, + enabled: open && props.mode === "region" && !!regionId, select: (res) => res.items, }); @@ -95,7 +104,7 @@ export const CreateModal = ({ .minute(selectedTime.minute()) .second(selectedTime.second()); - handleSubmitNewMeasurement({ + props.handleSubmitNewMeasurement({ region_id: 0, // Set by parent well_id: selectedWellID as number, timestamp: combinedDateTime.toISOString(), @@ -239,8 +248,9 @@ export const CreateModal = ({ setValue(newValue === "" ? null : Number(newValue)); }} /> - - + {props.mode === "region" && regionId ? ( + + ) : null} diff --git a/frontend/src/components/Modals/Region/Update.tsx b/frontend/src/components/Modals/Region/Update.tsx index 64ab5d91..1a0bf600 100644 --- a/frontend/src/components/Modals/Region/Update.tsx +++ b/frontend/src/components/Modals/Region/Update.tsx @@ -31,30 +31,52 @@ import { import { useGetUserList } from "@/service"; import { useQuery } from "react-query"; import { useFetchWithAuth } from "@/hooks"; -import { MonitoredWell, PatchRegionMeasurement } from "@/interfaces"; +import { + MonitoredWell, + PatchRegionMeasurement, + PatchWellMeasurement, +} from "@/interfaces"; + +type UpdateModalProps = + | { + mode: "region"; + region_id?: number; + open: boolean; + onClose: () => void; + measurement: Partial; + onUpdateMeasurement: (value: Partial) => void; + onSubmitUpdate: () => void; + onDeleteMeasurement: () => void; + title?: string; + } + | { + mode: "well"; + open: boolean; + onClose: () => void; + measurement: Partial; + onUpdateMeasurement: (value: Partial) => void; + onSubmitUpdate: () => void; + onDeleteMeasurement: () => void; + title?: string; + }; + +export const UpdateModal = (props: UpdateModalProps) => { + const { + open, + onClose, + onSubmitUpdate, + onDeleteMeasurement, + title = "Update Measurement", + } = props; -export const UpdateModal = ({ - region_id, //Used to filter wells - open, - onClose, - measurement, - onUpdateMeasurement, - onSubmitUpdate, - onDeleteMeasurement, - title = "Update Measurement", -}: { - region_id: number; //Used to filter wells - open: boolean; - onClose: () => void; - measurement: PatchRegionMeasurement; - onUpdateMeasurement: (value: Partial) => void; - onSubmitUpdate: () => void; - onDeleteMeasurement: () => void; - title?: string; -}) => { const userList = useGetUserList(); const fetchWithAuth = useFetchWithAuth(); + const regionId = props.mode === "region" ? props.region_id : undefined; + + const measurement = props.measurement as any; // only for local reading convenience + const onUpdateMeasurement = props.onUpdateMeasurement as any; + const [notSampled, setNotSampled] = useState( measurement.value === undefined || measurement.value === null, ); @@ -65,7 +87,7 @@ export const UpdateModal = ({ Error, MonitoredWell[] >({ - queryKey: ["wells", "has_chloride_groups", region_id], + queryKey: ["wells", "has_chloride_groups", regionId], queryFn: () => fetchWithAuth({ method: "GET", @@ -74,15 +96,18 @@ export const UpdateModal = ({ sort_by: "ra_number", sort_direction: "asc", has_chloride_group: true, - chloride_group_id: region_id, + chloride_group_id: regionId, limit: 100, }, }), - enabled: open, + enabled: open && props.mode === "region" && !!regionId, select: (res) => res.items, }); const handleToggleNotSampled = (checked: boolean) => { + // only meaningful in region mode + if (props.mode !== "region") return; + setNotSampled(checked); if (checked) { @@ -98,8 +123,10 @@ export const UpdateModal = ({ }; useEffect(() => { - setNotSampled(measurement.value == null); - }, [measurement.value]); + if (props.mode === "region") { + setNotSampled(measurement.value == null); + } + }, [props.mode, measurement.value]); return ( {wells ?.filter( - (well: MonitoredWell) => well.chloride_group_id === region_id, + (well: MonitoredWell) => well.chloride_group_id === regionId, ) .map((well: MonitoredWell) => ( diff --git a/frontend/src/components/TabPanel.tsx b/frontend/src/components/TabPanel.tsx index 26cae3d7..b869d461 100644 --- a/frontend/src/components/TabPanel.tsx +++ b/frontend/src/components/TabPanel.tsx @@ -1,24 +1,24 @@ -import React from 'react' - -interface TabPanelProps { - children?: React.ReactNode - tabIndex: number - currentTabIndex: number -} - -export default function TabPanel({children, tabIndex, currentTabIndex}: TabPanelProps) { - return ( - - ) -} +import React from "react"; + +interface TabPanelProps { + children?: React.ReactNode; + tabIndex: number; + currentTabIndex: number; +} + +export const TabPanel = ({ + children, + tabIndex, + currentTabIndex, +}: TabPanelProps) => { + return ( + + ); +}; diff --git a/frontend/src/components/Topbar.tsx b/frontend/src/components/Topbar.tsx index 13ec25d5..4d19b01d 100644 --- a/frontend/src/components/Topbar.tsx +++ b/frontend/src/components/Topbar.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, MouseEvent } from "react"; import { AppBar, Toolbar, @@ -18,7 +18,15 @@ import { useAuthUser, useSignOut } from "react-auth-kit"; import { Login, Logout, Settings } from "@mui/icons-material"; import { RoleChip, TopbarUserButton } from "./index"; -export default function Topbar({ open, onMenuClick, sx }: { open: boolean, onMenuClick: () => void; sx?: any }) { +export const Topbar = ({ + open, + onMenuClick, + sx, +}: { + open: boolean; + onMenuClick: () => void; + sx?: any; +}) => { const navigate = useNavigate(); const signOut = useSignOut(); const authUser = useAuthUser(); @@ -28,7 +36,7 @@ export default function Topbar({ open, onMenuClick, sx }: { open: boolean, onMen const role: string = authUser()?.user_role?.name; const isLoggedIn = !!authUser(); - const handleMenuOpen = (event: React.MouseEvent) => { + const handleMenuOpen = (event: MouseEvent) => { setAnchorEl(event.currentTarget); }; @@ -60,7 +68,7 @@ export default function Topbar({ open, onMenuClick, sx }: { open: boolean, onMen > - {!open ? + {!open ? ( Meter Manager - : null} + ) : null} {isLoggedIn ? ( @@ -95,8 +103,8 @@ export default function Topbar({ open, onMenuClick, sx }: { open: boolean, onMen anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose} - transformOrigin={{ horizontal: 'right', vertical: 'top' }} - anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} + transformOrigin={{ horizontal: "right", vertical: "top" }} + anchorOrigin={{ horizontal: "right", vertical: "bottom" }} > - + Role: @@ -116,8 +128,8 @@ export default function Topbar({ open, onMenuClick, sx }: { open: boolean, onMen { - navigate("/settings") - handleMenuClose() + navigate("/settings"); + handleMenuClose(); }} > @@ -128,8 +140,8 @@ export default function Topbar({ open, onMenuClick, sx }: { open: boolean, onMen { - fullSignOut() - handleMenuClose() + fullSignOut(); + handleMenuClose(); }} > @@ -139,37 +151,35 @@ export default function Topbar({ open, onMenuClick, sx }: { open: boolean, onMen - ) - : ( - - )} + + + + )} ); -} - +}; diff --git a/frontend/src/hooks/useFetchST2.ts b/frontend/src/hooks/useFetchST2.ts index a3dd4bf7..e41450fe 100644 --- a/frontend/src/hooks/useFetchST2.ts +++ b/frontend/src/hooks/useFetchST2.ts @@ -1,5 +1,5 @@ -import { formatQueryParams } from "../utils/HttpUtils"; -import { ST2Measurement, ST2Response } from "../interfaces"; +import { formatQueryParams } from "@/utils"; +import { ST2Measurement, ST2Response } from "@/interfaces"; export const useFetchST2 = () => { const ST2_API_BASE_URL = diff --git a/frontend/src/hooks/useFetchWithAuth.ts b/frontend/src/hooks/useFetchWithAuth.ts index 6ac7fbf3..2d76d6e3 100644 --- a/frontend/src/hooks/useFetchWithAuth.ts +++ b/frontend/src/hooks/useFetchWithAuth.ts @@ -1,9 +1,9 @@ import { useAuthHeader, useSignOut } from "react-auth-kit"; import { useNavigate } from "react-router-dom"; -import { formatQueryParams } from "../utils/HttpUtils"; +import { formatQueryParams } from "@/utils"; import { enqueueSnackbar } from "notistack"; -import { HttpStatus } from "../enums"; -import { API_URL } from "../config"; +import { HttpStatus } from "@/enums"; +import { API_URL } from "@/config"; export const useFetchWithAuth = () => { const authHeader = useAuthHeader(); diff --git a/frontend/src/service/ApiServiceNew.ts b/frontend/src/service/ApiServiceNew.ts index c6abd7f6..de897111 100644 --- a/frontend/src/service/ApiServiceNew.ts +++ b/frontend/src/service/ApiServiceNew.ts @@ -1372,7 +1372,7 @@ export function useCreateWaterLevel() { const authHeader = useAuthHeader(); return useMutation({ - mutationFn: async (newWaterLevel: NewWellMeasurement) => { + mutationFn: async (newWaterLevel: Partial) => { const response = await POSTFetch(route, newWaterLevel, authHeader()); if (!response.ok) { @@ -1411,7 +1411,7 @@ export function useUpdateWaterLevel(onSuccess: Function) { const authHeader = useAuthHeader(); return useMutation({ - mutationFn: async (updatedWaterLevel: PatchWellMeasurement) => { + mutationFn: async (updatedWaterLevel: Partial) => { const response = await PATCHFetch(route, updatedWaterLevel, authHeader()); if (!response.ok) { diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index 23db1355..61944980 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -1,4 +1,5 @@ export * from "./DateUtils"; +export * from "./DataStreamUtils"; export * from "./EmptyToNull"; export * from "./HttpUtils"; export * from "./GetMeterMarkerColor"; diff --git a/frontend/src/views/Activities/ActivitiesView.tsx b/frontend/src/views/Activities/ActivitiesView.tsx index e7b41bce..2e6caf27 100644 --- a/frontend/src/views/Activities/ActivitiesView.tsx +++ b/frontend/src/views/Activities/ActivitiesView.tsx @@ -1,8 +1,7 @@ import { CardContent, Card } from "@mui/material"; import MeterActivityEntry from "./MeterActivityEntry/MeterActivityEntry"; import { Construction } from "@mui/icons-material"; -import { BackgroundBox } from "../../components/BackgroundBox"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; +import { BackgroundBox, CustomCardHeader } from "@/components"; export const ActivitiesView = () => { return ( diff --git a/frontend/src/views/Activities/ActivityPhotoView.tsx b/frontend/src/views/Activities/ActivityPhotoView.tsx index deba9508..2935271c 100644 --- a/frontend/src/views/Activities/ActivityPhotoView.tsx +++ b/frontend/src/views/Activities/ActivityPhotoView.tsx @@ -1,9 +1,9 @@ import { useMemo, useState } from "react"; import { useParams } from "react-router-dom"; -import { API_URL } from "../../config"; import { Card, CardContent, Skeleton, Box, Alert } from "@mui/material"; import { Image } from "@mui/icons-material"; -import { BackgroundBox, CustomCardHeader } from "../../components"; +import { API_URL } from "@/config"; +import { BackgroundBox, CustomCardHeader } from "@/components"; export const ActivityPhotoView = () => { const { activity_id, photo_file_name } = useParams(); diff --git a/frontend/src/views/Activities/MeterActivityEntry/ActivityFormConfig.ts b/frontend/src/views/Activities/MeterActivityEntry/ActivityFormConfig.ts index 72a6d894..39dd1898 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/ActivityFormConfig.ts +++ b/frontend/src/views/Activities/MeterActivityEntry/ActivityFormConfig.ts @@ -1,12 +1,12 @@ import * as Yup from "yup"; +import Dayjs from "dayjs"; +import dayjs from "dayjs"; import { ActivityForm, ActivityFormControl, MeterListDTO, ObservationForm, -} from "../../../interfaces.d"; -import Dayjs from "dayjs"; -import dayjs from "dayjs"; +} from "@/interfaces"; // Form validation, these are applied to the current form when submitting export const ActivityResolverSchema: Yup.ObjectSchema = Yup.object() diff --git a/frontend/src/views/Activities/MeterActivityEntry/MaintenanceRepairSelection.tsx b/frontend/src/views/Activities/MeterActivityEntry/MaintenanceRepairSelection.tsx index 4457a3ca..42778c5f 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/MaintenanceRepairSelection.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/MaintenanceRepairSelection.tsx @@ -1,7 +1,7 @@ import { Box, Grid, Typography } from "@mui/material"; import { useFieldArray } from "react-hook-form"; import { ControlledTextbox, StyledToggleButton } from "@/components"; -import { useGetServiceTypes } from "@/service/ApiServiceNew"; +import { useGetServiceTypes } from "@/service"; export default function MaintenanceRepairSelection({ control, diff --git a/frontend/src/views/Activities/MeterActivityEntry/MeterActivityEntry.tsx b/frontend/src/views/Activities/MeterActivityEntry/MeterActivityEntry.tsx index dc4abc25..fdef0f41 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/MeterActivityEntry.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/MeterActivityEntry.tsx @@ -8,7 +8,7 @@ import { useAuthHeader } from "react-auth-kit"; import { yupResolver } from "@hookform/resolvers/yup"; import { ActivityFormControl, MeterListDTO } from "@/interfaces"; import { ActivityType } from "@/enums"; -import { useGetMeter, useGetWell } from "@/service/ApiServiceNew"; +import { useGetMeter, useGetWell } from "@/service"; import { API_URL } from "@/config"; import { MeterActivitySelection } from "./MeterActivitySelection"; import ObservationSelection from "./ObservationsSelection"; diff --git a/frontend/src/views/Activities/MeterActivityEntry/NotesSelection.tsx b/frontend/src/views/Activities/MeterActivityEntry/NotesSelection.tsx index 5a66924b..02a95727 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/NotesSelection.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/NotesSelection.tsx @@ -11,10 +11,10 @@ import { } from "@mui/material"; import ToggleButtonGroup from "@mui/material/ToggleButtonGroup"; import { Controller, useFieldArray } from "react-hook-form"; -import { ImageUploadWithPreview, StyledToggleButton } from "../../../components"; -import { NoteTypeLU } from "../../../interfaces"; -import { WorkingOnArrivalValue } from "../../../enums"; -import { useGetNoteTypes } from "../../../service/ApiServiceNew"; +import { ImageUploadWithPreview, StyledToggleButton } from "@/components"; +import { NoteTypeLU } from "@/interfaces"; +import { WorkingOnArrivalValue } from "@/enums"; +import { useGetNoteTypes } from "@/service"; export default function NotesSelection({ control, watch }: any) { const notesList = useGetNoteTypes(); @@ -134,7 +134,10 @@ export default function NotesSelection({ control, watch }: any) { name="photos" control={control} render={({ field }) => ( - field.onChange(files)} /> + field.onChange(files)} + /> )} /> diff --git a/frontend/src/views/Activities/MeterActivityEntry/ObservationsSelection.tsx b/frontend/src/views/Activities/MeterActivityEntry/ObservationsSelection.tsx index 6aa16506..02ddc0da 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/ObservationsSelection.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/ObservationsSelection.tsx @@ -4,7 +4,7 @@ import { UseQueryResult } from "react-query"; import { Delete } from "@mui/icons-material"; import { useFieldArray, useWatch } from "react-hook-form"; import { ObservedPropertyTypeLU } from "@/interfaces"; -import { useGetPropertyTypes } from "@/service/ApiServiceNew"; +import { useGetPropertyTypes } from "@/service"; import { ControlledSelectNonObject, ControlledTimepicker, diff --git a/frontend/src/views/Activities/MeterActivityEntry/PartsSelection.tsx b/frontend/src/views/Activities/MeterActivityEntry/PartsSelection.tsx index b0d65bcd..8c0820d2 100644 --- a/frontend/src/views/Activities/MeterActivityEntry/PartsSelection.tsx +++ b/frontend/src/views/Activities/MeterActivityEntry/PartsSelection.tsx @@ -10,9 +10,9 @@ import { Typography, } from "@mui/material"; import { useFieldArray } from "react-hook-form"; -import { Part } from "../../../interfaces"; -import { StyledToggleButton } from "../../../components"; -import { useGetMeterPartsList } from "../../../service/ApiServiceNew"; +import { Part } from "@/interfaces"; +import { StyledToggleButton } from "@/components"; +import { useGetMeterPartsList } from "@/service"; export default function PartsSelection({ control, watch, setValue }: any) { const partsList = useGetMeterPartsList({ diff --git a/frontend/src/views/Chlorides/ChloridesView.tsx b/frontend/src/views/Chlorides/ChloridesView.tsx index 1a8d0e78..c8ee5fa6 100644 --- a/frontend/src/views/Chlorides/ChloridesView.tsx +++ b/frontend/src/views/Chlorides/ChloridesView.tsx @@ -25,10 +25,7 @@ import { RegionMeasurementDTO, } from "@/interfaces"; import { useFetchWithAuth } from "@/hooks"; -import { - BackgroundBox, - CustomCardHeader -} from "@/components"; +import { BackgroundBox, CustomCardHeader } from "@/components"; import { emptyToNull } from "@/utils"; import { ChloridesTable } from "./ChloridesTable"; import { ChloridesPlot } from "./ChloridesPlot"; @@ -91,7 +88,7 @@ export const ChloridesView = () => { const milligramPerLiterUnitId = 14; const { mutateAsync: createChlorideLevel } = useMutation({ mutationKey: ["regions", "creation"], - mutationFn: (body: NewRegionMeasurement) => + mutationFn: (body: Partial) => fetchWithAuth({ method: "POST", route: "/chlorides", @@ -175,7 +172,7 @@ export const ChloridesView = () => { const error = errorRegions || errorManual; - const handleSubmitNewMeasurement = (data: NewRegionMeasurement) => { + const handleSubmitNewMeasurement = (data: Partial) => { if (regionId) { data.region_id = regionId; createChlorideLevel(data, { onSuccess: () => refetchManual() }); @@ -299,12 +296,14 @@ export const ChloridesView = () => { {authUser() && ( <> setIsNewModalOpen(false)} handleSubmitNewMeasurement={handleSubmitNewMeasurement} /> setIsUpdateModalOpen(false)} diff --git a/frontend/src/views/Home.tsx b/frontend/src/views/Home.tsx index 29016cc7..a1e28c33 100644 --- a/frontend/src/views/Home.tsx +++ b/frontend/src/views/Home.tsx @@ -1,10 +1,19 @@ -import { Grid, Card, CardContent, CardMedia, List, ListItem, ListItemText, Stack, Typography } from "@mui/material"; -import pvacd_logo from "../img/pvacd_logo.png"; -import meter_field from "../img/meter_field.jpg"; -import meter_storage from "../img/meter_storage.jpg"; +import { + Grid, + Card, + CardContent, + CardMedia, + List, + ListItem, + ListItemText, + Stack, + Typography, +} from "@mui/material"; +import pvacd_logo from "@/img/pvacd_logo.png"; +import meter_field from "@/img/meter_field.jpg"; +import meter_storage from "@/img/meter_storage.jpg"; import HomeIcon from "@mui/icons-material/Home"; -import { BackgroundBox } from "../components/BackgroundBox"; -import { CustomCardHeader } from "../components/CustomCardHeader"; +import { CustomCardHeader, BackgroundBox } from "@/components"; export const Home = () => { const versionHistory = [ @@ -28,7 +37,15 @@ export const Home = () => { - + { }} /> - PVACD Meter Manager Info + + PVACD Meter Manager Info + Version History {versionHistory.map((version) => ( diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx index 6b0a3c7e..5f07dca0 100644 --- a/frontend/src/views/Login.tsx +++ b/frontend/src/views/Login.tsx @@ -11,11 +11,11 @@ import { Stack, Grid, } from "@mui/material"; -import LoginIcon from '@mui/icons-material/Login'; +import { Login as LoginIcon } from "@mui/icons-material"; import { enqueueSnackbar } from "notistack"; -import { SecurityScope } from "../interfaces"; -import { API_URL } from "../config"; -import { CustomCardHeader } from "../components"; +import { SecurityScope } from "@/interfaces"; +import { API_URL } from "@/config"; +import { CustomCardHeader } from "@/components"; export const Login = () => { const [username, setUsername] = useState(""); @@ -38,7 +38,7 @@ export const Login = () => { .then(handleLogin) .catch((_) => { setError( - "Unable to connect to the server. Please check your internet connection and try again. If the issue persists, contact support." + "Unable to connect to the server. Please check your internet connection and try again. If the issue persists, contact support.", ); }); }; @@ -59,7 +59,7 @@ export const Login = () => { ) { enqueueSnackbar( "Your role does not have access to the site UI. Please try accessing data via our API.", - { variant: "error" } + { variant: "error" }, ); return; } @@ -95,10 +95,7 @@ export const Login = () => { }} > - + { }; export default Login; - diff --git a/frontend/src/views/Meters/MeterHistory/MeterHistory.tsx b/frontend/src/views/Meters/MeterHistory/MeterHistory.tsx index 70a108d3..245842e5 100644 --- a/frontend/src/views/Meters/MeterHistory/MeterHistory.tsx +++ b/frontend/src/views/Meters/MeterHistory/MeterHistory.tsx @@ -5,17 +5,17 @@ import { SelectedActivityDetails } from "./SelectedActivityDetails"; import { SelectedObservationDetails } from "./SelectedObservationDetails"; import { SelectedBlankCard } from "./SelectedBlankCard"; import { useLocation, useSearchParams } from "react-router-dom"; -import { useGetMeterHistory } from "../../../service/ApiServiceNew"; +import { useGetMeterHistory } from "@/service"; import { MeterHistoryDTO, PatchActivityForm, PatchObservationForm, -} from "../../../interfaces"; -import { MeterHistoryType } from "../../../enums"; +} from "@/interfaces"; +import { MeterHistoryType } from "@/enums"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; -import { CustomCardHeader, ImageDialog, ImagePreviewGrid } from "../../../components"; +import { CustomCardHeader, ImageDialog, ImagePreviewGrid } from "@/components"; import { ImageOutlined } from "@mui/icons-material"; dayjs.extend(utc); dayjs.extend(timezone); @@ -34,9 +34,7 @@ export const MeterHistory = ({ const photos = useMemo(() => { if (selectedHistoryItem?.history_type === MeterHistoryType.Activity) { - return ( - selectedHistoryItem.photos?.map((p: any) => p.url) ?? [] - ); + return selectedHistoryItem.photos?.map((p: any) => p.url) ?? []; } return []; }, [selectedHistoryItem]); diff --git a/frontend/src/views/Meters/MeterHistory/MeterHistoryTable.tsx b/frontend/src/views/Meters/MeterHistory/MeterHistoryTable.tsx index cc61ee32..8d222c32 100644 --- a/frontend/src/views/Meters/MeterHistory/MeterHistoryTable.tsx +++ b/frontend/src/views/Meters/MeterHistory/MeterHistoryTable.tsx @@ -7,9 +7,9 @@ import timezone from "dayjs/plugin/timezone"; dayjs.extend(utc); dayjs.extend(timezone); -import { MeterHistoryType } from "../../../enums"; -import { MeterHistoryDTO } from "../../../interfaces"; -import { CustomCardHeader } from "../../../components/CustomCardHeader"; +import { MeterHistoryType } from "@/enums"; +import { MeterHistoryDTO } from "@/interfaces"; +import { CustomCardHeader } from "@/components"; export const MeterHistoryTable = ({ onHistoryItemSelection, diff --git a/frontend/src/views/Meters/MeterHistory/SelectedBlankCard.tsx b/frontend/src/views/Meters/MeterHistory/SelectedBlankCard.tsx index c7b3f867..8507c5da 100644 --- a/frontend/src/views/Meters/MeterHistory/SelectedBlankCard.tsx +++ b/frontend/src/views/Meters/MeterHistory/SelectedBlankCard.tsx @@ -1,6 +1,6 @@ import { Grid, Card, CardContent } from "@mui/material"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { CustomCardHeader } from "../../../components/CustomCardHeader"; +import { CustomCardHeader } from "@/components"; // A blank card to display when no history item is selected export const SelectedBlankCard = () => { diff --git a/frontend/src/views/Meters/MeterHistory/SelectedHistoryDetails.tsx b/frontend/src/views/Meters/MeterHistory/SelectedHistoryDetails.tsx index 6a35a349..4d153877 100644 --- a/frontend/src/views/Meters/MeterHistory/SelectedHistoryDetails.tsx +++ b/frontend/src/views/Meters/MeterHistory/SelectedHistoryDetails.tsx @@ -1,7 +1,7 @@ import { TextField, Grid, Card, CardContent, CardHeader } from "@mui/material"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { MeterHistoryType } from "../../../enums"; -import { NoteTypeLU } from "../../../interfaces"; +import { InfoOutlined } from "@mui/icons-material"; +import { MeterHistoryType } from "@/enums"; +import { NoteTypeLU } from "@/interfaces"; import dayjs from "dayjs"; const disabledInputStyle = { @@ -44,7 +44,7 @@ export default function SelectedHistoryDetails({ title={
Selected History Details - +
} sx={{ mb: 0, pb: 0 }} diff --git a/frontend/src/views/Meters/MeterHistory/SelectedObservationDetails.tsx b/frontend/src/views/Meters/MeterHistory/SelectedObservationDetails.tsx index 5688a594..6491e47b 100644 --- a/frontend/src/views/Meters/MeterHistory/SelectedObservationDetails.tsx +++ b/frontend/src/views/Meters/MeterHistory/SelectedObservationDetails.tsx @@ -31,7 +31,7 @@ import { useGetPropertyTypes, useUpdateObservation, useDeleteObservation, -} from "@/service/ApiServiceNew"; +} from "@/service"; export const SelectedObservationDetails = ({ selectedObservation, diff --git a/frontend/src/views/Meters/MeterSelection/MeterSelection.tsx b/frontend/src/views/Meters/MeterSelection/MeterSelection.tsx index a2ae14dc..914da2d8 100644 --- a/frontend/src/views/Meters/MeterSelection/MeterSelection.tsx +++ b/frontend/src/views/Meters/MeterSelection/MeterSelection.tsx @@ -1,7 +1,6 @@ import { useState } from "react"; import { MeterSelectionTable } from "./MeterSelectionTable"; import MeterSelectionMap from "./MeterSelectionMap"; -import TabPanel from "../../../components/TabPanel"; import { Tabs, Tab, @@ -13,10 +12,9 @@ import { ToggleButton, InputAdornment, } from "@mui/material"; -import FormatListBulletedOutlinedIcon from "@mui/icons-material/FormatListBulletedOutlined"; -import { MeterStatusNames } from "../../../enums"; -import { CustomCardHeader } from "../../../components/CustomCardHeader"; -import { Search } from "@mui/icons-material"; +import { FormatListBulletedOutlined, Search } from "@mui/icons-material"; +import { MeterStatusNames } from "@/enums"; +import { CustomCardHeader, TabPanel } from "@/components"; export const MeterSelection = ({ onMeterSelection, @@ -70,13 +68,10 @@ export const MeterSelection = ({ return ( - + - + - + - + @@ -97,12 +100,17 @@ export default function MeterSelectionMap({ > {meterMarkers.isSuccess && meterMarkers.data.map((meter: MeterMapDTO) => { - const color = meter.last_pm ? getMeterMarkerColor(meter.last_pm) : "black"; + const color = meter.last_pm + ? getMeterMarkerColor(meter.last_pm) + : "black"; return ( onMeterSelection(meter.id), }} @@ -162,18 +170,27 @@ export default function MeterSelectionMap({ {/* Loading and empty states */} {meterMarkers.isLoading && ( - Loading meter markers... + + Loading meter markers... + )} {meterMarkers.isSuccess && meterMarkers?.data.length === 0 && ( - + No meters found for that search. @@ -181,10 +198,14 @@ export default function MeterSelectionMap({ {/* Error */} {meterMarkers.isError && ( - + Failed to load meters: {meterMarkers.error.message} diff --git a/frontend/src/views/MonitoringWells/MonitoringWellsView.tsx b/frontend/src/views/MonitoringWells/MonitoringWellsView.tsx index 80aa9f15..94c050a4 100644 --- a/frontend/src/views/MonitoringWells/MonitoringWellsView.tsx +++ b/frontend/src/views/MonitoringWells/MonitoringWellsView.tsx @@ -15,12 +15,6 @@ import { } from "@mui/material"; import { useQuery, useQueryClient } from "react-query"; import { useAuthUser } from "react-auth-kit"; -import { MonitoringWellsTable } from "./MonitoringWellsTable"; -import { MonitoringWellsPlot } from "./MonitoringWellsPlot"; -import { - CreateModal, - UpdateModal, -} from "../../components/Modals/MonitoredWell"; import { NewWellMeasurement, PatchWellMeasurement, @@ -28,19 +22,26 @@ import { SecurityScope, WellMeasurementDTO, MonitoredWell, -} from "../../interfaces"; +} from "@/interfaces"; import { useCreateWaterLevel, useUpdateWaterLevel, useDeleteWaterLevel, -} from "../../service/ApiServiceNew"; +} from "@/service"; import dayjs, { Dayjs } from "dayjs"; -import { useFetchWithAuth, useFetchST2 } from "../../hooks"; -import { getDataStreamId } from "../../utils/DataStreamUtils"; +import { enqueueSnackbar } from "notistack"; +import { useFetchWithAuth, useFetchST2 } from "@/hooks"; +import { getDataStreamId, separateAndSortMonitoredWells } from "@/utils"; import { MonitorHeart } from "@mui/icons-material"; -import { BackgroundBox } from "../../components/BackgroundBox"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; -import { separateAndSortMonitoredWells } from "../../utils"; +import { + CreateModal, + UpdateModal, + CustomCardHeader, + BackgroundBox, +} from "@/components"; + +import { MonitoringWellsTable } from "./MonitoringWellsTable"; +import { MonitoringWellsPlot } from "./MonitoringWellsPlot"; export const MonitoringWellsView = () => { const theme = useTheme(); @@ -50,13 +51,14 @@ export const MonitoringWellsView = () => { const fetchSt2 = useFetchST2(); const selectWellId = useId(); const [wellId, setWellId] = useState(); - const [selectedMeasurement, setSelectedMeasurement] = - useState({ - levelmeasurement_id: 0, - timestamp: dayjs(), - value: 0, - submitting_user_id: 0, - }); + const [selectedMeasurement, setSelectedMeasurement] = useState< + Partial + >({ + levelmeasurement_id: 0, + timestamp: dayjs(), + value: 0, + submitting_user_id: 0, + }); const [isNewModalOpen, setIsNewModalOpen] = useState(false); const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false); @@ -142,7 +144,7 @@ export const MonitoringWellsView = () => { errorSt2 || errorJohnsonSensorData; - const handleSubmitNewMeasurement = (data: NewWellMeasurement) => { + const handleSubmitNewMeasurement = (data: Partial) => { if (wellId) { data.well_id = wellId; createMeasurement.mutate(data, { @@ -170,12 +172,27 @@ export const MonitoringWellsView = () => { const handleDeleteMeasurement = () => { setIsUpdateModalOpen(false); + + const id = selectedMeasurement.levelmeasurement_id; + if (!id) { + enqueueSnackbar("No measurement selected to delete.", { + variant: "warning", + }); + return; + } + if (window.confirm("Are you sure you want to delete this measurement?")) { - deleteMeasurement.mutate(selectedMeasurement.levelmeasurement_id, { + deleteMeasurement.mutate(id, { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["manualMeasurements", wellId], }); + enqueueSnackbar("Measurement deleted.", { variant: "success" }); + }, + onError: (e: any) => { + enqueueSnackbar(e?.message ?? "Failed to delete measurement.", { + variant: "error", + }); }, }); } @@ -365,16 +382,18 @@ export const MonitoringWellsView = () => { {authUser() && ( <> setIsNewModalOpen(false)} handleSubmitNewMeasurement={handleSubmitNewMeasurement} /> setIsUpdateModalOpen(false)} measurement={selectedMeasurement} onUpdateMeasurement={(update) => - setSelectedMeasurement({ ...selectedMeasurement, ...update }) + setSelectedMeasurement((prev) => ({ ...prev, ...update })) } onSubmitUpdate={handleSubmitMeasurementUpdate} onDeleteMeasurement={handleDeleteMeasurement} diff --git a/frontend/src/views/NotFound.tsx b/frontend/src/views/NotFound.tsx index 3d176f94..6f5d901c 100644 --- a/frontend/src/views/NotFound.tsx +++ b/frontend/src/views/NotFound.tsx @@ -1,17 +1,17 @@ import { Box, Button, Card, CardContent, Typography } from "@mui/material"; -import DoNotTouchIcon from '@mui/icons-material/DoNotTouch'; -import { BackgroundBox, CustomCardHeader } from "../components"; +import { Home, DoNotTouch } from "@mui/icons-material"; import { Link } from "react-router-dom"; -import { Home } from "@mui/icons-material"; +import { BackgroundBox, CustomCardHeader } from "@/components"; export const NotFound = () => { return ( - + - Sorry, the page you are looking for does not exist or may have been moved. + Sorry, the page you are looking for does not exist or may have been + moved. @@ -28,4 +28,4 @@ export const NotFound = () => { ); -} +}; diff --git a/frontend/src/views/Parts/MeterTypeDetailsCard.tsx b/frontend/src/views/Parts/MeterTypeDetailsCard.tsx index 915744e5..c0a7df86 100644 --- a/frontend/src/views/Parts/MeterTypeDetailsCard.tsx +++ b/frontend/src/views/Parts/MeterTypeDetailsCard.tsx @@ -6,10 +6,7 @@ import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { enqueueSnackbar } from "notistack"; -import { - useCreateMeterType, - useUpdateMeterType, -} from "@/service/ApiServiceNew"; +import { useCreateMeterType, useUpdateMeterType } from "@/service"; import { ControlledTextbox, ControlledSelectNonObject, diff --git a/frontend/src/views/Parts/MeterTypesTable.tsx b/frontend/src/views/Parts/MeterTypesTable.tsx index 13c01d6c..ee2399ca 100644 --- a/frontend/src/views/Parts/MeterTypesTable.tsx +++ b/frontend/src/views/Parts/MeterTypesTable.tsx @@ -11,7 +11,7 @@ import { Typography, } from "@mui/material"; import { Search, Add, FormatListBulletedOutlined } from "@mui/icons-material"; -import { useGetMeterTypeList } from "@/service/ApiServiceNew"; +import { useGetMeterTypeList } from "@/service"; import { MeterTypeLU } from "@/interfaces"; import { CustomCardHeader, diff --git a/frontend/src/views/Parts/PartDetailsCard.tsx b/frontend/src/views/Parts/PartDetailsCard.tsx index f29e04a4..7a4e80c7 100644 --- a/frontend/src/views/Parts/PartDetailsCard.tsx +++ b/frontend/src/views/Parts/PartDetailsCard.tsx @@ -20,13 +20,12 @@ import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { enqueueSnackbar } from "notistack"; import { useFieldArray } from "react-hook-form"; - import { useCreatePart, useGetMeterTypeList, useGetPart, useUpdatePart, -} from "@/service/ApiServiceNew"; +} from "@/service"; import { ControlledTextbox, ControlledPartTypeSelect, diff --git a/frontend/src/views/Parts/PartsTable.tsx b/frontend/src/views/Parts/PartsTable.tsx index 1242f578..f9f6f433 100644 --- a/frontend/src/views/Parts/PartsTable.tsx +++ b/frontend/src/views/Parts/PartsTable.tsx @@ -17,7 +17,7 @@ import { FormatListBulletedOutlined, } from "@mui/icons-material"; import { useSnackbar } from "notistack"; -import { useGetParts, useAddParts } from "@/service/ApiServiceNew"; +import { useGetParts, useAddParts } from "@/service"; import { Part } from "@/interfaces"; import { CustomCardHeader, diff --git a/frontend/src/views/Parts/PartsView.tsx b/frontend/src/views/Parts/PartsView.tsx index 18b4d7ab..77e51379 100644 --- a/frontend/src/views/Parts/PartsView.tsx +++ b/frontend/src/views/Parts/PartsView.tsx @@ -1,11 +1,12 @@ import { useEffect, useState } from "react"; -import { PartsTable } from "./PartsTable"; import { Grid } from "@mui/material"; +import { MeterTypeLU } from "@/interfaces"; +import { BackgroundBox } from "@/components"; + +import { PartsTable } from "./PartsTable"; +import { MeterTypeDetailsCard } from "./MeterTypeDetailsCard"; import { PartDetailsCard } from "./PartDetailsCard"; import { MeterTypesTable } from "./MeterTypesTable"; -import { MeterTypeDetailsCard } from "./MeterTypeDetailsCard"; -import { MeterTypeLU } from "../../interfaces"; -import { BackgroundBox } from "../../components/BackgroundBox"; export const PartsView = () => { const [selectedPartID, setSelectedPartID] = useState(); @@ -50,6 +51,6 @@ export const PartsView = () => { /> - + ); }; diff --git a/frontend/src/views/Settings.tsx b/frontend/src/views/Settings.tsx index e56aeaf0..6a4ba6d0 100644 --- a/frontend/src/views/Settings.tsx +++ b/frontend/src/views/Settings.tsx @@ -1,4 +1,5 @@ -import * as yup from 'yup'; +import { useEffect, useState } from "react"; +import * as yup from "yup"; import { enqueueSnackbar } from "notistack"; import { yupResolver } from "@hookform/resolvers/yup"; import { useForm, Controller } from "react-hook-form"; @@ -24,23 +25,23 @@ import { } from "@mui/material"; import SettingsIcon from "@mui/icons-material/Settings"; import { useAuthUser, useSignIn } from "react-auth-kit"; +import { Check, Close, Edit, ExpandMore } from "@mui/icons-material"; +import { useMutation, useQuery, useQueryClient } from "react-query"; import { - Check, - Close, - Edit, - ExpandMore -} from '@mui/icons-material'; -import { BackgroundBox, CustomCardHeader, ImageUploadWithPreview, IsTrueChip, RoleChip } from "../components"; -import { navConfig } from '../constants'; -import { useFetchWithAuth } from '../hooks'; -import { useMutation, useQuery, useQueryClient } from 'react-query'; -import { SecurityScope } from '../interfaces'; -import { useEffect, useState } from 'react'; + BackgroundBox, + CustomCardHeader, + ImageUploadWithPreview, + IsTrueChip, + RoleChip, +} from "@/components"; +import { navConfig } from "@/constants"; +import { useFetchWithAuth } from "@/hooks"; +import { SecurityScope } from "@/interfaces"; const redirectOptions = { - public: navConfig.filter(item => !item.role), - technician: navConfig.filter(item => item.role === "Technician"), - admin: navConfig.filter(item => item.role === "Admin"), + public: navConfig.filter((item) => !item.role), + technician: navConfig.filter((item) => item.role === "Technician"), + admin: navConfig.filter((item) => item.role === "Admin"), }; const redirectSchema = yup.object().shape({ @@ -63,8 +64,8 @@ export const Settings = () => { const fetchWithAuth = useFetchWithAuth(); const scopes: Set = new Set( authUser()?.user_role?.security_scopes?.map( - (scope: SecurityScope) => scope.scope_string - ) ?? [] + (scope: SecurityScope) => scope.scope_string, + ) ?? [], ); const hasReadScope = scopes.has("read"); @@ -89,17 +90,19 @@ export const Settings = () => { }); }, onSuccess: (responseJson: any) => { - enqueueSnackbar("Display name updated successfully.", { variant: "success" }); + enqueueSnackbar("Display name updated successfully.", { + variant: "success", + }); // Grab the current auth state & update it if (user) { signIn({ token: localStorage.getItem("_auth")!, // reuse current token - expiresIn: 300, // reuse the expiry window you want + expiresIn: 300, // reuse the expiry window you want tokenType: "bearer", authState: { ...user, - display_name: responseJson.display_name, // overwrite just this field + display_name: responseJson.display_name, // overwrite just this field }, }); } @@ -110,16 +113,17 @@ export const Settings = () => { }); const onDisplayNameSubmit = ({ display_name }: { display_name: string }) => { - displayNameMutation.mutate({ display_name }) - } + displayNameMutation.mutate({ display_name }); + }; const queryClient = useQueryClient(); const getRedirectPageQuery = useQuery({ queryKey: ["redirectPage"], - queryFn: async () => fetchWithAuth({ - method: "GET", - route: "/settings/redirect_page", - }), + queryFn: async () => + fetchWithAuth({ + method: "GET", + route: "/settings/redirect_page", + }), }); const redirectMutation = useMutation({ @@ -130,19 +134,21 @@ export const Settings = () => { body: data, }); }, - onSuccess: (responseJson: { message: string, redirect_page: string }) => { - enqueueSnackbar("Redirect page updated successfully.", { variant: "success" }); + onSuccess: (responseJson: { message: string; redirect_page: string }) => { + enqueueSnackbar("Redirect page updated successfully.", { + variant: "success", + }); queryClient.invalidateQueries(["redirectPage"]); // Grab the current auth state & update it if (user) { signIn({ token: localStorage.getItem("_auth")!, // reuse current token - expiresIn: 300, // reuse the expiry window you want + expiresIn: 300, // reuse the expiry window you want tokenType: "bearer", authState: { ...user, - redirect_page: responseJson.redirect_page, // overwrite just this field + redirect_page: responseJson.redirect_page, // overwrite just this field }, }); } @@ -155,10 +161,12 @@ export const Settings = () => { const { control: redirectControl, handleSubmit: handleRedirectSubmit, - reset: redirectReset + reset: redirectReset, } = useForm({ resolver: yupResolver(redirectSchema), - defaultValues: { redirect_page: getRedirectPageQuery?.data?.redirect_page ?? "/" }, + defaultValues: { + redirect_page: getRedirectPageQuery?.data?.redirect_page ?? "/", + }, values: { redirect_page: getRedirectPageQuery?.data?.redirect_page ?? "/" }, // react-hook-form v7 pattern for sync }); @@ -189,7 +197,9 @@ export const Settings = () => { return await res.json(); }, onSuccess: () => { - enqueueSnackbar("Password reset request submitted.", { variant: "success" }); + enqueueSnackbar("Password reset request submitted.", { + variant: "success", + }); }, }); @@ -225,19 +235,55 @@ export const Settings = () => { - + Full Name: - + - + Email: - + - + Username: - + - + {!isEditing ? ( <> Display Name: @@ -246,7 +292,10 @@ export const Settings = () => { label={user?.display_name ?? "N/A"} variant="outlined" /> - setIsEditing(true)}> + setIsEditing(true)} + > @@ -269,24 +318,41 @@ export const Settings = () => { { - displayNameReset({ display_name: user?.display_name ?? "" }); + displayNameReset({ + display_name: user?.display_name ?? "", + }); setIsEditing(false); }} > - +
)}
- + Role: - + Active: @@ -312,7 +378,9 @@ export const Settings = () => { }> - Redirect Page After Login + + Redirect Page After Login + @@ -326,13 +394,22 @@ export const Settings = () => { render={({ field }) => { // flatten all available paths const availablePaths = [ - ...redirectOptions.public.map(o => o.path), - ...(hasReadScope ? redirectOptions.technician.map(o => o.path) : []), - ...(hasAdminScope ? redirectOptions.admin.map(o => o.path) : []), + ...redirectOptions.public.map((o) => o.path), + ...(hasReadScope + ? redirectOptions.technician.map( + (o) => o.path, + ) + : []), + ...(hasAdminScope + ? redirectOptions.admin.map((o) => o.path) + : []), ]; // guard: if no options available yet, render empty select - if (getRedirectPageQuery.isFetching && availablePaths.length === 0) { + if ( + getRedirectPageQuery.isFetching && + availablePaths.length === 0 + ) { return ( { ); } - const safeValue = availablePaths.includes(field.value) + const safeValue = availablePaths.includes( + field.value, + ) ? field.value : "/"; @@ -352,66 +431,122 @@ export const Settings = () => { {...field} select fullWidth - size='small' + size="small" label="Page to redirect after login" - disabled={getRedirectPageQuery?.isFetching || redirectMutation.isLoading} + disabled={ + getRedirectPageQuery?.isFetching || + redirectMutation.isLoading + } value={safeValue} onChange={(e) => field.onChange(e)} > {redirectOptions.public.length > 0 && [ - + Pages , - ...redirectOptions.public.map((option) => { - const Icon = option.icon; - return ( - - - - - - {option.label} - - - ); - }), - ]} - {hasReadScope && redirectOptions.technician.length > 0 && [ - - Pages - , - ...redirectOptions.technician.map((option) => { - const Icon = option.icon; - return ( - - - - - - {option.label}{option.parent === "reports" ? " Report" : null} - - - ); - }), - ]} - {hasAdminScope && redirectOptions.admin.length > 0 && [ - - Pages - , - ...redirectOptions.admin.map((option) => { - const Icon = option.icon; - return ( - - - - - - {option.label} - - - ); - }), + ...redirectOptions.public.map( + (option) => { + const Icon = option.icon; + return ( + + + + + + {option.label} + + + ); + }, + ), ]} + {hasReadScope && + redirectOptions.technician.length > 0 && [ + + Pages + , + ...redirectOptions.technician.map( + (option) => { + const Icon = option.icon; + return ( + + + + + + {option.label} + {option.parent === "reports" + ? " Report" + : null} + + + ); + }, + ), + ]} + {hasAdminScope && + redirectOptions.admin.length > 0 && [ + + Pages + , + ...redirectOptions.admin.map( + (option) => { + const Icon = option.icon; + return ( + + + + + + {option.label} + + + ); + }, + ), + ]}
); }} @@ -446,10 +581,12 @@ export const Settings = () => { {...field} type="password" fullWidth - size='small' + size="small" label="Current Password" error={!!passwordErrors.currentPassword} - helperText={passwordErrors.currentPassword?.message} + helperText={ + passwordErrors.currentPassword?.message + } /> )} /> @@ -463,10 +600,12 @@ export const Settings = () => { {...field} type="password" fullWidth - size='small' + size="small" label="New Password" error={!!passwordErrors.newPassword} - helperText={passwordErrors.newPassword?.message} + helperText={ + passwordErrors.newPassword?.message + } /> )} /> @@ -480,10 +619,12 @@ export const Settings = () => { {...field} type="password" fullWidth - size='small' + size="small" label="Confirm Password" error={!!passwordErrors.confirmPassword} - helperText={passwordErrors.confirmPassword?.message} + helperText={ + passwordErrors.confirmPassword?.message + } /> )} /> @@ -507,7 +648,6 @@ export const Settings = () => { - + ); }; - diff --git a/frontend/src/views/UserManagement/PermissionsTable.tsx b/frontend/src/views/UserManagement/PermissionsTable.tsx index 03c25c36..519b4b0c 100644 --- a/frontend/src/views/UserManagement/PermissionsTable.tsx +++ b/frontend/src/views/UserManagement/PermissionsTable.tsx @@ -10,7 +10,7 @@ import { Tooltip, } from "@mui/material"; import { Search, Add, FormatListBulletedOutlined } from "@mui/icons-material"; -import { useGetSecurityScopes } from "@/service/ApiServiceNew"; +import { useGetSecurityScopes } from "@/service"; import { SecurityScope } from "@/interfaces"; import { CustomCardHeader, GridFooterWithButton } from "@/components"; diff --git a/frontend/src/views/UserManagement/RoleDetailsCard.tsx b/frontend/src/views/UserManagement/RoleDetailsCard.tsx index 2c63d0ab..7aa784ad 100644 --- a/frontend/src/views/UserManagement/RoleDetailsCard.tsx +++ b/frontend/src/views/UserManagement/RoleDetailsCard.tsx @@ -20,11 +20,7 @@ import { yupResolver } from "@hookform/resolvers/yup"; import { enqueueSnackbar } from "notistack"; import { useFieldArray } from "react-hook-form"; -import { - useCreateRole, - useGetSecurityScopes, - useUpdateRole, -} from "@/service/ApiServiceNew"; +import { useCreateRole, useGetSecurityScopes, useUpdateRole } from "@/service"; import { ControlledTextbox, CustomCardHeader } from "@/components"; import { SecurityScope, UserRole } from "@/interfaces"; diff --git a/frontend/src/views/UserManagement/RolesTable.tsx b/frontend/src/views/UserManagement/RolesTable.tsx index fa3d6ca1..240f2f7c 100644 --- a/frontend/src/views/UserManagement/RolesTable.tsx +++ b/frontend/src/views/UserManagement/RolesTable.tsx @@ -10,7 +10,7 @@ import { TextField, } from "@mui/material"; import { Search, Add, FormatListBulletedOutlined } from "@mui/icons-material"; -import { useGetRoles } from "@/service/ApiServiceNew"; +import { useGetRoles } from "@/service"; import { UserRole } from "@/interfaces"; import { CustomCardHeader, GridFooterWithButton } from "@/components"; diff --git a/frontend/src/views/UserManagement/UserDetailsCard.tsx b/frontend/src/views/UserManagement/UserDetailsCard.tsx index 33a67a42..ed7ea211 100644 --- a/frontend/src/views/UserManagement/UserDetailsCard.tsx +++ b/frontend/src/views/UserManagement/UserDetailsCard.tsx @@ -28,7 +28,7 @@ import { useUpdateUser, useGetRoles, useUpdateUserPassword, -} from "@/service/ApiServiceNew"; +} from "@/service"; import { ControlledTextbox, ControlledSelect, diff --git a/frontend/src/views/UserManagement/UserManagementView.tsx b/frontend/src/views/UserManagement/UserManagementView.tsx index e3a38a6b..1d5c7bf2 100644 --- a/frontend/src/views/UserManagement/UserManagementView.tsx +++ b/frontend/src/views/UserManagement/UserManagementView.tsx @@ -1,12 +1,13 @@ import { Grid } from "@mui/material"; import { useEffect, useState } from "react"; +import { User, UserRole } from "@/interfaces"; +import { BackgroundBox } from "@/components"; + import { UsersTable } from "./UsersTable"; import { UserDetailsCard } from "./UserDetailsCard"; -import { User, UserRole } from "../../interfaces"; import { RolesTable } from "./RolesTable"; import { RoleDetailsCard } from "./RoleDetailsCard"; import { PermissionsTable } from "./PermissionsTable"; -import { BackgroundBox } from "../../components/BackgroundBox"; export const UserManagementView = () => { const [selectedUser, setSelectedUser] = useState(); @@ -53,6 +54,6 @@ export const UserManagementView = () => { - + ); }; diff --git a/frontend/src/views/UserManagement/UsersTable.tsx b/frontend/src/views/UserManagement/UsersTable.tsx index 34a2bcc1..886390ea 100644 --- a/frontend/src/views/UserManagement/UsersTable.tsx +++ b/frontend/src/views/UserManagement/UsersTable.tsx @@ -10,7 +10,7 @@ import { Typography, } from "@mui/material"; import { Search, Add, FormatListBulletedOutlined } from "@mui/icons-material"; -import { useGetUserAdminList } from "@/service/ApiServiceNew"; +import { useGetUserAdminList } from "@/service"; import { User } from "@/interfaces"; import { CustomCardHeader, diff --git a/frontend/src/views/WellManagement/WellDetailsCard.tsx b/frontend/src/views/WellManagement/WellDetailsCard.tsx index a8c3b54f..b38ade72 100644 --- a/frontend/src/views/WellManagement/WellDetailsCard.tsx +++ b/frontend/src/views/WellManagement/WellDetailsCard.tsx @@ -23,7 +23,7 @@ import { useGetWaterSources, useGetWellStatusTypes, useUpdateWell, -} from "@/service/ApiServiceNew"; +} from "@/service"; import { SubmitWellCreate, WellUpdate, diff --git a/frontend/src/views/WellManagement/WellSelectionMap.tsx b/frontend/src/views/WellManagement/WellSelectionMap.tsx index c622f495..7340fce7 100644 --- a/frontend/src/views/WellManagement/WellSelectionMap.tsx +++ b/frontend/src/views/WellManagement/WellSelectionMap.tsx @@ -2,11 +2,16 @@ import { useEffect } from "react"; import { useDebounce } from "use-debounce"; import { LayersControl, MapContainer, Marker, Tooltip } from "react-leaflet"; import { Box, Typography } from "@mui/material"; -import { useGetWellLocations } from "../../service/ApiServiceNew"; -import { Well } from "../../interfaces"; -import { OpenStreetMapLayer, SatelliteLayer, SoutheastGuideLayer, WellMapLegend } from "../../components"; -import { BlueMapIcon, RedMapIcon, BlackMapIcon } from "../../components/MapIcons"; -import { WellStatus } from "../../enums"; +import { useGetWellLocations } from "@/service"; +import { Well } from "@/interfaces"; +import { + OpenStreetMapLayer, + SatelliteLayer, + SoutheastGuideLayer, + WellMapLegend, +} from "@/components"; +import { BlueMapIcon, RedMapIcon, BlackMapIcon } from "@/components/MapIcons"; +import { WellStatus } from "@/enums"; import L from "leaflet"; import "leaflet/dist/leaflet.css"; @@ -38,16 +43,16 @@ export default function WellSelectionMap({ @@ -109,27 +114,41 @@ export default function WellSelectionMap({ {/* Loading first page */} {wellQuery.isLoading && ( - Loading well markers... + + Loading well markers... + )} {/* Loading additional pages */} {wellQuery.isFetchingNextPage && ( - Loading more wells... + + Loading more wells... + )} {wellQuery.isSuccess && wellMarkers.length === 0 && ( - + No wells found for that search. @@ -137,10 +156,14 @@ export default function WellSelectionMap({ {/* Error */} {wellQuery.isError && ( - + Failed to load wells: {wellQuery.error.message} @@ -157,5 +180,4 @@ const getWellIcon = (well: Well) => { return RedMapIcon; } return BlueMapIcon; -} - +}; diff --git a/frontend/src/views/WellManagement/WellSelectionTable.tsx b/frontend/src/views/WellManagement/WellSelectionTable.tsx index a4311fd0..0a2e2848 100644 --- a/frontend/src/views/WellManagement/WellSelectionTable.tsx +++ b/frontend/src/views/WellManagement/WellSelectionTable.tsx @@ -6,7 +6,7 @@ import { useAuthUser } from "react-auth-kit"; import { Box, Button, Stack } from "@mui/material"; import { Add } from "@mui/icons-material"; import { SecurityScope, Well, WellListQueryParams } from "@/interfaces"; -import { useGetWells } from "@/service/ApiServiceNew"; +import { useGetWells } from "@/service"; import { SortDirection, WellSortByField } from "@/enums"; import { GridFooterWithButton } from "@/components"; diff --git a/frontend/src/views/WellManagement/WellsTable.tsx b/frontend/src/views/WellManagement/WellsTable.tsx index 72d19fd2..c4738184 100644 --- a/frontend/src/views/WellManagement/WellsTable.tsx +++ b/frontend/src/views/WellManagement/WellsTable.tsx @@ -9,12 +9,12 @@ import { Box, InputAdornment, } from "@mui/material"; -import FormatListBulletedOutlinedIcon from "@mui/icons-material/FormatListBulletedOutlined"; +import { FormatListBulletedOutlined } from "@mui/icons-material"; import { Search } from "@mui/icons-material"; -import TabPanel from "../../components/TabPanel"; +import { CustomCardHeader, TabPanel } from "@/components"; + import WellSelectionTable from "./WellSelectionTable"; import WellSelectionMap from "./WellSelectionMap"; -import { CustomCardHeader } from "../../components/CustomCardHeader"; export const WellsTable = ({ setSelectedWell, @@ -29,22 +29,34 @@ export const WellsTable = ({ setCurrentTabIndex(newTabIndex); return ( - - + + - + - + Date: Tue, 10 Feb 2026 13:20:15 -0600 Subject: [PATCH 5/7] refactor(interfaces): Rm interfaces from interfaces.d.ts to their own files --- frontend/src/interfaces.d.ts | 686 ------------------ frontend/src/interfaces/ActivityForm.ts | 38 + .../src/interfaces/ActivityFormControl.ts | 41 ++ frontend/src/interfaces/ActivityTypeLU.ts | 6 + frontend/src/interfaces/BaseWell.ts | 15 + frontend/src/interfaces/CreateUser.ts | 10 + frontend/src/interfaces/LandOwner.ts | 9 + frontend/src/interfaces/Location.ts | 12 + frontend/src/interfaces/Meter.ts | 25 + frontend/src/interfaces/MeterActivity.ts | 22 + frontend/src/interfaces/MeterDetails.ts | 26 + .../src/interfaces/MeterDetailsQueryParams.ts | 3 + frontend/src/interfaces/MeterHistoryDTO.ts | 14 + frontend/src/interfaces/MeterListDTO.ts | 16 + frontend/src/interfaces/MeterListQuery.ts | 10 + .../src/interfaces/MeterListQueryParams.ts | 10 + frontend/src/interfaces/MeterListSortBy.ts | 3 + frontend/src/interfaces/MeterMapDTO.ts | 13 + frontend/src/interfaces/MeterPartParams.ts | 3 + frontend/src/interfaces/MeterRegister.ts | 13 + frontend/src/interfaces/MeterStatus.ts | 5 + frontend/src/interfaces/MeterType.ts | 10 + frontend/src/interfaces/MeterTypeLU.ts | 10 + frontend/src/interfaces/MonitoredRegion.ts | 9 + frontend/src/interfaces/MonitoredWell.ts | 11 + .../src/interfaces/NewRegionMeasurement.ts | 7 + frontend/src/interfaces/NewUser.ts | 11 + frontend/src/interfaces/NewWellMeasurement.ts | 7 + frontend/src/interfaces/NewWorkOrder.ts | 7 + frontend/src/interfaces/NoteTypeLU.ts | 7 + frontend/src/interfaces/ObservationForm.ts | 8 + .../src/interfaces/ObservedPropertyTypeLU.ts | 10 + frontend/src/interfaces/Organization.ts | 3 + frontend/src/interfaces/Page.ts | 6 + frontend/src/interfaces/Part.ts | 18 + frontend/src/interfaces/PartAssociation.ts | 10 + frontend/src/interfaces/PartTypeLU.ts | 7 + frontend/src/interfaces/PatchActivityForm.ts | 29 + .../src/interfaces/PatchActivitySubmit.ts | 19 + .../src/interfaces/PatchObservationForm.ts | 21 + .../src/interfaces/PatchObservationSubmit.ts | 15 + .../src/interfaces/PatchRegionMeasurement.ts | 9 + .../src/interfaces/PatchWellMeasurement.ts | 8 + frontend/src/interfaces/PatchWorkOrder.ts | 9 + .../src/interfaces/RegionMeasurementDTO.ts | 7 + frontend/src/interfaces/ST2Measurement.ts | 6 + frontend/src/interfaces/ST2Response.ts | 5 + .../interfaces/ST2WaterLevelQueryParams.ts | 5 + frontend/src/interfaces/SecurityScope.ts | 5 + frontend/src/interfaces/ServiceTypeLU.ts | 5 + frontend/src/interfaces/SubmitWellCreate.ts | 22 + frontend/src/interfaces/Unit.ts | 6 + .../src/interfaces/UpdatedUserPassword.ts | 4 + frontend/src/interfaces/User.ts | 14 + frontend/src/interfaces/UserRole.ts | 7 + .../src/interfaces/WaterLevelQueryParams.ts | 3 + frontend/src/interfaces/WaterSource.ts | 5 + frontend/src/interfaces/Well.ts | 21 + .../src/interfaces/WellDetailsQueryParams.ts | 3 + .../src/interfaces/WellListQueryParams.ts | 10 + frontend/src/interfaces/WellMeasurementDTO.ts | 8 + frontend/src/interfaces/WellMergeParams.ts | 4 + frontend/src/interfaces/WellStatus.ts | 5 + frontend/src/interfaces/WellUpdate.ts | 12 + frontend/src/interfaces/WellUseLU.ts | 6 + frontend/src/interfaces/WorkOrder.ts | 13 + frontend/src/interfaces/index.ts | 66 ++ frontend/src/interfaces/primitives.ts | 3 + frontend/src/service/ApiServiceNew.ts | 11 +- frontend/src/utils/AssertDefined.ts | 8 + frontend/src/utils/index.ts | 1 + .../src/views/Meters/MeterDetailsFields.tsx | 15 +- .../Meters/MeterHistory/MeterHistory.tsx | 20 +- .../MeterSelection/MeterSelectionTable.tsx | 15 +- frontend/src/views/Meters/MetersView.tsx | 8 +- .../src/views/Reports/Chlorides/index.tsx | 6 +- .../views/WellManagement/WellSelectionMap.tsx | 4 +- .../WellManagement/WellSelectionTable.tsx | 6 +- 78 files changed, 846 insertions(+), 724 deletions(-) delete mode 100644 frontend/src/interfaces.d.ts create mode 100644 frontend/src/interfaces/ActivityForm.ts create mode 100644 frontend/src/interfaces/ActivityFormControl.ts create mode 100644 frontend/src/interfaces/ActivityTypeLU.ts create mode 100644 frontend/src/interfaces/BaseWell.ts create mode 100644 frontend/src/interfaces/CreateUser.ts create mode 100644 frontend/src/interfaces/LandOwner.ts create mode 100644 frontend/src/interfaces/Location.ts create mode 100644 frontend/src/interfaces/Meter.ts create mode 100644 frontend/src/interfaces/MeterActivity.ts create mode 100644 frontend/src/interfaces/MeterDetails.ts create mode 100644 frontend/src/interfaces/MeterDetailsQueryParams.ts create mode 100644 frontend/src/interfaces/MeterHistoryDTO.ts create mode 100644 frontend/src/interfaces/MeterListDTO.ts create mode 100644 frontend/src/interfaces/MeterListQuery.ts create mode 100644 frontend/src/interfaces/MeterListQueryParams.ts create mode 100644 frontend/src/interfaces/MeterListSortBy.ts create mode 100644 frontend/src/interfaces/MeterMapDTO.ts create mode 100644 frontend/src/interfaces/MeterPartParams.ts create mode 100644 frontend/src/interfaces/MeterRegister.ts create mode 100644 frontend/src/interfaces/MeterStatus.ts create mode 100644 frontend/src/interfaces/MeterType.ts create mode 100644 frontend/src/interfaces/MeterTypeLU.ts create mode 100644 frontend/src/interfaces/MonitoredRegion.ts create mode 100644 frontend/src/interfaces/MonitoredWell.ts create mode 100644 frontend/src/interfaces/NewRegionMeasurement.ts create mode 100644 frontend/src/interfaces/NewUser.ts create mode 100644 frontend/src/interfaces/NewWellMeasurement.ts create mode 100644 frontend/src/interfaces/NewWorkOrder.ts create mode 100644 frontend/src/interfaces/NoteTypeLU.ts create mode 100644 frontend/src/interfaces/ObservationForm.ts create mode 100644 frontend/src/interfaces/ObservedPropertyTypeLU.ts create mode 100644 frontend/src/interfaces/Organization.ts create mode 100644 frontend/src/interfaces/Page.ts create mode 100644 frontend/src/interfaces/Part.ts create mode 100644 frontend/src/interfaces/PartAssociation.ts create mode 100644 frontend/src/interfaces/PartTypeLU.ts create mode 100644 frontend/src/interfaces/PatchActivityForm.ts create mode 100644 frontend/src/interfaces/PatchActivitySubmit.ts create mode 100644 frontend/src/interfaces/PatchObservationForm.ts create mode 100644 frontend/src/interfaces/PatchObservationSubmit.ts create mode 100644 frontend/src/interfaces/PatchRegionMeasurement.ts create mode 100644 frontend/src/interfaces/PatchWellMeasurement.ts create mode 100644 frontend/src/interfaces/PatchWorkOrder.ts create mode 100644 frontend/src/interfaces/RegionMeasurementDTO.ts create mode 100644 frontend/src/interfaces/ST2Measurement.ts create mode 100644 frontend/src/interfaces/ST2Response.ts create mode 100644 frontend/src/interfaces/ST2WaterLevelQueryParams.ts create mode 100644 frontend/src/interfaces/SecurityScope.ts create mode 100644 frontend/src/interfaces/ServiceTypeLU.ts create mode 100644 frontend/src/interfaces/SubmitWellCreate.ts create mode 100644 frontend/src/interfaces/Unit.ts create mode 100644 frontend/src/interfaces/UpdatedUserPassword.ts create mode 100644 frontend/src/interfaces/User.ts create mode 100644 frontend/src/interfaces/UserRole.ts create mode 100644 frontend/src/interfaces/WaterLevelQueryParams.ts create mode 100644 frontend/src/interfaces/WaterSource.ts create mode 100644 frontend/src/interfaces/Well.ts create mode 100644 frontend/src/interfaces/WellDetailsQueryParams.ts create mode 100644 frontend/src/interfaces/WellListQueryParams.ts create mode 100644 frontend/src/interfaces/WellMeasurementDTO.ts create mode 100644 frontend/src/interfaces/WellMergeParams.ts create mode 100644 frontend/src/interfaces/WellStatus.ts create mode 100644 frontend/src/interfaces/WellUpdate.ts create mode 100644 frontend/src/interfaces/WellUseLU.ts create mode 100644 frontend/src/interfaces/WorkOrder.ts create mode 100644 frontend/src/interfaces/primitives.ts create mode 100644 frontend/src/utils/AssertDefined.ts diff --git a/frontend/src/interfaces.d.ts b/frontend/src/interfaces.d.ts deleted file mode 100644 index 24d410ec..00000000 --- a/frontend/src/interfaces.d.ts +++ /dev/null @@ -1,686 +0,0 @@ -import { SortDirection, MeterSortByField, WellSortByField } from 'enums' -import internal from 'stream' -import { ActivityType, MeterStatusNames } from './enums' -import { DateCalendarClassKey } from '@mui/x-date-pickers' -import dayjs from 'dayjs' -import exp from 'constants' - -export interface ActivityForm { - - activity_details?: { - meter_id?: number - activity_type_id?: number - user_id?: number - date?: Dayjs - start_time?: Dayjs - end_time?: Dayjs - share_ose: boolean - work_order_id?: number - } - - current_installation?: { - contact_name?: string - contact_phone?: string - well_id?: number - notes?: string - water_users?: string - meter_owner?: string - } - - observations?: ObservationForm[] - - maintenance_repair?: { - service_type_ids: number[] - description: string - } - - notes?: { - working_on_arrival_slug: string - selected_note_ids: number[] - } - - part_used_ids?: number[] -} - -// This might could be the full things that are selected, but for now its only the things that are submitted/validated -// These need to be the actual interfaces eventually, meter -> MeterListDTO -export interface ActivityFormControl { - activity_details: { - selected_meter: Partial | null - activity_type: Partial | null - user: Partial | null - date: Dayjs - start_time: Dayjs - end_time: Dayjs - share_ose: boolean = false - work_order_id: number | null - }, - current_installation: { - meter: Partial | null - well: Partial | null - }, - observations: Array<{ - time: Dayjs - reading: '' | number - property_type_id: number | null - unit_id: number | null - }>, - maintenance_repair?: { - service_type_ids: number[] | null, - description: string - }, - notes: { - working_on_arrival_slug: string, - selected_note_ids: number[] | null - }, - photos?: File[], - part_used_ids?: [] -} - -export interface MeterActivity { - id: int - timestamp_start: Date - timestamp_end: Date - notes?: string - submitting_user_id: int - meter_id: int - activity_type_id: int - location_id: int - - submitting_user?: User - meter?: Meter - activity_type?: ActivityTypeLU - location?: Location - parts_used?: [] -} - -//This is designed to match the HistoryDetails form rather than the patch meter API -export interface PatchActivityForm { - activity_id: int - meter_id: int - activity_date: dayjs.Dayjs - activity_start_time: dayjs.Dayjs - activity_end_time: dayjs.Dayjs - activity_type: ActivityTypeLU - submitting_user: User - description: string - - well: Well | null - water_users?: string - - notes?: NoteTypeLU[] - services?: ServiceTypeLU[] - parts_used?: Part[] - - ose_share: boolean -} - -//This interface is designed to match the backend API patch endpoint -export interface PatchActivitySubmit { - activity_id: int - timestamp_start: string - timestamp_end: string - description: string - submitting_user_id: int - meter_id: int - activity_type_id: int - location_id: int | null - ose_share: boolean - water_users: string - - note_ids: int[] | null - service_ids: int[] | null - part_ids: int[] | null -} - -//Designed for the HistoryDetails component, not the patch endpoint -export interface PatchObservationForm { - observation_id: int - submitting_user: User - well: Well | null - observation_date: dayjs.Dayjs - observation_time: dayjs.Dayjs - property_type: ObservedPropertyTypeLU - unit: Unit - value: number - ose_share: boolean - notes?: string - meter_id: int -} - -export interface PatchObservationSubmit { - //Matches the backend API patch endpoint - observation_id: int - timestamp: string - value: number - notes: string | null - submitting_user_id: int - meter_id: int - observed_property_type_id: int - unit_id: int - location_id: int | null - ose_share: boolean -} - -export interface ObservationForm { - time: Dayjs - reading: '' | number - property_type_id: '' | number - unit_id: '' | number -} - -export interface WellUseLU { - id: number - use_type?: string - code?: string - description?: string -} - -export interface PartTypeLU { - id: int - name: string - description?: string -} - -export interface Part { - id: number - part_number: string - part_type_id: number - vendor?: string - note?: string - description?: string - initial_count?: number - current_count?: number - in_use: boolean - commonly_used: boolean - - part_type?: PartTypeLU - meter_types?: MeterTypeLU[] - -} - -export interface PartAssociation { - id: int - meter_type_id: int - part_id: int - commonly_used: boolean - part?: Part -} - -export interface ServiceTypeLU { - id: number - service_name: string - description?: string -} - -export interface NoteTypeLU { - id: number - note: string - details?: string - slug?: string - commonly_used: boolean -} - -export interface WellUseLU { - id: number - use_type: string - code: string - description: string -} - -export interface WaterSource { - id: number - name: string - description: string -} - -export interface WellStatus { - id: number - status: string - description: string -} - -export interface SubmitWellCreate { - name: string - ra_number: string - owners: string - osetag: string - water_source: WaterSource | null - chloride_group_id: number | null - - use_type: { - id: number - } - - location: { - name: string, - trss: string, - longitude: float, - latitude: float - } -} - -interface BaseWell { - id: number - name: string - ra_number: string - owners: string - osetag: string - casing: string - total_depth: number - outside_recorder: boolean - location_id: number - use_type_id: number - well_status_id: number - water_source_id: number - chloride_group_id: number | null -} - -export interface Well extends BaseWell { - use_type: WellUseLU | null - water_source: WaterSource | null - location: Location | null - well_status: WellStatus | null - - meters: [ - { - id: int - serial_number: string - water_users?: string - } - ] -} - -export interface WellUpdate extends BaseWell { - use_type: WellUseLU - water_source: WaterSource - location: Location - well_status: WellStatus -} - -export interface MeterDetailsQueryParams { - meter_id: number | undefined -} - -export interface MeterPartParams { - meter_id: number | undefined -} - -export interface WellDetailsQueryParams { - well_id: number | undefined -} - -export interface WaterLevelQueryParams { - well_id: number | undefined -} - -export interface WellMergeParams { - merge_well: string - target_well: string -} - -export interface ST2WaterLevelQueryParams { - $filter: string - $orderby: string - datastreamID: number | undefined -} - -export interface ActivityTypeLU { - id: number - name: string - description: string - permission: string -} - -export interface ObservedPropertyTypeLU { - id: number - name: string - description: string - context: string - - units?: Unit[] -} - -export interface Unit { - id: number - name: string - name_short: string - description: string -} - -export interface MeterHistoryDTO { - id: int - history_type: string - activity_type: string - date: Date - history_item: any - location: Location - well: Well | null - photos: any -} - -export interface MeterType { - id?: int - brand?: string - series?: string - model?: string - size?: float - description?: string -} - -export interface MeterRegister { - id: number - brand: string - meter_size: number - ratio: string | null - number_of_digits: number | null - decimal_digits: number | null - dial_units: Unit - totalizer_units: Unit - multiplier?: number | null -} - -export interface MeterStatus { - id: number - status_name?: string - description?: string -} - -export interface LandOwner { - id: number - contact_name?: string - land_owner_name?: string - organization?: string - phone?: string - email?: string - city?: string -} - -export interface Location { - name: string - latitude: float - longitude: float - trss: string - land_owner_id: number - - land_owner?: LandOwner -} - -//Depricate this??? need to assess -export interface MeterTypeLU { - id: number - brand: string - series: string - model: string - size: number - description: string - in_use: boolean -} - -export interface MeterDetails { - id?: number | null - serial_number?: string | null - contact_name?: string | null - contact_phone?: string | null - water_users?: string | null - meter_owner?: string | null - ra_number?: string | null - tag?: string | null - well_distance_ft?: float | null - notes?: string | null - meter_type_id?: int | null - well_id?: int | null - - meter_type: MeterType - status: MeterStatus - well: Well | null - meter_register: MeterRegister | null - // Also has parts_associated?: List[Part] -} - -export interface MeterListQueryParams { - search_string?: string - filter_by_status?: MeterStatusNames[] - sort_by?: MeterSortByField - sort_direction?: SortDirection - limit?: number - offset?: number -} - -export interface MeterMapDTO { - id: number - serial_number: string - well: { - ra_number: string - name: string - } - location: { - longitude: number - latitude: number - } - last_pm: string -} - -export interface Organization { - organization_name: string -} - -export interface Meter { - id: number - serial_number: string - contact_name?: string - contact_phone?: string - notes?: string - price?: number - - meter_type_id: number - status_id?: number - well_id: number - location_id?: number - - meter_register?: MeterRegister - meter_type?: MeterType - status?: MeterStatus - well?: Well - location?: Location -} - -export interface MeterListDTO { - id: number - serial_number: string - status?: { status_name?: string } - water_users: string - location: { - trss: string - longitude: number - latitude: number - } - well: { - ra_number: string - name: string - owners: string - } -} - -interface WellListQueryParams { - search_string?: string - // sort_by?: WellSortByField - sort_direction?: SortDirection - limit?: number - offset?: number - exclude_inactive?: boolean -} - -export interface Page { - items: T[] - total: number - limit: number - offset: number -} - -export interface MeterListQuery { - search_string: string - sort_by: MeterListSortBy - sort_direction: SortDirection - limit: number, - offset: number -} - -// Single manual measurement from a certain well -export interface WellMeasurementDTO { - id: number - timestamp: Date - value: number - submitting_user: { full_name: string } - well: { id: number, ra_number: string } -} - -export interface RegionMeasurementDTO { - id: number - timestamp: Date - value: number - submitting_user: { id: number, full_name: string } - well: { id: number, ra_number: string } -} - -// Single value from a NM ST2 endpoint, many other fields are returned, these are the only ones used at the moment -export interface ST2Measurement { - result: number - resultTime: Date - phenomenonTime: Date -} - -// Whole response returned from a NM ST2 endpoint -export interface ST2Response { - "@iot.nextLink": string - value: [] -} - -// The object that gets sent to the backend to add a new measurement -export interface NewWellMeasurement { - well_id: number - timestamp: string - value: number - submitting_user_id: number -} - -export interface PatchWellMeasurement { - levelmeasurement_id: number - submitting_user_id: number - timestamp: dayjs.Dayjs - value: number -} - -export interface NewRegionMeasurement { - region_id: number - timestamp: string - value?: number | null - submitting_user_id: number - well_id: number -} - -export interface PatchRegionMeasurement { - levelmeasurement_id: number - submitting_user_id: number - well_id: number - timestamp: dayjs.Dayjs - value?: number | null -} - -export interface CreateUser { - username: string - full_name: string - email: scope_string - disabled: boolean - user_role: { id: number } - password: string -} - -export interface UpdatedUserPassword { - user_id: number - new_password: string -} - -export interface User { - id: number - username?: string - full_name: string - display_name?: string - email?: scope_string - disabled: boolean - user_role_id?: number - user_role?: UserRole - password?: string -} - -export interface NewUser { - id: number - username: string - full_name: string - email: scope_string - disabled: boolean - user_role_id: number - password: string -} - -export interface UserRole { - id: number - name: string - security_scopes: SecurityScope[] -} - -export interface SecurityScope { - id: number - scope_string: string - description: string -} - -export interface WorkOrder { - work_order_id: number - date_created: Date - creator?: String - meter_serial: String - title: String - description: String - status: String - notes?: String - assigned_user_id?: number - assigned_user?: String - associated_activities?: number[] -} - -export interface NewWorkOrder { - //Just the bare minimum to create a new work order - //No work order ID since it is generated by the backend - date_created: Date //This should be on the frontend to ensure it doesn't reflect server time - meter_id: number - title: string -} - -export interface PatchWorkOrder { - // This is designed to match the backend API patch endpoint and is limited to the fields that can be updated - work_order_id: number - title?: string - description?: string - status?: string - notes?: string - assigned_user_id?: number -} - -export interface MonitoredWell { - id: number; - name: string; - ra_number: string; - datastream_id: number; - well_status: WellStatus; - outside_recorder?: boolean; - chloride_group_id?: number; -} - -export interface MonitoredRegion { - id: number; - name: string; - datastream_id: number; - well_status: WellStatus; - outside_recorder?: boolean; -} diff --git a/frontend/src/interfaces/ActivityForm.ts b/frontend/src/interfaces/ActivityForm.ts new file mode 100644 index 00000000..94e55bdb --- /dev/null +++ b/frontend/src/interfaces/ActivityForm.ts @@ -0,0 +1,38 @@ +import type { Dayjs } from "dayjs"; +import type { ObservationForm } from "./ObservationForm"; + +export interface ActivityForm { + activity_details?: { + meter_id?: number; + activity_type_id?: number; + user_id?: number; + date?: Dayjs; + start_time?: Dayjs; + end_time?: Dayjs; + share_ose: boolean; + work_order_id?: number; + }; + + current_installation?: { + contact_name?: string; + contact_phone?: string; + well_id?: number; + notes?: string; + water_users?: string; + meter_owner?: string; + }; + + observations?: ObservationForm[]; + + maintenance_repair?: { + service_type_ids: number[]; + description: string; + }; + + notes?: { + working_on_arrival_slug: string; + selected_note_ids: number[]; + }; + + part_used_ids?: number[]; +} diff --git a/frontend/src/interfaces/ActivityFormControl.ts b/frontend/src/interfaces/ActivityFormControl.ts new file mode 100644 index 00000000..b09886b6 --- /dev/null +++ b/frontend/src/interfaces/ActivityFormControl.ts @@ -0,0 +1,41 @@ +import type { Dayjs } from "dayjs"; +import type { ActivityTypeLU } from "./ActivityTypeLU"; +import type { MeterDetails } from "./MeterDetails"; +import type { MeterListDTO } from "./MeterListDTO"; +import type { User } from "./User"; +import type { Well } from "./Well"; + +// This might could be the full things that are selected, but for now its only the things that are submitted/validated +// These need to be the actual interfaces eventually, meter -> MeterListDTO +export interface ActivityFormControl { + activity_details: { + selected_meter: Partial | null; + activity_type: Partial | null; + user: Partial | null; + date: Dayjs; + start_time: Dayjs; + end_time: Dayjs; + share_ose: boolean; + work_order_id: number | null; + }; + current_installation: { + meter: Partial | null; + well: Partial | null; + }; + observations: Array<{ + time: Dayjs; + reading: "" | number; + property_type_id: number | null; + unit_id: number | null; + }>; + maintenance_repair?: { + service_type_ids: number[] | null; + description: string; + }; + notes: { + working_on_arrival_slug: string; + selected_note_ids: number[] | null; + }; + photos?: File[]; + part_used_ids?: []; +} diff --git a/frontend/src/interfaces/ActivityTypeLU.ts b/frontend/src/interfaces/ActivityTypeLU.ts new file mode 100644 index 00000000..4755c359 --- /dev/null +++ b/frontend/src/interfaces/ActivityTypeLU.ts @@ -0,0 +1,6 @@ +export interface ActivityTypeLU { + id: number; + name: string; + description: string; + permission: string; +} diff --git a/frontend/src/interfaces/BaseWell.ts b/frontend/src/interfaces/BaseWell.ts new file mode 100644 index 00000000..e12efce7 --- /dev/null +++ b/frontend/src/interfaces/BaseWell.ts @@ -0,0 +1,15 @@ +export interface BaseWell { + id: number; + name: string; + ra_number: string; + owners: string; + osetag: string; + casing: string; + total_depth: number; + outside_recorder: boolean; + location_id: number; + use_type_id: number; + well_status_id: number; + water_source_id: number; + chloride_group_id: number | null; +} diff --git a/frontend/src/interfaces/CreateUser.ts b/frontend/src/interfaces/CreateUser.ts new file mode 100644 index 00000000..5b35518d --- /dev/null +++ b/frontend/src/interfaces/CreateUser.ts @@ -0,0 +1,10 @@ +import type { scope_string } from "./primitives"; + +export interface CreateUser { + username: string; + full_name: string; + email: scope_string; + disabled: boolean; + user_role: { id: number }; + password: string; +} diff --git a/frontend/src/interfaces/LandOwner.ts b/frontend/src/interfaces/LandOwner.ts new file mode 100644 index 00000000..611ceced --- /dev/null +++ b/frontend/src/interfaces/LandOwner.ts @@ -0,0 +1,9 @@ +export interface LandOwner { + id: number; + contact_name?: string; + land_owner_name?: string; + organization?: string; + phone?: string; + email?: string; + city?: string; +} diff --git a/frontend/src/interfaces/Location.ts b/frontend/src/interfaces/Location.ts new file mode 100644 index 00000000..0d6f938d --- /dev/null +++ b/frontend/src/interfaces/Location.ts @@ -0,0 +1,12 @@ +import type { float } from "./primitives"; +import type { LandOwner } from "./LandOwner"; + +export interface Location { + name: string; + latitude: float; + longitude: float; + trss: string; + land_owner_id: number; + + land_owner?: LandOwner; +} diff --git a/frontend/src/interfaces/Meter.ts b/frontend/src/interfaces/Meter.ts new file mode 100644 index 00000000..10d9aa9f --- /dev/null +++ b/frontend/src/interfaces/Meter.ts @@ -0,0 +1,25 @@ +import type { Location } from "./Location"; +import type { MeterRegister } from "./MeterRegister"; +import type { MeterStatus } from "./MeterStatus"; +import type { MeterType } from "./MeterType"; +import type { Well } from "./Well"; + +export interface Meter { + id: number; + serial_number: string; + contact_name?: string; + contact_phone?: string; + notes?: string; + price?: number; + + meter_type_id: number; + status_id?: number; + well_id: number; + location_id?: number; + + meter_register?: MeterRegister; + meter_type?: MeterType; + status?: MeterStatus; + well?: Well; + location?: Location; +} diff --git a/frontend/src/interfaces/MeterActivity.ts b/frontend/src/interfaces/MeterActivity.ts new file mode 100644 index 00000000..6316bbf8 --- /dev/null +++ b/frontend/src/interfaces/MeterActivity.ts @@ -0,0 +1,22 @@ +import type { int } from "./primitives"; +import type { ActivityTypeLU } from "./ActivityTypeLU"; +import type { Location } from "./Location"; +import type { Meter } from "./Meter"; +import type { User } from "./User"; + +export interface MeterActivity { + id: int; + timestamp_start: Date; + timestamp_end: Date; + notes?: string; + submitting_user_id: int; + meter_id: int; + activity_type_id: int; + location_id: int; + + submitting_user?: User; + meter?: Meter; + activity_type?: ActivityTypeLU; + location?: Location; + parts_used?: []; +} diff --git a/frontend/src/interfaces/MeterDetails.ts b/frontend/src/interfaces/MeterDetails.ts new file mode 100644 index 00000000..49266399 --- /dev/null +++ b/frontend/src/interfaces/MeterDetails.ts @@ -0,0 +1,26 @@ +import type { float, int } from "./primitives"; +import type { MeterRegister } from "./MeterRegister"; +import type { MeterStatus } from "./MeterStatus"; +import type { MeterType } from "./MeterType"; +import type { Well } from "./Well"; + +export interface MeterDetails { + id?: number | null; + serial_number?: string | null; + contact_name?: string | null; + contact_phone?: string | null; + water_users?: string | null; + meter_owner?: string | null; + ra_number?: string | null; + tag?: string | null; + well_distance_ft?: float | null; + notes?: string | null; + meter_type_id?: int | null; + well_id?: int | null; + + meter_type: MeterType; + status: MeterStatus; + well: Well | null; + meter_register: MeterRegister | null; + // Also has parts_associated?: List[Part] +} diff --git a/frontend/src/interfaces/MeterDetailsQueryParams.ts b/frontend/src/interfaces/MeterDetailsQueryParams.ts new file mode 100644 index 00000000..39b33bfc --- /dev/null +++ b/frontend/src/interfaces/MeterDetailsQueryParams.ts @@ -0,0 +1,3 @@ +export interface MeterDetailsQueryParams { + meter_id: number | undefined; +} diff --git a/frontend/src/interfaces/MeterHistoryDTO.ts b/frontend/src/interfaces/MeterHistoryDTO.ts new file mode 100644 index 00000000..ec55c068 --- /dev/null +++ b/frontend/src/interfaces/MeterHistoryDTO.ts @@ -0,0 +1,14 @@ +import type { int } from "./primitives"; +import type { Location } from "./Location"; +import type { Well } from "./Well"; + +export interface MeterHistoryDTO { + id: int; + history_type: string; + activity_type: string; + date: Date; + history_item: any; + location: Location; + well: Well | null; + photos: any; +} diff --git a/frontend/src/interfaces/MeterListDTO.ts b/frontend/src/interfaces/MeterListDTO.ts new file mode 100644 index 00000000..30cbb5eb --- /dev/null +++ b/frontend/src/interfaces/MeterListDTO.ts @@ -0,0 +1,16 @@ +export interface MeterListDTO { + id: number; + serial_number: string; + status?: { status_name?: string }; + water_users: string; + location: { + trss: string; + longitude: number; + latitude: number; + }; + well: { + ra_number: string; + name: string; + owners: string; + }; +} diff --git a/frontend/src/interfaces/MeterListQuery.ts b/frontend/src/interfaces/MeterListQuery.ts new file mode 100644 index 00000000..20ad11c2 --- /dev/null +++ b/frontend/src/interfaces/MeterListQuery.ts @@ -0,0 +1,10 @@ +import type { SortDirection } from "@/enums"; +import type { MeterListSortBy } from "./MeterListSortBy"; + +export interface MeterListQuery { + search_string: string; + sort_by: MeterListSortBy; + sort_direction: SortDirection; + limit: number; + offset: number; +} diff --git a/frontend/src/interfaces/MeterListQueryParams.ts b/frontend/src/interfaces/MeterListQueryParams.ts new file mode 100644 index 00000000..46056f8e --- /dev/null +++ b/frontend/src/interfaces/MeterListQueryParams.ts @@ -0,0 +1,10 @@ +import type { MeterSortByField, MeterStatusNames, SortDirection } from "@/enums"; + +export interface MeterListQueryParams { + search_string?: string; + filter_by_status?: MeterStatusNames[]; + sort_by?: MeterSortByField; + sort_direction?: SortDirection; + limit?: number; + offset?: number; +} diff --git a/frontend/src/interfaces/MeterListSortBy.ts b/frontend/src/interfaces/MeterListSortBy.ts new file mode 100644 index 00000000..683204b6 --- /dev/null +++ b/frontend/src/interfaces/MeterListSortBy.ts @@ -0,0 +1,3 @@ +import type { MeterSortByField } from "@/enums"; + +export type MeterListSortBy = MeterSortByField; diff --git a/frontend/src/interfaces/MeterMapDTO.ts b/frontend/src/interfaces/MeterMapDTO.ts new file mode 100644 index 00000000..55b97326 --- /dev/null +++ b/frontend/src/interfaces/MeterMapDTO.ts @@ -0,0 +1,13 @@ +export interface MeterMapDTO { + id: number; + serial_number: string; + well: { + ra_number: string; + name: string; + }; + location: { + longitude: number; + latitude: number; + }; + last_pm: string; +} diff --git a/frontend/src/interfaces/MeterPartParams.ts b/frontend/src/interfaces/MeterPartParams.ts new file mode 100644 index 00000000..18368956 --- /dev/null +++ b/frontend/src/interfaces/MeterPartParams.ts @@ -0,0 +1,3 @@ +export interface MeterPartParams { + meter_id: number | undefined; +} diff --git a/frontend/src/interfaces/MeterRegister.ts b/frontend/src/interfaces/MeterRegister.ts new file mode 100644 index 00000000..24873aca --- /dev/null +++ b/frontend/src/interfaces/MeterRegister.ts @@ -0,0 +1,13 @@ +import type { Unit } from "./Unit"; + +export interface MeterRegister { + id: number; + brand: string; + meter_size: number; + ratio: string | null; + number_of_digits: number | null; + decimal_digits: number | null; + dial_units: Unit; + totalizer_units: Unit; + multiplier?: number | null; +} diff --git a/frontend/src/interfaces/MeterStatus.ts b/frontend/src/interfaces/MeterStatus.ts new file mode 100644 index 00000000..1294633d --- /dev/null +++ b/frontend/src/interfaces/MeterStatus.ts @@ -0,0 +1,5 @@ +export interface MeterStatus { + id: number; + status_name?: string; + description?: string; +} diff --git a/frontend/src/interfaces/MeterType.ts b/frontend/src/interfaces/MeterType.ts new file mode 100644 index 00000000..1f84b725 --- /dev/null +++ b/frontend/src/interfaces/MeterType.ts @@ -0,0 +1,10 @@ +import type { float, int } from "./primitives"; + +export interface MeterType { + id?: int; + brand?: string; + series?: string; + model?: string; + size?: float; + description?: string; +} diff --git a/frontend/src/interfaces/MeterTypeLU.ts b/frontend/src/interfaces/MeterTypeLU.ts new file mode 100644 index 00000000..92cd86c5 --- /dev/null +++ b/frontend/src/interfaces/MeterTypeLU.ts @@ -0,0 +1,10 @@ +//Depricate this??? need to assess +export interface MeterTypeLU { + id: number; + brand: string; + series: string; + model: string; + size: number; + description: string; + in_use: boolean; +} diff --git a/frontend/src/interfaces/MonitoredRegion.ts b/frontend/src/interfaces/MonitoredRegion.ts new file mode 100644 index 00000000..cd74b0be --- /dev/null +++ b/frontend/src/interfaces/MonitoredRegion.ts @@ -0,0 +1,9 @@ +import type { WellStatus } from "./WellStatus"; + +export interface MonitoredRegion { + id: number; + name: string; + datastream_id: number; + well_status: WellStatus; + outside_recorder?: boolean; +} diff --git a/frontend/src/interfaces/MonitoredWell.ts b/frontend/src/interfaces/MonitoredWell.ts new file mode 100644 index 00000000..4ffabb3f --- /dev/null +++ b/frontend/src/interfaces/MonitoredWell.ts @@ -0,0 +1,11 @@ +import type { WellStatus } from "./WellStatus"; + +export interface MonitoredWell { + id: number; + name: string; + ra_number: string; + datastream_id: number; + well_status: WellStatus; + outside_recorder?: boolean; + chloride_group_id?: number; +} diff --git a/frontend/src/interfaces/NewRegionMeasurement.ts b/frontend/src/interfaces/NewRegionMeasurement.ts new file mode 100644 index 00000000..5bccb7c8 --- /dev/null +++ b/frontend/src/interfaces/NewRegionMeasurement.ts @@ -0,0 +1,7 @@ +export interface NewRegionMeasurement { + region_id: number; + timestamp: string; + value?: number | null; + submitting_user_id: number; + well_id: number; +} diff --git a/frontend/src/interfaces/NewUser.ts b/frontend/src/interfaces/NewUser.ts new file mode 100644 index 00000000..398b1c23 --- /dev/null +++ b/frontend/src/interfaces/NewUser.ts @@ -0,0 +1,11 @@ +import type { scope_string } from "./primitives"; + +export interface NewUser { + id: number; + username: string; + full_name: string; + email: scope_string; + disabled: boolean; + user_role_id: number; + password: string; +} diff --git a/frontend/src/interfaces/NewWellMeasurement.ts b/frontend/src/interfaces/NewWellMeasurement.ts new file mode 100644 index 00000000..7d7abf0e --- /dev/null +++ b/frontend/src/interfaces/NewWellMeasurement.ts @@ -0,0 +1,7 @@ +// The object that gets sent to the backend to add a new measurement +export interface NewWellMeasurement { + well_id: number; + timestamp: string; + value: number; + submitting_user_id: number; +} diff --git a/frontend/src/interfaces/NewWorkOrder.ts b/frontend/src/interfaces/NewWorkOrder.ts new file mode 100644 index 00000000..12966c66 --- /dev/null +++ b/frontend/src/interfaces/NewWorkOrder.ts @@ -0,0 +1,7 @@ +//Just the bare minimum to create a new work order +//No work order ID since it is generated by the backend +export interface NewWorkOrder { + date_created: Date; //This should be on the frontend to ensure it doesn't reflect server time + meter_id: number; + title: string; +} diff --git a/frontend/src/interfaces/NoteTypeLU.ts b/frontend/src/interfaces/NoteTypeLU.ts new file mode 100644 index 00000000..2332536e --- /dev/null +++ b/frontend/src/interfaces/NoteTypeLU.ts @@ -0,0 +1,7 @@ +export interface NoteTypeLU { + id: number; + note: string; + details?: string; + slug?: string; + commonly_used: boolean; +} diff --git a/frontend/src/interfaces/ObservationForm.ts b/frontend/src/interfaces/ObservationForm.ts new file mode 100644 index 00000000..f809814c --- /dev/null +++ b/frontend/src/interfaces/ObservationForm.ts @@ -0,0 +1,8 @@ +import type { Dayjs } from "dayjs"; + +export interface ObservationForm { + time: Dayjs; + reading: "" | number; + property_type_id: "" | number; + unit_id: "" | number; +} diff --git a/frontend/src/interfaces/ObservedPropertyTypeLU.ts b/frontend/src/interfaces/ObservedPropertyTypeLU.ts new file mode 100644 index 00000000..746a1539 --- /dev/null +++ b/frontend/src/interfaces/ObservedPropertyTypeLU.ts @@ -0,0 +1,10 @@ +import type { Unit } from "./Unit"; + +export interface ObservedPropertyTypeLU { + id: number; + name: string; + description: string; + context: string; + + units?: Unit[]; +} diff --git a/frontend/src/interfaces/Organization.ts b/frontend/src/interfaces/Organization.ts new file mode 100644 index 00000000..332471ce --- /dev/null +++ b/frontend/src/interfaces/Organization.ts @@ -0,0 +1,3 @@ +export interface Organization { + organization_name: string; +} diff --git a/frontend/src/interfaces/Page.ts b/frontend/src/interfaces/Page.ts new file mode 100644 index 00000000..f1b1946e --- /dev/null +++ b/frontend/src/interfaces/Page.ts @@ -0,0 +1,6 @@ +export interface Page { + items: T[]; + total: number; + limit: number; + offset: number; +} diff --git a/frontend/src/interfaces/Part.ts b/frontend/src/interfaces/Part.ts new file mode 100644 index 00000000..f2251520 --- /dev/null +++ b/frontend/src/interfaces/Part.ts @@ -0,0 +1,18 @@ +import type { MeterTypeLU } from "./MeterTypeLU"; +import type { PartTypeLU } from "./PartTypeLU"; + +export interface Part { + id: number; + part_number: string; + part_type_id: number; + vendor?: string; + note?: string; + description?: string; + initial_count?: number; + current_count?: number; + in_use: boolean; + commonly_used: boolean; + + part_type?: PartTypeLU; + meter_types?: MeterTypeLU[]; +} diff --git a/frontend/src/interfaces/PartAssociation.ts b/frontend/src/interfaces/PartAssociation.ts new file mode 100644 index 00000000..e81db279 --- /dev/null +++ b/frontend/src/interfaces/PartAssociation.ts @@ -0,0 +1,10 @@ +import type { int } from "./primitives"; +import type { Part } from "./Part"; + +export interface PartAssociation { + id: int; + meter_type_id: int; + part_id: int; + commonly_used: boolean; + part?: Part; +} diff --git a/frontend/src/interfaces/PartTypeLU.ts b/frontend/src/interfaces/PartTypeLU.ts new file mode 100644 index 00000000..b92a9cf2 --- /dev/null +++ b/frontend/src/interfaces/PartTypeLU.ts @@ -0,0 +1,7 @@ +import type { int } from "./primitives"; + +export interface PartTypeLU { + id: int; + name: string; + description?: string; +} diff --git a/frontend/src/interfaces/PatchActivityForm.ts b/frontend/src/interfaces/PatchActivityForm.ts new file mode 100644 index 00000000..8e89456e --- /dev/null +++ b/frontend/src/interfaces/PatchActivityForm.ts @@ -0,0 +1,29 @@ +import type { Dayjs } from "dayjs"; +import type { int } from "./primitives"; +import type { ActivityTypeLU } from "./ActivityTypeLU"; +import type { NoteTypeLU } from "./NoteTypeLU"; +import type { Part } from "./Part"; +import type { ServiceTypeLU } from "./ServiceTypeLU"; +import type { User } from "./User"; +import type { Well } from "./Well"; + +//This is designed to match the HistoryDetails form rather than the patch meter API +export interface PatchActivityForm { + activity_id: int; + meter_id: int; + activity_date: Dayjs; + activity_start_time: Dayjs; + activity_end_time: Dayjs; + activity_type: ActivityTypeLU; + submitting_user: User; + description: string; + + well: Well | null; + water_users?: string; + + notes?: NoteTypeLU[]; + services?: ServiceTypeLU[]; + parts_used?: Part[]; + + ose_share: boolean; +} diff --git a/frontend/src/interfaces/PatchActivitySubmit.ts b/frontend/src/interfaces/PatchActivitySubmit.ts new file mode 100644 index 00000000..211019bc --- /dev/null +++ b/frontend/src/interfaces/PatchActivitySubmit.ts @@ -0,0 +1,19 @@ +import type { int } from "./primitives"; + +//This interface is designed to match the backend API patch endpoint +export interface PatchActivitySubmit { + activity_id: int; + timestamp_start: string; + timestamp_end: string; + description: string; + submitting_user_id: int; + meter_id: int; + activity_type_id: int; + location_id: int | null; + ose_share: boolean; + water_users: string; + + note_ids: int[] | null; + service_ids: int[] | null; + part_ids: int[] | null; +} diff --git a/frontend/src/interfaces/PatchObservationForm.ts b/frontend/src/interfaces/PatchObservationForm.ts new file mode 100644 index 00000000..dc5ada10 --- /dev/null +++ b/frontend/src/interfaces/PatchObservationForm.ts @@ -0,0 +1,21 @@ +import type { Dayjs } from "dayjs"; +import type { int } from "./primitives"; +import type { ObservedPropertyTypeLU } from "./ObservedPropertyTypeLU"; +import type { Unit } from "./Unit"; +import type { User } from "./User"; +import type { Well } from "./Well"; + +//Designed for the HistoryDetails component, not the patch endpoint +export interface PatchObservationForm { + observation_id: int; + submitting_user: User; + well: Well | null; + observation_date: Dayjs; + observation_time: Dayjs; + property_type: ObservedPropertyTypeLU; + unit: Unit; + value: number; + ose_share: boolean; + notes?: string; + meter_id: int; +} diff --git a/frontend/src/interfaces/PatchObservationSubmit.ts b/frontend/src/interfaces/PatchObservationSubmit.ts new file mode 100644 index 00000000..f2b24d7f --- /dev/null +++ b/frontend/src/interfaces/PatchObservationSubmit.ts @@ -0,0 +1,15 @@ +import type { int } from "./primitives"; + +export interface PatchObservationSubmit { + //Matches the backend API patch endpoint + observation_id: int; + timestamp: string; + value: number; + notes: string | null; + submitting_user_id: int; + meter_id: int; + observed_property_type_id: int; + unit_id: int; + location_id: int | null; + ose_share: boolean; +} diff --git a/frontend/src/interfaces/PatchRegionMeasurement.ts b/frontend/src/interfaces/PatchRegionMeasurement.ts new file mode 100644 index 00000000..9f9c8f68 --- /dev/null +++ b/frontend/src/interfaces/PatchRegionMeasurement.ts @@ -0,0 +1,9 @@ +import type { Dayjs } from "dayjs"; + +export interface PatchRegionMeasurement { + levelmeasurement_id: number; + submitting_user_id: number; + well_id: number; + timestamp: Dayjs; + value?: number | null; +} diff --git a/frontend/src/interfaces/PatchWellMeasurement.ts b/frontend/src/interfaces/PatchWellMeasurement.ts new file mode 100644 index 00000000..c567ec45 --- /dev/null +++ b/frontend/src/interfaces/PatchWellMeasurement.ts @@ -0,0 +1,8 @@ +import type { Dayjs } from "dayjs"; + +export interface PatchWellMeasurement { + levelmeasurement_id: number; + submitting_user_id: number; + timestamp: Dayjs; + value: number; +} diff --git a/frontend/src/interfaces/PatchWorkOrder.ts b/frontend/src/interfaces/PatchWorkOrder.ts new file mode 100644 index 00000000..92b6c959 --- /dev/null +++ b/frontend/src/interfaces/PatchWorkOrder.ts @@ -0,0 +1,9 @@ +// This is designed to match the backend API patch endpoint and is limited to the fields that can be updated +export interface PatchWorkOrder { + work_order_id: number; + title?: string; + description?: string; + status?: string; + notes?: string; + assigned_user_id?: number; +} diff --git a/frontend/src/interfaces/RegionMeasurementDTO.ts b/frontend/src/interfaces/RegionMeasurementDTO.ts new file mode 100644 index 00000000..5ddafc4c --- /dev/null +++ b/frontend/src/interfaces/RegionMeasurementDTO.ts @@ -0,0 +1,7 @@ +export interface RegionMeasurementDTO { + id: number; + timestamp: Date; + value: number; + submitting_user: { id: number; full_name: string }; + well: { id: number; ra_number: string }; +} diff --git a/frontend/src/interfaces/ST2Measurement.ts b/frontend/src/interfaces/ST2Measurement.ts new file mode 100644 index 00000000..9e020e0c --- /dev/null +++ b/frontend/src/interfaces/ST2Measurement.ts @@ -0,0 +1,6 @@ +// Single value from a NM ST2 endpoint, many other fields are returned, these are the only ones used at the moment +export interface ST2Measurement { + result: number; + resultTime: Date; + phenomenonTime: Date; +} diff --git a/frontend/src/interfaces/ST2Response.ts b/frontend/src/interfaces/ST2Response.ts new file mode 100644 index 00000000..689b51c9 --- /dev/null +++ b/frontend/src/interfaces/ST2Response.ts @@ -0,0 +1,5 @@ +// Whole response returned from a NM ST2 endpoint +export interface ST2Response { + "@iot.nextLink": string; + value: []; +} diff --git a/frontend/src/interfaces/ST2WaterLevelQueryParams.ts b/frontend/src/interfaces/ST2WaterLevelQueryParams.ts new file mode 100644 index 00000000..11c9f15b --- /dev/null +++ b/frontend/src/interfaces/ST2WaterLevelQueryParams.ts @@ -0,0 +1,5 @@ +export interface ST2WaterLevelQueryParams { + $filter: string; + $orderby: string; + datastreamID: number | undefined; +} diff --git a/frontend/src/interfaces/SecurityScope.ts b/frontend/src/interfaces/SecurityScope.ts new file mode 100644 index 00000000..cf94b789 --- /dev/null +++ b/frontend/src/interfaces/SecurityScope.ts @@ -0,0 +1,5 @@ +export interface SecurityScope { + id: number; + scope_string: string; + description: string; +} diff --git a/frontend/src/interfaces/ServiceTypeLU.ts b/frontend/src/interfaces/ServiceTypeLU.ts new file mode 100644 index 00000000..c1711eb5 --- /dev/null +++ b/frontend/src/interfaces/ServiceTypeLU.ts @@ -0,0 +1,5 @@ +export interface ServiceTypeLU { + id: number; + service_name: string; + description?: string; +} diff --git a/frontend/src/interfaces/SubmitWellCreate.ts b/frontend/src/interfaces/SubmitWellCreate.ts new file mode 100644 index 00000000..f34f8bf5 --- /dev/null +++ b/frontend/src/interfaces/SubmitWellCreate.ts @@ -0,0 +1,22 @@ +import type { float } from "./primitives"; +import type { WaterSource } from "./WaterSource"; + +export interface SubmitWellCreate { + name: string; + ra_number: string; + owners: string; + osetag: string; + water_source: WaterSource | null; + chloride_group_id: number | null; + + use_type: { + id: number; + }; + + location: { + name: string; + trss: string; + longitude: float; + latitude: float; + }; +} diff --git a/frontend/src/interfaces/Unit.ts b/frontend/src/interfaces/Unit.ts new file mode 100644 index 00000000..376a7ed6 --- /dev/null +++ b/frontend/src/interfaces/Unit.ts @@ -0,0 +1,6 @@ +export interface Unit { + id: number; + name: string; + name_short: string; + description: string; +} diff --git a/frontend/src/interfaces/UpdatedUserPassword.ts b/frontend/src/interfaces/UpdatedUserPassword.ts new file mode 100644 index 00000000..2579265e --- /dev/null +++ b/frontend/src/interfaces/UpdatedUserPassword.ts @@ -0,0 +1,4 @@ +export interface UpdatedUserPassword { + user_id: number; + new_password: string; +} diff --git a/frontend/src/interfaces/User.ts b/frontend/src/interfaces/User.ts new file mode 100644 index 00000000..1e3e600f --- /dev/null +++ b/frontend/src/interfaces/User.ts @@ -0,0 +1,14 @@ +import type { scope_string } from "./primitives"; +import type { UserRole } from "./UserRole"; + +export interface User { + id: number; + username?: string; + full_name: string; + display_name?: string; + email?: scope_string; + disabled: boolean; + user_role_id?: number; + user_role?: UserRole; + password?: string; +} diff --git a/frontend/src/interfaces/UserRole.ts b/frontend/src/interfaces/UserRole.ts new file mode 100644 index 00000000..eedfae03 --- /dev/null +++ b/frontend/src/interfaces/UserRole.ts @@ -0,0 +1,7 @@ +import type { SecurityScope } from "./SecurityScope"; + +export interface UserRole { + id: number; + name: string; + security_scopes: SecurityScope[]; +} diff --git a/frontend/src/interfaces/WaterLevelQueryParams.ts b/frontend/src/interfaces/WaterLevelQueryParams.ts new file mode 100644 index 00000000..dfa8853d --- /dev/null +++ b/frontend/src/interfaces/WaterLevelQueryParams.ts @@ -0,0 +1,3 @@ +export interface WaterLevelQueryParams { + well_id: number | undefined; +} diff --git a/frontend/src/interfaces/WaterSource.ts b/frontend/src/interfaces/WaterSource.ts new file mode 100644 index 00000000..c31645ec --- /dev/null +++ b/frontend/src/interfaces/WaterSource.ts @@ -0,0 +1,5 @@ +export interface WaterSource { + id: number; + name: string; + description: string; +} diff --git a/frontend/src/interfaces/Well.ts b/frontend/src/interfaces/Well.ts new file mode 100644 index 00000000..f2f29e42 --- /dev/null +++ b/frontend/src/interfaces/Well.ts @@ -0,0 +1,21 @@ +import type { int } from "./primitives"; +import type { BaseWell } from "./BaseWell"; +import type { Location } from "./Location"; +import type { WaterSource } from "./WaterSource"; +import type { WellStatus } from "./WellStatus"; +import type { WellUseLU } from "./WellUseLU"; + +export interface Well extends BaseWell { + use_type: WellUseLU | null; + water_source: WaterSource | null; + location: Location | null; + well_status: WellStatus | null; + + meters: [ + { + id: int; + serial_number: string; + water_users?: string; + } + ]; +} diff --git a/frontend/src/interfaces/WellDetailsQueryParams.ts b/frontend/src/interfaces/WellDetailsQueryParams.ts new file mode 100644 index 00000000..b1cc5caf --- /dev/null +++ b/frontend/src/interfaces/WellDetailsQueryParams.ts @@ -0,0 +1,3 @@ +export interface WellDetailsQueryParams { + well_id: number | undefined; +} diff --git a/frontend/src/interfaces/WellListQueryParams.ts b/frontend/src/interfaces/WellListQueryParams.ts new file mode 100644 index 00000000..433c95ba --- /dev/null +++ b/frontend/src/interfaces/WellListQueryParams.ts @@ -0,0 +1,10 @@ +import type { SortDirection } from "@/enums"; + +export interface WellListQueryParams { + search_string?: string; + // sort_by?: WellSortByField + sort_direction?: SortDirection; + limit?: number; + offset?: number; + exclude_inactive?: boolean; +} diff --git a/frontend/src/interfaces/WellMeasurementDTO.ts b/frontend/src/interfaces/WellMeasurementDTO.ts new file mode 100644 index 00000000..10978769 --- /dev/null +++ b/frontend/src/interfaces/WellMeasurementDTO.ts @@ -0,0 +1,8 @@ +// Single manual measurement from a certain well +export interface WellMeasurementDTO { + id: number; + timestamp: Date; + value: number; + submitting_user: { full_name: string }; + well: { id: number; ra_number: string }; +} diff --git a/frontend/src/interfaces/WellMergeParams.ts b/frontend/src/interfaces/WellMergeParams.ts new file mode 100644 index 00000000..d294bc80 --- /dev/null +++ b/frontend/src/interfaces/WellMergeParams.ts @@ -0,0 +1,4 @@ +export interface WellMergeParams { + merge_well: string; + target_well: string; +} diff --git a/frontend/src/interfaces/WellStatus.ts b/frontend/src/interfaces/WellStatus.ts new file mode 100644 index 00000000..18453d46 --- /dev/null +++ b/frontend/src/interfaces/WellStatus.ts @@ -0,0 +1,5 @@ +export interface WellStatus { + id: number; + status: string; + description: string; +} diff --git a/frontend/src/interfaces/WellUpdate.ts b/frontend/src/interfaces/WellUpdate.ts new file mode 100644 index 00000000..254e08f8 --- /dev/null +++ b/frontend/src/interfaces/WellUpdate.ts @@ -0,0 +1,12 @@ +import type { BaseWell } from "./BaseWell"; +import type { Location } from "./Location"; +import type { WaterSource } from "./WaterSource"; +import type { WellStatus } from "./WellStatus"; +import type { WellUseLU } from "./WellUseLU"; + +export interface WellUpdate extends BaseWell { + use_type: WellUseLU; + water_source: WaterSource; + location: Location; + well_status: WellStatus; +} diff --git a/frontend/src/interfaces/WellUseLU.ts b/frontend/src/interfaces/WellUseLU.ts new file mode 100644 index 00000000..bbf0abef --- /dev/null +++ b/frontend/src/interfaces/WellUseLU.ts @@ -0,0 +1,6 @@ +export interface WellUseLU { + id: number; + use_type: string; + code: string; + description: string; +} diff --git a/frontend/src/interfaces/WorkOrder.ts b/frontend/src/interfaces/WorkOrder.ts new file mode 100644 index 00000000..1fe3f1a1 --- /dev/null +++ b/frontend/src/interfaces/WorkOrder.ts @@ -0,0 +1,13 @@ +export interface WorkOrder { + work_order_id: number; + date_created: Date; + creator?: String; + meter_serial: String; + title: String; + description: String; + status: String; + notes?: String; + assigned_user_id?: number; + assigned_user?: String; + associated_activities?: number[]; +} diff --git a/frontend/src/interfaces/index.ts b/frontend/src/interfaces/index.ts index 5262ecb7..21fce508 100644 --- a/frontend/src/interfaces/index.ts +++ b/frontend/src/interfaces/index.ts @@ -1,8 +1,74 @@ +export * from "./ActivityForm"; +export * from "./ActivityFormControl"; +export * from "./ActivityTypeLU"; export * from "./BackupRow"; +export * from "./BaseWell"; +export * from "./CreateUser"; export * from "./DeviceAttributes"; export * from "./DevicePayload"; export * from "./IncreaseQuantityPayload"; +export * from "./LandOwner"; +export * from "./Location"; export * from "./Measurement"; +export * from "./Meter"; +export * from "./MeterActivity"; +export * from "./MeterDetails"; +export * from "./MeterDetailsQueryParams"; +export * from "./MeterHistoryDTO"; +export * from "./MeterListDTO"; +export * from "./MeterListQuery"; +export * from "./MeterListQueryParams"; +export * from "./MeterListSortBy"; +export * from "./MeterMapDTO"; +export * from "./MeterPartParams"; +export * from "./MeterRegister"; +export * from "./MeterStatus"; +export * from "./MeterType"; +export * from "./MeterTypeLU"; +export * from "./MonitoredRegion"; +export * from "./MonitoredWell"; +export * from "./NewRegionMeasurement"; +export * from "./NewUser"; +export * from "./NewWellMeasurement"; +export * from "./NewWorkOrder"; +export * from "./NoteTypeLU"; +export * from "./ObservationForm"; +export * from "./ObservedPropertyTypeLU"; +export * from "./Organization"; +export * from "./Page"; +export * from "./Part"; +export * from "./PartAssociation"; +export * from "./PartTypeLU"; +export * from "./PatchActivityForm"; +export * from "./PatchActivitySubmit"; +export * from "./PatchObservationForm"; +export * from "./PatchObservationSubmit"; +export * from "./PatchRegionMeasurement"; +export * from "./PatchWellMeasurement"; +export * from "./PatchWorkOrder"; +export * from "./RegionMeasurementDTO"; export * from "./ReportAveragesResponse"; +export * from "./ST2Measurement"; +export * from "./ST2Response"; +export * from "./ST2WaterLevelQueryParams"; +export * from "./SecurityScope"; export * from "./SensorAttributes"; export * from "./SensorData"; +export * from "./ServiceTypeLU"; +export * from "./SubmitWellCreate"; +export * from "./Unit"; +export * from "./UpdatedUserPassword"; +export * from "./User"; +export * from "./UserRole"; +export * from "./WaterLevelQueryParams"; +export * from "./WaterSource"; +export * from "./Well"; +export * from "./WellDetailsQueryParams"; +export * from "./WellListQueryParams"; +export * from "./WellMeasurementDTO"; +export * from "./WellMergeParams"; +export * from "./WellStatus"; +export * from "./WellUpdate"; +export * from "./WellUseLU"; +export * from "./WorkOrder"; +export * from "./primitives"; diff --git a/frontend/src/interfaces/primitives.ts b/frontend/src/interfaces/primitives.ts new file mode 100644 index 00000000..badf9893 --- /dev/null +++ b/frontend/src/interfaces/primitives.ts @@ -0,0 +1,3 @@ +export type int = number; +export type float = number; +export type scope_string = string; diff --git a/frontend/src/service/ApiServiceNew.ts b/frontend/src/service/ApiServiceNew.ts index de897111..fc6b0f43 100644 --- a/frontend/src/service/ApiServiceNew.ts +++ b/frontend/src/service/ApiServiceNew.ts @@ -51,7 +51,7 @@ import { WaterSource, WellStatus, } from "@/interfaces"; -import { IncreaseQuantityPayload } from "@/interfaces/IncreaseQuantityPayload"; +import { IncreaseQuantityPayload } from "@/interfaces"; import { WorkOrderStatus } from "@/enums"; import { API_URL } from "@/config"; import { useNavigate } from "react-router-dom"; @@ -1274,9 +1274,14 @@ export function useCreatePart(onSuccess: Function) { return useMutation({ mutationFn: async (part: Part) => { try { - //Due to the way the form gets generated for a new part, I need to populate part_type_id manually here + if (!part.part_type?.id) { + throw new Error("part_type_id is required but missing"); + } + + // Due to the way the form gets generated for a new part, + // I need to populate part_type_id manually here part.part_type_id = part.part_type?.id; - console.log(part); + const response = await POSTFetch(route, part, authHeader()); if (!response.ok) { diff --git a/frontend/src/utils/AssertDefined.ts b/frontend/src/utils/AssertDefined.ts new file mode 100644 index 00000000..ad9efe0f --- /dev/null +++ b/frontend/src/utils/AssertDefined.ts @@ -0,0 +1,8 @@ +export function assertDefined( + value: T, + message = "Value is required", +): asserts value is NonNullable { + if (value === undefined || value === null) { + throw new Error(message); + } +} diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index 61944980..e2601704 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -1,3 +1,4 @@ +export * from "./AssertDefined"; export * from "./DateUtils"; export * from "./DataStreamUtils"; export * from "./EmptyToNull"; diff --git a/frontend/src/views/Meters/MeterDetailsFields.tsx b/frontend/src/views/Meters/MeterDetailsFields.tsx index ee18d103..508d45be 100644 --- a/frontend/src/views/Meters/MeterDetailsFields.tsx +++ b/frontend/src/views/Meters/MeterDetailsFields.tsx @@ -13,12 +13,6 @@ import { TableHead, TableRow, } from "@mui/material"; -import { SecurityScope, Meter } from "@/interfaces"; -import { - useCreateMeter, - useGetMeter, - useUpdateMeter, -} from "../../service/ApiServiceNew"; import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { @@ -29,6 +23,8 @@ import { ControlledMeterStatusTypeSelect, ControlledMeterRegisterSelect, } from "@/components"; +import { SecurityScope, Meter } from "@/interfaces"; +import { useCreateMeter, useGetMeter, useUpdateMeter } from "@/service"; import { formatLatLong } from "@/conversions"; const MeterResolverSchema: Yup.ObjectSchema = Yup.object().shape({ @@ -217,11 +213,12 @@ export const MeterDetailsFields = ({ : watch("well")?.location?.trss} - {watch("well")?.location?.latitude == null + {!watch("well")?.location?.latitude || + !watch("well")?.location?.longitude ? "--" : formatLatLong( - watch("well")?.location?.latitude, - watch("well")?.location?.longitude, + watch("well")?.location?.latitude ?? 0, + watch("well")?.location?.longitude ?? 0, )} diff --git a/frontend/src/views/Meters/MeterHistory/MeterHistory.tsx b/frontend/src/views/Meters/MeterHistory/MeterHistory.tsx index 245842e5..9d6a77a6 100644 --- a/frontend/src/views/Meters/MeterHistory/MeterHistory.tsx +++ b/frontend/src/views/Meters/MeterHistory/MeterHistory.tsx @@ -1,9 +1,6 @@ import { useState, useEffect, useMemo } from "react"; import { Box, Card, CardContent, Grid } from "@mui/material"; -import { MeterHistoryTable } from "./MeterHistoryTable"; -import { SelectedActivityDetails } from "./SelectedActivityDetails"; -import { SelectedObservationDetails } from "./SelectedObservationDetails"; -import { SelectedBlankCard } from "./SelectedBlankCard"; +import { ImageOutlined } from "@mui/icons-material"; import { useLocation, useSearchParams } from "react-router-dom"; import { useGetMeterHistory } from "@/service"; import { @@ -12,14 +9,20 @@ import { PatchObservationForm, } from "@/interfaces"; import { MeterHistoryType } from "@/enums"; +import { CustomCardHeader, ImageDialog, ImagePreviewGrid } from "@/components"; + import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; -import { CustomCardHeader, ImageDialog, ImagePreviewGrid } from "@/components"; -import { ImageOutlined } from "@mui/icons-material"; dayjs.extend(utc); dayjs.extend(timezone); +import { MeterHistoryTable } from "./MeterHistoryTable"; +import { SelectedActivityDetails } from "./SelectedActivityDetails"; +import { SelectedObservationDetails } from "./SelectedObservationDetails"; +import { SelectedBlankCard } from "./SelectedBlankCard"; +import { assertDefined } from "@/utils"; + export const MeterHistory = ({ selectedMeterID, }: { @@ -83,6 +86,11 @@ export const MeterHistory = ({ function convertHistoryActivity( historyItem: MeterHistoryDTO, ): PatchActivityForm { + assertDefined( + selectedMeterID, + "No meter selected (selectedMeterID is undefined)", + ); + let activity_details: PatchActivityForm = { activity_id: historyItem.history_item.id, meter_id: selectedMeterID, diff --git a/frontend/src/views/Meters/MeterSelection/MeterSelectionTable.tsx b/frontend/src/views/Meters/MeterSelection/MeterSelectionTable.tsx index 2b5b6b5e..250b2a9c 100644 --- a/frontend/src/views/Meters/MeterSelection/MeterSelectionTable.tsx +++ b/frontend/src/views/Meters/MeterSelection/MeterSelectionTable.tsx @@ -27,8 +27,8 @@ export const MeterSelectionTable = ({ useState({ search_string: "", filter_by_status: [MeterStatusNames.Installed], - sort_by: "serial_number", - sort_direction: "asc", + sort_by: MeterSortByField.SerialNumber, + sort_direction: SortDirection.Ascending, limit: 25, offset: 0, }); @@ -80,12 +80,11 @@ export const MeterSelectionTable = ({ const newParams = { search_string: meterSearchQueryDebounced, filter_by_status: meterStatusFilter, - sort_by: gridSortModel - ? gridSortModel[0]?.field - : MeterSortByField.SerialNumber, - sort_direction: gridSortModel - ? gridSortModel[0]?.sort - : SortDirection.Ascending, + sort_by: + (gridSortModel?.[0]?.field as MeterSortByField) ?? + MeterSortByField.SerialNumber, + sort_direction: + (gridSortModel?.[0]?.sort as SortDirection) ?? SortDirection.Ascending, limit: paginationModel.pageSize, offset: paginationModel.page * paginationModel.pageSize, }; diff --git a/frontend/src/views/Meters/MetersView.tsx b/frontend/src/views/Meters/MetersView.tsx index f8245ba3..03f2db6b 100644 --- a/frontend/src/views/Meters/MetersView.tsx +++ b/frontend/src/views/Meters/MetersView.tsx @@ -1,12 +1,12 @@ -import { useEffect } from "react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; +import { Grid } from "@mui/material"; + import { MeterSelection } from "./MeterSelection/MeterSelection"; import { MeterDetailsFields } from "./MeterDetailsFields"; import { MeterHistory } from "./MeterHistory/MeterHistory"; -import { Grid } from "@mui/material"; -import { BackgroundBox } from "../../components/BackgroundBox"; +import { BackgroundBox } from "@/components"; // Main view for the Meters page // Can pass state to this view to pre-select a meter and meter history using React Router useLocation diff --git a/frontend/src/views/Reports/Chlorides/index.tsx b/frontend/src/views/Reports/Chlorides/index.tsx index 6bc80848..8c1aa851 100644 --- a/frontend/src/views/Reports/Chlorides/index.tsx +++ b/frontend/src/views/Reports/Chlorides/index.tsx @@ -42,7 +42,7 @@ import { } from "@/components"; import { RedMapIcon, BlackMapIcon } from "@/components/MapIcons"; import { useFetchWithAuth } from "@/hooks"; -import { useGetWellLocations } from "@/service/ApiServiceNew"; +import { useGetWellLocations } from "@/service"; import { Well } from "@/interfaces"; import { WellStatus } from "@/enums"; @@ -363,8 +363,8 @@ export const ChloridesReportView = () => { setSelectedWell(well), diff --git a/frontend/src/views/WellManagement/WellSelectionTable.tsx b/frontend/src/views/WellManagement/WellSelectionTable.tsx index 0a2e2848..29de6ada 100644 --- a/frontend/src/views/WellManagement/WellSelectionTable.tsx +++ b/frontend/src/views/WellManagement/WellSelectionTable.tsx @@ -111,9 +111,9 @@ export default function WellSelectionTable({ const newParams = { search_string: wellSearchQueryDebounced, sort_by: gridSortModel?.at(0)?.field ?? WellSortByField.Name, - sort_direction: gridSortModel - ? gridSortModel[0]?.sort - : SortDirection.Ascending, + sort_direction: + (gridSortModel?.at(0)?.sort as SortDirection) ?? + SortDirection.Ascending, limit: paginationModel.pageSize, offset: paginationModel.page * paginationModel.pageSize, }; From fb6dcd93b340879d1a8093048e2b71c14b8589ee Mon Sep 17 00:00:00 2001 From: Tyler Adam Martinez Date: Thu, 12 Feb 2026 10:50:37 -0600 Subject: [PATCH 6/7] refactor(api): rm unused files --- api/__init__.py | 18 --- api/backupdb/restore.sh | 1 - api/config.py | 1 - api/dbsetup.py | 236 ---------------------------------- api/models/main_models.py | 20 --- api/route_util.py | 18 --- api/routes/__init__.py | 1 - api/session.py | 26 ---- api/tests/__init__.py | 17 --- api/tests/test_main.py | 263 -------------------------------------- api/winenv.bat | 11 -- api/winenv.ps1 | 8 -- api/xls_persistence.py | 47 ------- 13 files changed, 667 deletions(-) delete mode 100644 api/backupdb/restore.sh delete mode 100644 api/dbsetup.py delete mode 100644 api/tests/__init__.py delete mode 100644 api/tests/test_main.py delete mode 100644 api/winenv.bat delete mode 100644 api/winenv.ps1 delete mode 100644 api/xls_persistence.py diff --git a/api/__init__.py b/api/__init__.py index 72f778cc..e69de29b 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -1,18 +0,0 @@ -# =============================================================================== -# Copyright 2022 ross -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# =============================================================================== - - -# ============= EOF ============================================= diff --git a/api/backupdb/restore.sh b/api/backupdb/restore.sh deleted file mode 100644 index edfb5c15..00000000 --- a/api/backupdb/restore.sh +++ /dev/null @@ -1 +0,0 @@ -pg_restore -d appdb_local appdb.sql \ No newline at end of file diff --git a/api/config.py b/api/config.py index 879ce00b..a5011828 100644 --- a/api/config.py +++ b/api/config.py @@ -30,4 +30,3 @@ class Settings: settings = Settings() -# ============= EOF ============================================= diff --git a/api/dbsetup.py b/api/dbsetup.py deleted file mode 100644 index f9418045..00000000 --- a/api/dbsetup.py +++ /dev/null @@ -1,236 +0,0 @@ -# # =============================================================================== -# This script builds the database from scratch and so should only be run as needed -# # =============================================================================== - -import os -import api.models -from api.security import get_password_hash -from sqlalchemy import create_engine -from sqlalchemy.sql import text -from api.session import SessionLocal -from .config import settings - -# Set up a connection -SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL -engine = create_engine(SQLALCHEMY_DATABASE_URL) - -print("Setting up the database") -api.models.main_models.Base.metadata.create_all(engine) - -# Add initial users, roles, and scopes -db = SessionLocal() - -SecurityScopes = api.models.security_models.SecurityScopes -UserRoles = api.models.security_models.UserRoles -Users = api.models.security_models.Users - -admin_scope = SecurityScopes(scope_string="admin", description="Admin-specific scope.") -meter_write_scope = SecurityScopes( - scope_string="meter:write", description="Write meters" -) -activities_write_scope = SecurityScopes( - scope_string="activities:write", description="Write activities" -) -well_measurements_write_scope = SecurityScopes( - scope_string="well_measurement:write", - description="Write well measurements, i.e. Water Levels and Chlorides", -) -reports_run_scope = SecurityScopes( - scope_string="reports:run", description="Run reports" -) -read_scope = SecurityScopes(scope_string="read", description="Read all data.") -ose_scope = SecurityScopes(scope_string="ose", description="Scope given to the OSE") - -technician_role = UserRoles( - name="Technician", - security_scopes=[ - read_scope, - meter_write_scope, - activities_write_scope, - well_measurements_write_scope, - reports_run_scope, - ], -) -admin_role = UserRoles( - name="Admin", - security_scopes=[ - read_scope, - meter_write_scope, - activities_write_scope, - well_measurements_write_scope, - reports_run_scope, - ose_scope, - admin_scope, - ], -) -ose_role = UserRoles( - name="OSE", - security_scopes=[read_scope, ose_scope], -) - -admin_user = Users( - full_name="NMWDI Admin", - username="nmwdi_admin", - email="johndoe@example.com", - hashed_password=get_password_hash("testthisapp"), - user_role=technician_role, -) - -db.add_all( - [ - admin_scope, - meter_write_scope, - activities_write_scope, - well_measurements_write_scope, - reports_run_scope, - read_scope, - ose_scope, - technician_role, - admin_role, - ose_role, - admin_user, - ] -) - -db.commit() -db.close() - - -# Load seed data from CSV -# Follows - https://stackoverflow.com/questions/31394998/using-sqlalchemy-to-load-csv-file-into-a-database -# Get the psycopg2 connector - enables running of lower level functions -conn = engine.raw_connection() -cursor = conn.cursor() - -with open("../PVACDdb_migration/csv_data/tables/metertypes.csv", "r") as f: - qry = 'COPY "MeterTypeLU"(id,brand,series,model_number,size,description) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/NoteTypeLU.csv", "r") as f: - qry = 'COPY "NoteTypeLU"(id,note,details,slug) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/ServiceTypeLU.csv", "r") as f: - qry = 'COPY "ServiceTypeLU"(id,service_name,description) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/landowners.csv", "r") as f: - qry = 'COPY "LandOwners"(organization,address,city,state,zip,phone,mobile,note,id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/meterstatus.csv", "r") as f: - qry = 'COPY "MeterStatusLU"(id,status_name,description) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/observedproperties.csv", "r") as f: - qry = 'COPY "ObservedPropertyTypeLU"(id,name,description,context) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/units.csv", "r") as f: - qry = 'COPY "Units"(id,name,name_short,description) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/propertyunits.csv", "r") as f: - qry = 'COPY "PropertyUnits"(property_id,unit_id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/locationtypeLU.csv", "r") as f: - qry = 'COPY "LocationTypeLU"(id,type_name,description) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/locations.csv", "r") as f: - qry = 'COPY "Locations"(id,name,type_id,latitude,longitude,trss) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/welluseLU.csv", "r") as f: - qry = 'COPY "WellUseLU"(id,use_type,code,description) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/wells.csv", "r") as f: - qry = 'COPY "Wells"(id,name,use_type_id,location_id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/meters.csv", "r") as f: - qry = 'COPY "Meters"(serial_number,meter_type_id,status_id,location_id,well_id,id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/activities.csv", "r") as f: - qry = 'COPY "ActivityTypeLU"(id,name,description,permission) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open( - "../PVACDdb_migration/csv_data/testing/devdata_wellMeasurement.csv", "r" -) as f: - qry = 'COPY "WellMeasurements"(timestamp,value,well_id,observed_property_id,submitting_user_id,unit_id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/parttypeLU.csv", "r") as f: - qry = 'COPY "PartTypeLU"(id,name,description) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/parts.csv", "r") as f: - qry = 'COPY "Parts"(id,part_number,part_type_id,description,count,note) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -with open("../PVACDdb_migration/csv_data/tables/partsassociated.csv", "r") as f: - qry = 'COPY "PartAssociation"(meter_type_id,part_id,commonly_used) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -# Only load the following for local testing -testing = False -if testing: - with open("api/data/testdata_users.csv", "r") as f: - qry = 'COPY "Users"(id, username, full_name, email, hashed_password, disabled, user_role_id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - - # with open("api/data/testdata_meterobservations.csv", "r") as f: - # qry = 'COPY "MeterObservations"(timestamp, value, notes, submitting_user_id, meter_id, observed_property_type_id, unit_id, location_id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - # cursor.copy_expert(qry, f) - - # with open("api/data/testdata_meteractivities.csv", "r") as f: - # qry = 'COPY "MeterActivities"(id, timestamp_start, timestamp_end, notes, submitting_user_id, meter_id, activity_type_id, location_id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - # cursor.copy_expert(qry, f) - - # with open("api/data/testdata_partsused.csv", "r") as f: - # qry = 'COPY "PartsUsed"(meter_activity_id, part_id, count) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - # cursor.copy_expert(qry, f) - - with open("api/data/devdata_chloridemeasurements.csv", "r") as f: - qry = 'COPY "WellMeasurements"(timestamp,value,well_id,observed_property_id,submitting_user_id,unit_id) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)' - cursor.copy_expert(qry, f) - -# Create geometries from location lat longs -cursor.execute('update "Locations" set geom = ST_MakePoint(longitude,latitude)') - -conn.commit() -conn.close() - - -# SQL for activity type security scopes if we ever decide to go that route -# INSERT INTO "SecurityScopes" (scope_string, description) -# VALUES -# ('activities:install', 'Submit install activities'), -# ('activities:uninstall', 'Submit install activities'), -# ('activities:general_maintenance', 'Submit general maintenance activities'), -# ('activities:preventative_maintenance', 'Submit preventative maintenance activities'), -# ('activities:repair', 'Submit repair activities'), -# ('activities:rate_meter', 'Submit rate meter activities'), -# ('activities:sell', 'Submit sell activities'), -# ('activities:scrap', 'Submit scrap activities'); - -# INSERT INTO "ScopesRoles" (security_scope_id, user_role_id) -# VALUES -# (7, 2), -# (8, 2), -# (9, 2), -# (10, 2), -# (11, 2), -# (12, 2), -# (13, 2), -# (14, 2), -# (7, 1), -# (8, 1), -# (9, 1), -# (10, 1), -# (11, 1), -# (12, 1); diff --git a/api/models/main_models.py b/api/models/main_models.py index 2fc32019..693e2893 100644 --- a/api/models/main_models.py +++ b/api/models/main_models.py @@ -33,9 +33,6 @@ class Base(DeclarativeBase): __name__: str -# ---------- Parts/Services/Notes ------------ - - class PartTypeLU(Base): """ The types of parts @@ -162,8 +159,6 @@ class NoteTypeLU(Base): Column("note_type_id", ForeignKey("NoteTypeLU.id"), nullable=False), ) -# --------- Meter Related Tables --------- - class Meters(Base): """ @@ -217,8 +212,6 @@ class MeterTypeLU(Base): description: Mapped[str] = mapped_column(String) in_use: Mapped[bool] = mapped_column(Boolean, nullable=False) - # parts: Mapped[List["Parts"]] = relationship(secondary=PartAssociation) - class MeterStatusLU(Base): """ @@ -375,7 +368,6 @@ class Units(Base): description: Mapped[str] = mapped_column(String) -# Association table that links observed property types and their appropriate units PropertyUnits = Table( "PropertyUnits", Base.metadata, @@ -383,8 +375,6 @@ class Units(Base): Column("unit_id", ForeignKey("Units.id"), nullable=False), ) -# ---------- Other Tables --------------- - class Locations(Base): """ @@ -402,7 +392,6 @@ class Locations(Base): quarter: Mapped[int] = mapped_column(Integer) half_quarter: Mapped[int] = mapped_column(Integer) quarter_quarter: Mapped[int] = mapped_column(Integer) - # geom = mapped_column(Geometry("POINT")) # SQLAlchemy/FastAPI has some issue sending this type_id: Mapped[int] = mapped_column( Integer, ForeignKey("LocationTypeLU.id"), nullable=False @@ -460,9 +449,6 @@ class LandOwners(Base): note: Mapped[str] = mapped_column(String) -# ----------- Security Tables --------------- - - class Users(Base): """ All info about a user of the app @@ -517,9 +503,6 @@ class UserRoles(Base): ) -# ------------ Wells -------------- - - class WellUseLU(Base): """ The type of well @@ -647,9 +630,6 @@ class workOrders(Base): ) ose_request_id: Mapped[int] = mapped_column(Integer, nullable=True) - # Associated Activities - # associated_activities: Mapped[List['MeterActivities']] = relationship("MeterActivities") - meter: Mapped["Meters"] = relationship() status: Mapped["workOrderStatusLU"] = relationship() assigned_user: Mapped["Users"] = relationship() diff --git a/api/route_util.py b/api/route_util.py index f73e5c1c..389c70f2 100644 --- a/api/route_util.py +++ b/api/route_util.py @@ -1,18 +1,3 @@ -# =============================================================================== -# Copyright 2022 ross -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# =============================================================================== from fastapi import HTTPException from pydantic import BaseModel @@ -53,6 +38,3 @@ def _get(db, table, dbid): raise HTTPException(status_code=404, detail=f"{table}.{dbid} not found") return db_item - - -# ============= EOF ============================================= diff --git a/api/routes/__init__.py b/api/routes/__init__.py index 34d53041..e69de29b 100644 --- a/api/routes/__init__.py +++ b/api/routes/__init__.py @@ -1 +0,0 @@ -# =============================================================================== diff --git a/api/session.py b/api/session.py index dbf1df85..fb3c20d8 100644 --- a/api/session.py +++ b/api/session.py @@ -1,18 +1,3 @@ -# =============================================================================== -# Copyright 2022 ross -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# =============================================================================== from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker @@ -21,14 +6,6 @@ SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL engine = create_engine(SQLALCHEMY_DATABASE_URL) -# if you don't want to install postgres or any database, use sqlite, a file system based database, -# uncomment below lines if you would like to use sqlite and comment above 2 lines of SQLALCHEMY_DATABASE_URL AND engine - -# SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" -# engine = create_engine( -# SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} -# ) -print(SQLALCHEMY_DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) @@ -38,6 +15,3 @@ def get_db(): yield db finally: db.close() - - -# ============= EOF ============================================= diff --git a/api/tests/__init__.py b/api/tests/__init__.py deleted file mode 100644 index a6c2e2d1..00000000 --- a/api/tests/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# =============================================================================== -# Copyright 2022 ross -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# =============================================================================== - -# ============= EOF ============================================= diff --git a/api/tests/test_main.py b/api/tests/test_main.py deleted file mode 100644 index 1b3a1733..00000000 --- a/api/tests/test_main.py +++ /dev/null @@ -1,263 +0,0 @@ -# =============================================================================== -# Copyright 2022 ross -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# =============================================================================== -import datetime -import os - -import pytest -from fastapi.testclient import TestClient -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.event import listen -from sqlite3 import OperationalError - -from api.dbsetup import setup_db -from api.main import app, get_db -from api.mdels.main_models import Base -from api.routes.alerts import write_user -from api.routes.reports import report_user -from api.security import get_current_user -from api.models.security_models import User - -SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" - - -def load_spatialite(dbapi_conn, connection_record): - dbapi_conn.enable_load_extension(True) - try: - dbapi_conn.load_extension("/usr/lib/x86_64-linux-gnu/mod_spatialite.so") - except OperationalError: - dbapi_conn.load_extension("/usr/lib/aarch64-linux-gnu/mod_spatialite.so") - - -engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} -) - -listen(engine, "connect", load_spatialite) - -Base.metadata.create_all(bind=engine) -TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - - -def override_get_db(): - try: - db = TestingSessionLocal() - yield db - finally: - db.close() - - -os.environ["POPULATE_DB"] = "true" -setup_db(engine, next(override_get_db())) - - -def override_user(): - return User(disabled=False) - - -app.dependency_overrides[get_db] = override_get_db -app.dependency_overrides[get_current_user] = override_user -client = TestClient(app) - - -def test_read_repair_report(): - response = client.get("/repair_report") - assert response.status_code == 200 - data = response.json() - assert len(data) == 4 - assert data[0]["meter_serial_number"] == "1992-4-1234" - assert data[0]["e_read"] == "E 2412341" - assert data[0]["h2o_read"] == 638.831 - - -def test_read_meters(): - response = client.get("/meters") - assert response.status_code == 200 - data = response.json() - assert data[0]["serial_number"] == "1992-4-1234" - assert data[0]["name"] == "moo" - assert data[1]["name"] == "tor" - assert data[2]["name"] == "hag" - - -def test_patch_alert(): - response = client.patch("/alerts/1", json={"alert": "patched alert"}) - assert response.status_code == 200 - - -def test_read_alerts(): - response = client.get("/alerts") - assert response.status_code == 200 - assert response.json()[0]["alert"] == "patched alert" - assert response.json()[0]["meter_serial_number"] == "1992-4-1234" - assert "open_timestamp" in response.json()[0].keys() - assert response.json()[0]["closed_timestamp"] is None - assert response.json()[0]["active"] - - -def test_patch_alert_closed(): - response = client.patch( - "/alerts/1", json={"closed_timestamp": datetime.datetime.now().isoformat()} - ) - assert response.status_code == 200 - - -def test_read_wells(): - response = client.get("/wells") - assert response.status_code == 200 - assert sorted(response.json()[0].keys()) == [ - "id", - "latitude", - "location", - "longitude", - "name", - "osepod", - "owner_id", - ] - - -# -# -def test_post_meter(): - response = client.post( - "/meters", - json={ - "id": 10, - "name": "foo", - "serial_id": 1234, - "serial_case_diameter": 4, - "serial_year": 1990, - }, - ) - assert response.status_code == 200 - response = client.get("/meters") - assert response.status_code == 200 - assert len(response.json()) == 4 - - -def test_post_alert(): - response = client.post("/alerts", json={"meter_id": 1, "alert": "this is an alert"}) - assert response.status_code == 200 - response = client.get("/alerts") - assert response.status_code == 200 - assert len(response.json()) == 2 - - -def test_read_alert(): - response = client.get("/alerts/1") - assert response.status_code == 200 - - -def test_api_status(): - response = client.get("/api_status") - assert response.status_code == 200 - assert response.json() == {"ok": True} - - -def test_meter_status_lu(): - response = client.get("/meter_status_lu") - assert response.status_code == 200 - - data = response.json() - assert len(data) == 3 - assert data[0]["name"] == "POK" - assert data[0]["description"] == "Pump OK" - - -def test_wellconstruction(): - response = client.get("/wellconstruction/1") - assert response.status_code == 200 - data = response.json() - assert data["id"] == 1 - assert data["casing_diameter"] == 0 - assert data["hole_depth"] == 0 - assert data["well_depth"] == 0 - assert data["screens"] == [{"id": 1, "top": 10, "bottom": 20}] - - -def test_waterlevels(): - response = client.get("/waterlevels") - assert response.status_code == 200 - - -def test_well_waterlevels(): - response = client.get("/waterlevels?well_id=1") - assert response.status_code == 200 - assert len(response.json()) == 1 - - response = client.get("/waterlevels?well_id=0") - assert response.status_code == 200 - assert len(response.json()) == 0 - - -def test_well_chlorides(): - response = client.get("/chlorides?well_id=1") - assert response.status_code == 200 - assert len(response.json()) == 1 - assert response.json()[0]["value"] == 1234.0 - - response = client.get("/chlorides?well_id=0") - assert response.status_code == 200 - assert len(response.json()) == 0 - - -def test_fuzzy_meter_search(): - response = client.get("/meters?fuzzy_serial=1990") - assert response.status_code == 200 - assert len(response.json()) == 1 - - response = client.get("/meters?fuzzy_owner_name=spen") - assert response.status_code == 200 - data = response.json() - assert len(data) == 1 - assert data[0]["name"] == "tor" - - -def test_fuzzy_well_osepod_search(): - response = client.get("/wells?osepod=1237") - assert response.status_code == 200 - assert len(response.json()) == 1 - - -def test_wells_by_plss(): - response = client.get("/wells?township=100") - assert response.status_code == 200 - assert len(response.json()) == 3 - - response = client.get("/wells?township=100&range_=10") - assert response.status_code == 200 - assert len(response.json()) == 3 - - response = client.get("/wells?township=100&range_=10§ion=4") - assert response.status_code == 200 - assert len(response.json()) == 3 - - response = client.get("/wells?township=100&range_=10§ion=4&quarter=2") - assert response.status_code == 200 - assert len(response.json()) == 1 - - response = client.get("/wells?township=100&half_quarter=1") - assert response.status_code == 200 - assert len(response.json()) == 1 - - -# spatial queries not compatible with spatialite -# def test_read_wells_spatial(): -# response = client.get('/wells?radius=50&latlng=35.4,-105.2') -# assert response.status_code == 200 -# data = response.json() -# assert len(data) == 1 -# ============= EOF ============================================= diff --git a/api/winenv.bat b/api/winenv.bat deleted file mode 100644 index 1a5349c9..00000000 --- a/api/winenv.bat +++ /dev/null @@ -1,11 +0,0 @@ -:: A batch file to quickly set environmental variables -@echo off -set POSTGRES_USER=docker -set POSTGRES_PASSWORD=docker -set POSTGRES_SERVER=db -set POSTGRES_PORT=5432 -set POSTGRES_DB=gis - -:: Uncomment these to initially populate database -set SETUP_DB=1 -set POPULATE_DB=1 \ No newline at end of file diff --git a/api/winenv.ps1 b/api/winenv.ps1 deleted file mode 100644 index 84bbf909..00000000 --- a/api/winenv.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -$Env:POSTGRES_USER='docker' -$Env:POSTGRES_PASSWORD='docker' -$Env:POSTGRES_SERVER='db' -$Env:POSTGRES_PORT='5432' -$Env:POSTGRES_DB='gis' - -$Env:SETUP_DB='1' -$Env:POPULATE_DB='1' diff --git a/api/xls_persistence.py b/api/xls_persistence.py deleted file mode 100644 index e4b0c09e..00000000 --- a/api/xls_persistence.py +++ /dev/null @@ -1,47 +0,0 @@ -# =============================================================================== -# Copyright 2022 ross -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# =============================================================================== -import xlsxwriter -from geoalchemy2.elements import WKBElement - - -def populate_sheet(sh, records, columns): - for col, attr in enumerate(columns): - sh.write(0, col, attr.name) - # - for row, record in enumerate(records): - for col, attr in enumerate(columns): - try: - value = getattr(record, attr.name) - sh.write(row + 1, col, value) - except BaseException: - sh.write(row + 1, col, "") - - -def make_xls_backup(db, tables): - path = "backup.xlsx" - wb = xlsxwriter.Workbook(path) - - for table in tables: - records = db.query(table).all() - sh = wb.add_worksheet(table.__tablename__) - populate_sheet(sh, records, table.__table__.columns) - - wb.close() - - return path - - -# ============= EOF ============================================= From 1360fcc07b848aa88d85dbae7ff424ed96df882d Mon Sep 17 00:00:00 2001 From: Tyler Adam Martinez Date: Thu, 12 Feb 2026 11:18:02 -0600 Subject: [PATCH 7/7] feat(Settings): Add remove avatar btn to settings page --- .../src/components/ImageUploadWithPreview.tsx | 26 ++++++++--------- frontend/src/views/Settings.tsx | 29 ++++++++++++++++--- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/ImageUploadWithPreview.tsx b/frontend/src/components/ImageUploadWithPreview.tsx index 98abf5de..efefffe6 100644 --- a/frontend/src/components/ImageUploadWithPreview.tsx +++ b/frontend/src/components/ImageUploadWithPreview.tsx @@ -7,10 +7,7 @@ import { enqueueSnackbar } from "notistack"; const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB const VisuallyHiddenInput = (props: any) => ( - + ); export const ImageUploadWithPreview = ({ @@ -30,7 +27,7 @@ export const ImageUploadWithPreview = ({ if (!files) return; let imageFiles = Array.from(files).filter((file) => - file.type.startsWith("image/") + file.type.startsWith("image/"), ); // enforce max file size @@ -38,7 +35,7 @@ export const ImageUploadWithPreview = ({ if (tooBig.length > 0) { enqueueSnackbar( `Some files are too large. Max allowed size is ${MAX_FILE_SIZE / 1024 / 1024} MB.`, - { variant: "error" } + { variant: "error" }, ); imageFiles = imageFiles.filter((f) => f.size <= MAX_FILE_SIZE); } @@ -59,9 +56,12 @@ export const ImageUploadWithPreview = ({ } if (imageFiles.length > remaining) { - enqueueSnackbar(`Only ${remaining} more image${remaining > 1 ? "s" : ""} allowed.`, { - variant: "info", - }); + enqueueSnackbar( + `Only ${remaining} more image${remaining > 1 ? "s" : ""} allowed.`, + { + variant: "info", + }, + ); imageFiles = imageFiles.slice(0, remaining); } } @@ -104,7 +104,7 @@ export const ImageUploadWithPreview = ({ startIcon={} disabled={fileLimit !== undefined && files.length >= fileLimit} // disable when limit reached > - Upload photos + {`Upload photo${(fileLimit ?? 0) >= 2 ? "s" : ""}`} {fileLimit && ( - {files.length}/{fileLimit} images uploaded + {files.length}/{fileLimit} + {` image${(fileLimit ?? 0) >= 2 ? "s" : ""} uploaded`} )} @@ -137,5 +138,4 @@ export const ImageUploadWithPreview = ({ )} ); -} - +}; diff --git a/frontend/src/views/Settings.tsx b/frontend/src/views/Settings.tsx index 6a4ba6d0..a6cdf344 100644 --- a/frontend/src/views/Settings.tsx +++ b/frontend/src/views/Settings.tsx @@ -25,7 +25,7 @@ import { } from "@mui/material"; import SettingsIcon from "@mui/icons-material/Settings"; import { useAuthUser, useSignIn } from "react-auth-kit"; -import { Check, Close, Edit, ExpandMore } from "@mui/icons-material"; +import { Check, Close, Delete, Edit, ExpandMore } from "@mui/icons-material"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { BackgroundBox, @@ -369,9 +369,30 @@ export const Settings = () => { Avatar Configuration - - - + + + + + + + +