Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1f92f55
LF-5182 Commit phase 1 agent completion
kathyavini Mar 2, 2026
ceba077
Merge branch 'integration' into LF-5182-experiment-tape-plan-phase-1-…
SayakaOno Mar 2, 2026
69e9dce
LF-5182 Adjust getSurveyVersion
SayakaOno Mar 2, 2026
73aae23
LF-5182 Add useTapeSurveyJsonForFarmCountry hook
SayakaOno Mar 3, 2026
ccf2d7b
LF-5182 Use useTapeSurveyJsonForFarmCountry hook in TAPESurvey
SayakaOno Mar 3, 2026
8ae4dc9
LF-5182 Replace tapeQuestions.json with useTapeSurveyJsonForFarmCount…
SayakaOno Mar 3, 2026
8176e0d
LF-5182 Update survey json path, add useGetTapeSurveyQuery in tapeSur…
SayakaOno Mar 3, 2026
cbfa4f2
LF-5182 Get isTapeSurveyCompleted status from via the API
SayakaOno Mar 3, 2026
dc22279
LF-5182 Export prefetch from tapeSurveyApi
SayakaOno Mar 3, 2026
aa8808b
LF-5182 Adjust handleComplete
SayakaOno Mar 3, 2026
a0ad9b2
LF-5182 Adjust tapeSurveyStatusSelector
SayakaOno Mar 3, 2026
de015c3
LF-5182 Update tapeSurveyStatusSelector
SayakaOno Mar 3, 2026
a89b028
LF-5182 Fix initialData in TAPESurvey
SayakaOno Mar 3, 2026
9c63f5b
LF-5182 Fix tape's insightData
SayakaOno Mar 4, 2026
301adda
LF-5182 Pass isCompactSideMenu prop to TapeSurvey in routes
SayakaOno Mar 4, 2026
3a58c8d
LF-5182 Adjust the survey.js error position
SayakaOno Mar 4, 2026
759f726
LF-5182 Handle survey json loading error with snackbar
SayakaOno Mar 5, 2026
cc9fb45
LF-5182 Refactor tape
SayakaOno Mar 5, 2026
412f230
LF-5182 Remove useTapeSurveyJsonForFarmCountry hook
SayakaOno Mar 5, 2026
273c164
LF-5182 Conditionally show TAPE in insights
SayakaOno Mar 5, 2026
1dfb67f
LF-5182 Update TapeResults to use surveyData in DB
SayakaOno Mar 6, 2026
52dec6d
LF-5182 Update tapeSurveySlice
SayakaOno Mar 6, 2026
cd441fe
LF-5182 Update and add function to reopen survey
SayakaOno Mar 6, 2026
44ad39d
LF-5182 Update submitTapeSurvey endpoint in tapeSurveyApi, update mut…
SayakaOno Mar 6, 2026
502720e
LF-5182 Adjust TapeSurveyRecord interface
SayakaOno Mar 9, 2026
4442369
LF-5182 Replace submission_type with id
SayakaOno Mar 9, 2026
dd99b55
LF-5182 Handle update tape survey
SayakaOno Mar 9, 2026
08b13c6
LF-5182 Rename submitTapeSurvey endpoint
SayakaOno Mar 9, 2026
c2344aa
LF-5182 Fix isTapeSurveyCompleted condition in Insights
SayakaOno Mar 9, 2026
4f40909
LF-5182 Update comment
SayakaOno Mar 9, 2026
8ee6bfe
Merge branch 'integration' into LF-5182-experiment-tape-plan-phase-1-…
SayakaOno Mar 10, 2026
17595e0
LF-5182 Delete tapeQuestions.json
SayakaOno Mar 10, 2026
1f11427
LF-5182 Correct types for tapeSurveyApi
SayakaOno Mar 10, 2026
447b4dc
LF-5182 Fix API args in TapeSurvey
SayakaOno Mar 10, 2026
1176ae1
Comment out TapeSurvey Step 2
SayakaOno Mar 10, 2026
172e8e2
LF-5182 Fix isTapeSurveyCompleted condition
SayakaOno Mar 10, 2026
de7b624
LF-5182 Remove code for TAPE survey update
SayakaOno Mar 10, 2026
f6c4421
LF-5182 Restore comment
SayakaOno Mar 10, 2026
319686a
LF-5182 Redirect user to survey when surveyData doesn't exist
SayakaOno Mar 10, 2026
575498a
LF-5182 Handle TAPE results errors
SayakaOno Mar 10, 2026
c37a76f
Merge branch 'integration' into LF-5182-experiment-tape-plan-phase-1-…
SayakaOno Mar 10, 2026
e9d1fff
LF-5182 Remove Tape survey step 2 code
SayakaOno Mar 11, 2026
3dc6153
LF-5182 Update Tape result calculation
SayakaOno Mar 11, 2026
3243b03
LF-5182 Disable TAPE menu onClick while fetching
SayakaOno Mar 11, 2026
cc9a2f5
LF-5182 Make TapeResults chart responsive
SayakaOno Mar 11, 2026
1a87abb
LF-5182 Adjust TAPE components styles
SayakaOno Mar 11, 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
1 change: 1 addition & 0 deletions packages/webapp/src/apiConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export const marketProductCategoryUrl = `${URI}/market_product_categories`;
export const marketDirectoryPartnersUrl = `${URI}/market_directory_partners`;
export const supportTicketUrl = `${URI}/support_ticket`;
export const offlineEventLogUrl = `${URI}/offline_event_log`;
export const tapeSurveyUrl = `${URI}/tape_survey`;

export const url = URI;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2026 LiteFarm.org
* This file is part of LiteFarm.
*
* LiteFarm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LiteFarm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
*/

/**
* Returns the survey version key used to fetch the correct JSON from DO CDN,
* based on the farm's 2-letter ISO country code.
*
* To add a new country-specific version:
* 1. Upload `tape-surveys/v1/<key>.json` to the DO Spaces bucket.
* 2. Add a case here mapping the relevant country_code(s) to that key.
*/
export const getSurveyVersion = (countryCode: string | undefined): string => {
if (countryCode === 'US') return 'us';
return 'default';
};
64 changes: 56 additions & 8 deletions packages/webapp/src/containers/Insights/TapeSurvey/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,41 @@ import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useTapeSurveyPrepopulatedData } from './useTapeSurveyPrepopulatedData';
import { saveSurveyProgress, completeSurvey, tapeSurveySelector } from './tapeSurveySlice';
import {
saveSurveyProgress,
completeSurvey,
clearSurvey,
tapeSurveySelector,
} from './tapeSurveySlice';
import SurveyComponent from '../../../components/SurveyComponent';
import surveyJson from './tapeQuestions.json';
import PageTitle from '../../../components/PageTitle';
import { userFarmSelector } from '../../userFarmSlice';
import {
useGetTapeSurveyJsonQuery,
useSubmitTapeSurveyMutation,
} from '../../../store/api/tapeSurveyApi';
import { getSurveyVersion } from './getSurveyVersion';
import { enqueueErrorSnackbar } from '../../Snackbar/snackbarSlice';

function TAPESurvey() {
const { t } = useTranslation();
const history = useHistory();
const dispatch = useDispatch();

const { prepopulatedData, isLoading } = useTapeSurveyPrepopulatedData();
const { prepopulatedData, isLoading: isPrepopulatedDataLoading } =
useTapeSurveyPrepopulatedData();

// @ts-expect-error -- userFarmSelector issue
const { country_code } = useSelector(userFarmSelector);
const versionKey = getSurveyVersion(country_code);

const {
data: surveyJson,
isLoading: isSurveyJsonLoading,
isError: isSurveyJsonError,
} = useGetTapeSurveyJsonQuery(versionKey);

const [submitTapeSurvey] = useSubmitTapeSurveyMutation();

const { surveyData: savedData, currentPageNo: savedPageNo } = useSelector(tapeSurveySelector);

Expand All @@ -38,15 +62,39 @@ function TAPESurvey() {
dispatch(saveSurveyProgress({ currentPageNo, surveyData }));
}, []);

const handleComplete = useCallback((currentPageNo: number, surveyData: any) => {
dispatch(completeSurvey({ currentPageNo, surveyData }));
history.push('/insights/tape/results');
}, []);
const handleComplete = useCallback(
async (currentPageNo: number, surveyData: any) => {
// Persist completion state immediately so TapeResults has data during the transition.
dispatch(completeSurvey({ currentPageNo, surveyData }));
try {
await submitTapeSurvey({ survey_data: surveyData }).unwrap();
// TODO: Once TapeResults reads from RTK Query cache instead of Redux, remove this
// clearSurvey call (or move it to after TapeResults has rendered from the cache).
dispatch(clearSurvey());
history.push('/insights/tape/results');
} catch {
// Submission failed; data stays in Redux so the user can retry.
dispatch(enqueueErrorSnackbar(t('INSIGHTS.TAPE.SUBMIT_ERROR')));
}
},
[submitTapeSurvey, dispatch, history, t],
);

const isLoading = isPrepopulatedDataLoading || isSurveyJsonLoading;

if (isSurveyJsonError) {
return (
<>
<PageTitle title={t('INSIGHTS.TAPE.TITLE')} backUrl="/Insights" />
<p>{t('INSIGHTS.TAPE.LOAD_ERROR')}</p>
</>
);
}

return (
<>
<PageTitle title={t('INSIGHTS.TAPE.TITLE')} backUrl="/Insights" />
{!isLoading && ( // wait for prepopulated data to load
{!isLoading && surveyJson && (
<SurveyComponent
surveyJson={surveyJson}
onComplete={handleComplete}
Expand Down
1 change: 1 addition & 0 deletions packages/webapp/src/store/api/apiTags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const FarmTags = [
'SensorReadings',
'Weather',
'MarketDirectoryInfo',
'TapeSurvey',
] as const;

/**
Expand Down
58 changes: 58 additions & 0 deletions packages/webapp/src/store/api/tapeSurveyApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2026 LiteFarm.org
* This file is part of LiteFarm.
*
* LiteFarm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LiteFarm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
*/

import { api } from './apiSlice';
import { tapeSurveyUrl } from '../../apiConfig';
import { DO_CDN_URL } from '../../util/constants';

export interface TapeSurveyRecord {
tape_survey_id: number;
farm_id: string;
user_id: string;
survey_data: Record<string, any>;
}

export const tapeSurveyApi = api.injectEndpoints({
endpoints: (build) => ({
// Fetches the SurveyJS JSON definition from DO CDN.
// Uses queryFn (not query) because this bypasses the LiteFarm API base URL and auth headers.
getTapeSurveyJson: build.query<Record<string, any>, string>({
queryFn: async (versionKey) => {
try {
const response = await fetch(`${DO_CDN_URL}/tape-surveys/v1/${versionKey}.json`);
if (!response.ok) {
return {
error: { status: response.status, data: `Failed to fetch survey JSON` },
};
}
const data = await response.json();
return { data };
} catch (error) {
return { error: { status: 'FETCH_ERROR', error: String(error) } };
}
},
}),
submitTapeSurvey: build.mutation<TapeSurveyRecord, { survey_data: Record<string, any> }>({
query: (body) => ({
url: tapeSurveyUrl,
method: 'POST',
body,
}),
invalidatesTags: ['TapeSurvey'],
}),
}),
});

export const { useGetTapeSurveyJsonQuery, useSubmitTapeSurveyMutation } = tapeSurveyApi;