Skip to content
Draft
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
aa9329f
FINALLY a working factory function with full typing
Duncan-Brain Jan 15, 2026
8a3ba9d
Let AI tidy things up after its useless attempts to help create the f…
Duncan-Brain Jan 15, 2026
acf7bfe
Make withFarmId type
Duncan-Brain Jan 15, 2026
f9d447a
Move factory function to util file
Duncan-Brain Jan 15, 2026
c618043
Add farm tags creation utility
Duncan-Brain Jan 15, 2026
f42d9f2
Add a useLazyQuery wrapper
Duncan-Brain Jan 16, 2026
b5c29c6
Use the getUseQueryWithFarmId for all get hook that are farm tags
Duncan-Brain Jan 16, 2026
89554e2
Add farm library tag to definition of rtk tag utility function
Duncan-Brain Feb 5, 2026
b58e9f9
Add the FarmLibrary get request for default types
Duncan-Brain Feb 5, 2026
925eb5f
Upgrade to 1.9.7 for better typings
Duncan-Brain Feb 6, 2026
abeb944
Create an alternate withFarmId type to retain T properties more accur…
Duncan-Brain Feb 6, 2026
46d522e
create and test mutation wrapper
Duncan-Brain Feb 6, 2026
0ed2c17
consolidate farm tags functions
Duncan-Brain Feb 6, 2026
75f9f2a
Add all other mutations to use the new wrapper function
Duncan-Brain Feb 8, 2026
404a1c1
Update definition of mutation promise
Duncan-Brain Feb 8, 2026
ba57050
update logic for use with more conservative error typing in wrapper
Duncan-Brain Feb 8, 2026
8346e35
Fix refetch typings for query
Duncan-Brain Feb 18, 2026
53a6a9e
extract map farm tags function for use outside of querybuilder
Duncan-Brain Feb 18, 2026
720b77f
Update invalidate tags usage appwide
Duncan-Brain Feb 18, 2026
9f5485b
Fix naming of params used in useQueries hook
Duncan-Brain Feb 18, 2026
a42b06b
update manual initiation of get sensors
Duncan-Brain Feb 18, 2026
7ca95b4
Update invalidateTags and initiate calls from integration rebasing
Duncan-Brain Feb 18, 2026
b77b41a
fix imports
Duncan-Brain Feb 18, 2026
11c7411
fix empty object query param
Duncan-Brain Feb 18, 2026
561453c
Name payload prop in mutation util
Duncan-Brain Feb 19, 2026
b3e848d
Update initiate in js files
Duncan-Brain Feb 19, 2026
955463a
Clean up onQueryStarted using more accurate arg
Duncan-Brain Feb 19, 2026
e12707e
Move market directory partner request to library tags
Duncan-Brain Feb 20, 2026
8daa7cf
Switch withFarmId to withFarmIdPayload type and cleanup
Duncan-Brain Feb 20, 2026
6de5764
undo objectifying id for irrigation
Duncan-Brain Feb 20, 2026
67e7956
Fix lazy query for sensor readings
Duncan-Brain Feb 20, 2026
94982ca
Undo moving MarketDirectoryPartners tag to library tags, clarify further
Duncan-Brain Feb 20, 2026
1439eee
Merge branch 'integration' into rtk-upgrade/bug-invalidate-tags-farmi…
Duncan-Brain Mar 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@mui/x-date-pickers": "^8.0.0",
"@react-oauth/google": "^0.7.0",
"@reactour/tour": "^3.1.6",
"@reduxjs/toolkit": "^1.9.1",
"@reduxjs/toolkit": "1.9.7",
Copy link
Copy Markdown
Collaborator Author

@Duncan-Brain Duncan-Brain Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This improved typings a little bit without having to upgrade to v2.0.

Basically by reusing tome types in the previous version it could break parent types that were otherwise untouched. I never realized typescript could be so fragile.

"@sentry/react": "^6.19.7",
"@sentry/tracing": "^6.19.7",
"@turf/boolean-point-in-polygon": "7.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/webapp/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions packages/webapp/src/containers/AddFarm/saga.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ import { axios, getHeader } from '../saga';
import { createAction } from '@reduxjs/toolkit';
import i18n from '../../locales/i18n';
import { enqueueErrorSnackbar } from '../Snackbar/snackbarSlice';
import { invalidateTags } from '../../store/api/apiSlice';
import { FarmLibraryTags, FarmTags } from '../../store/api/apiTags';

const patchRoleUrl = (farm_id, user_id) => `${userFarmUrl}/role/farm/${farm_id}/user/${user_id}`;
const patchFarmUrl = (farm_id) => `${farmUrl}/owner_operated/${farm_id}`;
Expand Down Expand Up @@ -61,9 +59,6 @@ export function* postFarmSaga({ payload: { showFarmNameCharacterLimitExceededErr
};
yield call(axios.patch, patchStepUrl(farm_id, user_id), step, getHeader(user_id, farm_id));

// Clear old farm RTK Query data
yield put(invalidateTags([...FarmTags, ...FarmLibraryTags]));
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer need to invalidate tags on switch farm. Store will clear on logout only.


const user = getUserResult?.data;
yield put(
postFarmSuccess({
Expand Down
6 changes: 3 additions & 3 deletions packages/webapp/src/containers/AddSensors/Partners.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const Partners = ({
}: {
setIsEditing: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const { data: esciData = [], isLoading: esciLoading } = useGetFarmAddonQuery(
`?addon_partner_id=${PARTNERS.ESCI.id}`,
);
const { data: esciData = [], isLoading: esciLoading } = useGetFarmAddonQuery({
params: `?addon_partner_id=${PARTNERS.ESCI.id}`,
Copy link
Copy Markdown
Collaborator Author

@Duncan-Brain Duncan-Brain Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

params arguments standardized for use with useQueries across GET endpoints.

});

const hasActiveConnection = {
esci: esciData.length > 0,
Expand Down
6 changes: 5 additions & 1 deletion packages/webapp/src/containers/AddSensors/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ const AddSensor = ({ isCompactSideMenu }: AddSensorProps) => {
const result = await addFarmAddon(values[PARTNER]);

if ('error' in result) {
const isInvalidId = 'data' in result.error && result.error.data === 'Organisation not found';
const isInvalidId =
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error is now unknown requiring stricter narrowing

result.error &&
typeof result.error === 'object' &&
'data' in result.error &&
result.error.data === 'Organisation not found';
const errorMessage = isInvalidId
? t('SENSOR.ESCI.ORGANISATION_ID_ERROR')
: t('SENSOR.ESCI.ORGANISATION_ID_GENERIC_ERROR');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ const FarmAddons = () => {
const history = useHistory();
const routerTabs = useFarmSettingsRouterTabs();

const { data: esciDataArray = [] } = useGetFarmAddonQuery(
`?addon_partner_id=${PARTNERS.ESCI.id}`,
);
const { data: esciDataArray = [] } = useGetFarmAddonQuery({
params: `?addon_partner_id=${PARTNERS.ESCI.id}`,
});

const hasActiveConnection = {
esci: esciDataArray.length > 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ type FarmSettingsContextType = {
const FarmSettingsContext = createContext<FarmSettingsContextType | undefined>(undefined);

export const FarmSettingsProvider = ({ children }: { children: ReactNode }) => {
const { data: esciDataArray = [] } = useGetFarmAddonQuery(
`?addon_partner_id=${PARTNERS.ESCI.id}`,
);
const { data: esciDataArray = [] } = useGetFarmAddonQuery({
params: `?addon_partner_id=${PARTNERS.ESCI.id}`,
});

const hasActiveConnection = esciDataArray.length > 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ const MarketDirectoryConsent = ({

const savedConsent = marketDirectoryInfo?.[CONSENTED_TO_SHARE] || false;

const { data: marketDirectoryPartners = [] } =
useGetMarketDirectoryPartnersQuery('?filter=country');
const { data: marketDirectoryPartners = [] } = useGetMarketDirectoryPartnersQuery({
params: '?filter=country',
});
const [updateMarketDirectoryInfo, { isLoading }] = useUpdateMarketDirectoryInfoMutation();

const [isConsentReadonly, setIsConsentReadonly] = useState(savedConsent === true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ function useFormattedSensorReadings({
data: sensorReadings,
} = useGetSensorReadingsQuery(
{
esids: sensorIds.join(','),
startTime: startDate,
endTime: getAdjustedEndTime(endDate),
truncPeriod,
params: {
esids: sensorIds.join(','),
startTime: startDate,
endTime: getAdjustedEndTime(endDate),
truncPeriod,
},
},
{ refetchOnMountOrArgChange: true },
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ function useLatestReading(sensors: Sensor[]): {
// The "minute" and "second" truncation periods do not guarantee data for every minute or second.
// Use "minute" here to ensure we get data.
return triggerGetSensorReadings({
esids: sensors.map(({ external_id }) => external_id).join(','),
startTime: adjustedStartTime.toISOString(),
truncPeriod: 'minute',
params: {
esids: sensors.map(({ external_id }) => external_id).join(','),
startTime: adjustedStartTime.toISOString(),
truncPeriod: 'minute',
},
});
};

Expand Down
8 changes: 4 additions & 4 deletions packages/webapp/src/containers/Task/saga.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ import {
getMovementTaskBody,
getSoilSampleTaskBody,
} from './sagaUtils';
import { api } from '../../store/api/apiSlice';
import { invalidateTags } from '../../store/api/apiSlice';

const taskTypeEndpoint = [
'cleaning_task',
Expand Down Expand Up @@ -692,7 +692,7 @@ export function* createTaskSaga({ payload }) {
task_translation_key === 'HARVEST_TASK' ? result.data[0] : result.data;
yield call(getTasksSuccessSaga, { payload: isHarvest ? result.data : [result.data] });
if (task_translation_key === 'IRRIGATION_TASK') {
yield put(api.util.invalidateTags(['IrrigationPrescriptions']));
yield put(invalidateTags(['IrrigationPrescriptions'], farm_id));
}
if (alreadyCompleted) {
if (['CLEANING_TASK', 'PEST_CONTROL_TASK'].includes(task_translation_key)) {
Expand Down Expand Up @@ -905,7 +905,7 @@ export function* completeTaskSaga({ payload: { task_id, data, returnPath } }) {
});

if (task_translation_key === 'MOVEMENT_TASK') {
yield put(api.util.invalidateTags(['Animals', 'AnimalBatches']));
yield put(invalidateTags(['Animals', 'AnimalBatches'], farm_id));
}
}
} catch (e) {
Expand Down Expand Up @@ -1096,7 +1096,7 @@ export function* deleteTaskSaga({ payload: data }) {
}
yield put(deleteTaskSuccess(result.data));
if (task_type.task_translation_key === 'IRRIGATION_TASK') {
yield put(api.util.invalidateTags(['IrrigationPrescriptions']));
yield put(invalidateTags(['IrrigationPrescriptions'], farm_id));
}
yield put(enqueueSuccessSnackbar(i18n.t('TASK.DELETE.SUCCESS')));
}
Expand Down
16 changes: 7 additions & 9 deletions packages/webapp/src/containers/saga.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,7 @@ import {
onLoadingWatercourseFail,
onLoadingWatercourseStart,
} from './watercourseSlice';
import { api, invalidateTags } from '../store/api/apiSlice';
import { FarmLibraryTags, FarmTags } from '../store/api/apiTags';
import { api } from '../store/api/apiSlice';
import {
getSoilSampleLocationsSuccess,
onLoadingSoilSampleLocationFail,
Expand Down Expand Up @@ -603,7 +602,7 @@ export function* fetchAllSaga() {
const { has_consent, user_id, farm_id } = yield select(userFarmSelector);
if (!has_consent) return history.push('/consent');

yield put(api.endpoints.getSensors.initiate());
yield put(api.endpoints.getSensors.initiate({ farm_id, payload: undefined }));
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cant deny this is ugly, but the goal is to remove sagas if I recall. So using initiate() should be rare.


const isAdmin = yield select(isAdminSelector);
const adminTasks = [
Expand All @@ -629,12 +628,12 @@ export function* fetchAllSaga() {

// Animals
yield all([
put(api.endpoints.getAnimals.initiate()),
put(api.endpoints.getAnimalBatches.initiate()),
put(api.endpoints.getDefaultAnimalTypes.initiate()),
put(api.endpoints.getAnimals.initiate({ farm_id, payload: undefined })),
put(api.endpoints.getAnimalBatches.initiate({ farm_id, payload: undefined })),
put(api.endpoints.getDefaultAnimalTypes.initiate({ farm_id, payload: undefined })),
put(api.endpoints.getDefaultAnimalBreeds.initiate()),
put(api.endpoints.getCustomAnimalTypes.initiate()),
put(api.endpoints.getCustomAnimalBreeds.initiate()),
put(api.endpoints.getCustomAnimalTypes.initiate({ farm_id, payload: undefined })),
put(api.endpoints.getCustomAnimalBreeds.initiate({ farm_id, payload: undefined })),
put(api.endpoints.getAnimalSexes.initiate()),
put(api.endpoints.getAnimalIdentifierTypes.initiate()),
put(api.endpoints.getAnimalIdentifierColors.initiate()),
Expand All @@ -658,7 +657,6 @@ export function* fetchAllSaga() {
export function* clearOldFarmStateSaga() {
yield put(resetTasks());
yield put(resetDateRange());
yield put(invalidateTags([...FarmTags, ...FarmLibraryTags]));

// Reset finance loading state
yield put(setIsFetchingData(true));
Expand Down
4 changes: 3 additions & 1 deletion packages/webapp/src/hooks/api/useQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
*/

const useQueries = (queries) => {
const result = queries.map((query) => query.hook(query.params));
const result = queries.map((query) =>
query.hook(query.params ? { params: query.params } : undefined),
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass params only if exists.

);

const data = queries.reduce(
(dataObj, query, index) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/

import { useEffect, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import {
enqueueSuccessSnackbar,
Expand All @@ -23,6 +23,7 @@ import {
import { getTasks } from '../../containers/Task/saga';
import { getManagementPlans } from '../../containers/saga';
import { invalidateTags } from '../../store/api/apiSlice';
import { loginSelector } from '../../containers/userFarmSlice';

type SyncArea =
| 'tasks.create'
Expand Down Expand Up @@ -66,7 +67,7 @@ function resolveAreaFromUrl(method: string, url: string): SyncArea {
export function useServiceWorkerListener() {
const dispatch = useDispatch();
const { t } = useTranslation();

const { farm_id } = useSelector(loginSelector);
const syncConfig = useMemo(
(): Record<SyncArea, SyncConfig> => ({
'tasks.create': {
Expand All @@ -76,7 +77,7 @@ export function useServiceWorkerListener() {
},
onSuccess: (response) => {
if (response?.taskType?.task_translation_key === 'IRRIGATION_TASK') {
return invalidateTags(['IrrigationPrescriptions']);
return invalidateTags(['IrrigationPrescriptions'], farm_id);
}
},
refresh: () => {
Expand All @@ -94,7 +95,7 @@ export function useServiceWorkerListener() {
onSuccess: (response) => {
// taskType not returned in the completion API response
if (response?.animal_movement_task) {
return invalidateTags(['Animals', 'AnimalBatches']);
return invalidateTags(['Animals', 'AnimalBatches'], farm_id);
}
},
refresh: () => dispatch(getTasks()),
Expand All @@ -121,13 +122,13 @@ export function useServiceWorkerListener() {
},
onSuccess: (response) => {
if (response?.taskType?.task_translation_key === 'IRRIGATION_TASK') {
return invalidateTags(['IrrigationPrescriptions']);
return invalidateTags(['IrrigationPrescriptions'], farm_id);
}
},
refresh: () => dispatch(getTasks()),
},
}),
[t, dispatch],
[t, dispatch, farm_id],
);

useEffect(() => {
Expand Down
Loading