Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WAITP-1452: Recent survey response page #5157

Merged
merged 27 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eaaf19f
WAITP-1478 Add types definition for RecentSurveyResponseRoute
EMcQ-BES Nov 8, 2023
78c41d8
WAITP-1478 Regen types
EMcQ-BES Nov 8, 2023
3f2b785
WAITP-1478 Add route for RecentSurveyResponse
EMcQ-BES Nov 8, 2023
86bbac7
WAITP-1478 Fixup export
EMcQ-BES Nov 8, 2023
ee927d9
WAITP-1478 Update url
EMcQ-BES Nov 8, 2023
ffad9f1
WAITP-1478 Regen types
EMcQ-BES Nov 8, 2023
52bc53b
WAITP-1478 Merge branch 'datatrak-web-epic' into waitp-1478-recent-su…
EMcQ-BES Nov 8, 2023
33796e9
useSurveyResponse
tcaiger Nov 9, 2023
a60ad2c
survey review screen
tcaiger Nov 9, 2023
f537152
Merge branch 'datatrak-web-epic' into waitp-1452-recent-survey-response
tcaiger Nov 9, 2023
fe2ad00
wip set default values
tcaiger Nov 9, 2023
9dcfbb9
set up route
tcaiger Nov 9, 2023
2794127
Update useLogin.ts
tcaiger Nov 9, 2023
c7b6535
refactor survey context to be at survey level
tcaiger Nov 10, 2023
83a599d
Merge branch 'datatrak-web-epic' into waitp-1452-refactor-survey-context
tcaiger Nov 10, 2023
093cfac
Merge branch 'waitp-1452-refactor-survey-context' into waitp-1452-rec…
tcaiger Nov 10, 2023
ad4ca1f
remove bad merge
tcaiger Nov 10, 2023
713410a
survey data
tcaiger Nov 12, 2023
d0634e3
basic working setup
tcaiger Nov 12, 2023
428c54f
Merge branch 'datatrak-web-epic' into waitp-1452-recent-survey-response
tcaiger Nov 12, 2023
b330e94
remove debug changes
tcaiger Nov 12, 2023
62fbb4f
split out paginator
tcaiger Nov 12, 2023
aed8a00
put page back in survey layout
tcaiger Nov 12, 2023
25604c9
refactor layout components
tcaiger Nov 13, 2023
762168b
Update index.ts
tcaiger Nov 13, 2023
006c683
Update SurveyContext.tsx
tcaiger Nov 13, 2023
862e048
Update SurveyReviewSection.tsx
tcaiger Nov 13, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,42 @@ export type SingleSurveyResponseRequest = Request<
DatatrakWebSingleSurveyResponseRequest.ReqQuery
>;

const ANSWER_COLUMNS = [
'text', 'question_id'
]
const ANSWER_COLUMNS = ['text', 'question_id'];

const DEFAULT_FIELDS = [
'assessor_name',
'country.name',
'data_time',
'entity.name',
'id',
'survey.name',
'survey.code',
];

export class SingleSurveyResponseRoute extends Route<SingleSurveyResponseRequest> {
public async buildResponse() {
const { ctx, params } = this.req;
const { ctx, params, query } = this.req;
const { id: responseId } = params;

const surveyResponse = await ctx.services.central.fetchResources(`surveyResponses/${responseId}`);
const { fields = DEFAULT_FIELDS } = query;

const surveyResponse = await ctx.services.central.fetchResources(
`surveyResponses/${responseId}`,
{ columns: fields },
);
const answerList = await ctx.services.central.fetchResources('answers', {
filter: { survey_response_id: surveyResponse.id },
columns: ANSWER_COLUMNS,
});
const answers = answerList.reduce((output: Record<string, string>, answer: { question_id: string, text: string }) => ({ ...output, [answer.question_id]: answer.text }), {});
const answers = answerList.reduce(
(output: Record<string, string>, answer: { question_id: string; text: string }) => ({
...output,
[answer.question_id]: answer.text,
}),
{},
);

return camelcaseKeys({ ...surveyResponse, answers }, { deep: true });
// Don't return the answers in camel case because the keys are question IDs which we want in lowercase
return camelcaseKeys({ ...surveyResponse, answers });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const DEFAULT_FIELDS = [
'entity.name',
'id',
'survey.name',
'survey.code',
];

const DEFAULT_LIMIT = 16;
Expand Down
5 changes: 4 additions & 1 deletion packages/datatrak-web-server/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export { EntitiesRequest, EntitiesRoute } from './EntitiesRoute';
export { ProjectRequest, ProjectRoute } from './ProjectRoute';
export { SubmitSurveyRequest, SubmitSurveyRoute } from './SubmitSurvey/SubmitSurveyRoute';
export { RecentSurveysRequest, RecentSurveysRoute } from './RecentSurveysRoute';
export { SingleSurveyResponseRequest, SingleSurveyResponseRoute } from './SingleSurveyResponseRoute';
export {
SingleSurveyResponseRequest,
SingleSurveyResponseRoute,
} from './SingleSurveyResponseRoute';
export { LeaderboardRequest, LeaderboardRoute } from './LeaderboardRoute';
export { ActivityFeedRequest, ActivityFeedRoute } from './ActivityFeedRoute';
2 changes: 2 additions & 0 deletions packages/datatrak-web/src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
ForgotPasswordPage,
ResetPasswordPage,
AccountSettingsPage,
SurveyResponsePage,
} from './views';
import { useUser } from './api/queries';
import { ROUTES } from './constants';
Expand Down Expand Up @@ -113,6 +114,7 @@ export const SurveyPageRoutes = (
<Route index element={<SurveyStartRedirect />} />
<Route path={ROUTES.SURVEY_SUCCESS} element={<SurveySuccessScreen />} />
<Route element={<SurveyLayout />}>
<Route path={ROUTES.SURVEY_RESPONSE} element={<SurveyResponsePage />} />
<Route path={ROUTES.SURVEY_REVIEW} element={<SurveyReviewScreen />} />
<Route
path={ROUTES.SURVEY_SCREEN}
Expand Down
1 change: 1 addition & 0 deletions packages/datatrak-web/src/api/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export { useUser } from './useUser';
export { useProjects } from './useProjects';
export { useSurveys } from './useSurveys';
export { useSurvey } from './useSurvey';
export { useSurveyResponse } from './useSurveyResponse';
export { useSurveyResponses, useCurrentUserSurveyResponses } from './useSurveyResponses';
export { useEntities } from './useEntities';
export { useEntity } from './useEntity';
Expand Down
16 changes: 16 additions & 0 deletions packages/datatrak-web/src/api/queries/useSurveyResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import { useQuery } from 'react-query';
import { DatatrakWebSingleSurveyResponseRequest } from '@tupaia/types';
import { get } from '../api';

export const useSurveyResponse = (surveyResponseId?: string) => {
return useQuery(
['surveyResponse', surveyResponseId],
(): Promise<DatatrakWebSingleSurveyResponseRequest.ResBody> =>
get(`surveyResponse/${surveyResponseId}`),
{ enabled: !!surveyResponseId },
);
};
1 change: 1 addition & 0 deletions packages/datatrak-web/src/constants/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const ROUTES = {
SURVEY_SCREEN: `${SURVEY_URL}/:screenNumber`,
SURVEY_SUCCESS: `${SURVEY_URL}/success`,
SURVEY_REVIEW: `${SURVEY_URL}/review`,
SURVEY_RESPONSE: `${SURVEY_URL}/response/:surveyResponseId`,
ACCOUNT_SETTINGS: '/account-settings',
CHANGE_PROJECT: '/change-project',
VERIFY_EMAIL: '/verify-email',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import React from 'react';
import styled from 'styled-components';
import { useOutletContext } from 'react-router-dom';
import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos';
import { useSurveyForm } from '../SurveyContext';
import { Button } from '../../../components';
import { useIsMobile } from '../../../utils';

const FormActions = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0.5rem;
border-top: 1px solid ${props => props.theme.palette.divider};
button:last-child {
margin-left: auto;
}
${({ theme }) => theme.breakpoints.up('md')} {
padding: 1rem;
}
`;

const ButtonGroup = styled.div`
display: flex;
button,
a {
&:last-child {
margin-left: 1rem;
}
}
`;

const BackButton = styled(Button).attrs({
startIcon: <ArrowBackIosIcon />,
variant: 'text',
color: 'default',
})`
${({ theme }) => theme.breakpoints.down('md')} {
padding-left: 0.8rem;
.MuiButton-startIcon {
margin-right: 0.25rem;
}
}
`;

type SurveyLayoutContextT = { isLoading: boolean; onStepPrevious: () => void };

export const SurveyPaginator = () => {
const { isLast, isReviewScreen, openCancelConfirmation } = useSurveyForm();
const isMobile = useIsMobile();
const { isLoading, onStepPrevious } = useOutletContext<SurveyLayoutContextT>();

const getNextButtonText = () => {
if (isReviewScreen) return 'Submit';
if (isLast) {
return isMobile ? 'Review' : 'Review and submit';
}
return 'Next';
};

const nextButtonText = getNextButtonText();

return (
<FormActions>
<BackButton onClick={onStepPrevious} disabled={isLoading}>
Back
</BackButton>
<ButtonGroup>
<Button onClick={openCancelConfirmation} variant="outlined" disabled={isLoading}>
Cancel
</Button>
<Button type="submit" disabled={isLoading}>
{nextButtonText}
</Button>
</ButtonGroup>
</FormActions>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import React from 'react';
import { QuestionType } from '@tupaia/types';
import styled from 'styled-components';
import { Typography } from '@material-ui/core';
Copy link
Contributor

Choose a reason for hiding this comment

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

Just need to reorder these imports

import { SurveyQuestionGroup } from './SurveyQuestionGroup';
import { formatSurveyScreenQuestions, getSurveyScreenNumber } from '../utils';
import { useSurveyForm } from '../SurveyContext';

const Section = styled.section`
padding: 1rem 0;
&:first-child {
padding-top: 0;
}
`;

const SectionHeader = styled(Typography).attrs({
variant: 'h3',
})`
font-size: 1rem;
font-weight: ${({ theme }) => theme.typography.fontWeightMedium};
margin-bottom: 1rem;
${({ theme }) => theme.breakpoints.up('sm')} {
font-size: 1.125rem;
}
`;

const Fieldset = styled.fieldset.attrs({
disabled: true,
})`
border: none;
margin: 0;
padding: 0;
input,
label,
button,
.MuiInputBase-root,
link {
pointer-events: none;
}
`;
export const SurveyReviewSection = () => {
const { visibleScreens } = useSurveyForm();

if (!visibleScreens || !visibleScreens.length) {
return null;
}

// split the questions into sections by screen so it's easier to read the long form
const questionSections = visibleScreens.map(screen => {
const { surveyScreenComponents } = screen;
const screenNumber = getSurveyScreenNumber(visibleScreens, screen);
const heading = surveyScreenComponents[0].text;
const firstQuestionIsInstruction = surveyScreenComponents[0].type === QuestionType.Instruction;

// if the first question is an instruction, don't display it, because it will be displayed as the heading
const questionsToDisplay = firstQuestionIsInstruction
? surveyScreenComponents.slice(1)
: surveyScreenComponents;
return {
heading,
questions: formatSurveyScreenQuestions(questionsToDisplay, screenNumber),
};
});
return (
<Fieldset>
{questionSections.map(({ heading, questions }, index) => (
<Section key={index}>
<SectionHeader>{heading}</SectionHeader>
<SurveyQuestionGroup questions={questions} />
</Section>
))}
</Fieldset>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ export const SurveySideMenu = () => {
setFormData,
isReviewScreen,
isSuccessScreen,
isResponseScreen,
} = useSurveyForm();
if (isReviewScreen || isSuccessScreen) return null;
if (isReviewScreen || isSuccessScreen || isResponseScreen) return null;
const onChangeScreen = () => {
setFormData(getValues());
if (isMobile) toggleSideMenu();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const CountryName = styled.span`

export const SurveyToolbar = () => {
const { surveyCode, screenNumber: screenNumberParam } = useParams();
const { screenNumber, numberOfScreens } = useSurveyForm();
const { screenNumber, numberOfScreens, isResponseScreen } = useSurveyForm();
const { data: survey } = useSurvey(surveyCode);
const { data: user } = useUser();
const isMobile = useIsMobile();
Expand All @@ -69,6 +69,10 @@ export const SurveyToolbar = () => {
};
const surveyName = getDisplaySurveyName();

if (isResponseScreen) {
return null;
}

return (
<Toolbar $toolbarIsTransparent={!screenNumberParam}>
<SurveyTitleWrapper>
Expand Down
2 changes: 2 additions & 0 deletions packages/datatrak-web/src/features/Survey/Components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export { SurveyQuestion } from './SurveyQuestion';
export { SurveyQuestionGroup } from './SurveyQuestionGroup';
export { SurveyToolbar } from './SurveyToolbar';
export * from './SurveySideMenu';
export { SurveyReviewSection } from './SurveyReviewSection';
export { SurveyPaginator } from './SurveyPaginator';
Loading