Skip to content

Commit

Permalink
feat(dataTrak): RN-1274: Keep 'outdated' historical survey responses …
Browse files Browse the repository at this point in the history
…when resubmitting (#5758)

* RN-1274: change route to `surveyResponses/:id/resubmit`

* RN-1274: Reworked ResubmitSurveyResponse route to create a new response and outdate the previous one

* RN-1274: Added resubmitSurveyResponse to the CentralApi

* RN-1274: Added ResubmitSurveyResponseRoute to datatrak-web-server

* RN-1274: Reworked survey response resubmission in datatrak to use the new backend routes

* Fix tests

* Edit survey response metadata

* Fix tests

* Edit survey response metadata

* Resubmit survey response with original data time and user ID

* PR fixes

* take 1

* feat(adminPanel): RN-1228: Link surveys to Datatrak Web (#5671)

* Make links

* Use projectId

* Update user preferences if project id is in url

* Add comment

* Allow country codes to be fetched for surveys

* Link directly to survey

* Default to DL and alphabetise the country codes

* Change tooltip text

* Update copy

* Hide button for surveys with no countries

---------

Co-authored-by: Andrew <vanbeekandrew@gmail.com>

* Fix dataTime timezone change

* Allow file questions to be viewed and changed

* feat(tupaiaWeb): RN-1367: Multiphotograph viz captions + restyle (#5769)

* Add `label` property to view data

* Preview display

* Display max 3

* WIP carousel

* WIP

* Working thumbnails

* Working carousel

* WiP

* Styling

* Add comments

* Update schemas.ts

* Adjust height and alignment

* Make images contained

* Fix responsive issue

---------

Co-authored-by: Andrew <vanbeekandrew@gmail.com>

* Fix issue

* Fix date of data

* Add outdated column to survey responses in admin panel

* Reset entity question values when filter questions change

* fix(datatrakWeb): Fix country code selector in reports export

* fix(adminPanel): RN-1375: update 'Add' project editor for consistency (#5816)

update editor column for consistency

* Handle existing file answers

* Use existing entity id if present

* Add pill styling for response status field

* Handle file names

* Change pill colours around

* Handle survey response file names

* Don't save file url in answer

* Fix tests

* Don't default dates on resubmit

* Handle when photo answer is a url

* Allow `null` default date for resubmission

* Save previous metadata on tracked entity

* Fix undefined models error

* Update project.pbxproj

* Hide survey resubmit button for outdated responses

* tweak(tupaiaWeb): RN-1394: Update tool tip for visualisation export (#5824)

Tool tip update

* tweak(adminPanel): RN-1399: Update icon and color as per Figma layouts (#5825)

Download Icon update

* tweak(adminPanel): RN-1274: Remove outdated survey responses and associated answers from DHIS via sync queue (#5827)

* Remove outdated survey responses and associated answers from dhis via sync queue

* Add tests

* Add answers back into queue when survey response is changed back to current

* Handle answers for outdated->current tests

* Fix tests

* Revert change to filter

* Ignore outdated surveys from exports

* Code question should be code generator type

* Fix tests

* Fix timezone issues

* Update processSurveyResponse.test.ts

* Get all answers for survey response

* Handle when photo includes a url

* Fix tests

* Fix crashing error

* Concert jpeg to jpg

* Keep existing survey response timezone

* fix(tupaiaWeb): RN-1414: Fix dashboard item permission error (#5836)

Update ReportPermissionsChecker.js

* Timezones

* fix(adminPanel): RN-1289: update the entity associated with a survey resubmission (#5817)

* Initial update

* test updates

* Update importSurveyResponses.js

* Update importSurveyResponses.js

* Update importSurveyResponses.js

* Update SurveyResponseUpdatePersistor.js

* Delete ~$nonPeriodicUpdates.xlsx

* test updates

* review comments

* review updates

* addition of tests

---------

Co-authored-by: Andrew <vanbeekandrew@gmail.com>

* Convert data_time to timezone date on server

* Fix tests

* Make dates/times zoneless so that they appear the same to everyone

* Fix tests

* Fix timezone offsets

* Handle timezones with DST

* Fixes

---------

Co-authored-by: alexd-bes <129009580+alexd-bes@users.noreply.github.com>
Co-authored-by: Salman <114740396+hrazasalman@users.noreply.github.com>
Co-authored-by: Andrew <vanbeekandrew@gmail.com>
Co-authored-by: Tom Caiger <caigertom@gmail.com>
  • Loading branch information
5 people authored Aug 15, 2024
1 parent 79212bf commit a193aec
Show file tree
Hide file tree
Showing 89 changed files with 1,722 additions and 804 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
import { useMutation, useQueryClient } from 'react-query';
import { useApiContext } from '../../utilities/ApiProvider';

export const useResubmitSurveyResponse = (surveyResponseId, updatedSurveyResponse) => {
export const useEditSurveyResponse = (surveyResponseId, updatedSurveyResponse) => {
const queryClient = useQueryClient();
const api = useApiContext();
return useMutation(
[`surveyResubmit`, surveyResponseId, updatedSurveyResponse],
[`surveyResponseEdit`, surveyResponseId, updatedSurveyResponse],
() => {
return api.post(`surveyResponse/${surveyResponseId}/resubmit`, null, updatedSurveyResponse);
return api.put(`surveyResponses/${surveyResponseId}`, null, updatedSurveyResponse);
},
{
throwOnError: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/admin-panel/src/importExport/ExportButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import React from 'react';
import PropTypes from 'prop-types';
import ExportIcon from '@material-ui/icons/GetApp';
import { ExportIcon } from '../icons';
import { makeSubstitutionsInString } from '../utilities';
import { useApiContext } from '../utilities/ApiProvider';
import { ColumnActionButton } from '../table/columnTypes/ColumnActionButton';
Expand Down
33 changes: 19 additions & 14 deletions packages/admin-panel/src/routes/projects/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,6 @@ const DEFAULT_FIELDS = [
maxWidth: 480,
},
},
{
Header: 'Config',
source: 'config',
type: 'jsonTooltip',
editConfig: {
type: 'jsonEditor',
labelTooltip: 'eg. { "tileSets": "osm,satellite,terrain", "permanentRegionLabels": true }',
},
},
{
Header: 'Sort',
source: 'sort_order',
width: 80,
},
];

const CREATE_FIELDS = [
Expand All @@ -101,6 +87,11 @@ const CREATE_FIELDS = [
required: true,
},
...DEFAULT_FIELDS,
{
Header: 'Sort',
source: 'sort_order',
width: 80,
},
];

const EDIT_FIELDS = [
Expand All @@ -111,6 +102,20 @@ const EDIT_FIELDS = [
required: true,
},
...DEFAULT_FIELDS,
{
Header: 'Config',
source: 'config',
type: 'jsonTooltip',
editConfig: {
type: 'jsonEditor',
labelTooltip: 'eg. { "tileSets": "osm,satellite,terrain", "permanentRegionLabels": true }',
},
},
{
Header: 'Sort',
source: 'sort_order',
width: 80,
},
];

const NEW_PROJECT_COLUMNS = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,45 @@
* Tupaia
* Copyright (c) 2017 - 2024 Beyond Essential Systems Pty Ltd
*/

import React from 'react';
import { getBrowserTimeZone } from '@tupaia/utils';
import moment from 'moment';
import { ApprovalStatus } from '@tupaia/types';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { SurveyResponsesExportModal } from '../../importExport';
import { getPluralForm } from '../../pages/resources/resourceName';
import { OutdatedFilter } from '../../table/columnTypes/columnFilters';

const RESOURCE_NAME = { singular: 'survey response' };

// Don't include not_required as an editable option because it can lead to
// mis-matches between surveys and survey responses
export const APPROVAL_STATUS_TYPES = Object.values(ApprovalStatus).map(type => ({
label: type,
value: type,
}));
const GREEN = '#47CA80';
const GREY = '#898989';

const Pill = styled.span`
background-color: ${({ $color }) => {
return `${$color}33`; // slightly transparent
}};
border-radius: 1.5rem;
padding: 0.3rem 0.9rem;
color: ${({ $color }) => $color};
.cell-content:has(&) > div {
overflow: visible;
}
`;

const ResponseStatusPill = ({ value }) => {
const text = value ? 'Outdated' : 'Current';
const color = value ? GREY : GREEN;
return <Pill $color={color}>{text}</Pill>;
};

ResponseStatusPill.propTypes = {
value: PropTypes.bool,
};

ResponseStatusPill.defaultProps = {
value: false,
};

const surveyName = {
Header: 'Survey',
Expand Down Expand Up @@ -56,9 +80,13 @@ const dateOfData = {
},
};

const approvalStatus = {
Header: 'Approval status',
source: 'approval_status',
const responseStatus = {
Header: 'Response status',
source: 'outdated',
Filter: OutdatedFilter,
width: 180,
// eslint-disable-next-line react/prop-types
Cell: ({ value }) => <ResponseStatusPill value={value} />,
};

const entityName = {
Expand All @@ -80,7 +108,7 @@ export const SURVEY_RESPONSE_COLUMNS = [
assessorName,
date,
dateOfData,
approvalStatus,
responseStatus,
{
Header: 'Export',
type: 'export',
Expand Down
25 changes: 25 additions & 0 deletions packages/admin-panel/src/routes/surveys/surveys.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { SurveyEditFields } from '../../surveys/SurveyEditFields';
import { QUESTION_FIELDS as BASE_QUESTION_FIELDS } from './questions';
import { EditSurveyPage } from '../../pages/resources';

const { REACT_APP_DATATRAK_WEB_URL } = import.meta.env;

const RESOURCE_NAME = { singular: 'survey' };

const PERIOD_GRANULARITIES = [
Expand Down Expand Up @@ -175,6 +177,16 @@ const SURVEY_COLUMNS = [
SURVEY_FIELDS.project,
SURVEY_FIELDS.name,
SURVEY_FIELDS.code,
{
Header: 'Project ID',
source: 'project.id',
show: false,
},
{
Header: 'countries',
source: 'countryCodes',
show: false,
},
{
Header: 'Permission group',
source: 'permission_group.name',
Expand All @@ -183,6 +195,19 @@ const SURVEY_COLUMNS = [
Header: 'Survey group',
source: 'survey_group.name',
},
{
Header: 'View',
type: 'externalLink',
actionConfig: {
generateUrl: row => {
const { code, countryCodes } = row;
if (!countryCodes || !countryCodes.some(countryCode => !!countryCode)) return null;
const countryCodeToUse = countryCodes.includes('DL') ? 'DL' : countryCodes[0];
return `${REACT_APP_DATATRAK_WEB_URL}/survey/${countryCodeToUse}/${code}/1`;
},
title: 'View in DataTrak',
},
},
{
Header: 'Export',
type: 'export',
Expand Down
25 changes: 11 additions & 14 deletions packages/admin-panel/src/surveyResponse/Form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import styled from 'styled-components';
import { Button } from '@tupaia/ui-components';
import { useGetExistingData } from './useGetExistingData';
import { ModalContentProvider, ModalFooter } from '../widgets';
import { useResubmitSurveyResponse } from '../api/mutations/useResubmitSurveyResponse';
import { useEditSurveyResponse } from '../api/mutations/useEditSurveyResponse';
import { ResponseFields } from './ResponseFields';

const ButtonGroup = styled.div`
Expand All @@ -19,23 +19,23 @@ const ButtonGroup = styled.div`
`;

export const Form = ({ surveyResponseId, onDismiss, onAfterMutate }) => {
const [surveyResubmission, setSurveyResubmission] = useState({});
const isUnchanged = Object.keys(surveyResubmission).length === 0;
const [editedData, setEditedData] = useState({});
const isUnchanged = Object.keys(editedData).length === 0;

const [selectedEntity, setSelectedEntity] = useState({});

const {
mutateAsync: resubmitResponse,
mutateAsync: editResponse,
isLoading,
isError,
error: resubmitError,
error: editError,
reset, // reset the mutation state so we can dismiss the error
isSuccess,
} = useResubmitSurveyResponse(surveyResponseId, surveyResubmission);
} = useEditSurveyResponse(surveyResponseId, editedData);

const { data, isLoading: isFetching, error: fetchError } = useGetExistingData(surveyResponseId);

const existingAndNewFields = { ...data?.surveyResponse, ...surveyResubmission };
const existingAndNewFields = { ...data?.surveyResponse, ...editedData };

useEffect(() => {
if (!data) {
Expand All @@ -46,7 +46,7 @@ export const Form = ({ surveyResponseId, onDismiss, onAfterMutate }) => {
}, [data]);

const resubmitSurveyResponse = async () => {
await resubmitResponse();
await editResponse();
onAfterMutate();
};

Expand All @@ -61,7 +61,7 @@ export const Form = ({ surveyResponseId, onDismiss, onAfterMutate }) => {
const resubmitResponseAndRedirect = async () => {
// If the response has been changed, resubmit it before redirecting
if (!isUnchanged) {
await resubmitResponse();
await editResponse();
onAfterMutate();
}
const { country_code: updatedCountryCode } = selectedEntity;
Expand Down Expand Up @@ -107,17 +107,14 @@ export const Form = ({ surveyResponseId, onDismiss, onAfterMutate }) => {

return (
<>
<ModalContentProvider
isLoading={isFetching || isResubmitting}
error={fetchError || resubmitError}
>
<ModalContentProvider isLoading={isFetching || isLoading} error={fetchError || editError}>
{!isFetching && !isSuccess && (
<ResponseFields
selectedEntity={selectedEntity}
surveyName={data?.survey.name}
fields={existingAndNewFields}
onChange={(field, updatedField) =>
setSurveyResubmission({ ...surveyResubmission, [field]: updatedField })
setEditedData({ ...editedData, [field]: updatedField })
}
setSelectedEntity={setSelectedEntity}
/>
Expand Down
39 changes: 39 additions & 0 deletions packages/admin-panel/src/table/columnTypes/ExternalLinkButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Tupaia
* Copyright (c) 2017 - 2024 Beyond Essential Systems Pty Ltd
*/

import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '@material-ui/core';
import { OpenInNewRounded } from '@material-ui/icons';
import { ColumnActionButton } from './ColumnActionButton';
import { makeSubstitutionsInString } from '../../utilities';

export const ExternalLinkButton = ({ actionConfig, row }) => {
const getUrl = () => {
if (actionConfig.generateUrl) {
return actionConfig.generateUrl(row.original);
}
return makeSubstitutionsInString(actionConfig.url, row.original);
};
const fullUrl = getUrl();
if (!fullUrl) return null;

return (
<ColumnActionButton
className="link-button"
title={actionConfig.title}
component={Link}
href={fullUrl}
target="_blank"
>
<OpenInNewRounded />
</ColumnActionButton>
);
};

ExternalLinkButton.propTypes = {
actionConfig: PropTypes.object.isRequired,
row: PropTypes.object.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import EditIcon from '@material-ui/icons/Edit';
import { openResubmitSurveyResponseModal } from '../../surveyResponse/actions';
import { ColumnActionButton } from './ColumnActionButton';

export const ResubmitSurveyResponseButtonComponent = ({ openModal }) => {
export const ResubmitSurveyResponseButtonComponent = ({ openModal, row }) => {
if (row.original.outdated) return null;
return (
<ColumnActionButton onClick={openModal}>
<EditIcon />
Expand All @@ -20,6 +21,11 @@ export const ResubmitSurveyResponseButtonComponent = ({ openModal }) => {

ResubmitSurveyResponseButtonComponent.propTypes = {
openModal: PropTypes.func.isRequired,
row: PropTypes.shape({
original: PropTypes.shape({
outdated: PropTypes.bool.isRequired,
}).isRequired,
}).isRequired,
};

const mapDispatchToProps = (dispatch, ownProps) => {
Expand Down
35 changes: 35 additions & 0 deletions packages/admin-panel/src/table/columnTypes/columnFilters.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,41 @@ BooleanSelectFilter.defaultProps = {
filter: {},
};

/*
* Makes outdated field work with the database filter
*/

export const OutdatedFilter = ({ filter, onChange, column }) => {
return (
<Select
id={column.id}
options={[
{ label: 'Show All', value: '' },
{ label: 'Outdated', value: true },
{ label: 'Current', value: false },
]}
onChange={e => onChange(e.target.value)}
value={filter?.value ?? ''}
/>
);
};

OutdatedFilter.propTypes = {
column: PropTypes.PropTypes.shape({
id: PropTypes.string,
}),
filter: PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]).isRequired,
}),
onChange: PropTypes.func,
};

OutdatedFilter.defaultProps = {
onChange: null,
column: {},
filter: {},
};

export const VerifiedFilter = ({ filter, onChange, column }) => {
return (
<Select
Expand Down
Loading

0 comments on commit a193aec

Please sign in to comment.