Skip to content

Commit

Permalink
App changes for the update telemetry API/DB
Browse files Browse the repository at this point in the history
  • Loading branch information
NickPhura committed Oct 31, 2024
1 parent 8f8b52f commit 8969095
Show file tree
Hide file tree
Showing 56 changed files with 2,652 additions and 1,043 deletions.
10 changes: 9 additions & 1 deletion app/src/components/fields/AnimalAutocompleteField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useFormikContext } from 'formik';
import { useSurveyContext } from 'hooks/useContext';
import { ICritterSimpleResponse } from 'interfaces/useCritterApi.interface';
import { get } from 'lodash-es';
import { useState } from 'react';
import { useEffect, useState } from 'react';

export interface IAnimalAutocompleteFieldProps {
/**
Expand Down Expand Up @@ -93,6 +93,14 @@ export const AnimalAutocompleteField = <T extends string | number>(props: IAnima
// The input field value
const [inputValue, setInputValue] = useState(defaultAnimal?.animal_id ?? '');

useEffect(() => {

Check warning on line 96 in app/src/components/fields/AnimalAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/AnimalAutocompleteField.tsx#L96

Added line #L96 was not covered by tests
if (!defaultAnimal) {
return;

Check warning on line 98 in app/src/components/fields/AnimalAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/AnimalAutocompleteField.tsx#L98

Added line #L98 was not covered by tests
}

setInputValue(String(defaultAnimal.animal_id));

Check warning on line 101 in app/src/components/fields/AnimalAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/AnimalAutocompleteField.tsx#L101

Added line #L101 was not covered by tests
}, [defaultAnimal]);

// Survey animals to choose from
const options = surveyContext.critterDataLoader.data;

Expand Down
186 changes: 186 additions & 0 deletions app/src/components/fields/DeviceAutocompleteField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import Autocomplete from '@mui/material/Autocomplete';
import Box from '@mui/material/Box';
import grey from '@mui/material/colors/grey';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import { IAutocompleteFieldOption } from 'components/fields/AutocompleteField';
import { useFormikContext } from 'formik';
import { useCodesContext } from 'hooks/useContext';
import { TelemetryDevice } from 'interfaces/useTelemetryDeviceApi.interface';
import { get } from 'lodash-es';
import { useEffect, useState } from 'react';

export interface IDeviceAutocompleteFieldProps {
/**
* Formik field name.
*
* @type {string}
* @memberof IDeviceAutocompleteFieldProps
*/
formikFieldName: string;
/**
* The field label.
*
* @type {string}
* @memberof IDeviceAutocompleteFieldProps
*/
label: string;
/**
* The array of options to choose from.
*
* @type {TelemetryDevice[]}
* @memberof IDeviceAutocompleteFieldProps
*/
options: TelemetryDevice[];
/**
* Callback fired on option selection.
*
* @memberof IDeviceAutocompleteFieldProps
*/
onSelect: (device: TelemetryDevice) => void;
/**
* Optional callback fired on option de-selected/cleared.
*
* @memberof IDeviceAutocompleteFieldProps
*/
onClear?: () => void;
/**
* Default device to render for input and options.
*
* @type {TelemetryDevice}
* @memberof IDeviceAutocompleteFieldProps
*/
defaultDevice?: TelemetryDevice;
/**
* If field is required.
*
* @type {boolean}
* @memberof IDeviceAutocompleteFieldProps
*/
required?: boolean;
/**
* If field is disabled.
*
* @type {boolean}
* @memberof IDeviceAutocompleteFieldProps
*/
disabled?: boolean;
/**
* If `true`, clears the input field after a selection is made.
*
* @memberof IDeviceAutocompleteFieldProps
*/
clearOnSelect?: boolean;
/**
* Placeholder text for the TextField
*
* @type {string}
* @memberof IDeviceAutocompleteFieldProps
*/
placeholder?: string;
}

/**
* An autocomplete field for selecting an existing device from the Survey.
*
* @template T
* @param {IDeviceAutocompleteFieldProps<T>} props
* @return {*}
*/
export const DeviceAutocompleteField = <T extends string | number>(props: IDeviceAutocompleteFieldProps) => {

Check warning on line 90 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L90

Added line #L90 was not covered by tests
const { formikFieldName, label, options, onSelect, defaultDevice, required, disabled, clearOnSelect, placeholder } =
props;

Check warning on line 92 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L92

Added line #L92 was not covered by tests

const { touched, errors, setFieldValue } = useFormikContext<IAutocompleteFieldOption<T>>();

Check warning on line 94 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L94

Added line #L94 was not covered by tests

const codesContext = useCodesContext();

Check warning on line 96 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L96

Added line #L96 was not covered by tests

useEffect(() => {
codesContext.codesDataLoader.load();

Check warning on line 99 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L98-L99

Added lines #L98 - L99 were not covered by tests
}, [codesContext.codesDataLoader]);

// The input field value
const [inputValue, setInputValue] = useState(String(defaultDevice?.device_id ?? ''));

useEffect(() => {

Check warning on line 105 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L105

Added line #L105 was not covered by tests
if (!defaultDevice) {
return;

Check warning on line 107 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L107

Added line #L107 was not covered by tests
}

setInputValue(String(defaultDevice.device_id));

Check warning on line 110 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L110

Added line #L110 was not covered by tests
}, [defaultDevice]);

return (

Check warning on line 113 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L113

Added line #L113 was not covered by tests
<Autocomplete
id={formikFieldName}
disabled={disabled}
data-testid={formikFieldName}
filterSelectedOptions
noOptionsText="No matching options"
options={options ?? []}
getOptionLabel={(option) => option.serial}

Check warning on line 121 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L121

Added line #L121 was not covered by tests
isOptionEqualToValue={(option, value) => {
return option.device_id === value.device_id;

Check warning on line 123 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L123

Added line #L123 was not covered by tests
}}
filterOptions={(item) => item}

Check warning on line 125 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L125

Added line #L125 was not covered by tests
inputValue={inputValue}
onInputChange={(_, _value, reason) => {
if (clearOnSelect && reason === 'clear') {
setFieldValue(formikFieldName, '');
setInputValue('');

Check warning on line 130 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L129-L130

Added lines #L129 - L130 were not covered by tests
}
}}
onChange={(_, option) => {
if (option) {
onSelect(option);
setInputValue(String(option.serial));

Check warning on line 136 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L135-L136

Added lines #L135 - L136 were not covered by tests
}
}}
renderOption={(renderProps, renderOption) => {
return (

Check warning on line 140 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L140

Added line #L140 was not covered by tests
<Box
component="li"
sx={{
'& + li': {
borderTop: '1px solid' + grey[300]
}
}}
{...renderProps}
key={renderOption.device_id}>
<Box py={1} width="100%">
<Box justifyContent="space-between" display="flex">
<Typography fontWeight={700}>{renderOption.serial}&nbsp;</Typography>
<Typography color="textSecondary">{renderOption.device_id}</Typography>
</Box>
<Typography color="textSecondary" variant="body2">
{renderOption.model}
</Typography>
<Typography color="textSecondary" variant="body2">
{renderOption.comment}
</Typography>
</Box>
</Box>
);
}}
renderInput={(params) => (
<TextField

Check warning on line 166 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L166

Added line #L166 was not covered by tests
{...params}
label={label}
variant="outlined"
name={formikFieldName}
onChange={(event) => setInputValue(event.currentTarget.value)}

Check warning on line 171 in app/src/components/fields/DeviceAutocompleteField.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/fields/DeviceAutocompleteField.tsx#L171

Added line #L171 was not covered by tests
required={required}
sx={{ opacity: props?.disabled ? 0.25 : 1 }}
error={get(touched, formikFieldName) && Boolean(get(errors, formikFieldName))}
helperText={get(touched, formikFieldName) && get(errors, formikFieldName)}
fullWidth
placeholder={placeholder || 'Search for an device in the Survey'}
InputProps={{
...params.InputProps,
endAdornment: <>{params.InputProps.endAdornment}</>
}}
/>
)}
/>
);
};
13 changes: 12 additions & 1 deletion app/src/constants/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,17 @@ export const TelemetryDeviceKeyFileI18N = {
'An error has occurred while attempting to download the device key file, please try again. If the error persists, please contact your system administrator.'
};

export const TelemetryDeviceI18N = {
cancelTitle: 'Discard changes and exit?',
cancelText: 'Any changes you have made will not be saved. Do you want to proceed?',
createErrorTitle: 'Error Creating Device',
createErrorText:
'An error has occurred while attempting to create your device. Please try again. If the error persists, please contact your system administrator.',
editErrorTitle: 'Error Editing Device',
editErrorText:
'An error has occurred while attempting to edit your device. Please try again. If the error persists, please contact your system administrator.'
};

export const CreateAnimalDeploymentI18N = {
cancelTitle: 'Discard changes and exit?',
cancelText: 'Any changes you have made will not be saved. Do you want to proceed?',
Expand All @@ -490,7 +501,7 @@ export const EditAnimalDeploymentI18N = {
cancelText: 'Any changes you have made will not be saved. Do you want to proceed?',
createErrorTitle: 'Error Creating Deployment',
createErrorText:
'An error has occurred while attempting to create your deployment. Please try again. If the error persists, please contact your system administrator.'
'An error has occurred while attempting to edit your deployment. Please try again. If the error persists, please contact your system administrator.'
};

export const SurveyExportI18N = {
Expand Down
2 changes: 1 addition & 1 deletion app/src/contexts/animalPageContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const AnimalPageContextProvider = (props: PropsWithChildren<Record<never,
}

const critterDataLoader = useDataLoader((projectId: number, surveyId: number, critterId: number) =>
biohubApi.survey.getCritterById(projectId, surveyId, critterId)
biohubApi.survey.getCritterById(projectId, surveyId, critterId, ['attachments'])

Check warning on line 56 in app/src/contexts/animalPageContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/animalPageContext.tsx#L56

Added line #L56 was not covered by tests
);

// The currently selected animal
Expand Down
53 changes: 0 additions & 53 deletions app/src/contexts/telemetryDataContext.tsx

This file was deleted.

14 changes: 7 additions & 7 deletions app/src/contexts/telemetryTableContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import { v4 as uuidv4 } from 'uuid';
import { RowValidationError, TableValidationModel } from '../components/data-grid/DataGridValidationAlert';

export interface IManualTelemetryRecord {
deployment_id: string;
deployment_id: number;
device_id: string;
latitude: number;
longitude: number;
latitude: number | null;
longitude: number | null;
date: string;
time: string;
telemetry_type: string;
Expand Down Expand Up @@ -539,7 +539,7 @@ export const TelemetryTableContextProvider = (props: IAllTelemetryTableContextPr

const newRecord: IManualTelemetryTableRow = {
id,
deployment_id: '',
deployment_id: '' as unknown as number,
latitude: '' as unknown as number, // empty strings to satisfy text fields
longitude: '' as unknown as number,
date: '',
Expand Down Expand Up @@ -684,14 +684,14 @@ export const TelemetryTableContextProvider = (props: IAllTelemetryTableContextPr

const rows: IManualTelemetryTableRow[] = telemetryData.map((item) => {
return {
id: item.id,
id: item.telemetry_id,
deployment_id: item.deployment_id,
device_id: item.device_id,
device_id: item.serial,
latitude: item.latitude,
longitude: item.longitude,
date: dayjs(item.acquisition_date).format('YYYY-MM-DD'),
time: dayjs(item.acquisition_date).format('HH:mm:ss'),
telemetry_type: item.telemetry_type
telemetry_type: item.vendor
};
});

Expand Down
25 changes: 16 additions & 9 deletions app/src/features/surveys/SurveyRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { ProjectRoleRouteGuard } from 'components/security/RouteGuards';
import { PROJECT_PERMISSION, SYSTEM_ROLE } from 'constants/roles';
import { AnimalPageContextProvider } from 'contexts/animalPageContext';
import { DialogContextProvider } from 'contexts/dialogContext';
import { TelemetryDataContextProvider } from 'contexts/telemetryDataContext';
import { AnimalRouter } from 'features/surveys/animals/AnimalRouter';
import EditSurveyPage from 'features/surveys/edit/EditSurveyPage';
import { SurveyObservationPage } from 'features/surveys/observations/SurveyObservationPage';
Expand Down Expand Up @@ -46,7 +45,11 @@ const SurveyRouter: React.FC = () => {
{/* Animals Routes */}
<RouteWithTitle path="/admin/projects/:id/surveys/:survey_id/animals" title={getTitle('Manage Animals')}>
<ProjectRoleRouteGuard
validProjectPermissions={[PROJECT_PERMISSION.COORDINATOR, PROJECT_PERMISSION.COLLABORATOR]}
validProjectPermissions={[
PROJECT_PERMISSION.COORDINATOR,
PROJECT_PERMISSION.COLLABORATOR,
PROJECT_PERMISSION.OBSERVER
]}
validSystemRoles={[SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR]}>
<DialogContextProvider>
<AnimalPageContextProvider>
Expand All @@ -59,13 +62,13 @@ const SurveyRouter: React.FC = () => {
{/* Telemetry Routes */}
<RouteWithTitle path="/admin/projects/:id/surveys/:survey_id/telemetry" title={getTitle('Manage Telemetry')}>
<ProjectRoleRouteGuard
validProjectPermissions={[PROJECT_PERMISSION.COORDINATOR, PROJECT_PERMISSION.COLLABORATOR]}
validProjectPermissions={[
PROJECT_PERMISSION.COORDINATOR,
PROJECT_PERMISSION.COLLABORATOR,
PROJECT_PERMISSION.OBSERVER
]}
validSystemRoles={[SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR]}>
<AnimalPageContextProvider>
<TelemetryDataContextProvider>
<TelemetryRouter />
</TelemetryDataContextProvider>
</AnimalPageContextProvider>
<TelemetryRouter />
</ProjectRoleRouteGuard>
</RouteWithTitle>

Expand All @@ -75,7 +78,11 @@ const SurveyRouter: React.FC = () => {
path="/admin/projects/:id/surveys/:survey_id/observations"
title={getTitle('Manage Observations')}>
<ProjectRoleRouteGuard
validProjectPermissions={[PROJECT_PERMISSION.COORDINATOR, PROJECT_PERMISSION.COLLABORATOR]}
validProjectPermissions={[
PROJECT_PERMISSION.COORDINATOR,
PROJECT_PERMISSION.COLLABORATOR,
PROJECT_PERMISSION.OBSERVER
]}
validSystemRoles={[SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR]}>
<SurveyObservationPage />
</ProjectRoleRouteGuard>
Expand Down
Loading

0 comments on commit 8969095

Please sign in to comment.