Skip to content

Commit

Permalink
Merge branch 'dev' into revert-3717-revert-public-map-component
Browse files Browse the repository at this point in the history
  • Loading branch information
meghna0593 authored Dec 12, 2024
2 parents f0076f1 + 9d561b7 commit 81556cf
Show file tree
Hide file tree
Showing 19 changed files with 261 additions and 142 deletions.
2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dev": "nodemon --exec 'tsx' src/server --watch ../sharedAPI --watch .",
"start": "tsx src/server",
"export-map": "tsx src/map-exporter",
"export-geojson": "ts-node src/geojson-exporter",
"export-geojson": "tsx src/geojson-exporter",
"test": "NODE_OPTIONS='--import tsx' mocha \"./src/tests/**/*.test.ts\" --timeout 10000",
"lint": "eslint",
"lint-fix": "eslint --fix"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ MonitoringBiocontrolReleaseTerrestrialPlant.columns = [
.isRequired()
.build(),

new TemplateColumnBuilder('Monitoring - Linked Treatment ID', 'text', 'form_data.activity_type_data.linked_id')
new TemplateColumnBuilder('Monitoring - Linked Treatment ID', 'linked_id', 'form_data.activity_type_data.linked_id')
.isRequired()
.build(),
new TemplateColumnBuilder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ MonitoringChemicalTemp.columns = [

new TemplateColumnBuilder(
'Monitoring - Linked Treatment ID',
'text',
'linked_id',
'form_data.activity_type_data.linked_id'
).build(),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ MonitoringMechanical.columns = [

new TemplateColumnBuilder(
'Monitoring - Linked Treatment ID',
'text',
'linked_id',
'form_data.activity_type_data.linked_id'
).build(),

Expand Down
77 changes: 52 additions & 25 deletions api/src/utils/batch/validation/validation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import slugify from 'slugify';
import moment from 'moment';
import { ActivityLetter, lookupAreaLimit } from 'sharedAPI';
import { ActivityLetter, ActivitySubtype, lookupAreaLimit } from 'sharedAPI';
import circle from '@turf/circle';
import booleanIntersects from '@turf/boolean-intersects';
import {
Expand Down Expand Up @@ -197,48 +197,48 @@ const _handleBooleanCell = (data: string, result: CellValidationResult) => {
* @param activityLetter The Letter corresponsing to the type of entry being uploaded
* @returns Regex Pattern matches ShortID
*/
const validateShortID = (shortId, activityLetter) => {
const validateShortID = (shortId: string, activityLetter: string) => {
const shortIdPattern = RegExp(`^[0-9]{2}${activityLetter}[0-9A-Z]{8}$`);
return shortIdPattern.test(shortId);
};

/**
* @desc Validation Handler for batch records with type Activity_Monitoring_ChemicalTerrestrialAquaticPlant
* @desc Customization for Linked ID Validation
* @property {string[]} expectedRecordTypes Recordtypes that must match to be properly linked
* @property {string[]} shortIdActivityLetters unique three letter code in Activity Short ID identifying it as a record type. e.g. 'PAC', 'PTM', etc
*/
interface LinkedIdOptions {
expectedRecordTypes: string[];
shortIdActivityLetters: string[];
}
/**
* @desc Validation Handler for matching Records with linked_id
* Validates fields in form to ensure data properly links with an existing record.
* @param data
* @param result
*/
const _handleActivity_Monitoring_ChemicalTerrestrialAquaticPlant = async (
const _handleGetLinkedId = async (
shortId: string,
result: CellValidationResult,
row: Record<string, any>
row: Record<string, any>,
options: LinkedIdOptions
) => {
try {
const isValidShortID =
validateShortID(shortId, ActivityLetter.Activity_Treatment_ChemicalPlantAquatic) ||
validateShortID(shortId, 'PTC');
const isValidShortID = options.shortIdActivityLetters.some((letters) => validateShortID(shortId, letters));
if (!isValidShortID) {
result.validationMessages.push(invalidShortID);
return;
}
const expectedRecordTypes = [
'Activity_Treatment_ChemicalPlantAquatic',
'Activity_Treatment_ChemicalPlantTerrestrial'
];
const batchUploadInvasivePlantRow = 'Monitoring - Terrestrial Invasive Plant';
const batchUploadTerrestrialPlantRow = 'Monitoring - Aquatic Invasive Plant';
const linkedRecord = await getRecordFromShort(shortId);
if (!linkedRecord) {
result.validationMessages.push(invalidLongID(shortId));
return;
}
const isItTheRightRecordType = expectedRecordTypes.includes(linkedRecord['activity_subtype']);
const isItTheRightRecordType = options.expectedRecordTypes.includes(linkedRecord['activity_subtype']);
const doTheSpeciesMatch =
linkedRecord['species_treated']?.includes(row.data[batchUploadInvasivePlantRow]) ||
linkedRecord['species_treated']?.includes(row.data[batchUploadTerrestrialPlantRow]);
linkedRecord['species_treated']?.includes(row.data['Monitoring - Terrestrial Invasive Plant']) ||
linkedRecord['species_treated']?.includes(row.data['Monitoring - Aquatic Invasive Plant']);
const thisGeoJSON: any = row.data['WKT'];
const isValidGeoJSON: boolean = thisGeoJSON || false;
const linkedGeoJSON: any = JSON.parse(linkedRecord['sample']) || false;
const isValidGeoJSON: boolean = thisGeoJSON ?? false;
const linkedGeoJSON: any = JSON.parse(linkedRecord['sample']) ?? false;
const doTheyOverlap =
isValidGeoJSON && linkedGeoJSON && booleanIntersects(thisGeoJSON.parsedValue?.geojson, linkedGeoJSON);
if (!doTheSpeciesMatch) {
Expand All @@ -257,11 +257,11 @@ const _handleActivity_Monitoring_ChemicalTerrestrialAquaticPlant = async (
result.validationMessages.push(invalidWKT);
}
if (linkedRecord) {
result.parsedValue = linkedRecord['activity_id'] || '';
result.parsedValue = linkedRecord['activity_id'] ?? '';
}
} catch (e) {
defaultLog.error({
message: '[handleActivity_Monitoring_ChemicalTerrestrialAquaticPlant]',
message: '[_handleGetLinkedId]',
error: e
});
}
Expand Down Expand Up @@ -303,8 +303,35 @@ async function _validateCell(
}
const thisRecordType = template.subtype;
switch (thisRecordType) {
case 'Activity_Monitoring_ChemicalTerrestrialAquaticPlant':
await _handleActivity_Monitoring_ChemicalTerrestrialAquaticPlant(data, result, row);
case ActivitySubtype.Monitoring_ChemicalTerrestrialAquaticPlant:
await _handleGetLinkedId(data, result, row, {
shortIdActivityLetters: [
ActivityLetter[ActivitySubtype.Treatment_ChemicalPlantAquatic],
ActivityLetter[ActivitySubtype.Treatment_ChemicalPlant]
],
expectedRecordTypes: [
ActivitySubtype.Treatment_ChemicalPlantAquatic,
ActivitySubtype.Treatment_ChemicalPlant
]
});
break;
case ActivitySubtype.Monitoring_MechanicalTerrestrialAquaticPlant:
await _handleGetLinkedId(data, result, row, {
shortIdActivityLetters: [
ActivityLetter[ActivitySubtype.Treatment_MechanicalPlantAquatic],
ActivityLetter[ActivitySubtype.Treatment_MechanicalPlant]
],
expectedRecordTypes: [
ActivitySubtype.Treatment_MechanicalPlantAquatic,
ActivitySubtype.Treatment_MechanicalPlant
]
});
break;
case ActivitySubtype.Monitoring_BiologicalTerrestrialPlant:
await _handleGetLinkedId(data, result, row, {
shortIdActivityLetters: [ActivityLetter[ActivitySubtype.Treatment_BiologicalPlant]],
expectedRecordTypes: [ActivitySubtype.Treatment_BiologicalPlant]
});
break;
default:
break;
Expand Down
1 change: 0 additions & 1 deletion app/src/UI/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { OfflineDataSyncDialog } from 'UI/OfflineDataSync/OfflineDataSyncDialog'
import Spinner from 'UI/Spinner/Spinner';
import { WebOnly } from 'UI/Predicates/WebOnly';
import { useSelector } from 'utils/use_selector';
import { MobileBetaAccessMessage } from 'UI/Overlay/MobileBetaAccess/MobileBetaAccessMessage';
import AlertsContainer from './AlertsContainer/AlertsContainer';
import UserInputModalController from './UserInputModals/UserInputModalController';
import { MOBILE, PLATFORM, Platform } from 'state/build-time-config';
Expand Down
4 changes: 2 additions & 2 deletions app/src/UI/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,9 @@ const LoginOrOutMemo = React.memo(() => {

const NetworkStateControl: React.FC = () => {
const handleNetworkStateChange = () => {
dispatch(connected ? NetworkActions.offline() : NetworkActions.online());
dispatch(connected ? NetworkActions.setAdministrativeStatus(false) : NetworkActions.manualReconnect());
};
const { connected } = useSelector((state) => state.Network);
const connected = useSelector((state) => state.Network.connected);
const dispatch = useDispatch();
return (
<div className={'network-state-control'}>
Expand Down
9 changes: 6 additions & 3 deletions app/src/UI/OfflineDataSync/OfflineDataSyncTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { Button, IconButton, LinearProgress } from '@mui/material';
import { OfflineActivityRecord, selectOfflineActivity } from 'state/reducers/offlineActivity';
import { useSelector } from 'utils/use_selector';
import { useDispatch } from 'react-redux';
import { ACTIVITY_OFFLINE_DELETE_ITEM, ACTIVITY_RUN_OFFLINE_SYNC } from 'state/actions';
import {
ACTIVITY_OFFLINE_DELETE_ITEM,
ACTIVITY_OFFLINE_SYNC_DIALOG_SET_STATE,
ACTIVITY_RUN_OFFLINE_SYNC
} from 'state/actions';
import Delete from '@mui/icons-material/Delete';
import './OfflineDataSync.css';
import moment from 'moment';
Expand All @@ -16,7 +20,6 @@ export const OfflineDataSyncTable = () => {
const { working, serializedActivities } = useSelector(selectOfflineActivity);
const { authenticated, workingOffline } = useSelector((state) => state.Auth);
const connected = useSelector((state) => state.Network.connected);

const [syncDisabled, setSyncDisabled] = useState(false);

const history = useHistory();
Expand Down Expand Up @@ -63,8 +66,8 @@ export const OfflineDataSyncTable = () => {
disabled={!(workingOffline || authenticated)}
color="primary"
onClick={() => {
dispatch(Activity.getLocal(key));
history.push(`/Records/Activity:${key}/form`);
dispatch({ type: ACTIVITY_OFFLINE_SYNC_DIALOG_SET_STATE, payload: { open: false } });
}}
>
<FileOpen></FileOpen>
Expand Down

This file was deleted.

44 changes: 0 additions & 44 deletions app/src/UI/Overlay/MobileBetaAccess/MobileBetaAccessMessage.tsx

This file was deleted.

33 changes: 1 addition & 32 deletions app/src/UI/Overlay/Records/Records.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { MouseEvent, useEffect, useState } from 'react';

import { Button } from '@mui/material';
import './Records.css';
import { OverlayHeader } from '../OverlayHeader';
import Spinner from 'UI/Spinner/Spinner';
import { useHistory } from 'react-router-dom';
import { useDispatch, useSelector } from 'utils/use_selector';
import UserSettings from 'state/actions/userSettings/UserSettings';
Expand All @@ -16,15 +14,9 @@ import filterRecordsetsByNetworkState from 'utils/filterRecordsetsByNetworkState

export const Records = () => {
const DEFAULT_RECORD_TYPES = ['All InvasivesBC Activities', 'All IAPP Records', 'My Drafts'];
const activitiesGeoJSONState = useSelector((state) => state.Map?.activitiesGeoJSONDict);
const isIAPPGeoJSONLoaded = useSelector((state) => state.Map?.IAPPGeoJSONDict !== undefined);
const mapLayers = useSelector((state) => state.Map.layers);
const MapMode = useSelector((state) => state.Map.MapMode);
const recordSets = useSelector((state) => state.UserSettings?.recordSets);
const connected = useSelector((state) => state.Network.connected);
const [highlightedSet, setHighlightedSet] = useState<string | null>();
const [isActivitiesGeoJSONLoaded, setIsActivitiesGeoJSONLoaded] = useState(false);
const [loadMap, setLoadMap] = useState({});

const history = useHistory();
const dispatch = useDispatch();
Expand All @@ -37,23 +29,6 @@ export const Records = () => {
dispatch(UserSettings.RecordSet.syncCacheStatusWithCacheService());
}, []);

useEffect(() => {
setIsActivitiesGeoJSONLoaded(activitiesGeoJSONState.hasOwnProperty('s3'));
}, [activitiesGeoJSONState]);

useEffect(() => {
const rv = {};
mapLayers.forEach((layer) => {
const geojson = layer?.type === RecordSetType.Activity ? isActivitiesGeoJSONLoaded : isIAPPGeoJSONLoaded;
if (MapMode !== 'VECTOR_ENDPOINT') {
rv[layer?.recordSetID] = !layer?.loading && geojson;
} else {
rv[layer?.recordSetID] = !layer?.loading;
}
});
setLoadMap(rv);
}, [JSON.stringify(mapLayers), isActivitiesGeoJSONLoaded, isIAPPGeoJSONLoaded, MapMode]);

//Record set handlers:
const handleToggleLabel = (set: string, e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
Expand Down Expand Up @@ -116,13 +91,7 @@ export const Records = () => {
isDefaultRecordset={DEFAULT_RECORD_TYPES.includes(recordSets[set]?.recordSetName)}
handleNameChange={handleNameChange}
recordsetKey={set}
>
{!loadMap?.[set] && (
<div>
<Spinner />
</div>
)}
</RecordSetDetails>
></RecordSetDetails>

<RecordSetControl
isDefaultRecordset={DEFAULT_RECORD_TYPES.includes(recordSets[set]?.recordSetName)}
Expand Down
6 changes: 6 additions & 0 deletions app/src/constants/alerts/networkAlerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ const networkAlertMessages: Record<string, AlertMessage> = {
severity: AlertSeverity.Error,
subject: AlertSubjects.Network,
autoClose: 10
},
automaticReconnectFailed: {
content: 'We were unable to bring you back online. Please try again later.',
severity: AlertSeverity.Error,
subject: AlertSubjects.Network,
autoClose: 10
}
};

Expand Down
2 changes: 2 additions & 0 deletions app/src/constants/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export const MediumDateTimeFormat = 'MMMM D, YYYY, H:mm a'; //January 5, 2020, 3
export const LongDateFormat = 'dddd, MMMM D, YYYY, H:mm a'; //Monday, January 5, 2020, 3:30 pm

export const LongDateTimeFormat = 'dddd, MMMM D, YYYY, H:mm a'; //Monday, January 5, 2020, 3:30 pm

export const HEALTH_ENDPOINT = '/api/misc/version';
11 changes: 10 additions & 1 deletion app/src/state/actions/network/NetworkActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@ import { createAction } from '@reduxjs/toolkit';

class NetworkActions {
private static readonly PREFIX = 'NetworkActions';

static readonly online = createAction(`${this.PREFIX}/online`);
static readonly offline = createAction(`${this.PREFIX}/offline`);
static readonly monitorHeartbeat = createAction(`${this.PREFIX}/monitorHeartbeat`, (cancel: boolean = false) => ({
payload: cancel
}));
static readonly userLostConnection = createAction(`${this.PREFIX}/userLostConnection`);
static readonly manualReconnect = createAction(`${this.PREFIX}/manualReconnect`);
static readonly automaticReconnectFailed = createAction(`${this.PREFIX}/automaticReconnectFailed`);
static readonly checkInitConnection = createAction(`${this.PREFIX}/checkInitConnection`);
static readonly setAdministrativeStatus = createAction<boolean>(`${this.PREFIX}/toggleAdministrativeStatus`);
static readonly setOperationalStatus = createAction<boolean>(`${this.PREFIX}/setOperationalStatus`);
static readonly updateConnectionStatus = createAction<boolean>(`${this.PREFIX}/updateConnectionStatus`);
}
export default NetworkActions;
Loading

0 comments on commit 81556cf

Please sign in to comment.