Skip to content

Commit

Permalink
(feat: M2-7146) [Web] Processing Screen implementation (Basic UI) (#499)
Browse files Browse the repository at this point in the history
* The EntityTimer UI was integrated into the Survey Header

* The ProcessingScreen UI

* Provide the respondentMeta to SurveyContext

* Improve the navigateTo handler for the processingAnswers routes

* EN/FR translations for the processing screen

* Provide custom title to the SurveyHeader

* Add the ability to complete the flow by force

* Update the survey header styles

* CheckCircle icon

* The main screen for the Processing screen done

* Loader added to the ProcessingScreen

* Change the SurveyHeader template

* ProgressBar style fixes after code review

---------

Co-authored-by: Viktor Riabkov <[email protected]>
  • Loading branch information
moiskillnadne and Viktor Riabkov authored Jul 4, 2024
1 parent 05da2a9 commit ab107b0
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 42 deletions.
12 changes: 8 additions & 4 deletions src/entities/applet/model/hooks/useEntityComplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type Props = {
flow: ActivityFlowDTO | null;
};

type CompleteFlowInput = {
forceComplete: boolean;
};

export const useEntityComplete = (props: Props) => {
const navigator = useCustomNavigation();
const { featureFlags } = useFeatureFlags();
Expand Down Expand Up @@ -78,7 +82,7 @@ export const useEntityComplete = (props: Props) => {
);
};

const completeFlow = () => {
const completeFlow = (input?: CompleteFlowInput) => {
const { flow } = props;

const groupProgress = getGroupProgress({
Expand Down Expand Up @@ -113,11 +117,11 @@ export const useEntityComplete = (props: Props) => {

removeActivityProgress({ activityId: props.activityId, eventId: props.eventId });

if (!nextActivityId) {
return completeEntityAndRedirect();
if (nextActivityId && !input?.forceComplete) {
return redirectToNextActivity(nextActivityId);
}

return redirectToNextActivity(nextActivityId);
return completeEntityAndRedirect();
};

const completeActivity = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useContext } from 'react';

import { EntityTimer } from './EntityTimer';
import { SurveyContext } from '../lib';
import { SurveyContext } from '../../lib';
import { EntityTimer } from '../EntityTimer';

import { SaveAndExitButton } from '~/features/SaveAssessmentAndExit';
import { MultiInformantTooltip } from '~/features/TakeNow';
Expand All @@ -10,6 +10,7 @@ import { AvatarBase, BaseProgressBar, Box, Text } from '~/shared/ui';
import { HourMinute, isStringExist, useCustomMediaQuery } from '~/shared/utils';

type Props = {
title?: string;
progress?: number;
isSaveAndExitButtonShown: boolean;

Expand All @@ -25,6 +26,12 @@ const SurveyHeader = (props: Props) => {
return str.length > length ? `${str.substring(0, length)}...` : str;
};

const title = props.title ?? context.activity.name;

const isProgressDefined = props.progress !== undefined;

const titleMarginBottom = greaterThanSM ? '8px' : '10px';

return (
<Box
paddingX={greaterThanSM ? '24px' : '16px'}
Expand All @@ -43,24 +50,27 @@ const SurveyHeader = (props: Props) => {

<Box
id="activity-details-header"
display="flex"
display="grid"
gridAutoFlow="column"
alignItems="center"
justifyContent="space-between"
height="100px"
justifyContent="center"
gridTemplateColumns="1fr minmax(300px, 900px) 1fr"
gap={1.5}
>
{greaterThanSM && props.entityTimer && (
<EntityTimer entityTimerSettings={props.entityTimer} />
<Box flex={1}>
<EntityTimer entityTimerSettings={props.entityTimer} />
</Box>
)}

{greaterThanSM && <MultiInformantTooltip />}

<Box flex={1} minWidth="300px" maxWidth="900px">
<Box gridColumn="2/3">
<Box
display="flex"
justifyContent={greaterThanSM ? 'center' : 'space-between'}
alignItems="center"
marginBottom={greaterThanSM ? '8px' : '10px'}
marginBottom={isProgressDefined ? titleMarginBottom : undefined}
>
<Box display="flex" alignItems="center" gap="8px">
{isStringExist(context.watermark) && (
Expand All @@ -72,9 +82,7 @@ const SurveyHeader = (props: Props) => {
testid="assessment-activity-title"
sx={{ textAlign: greaterThanSM ? 'center' : 'left' }}
>
{greaterThanSM
? context.activity.name
: cutStringToLength(context.activity.name, 30)}
{greaterThanSM ? title : cutStringToLength(title, 30)}
</Text>
</Box>
{!greaterThanSM && props.isSaveAndExitButtonShown && (
Expand All @@ -84,9 +92,10 @@ const SurveyHeader = (props: Props) => {
/>
)}
</Box>
{props.progress !== undefined && (

{isProgressDefined && (
<BaseProgressBar
percentage={props.progress}
percentage={props.progress as number}
testid="assessment-activity-progress-bar"
/>
)}
Expand All @@ -96,6 +105,7 @@ const SurveyHeader = (props: Props) => {
<Box
width="125px"
height="100%"
gridColumn="3/4"
display="flex"
alignItems="center"
justifyContent="center"
Expand Down
2 changes: 2 additions & 0 deletions src/features/PassSurvey/ui/SurveyLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Banners, Box } from '~/shared/ui';
import { HourMinute } from '~/shared/utils';

type Props = PropsWithChildren<{
title?: string;
progress?: number;
entityTimer?: HourMinute;

Expand All @@ -28,6 +29,7 @@ const SurveyLayout = (props: Props) => {
progress={props.progress}
isSaveAndExitButtonShown={props.isSaveAndExitButtonShown}
entityTimer={props.entityTimer}
title={props.title}
/>

<Box
Expand Down
8 changes: 8 additions & 0 deletions src/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@
"the_following_day": "the following day"
},

"answerProcessingScreen": {
"title": "Processing answers",
"description": "Please wait and do not close the app. Your answers are being processed.",
"processInProgress": "We are still processing your answers. Please wait.",
"processCompleted": "Your answers have been processed. You can now close the page.",
"processMyAnswers": "Process my answers"
},

"data_sharing": {
"consent": "I consent to making my responses",
"media_consent": "I consent to making my media responses",
Expand Down
8 changes: 8 additions & 0 deletions src/i18n/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@
"the_following_day": "le jour suivant"
},

"answerProcessingScreen": {
"title": "Traitement des réponses",
"description": "Veuillez patienter et ne fermez pas l'application. Vos réponses sont en cours de traitement.",
"processInProgress": "Nous traitons toujours vos réponses. Veuillez patienter.",
"processCompleted": "Vos réponses ont été traitées. Vous pouvez maintenant fermer la page.",
"processMyAnswers": "Traiter mes réponses"
},

"data_sharing": {
"consent": "Je consens à rendre mes réponses",
"media_consent": "Je consens à rendre mes réponses des médias",
Expand Down
2 changes: 1 addition & 1 deletion src/pages/PublicSurveyAnswerProcessing/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useSearchParams } from 'react-router-dom';

import SurveyAnswerProcessingWidget from '~/widgets/SurveyAnswerProcessing';
import { SurveyAnswerProcessingWidget } from '~/widgets/SurveyAnswerProcessing';

function PublicSurveyAnswerProcessing() {
const [searchParams] = useSearchParams();
Expand Down
2 changes: 1 addition & 1 deletion src/pages/SurveyAnswerProcessing/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useSearchParams } from 'react-router-dom';

import SurveyAnswerProcessingWidget from '~/widgets/SurveyAnswerProcessing';
import { SurveyAnswerProcessingWidget } from '~/widgets/SurveyAnswerProcessing';

function SurveyAnswerProcessing() {
const [searchParams] = useSearchParams();
Expand Down
4 changes: 2 additions & 2 deletions src/shared/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const ROUTES = {
flowId: string | null;
publicAppletKey: string | null;
}) =>
`/public/answer-processing?appletId=${appletId}&eventId=${eventId}&activityId=${activityId}&flowId=${flowId}&publicAppletKey=${publicAppletKey}`,
`/public/answer-processing?appletId=${appletId}&eventId=${eventId}&activityId=${activityId}${flowId ? `flowId=${flowId}` : ''}${publicAppletKey ? `publicAppletKey=${publicAppletKey}` : ''}`,
},

// Protected routes
Expand Down Expand Up @@ -119,7 +119,7 @@ const ROUTES = {
flowId: string | null;
publicAppletKey: string | null;
}) =>
`/protected/answer-processing?appletId=${appletId}&eventId=${eventId}&activityId=${activityId}&flowId=${flowId}&publicAppletKey=${publicAppletKey}`,
`/protected/answer-processing?appletId=${appletId}&eventId=${eventId}&activityId=${activityId}${flowId ? `flowId=${flowId}` : ''}${publicAppletKey ? `publicAppletKey=${publicAppletKey}` : ''}`,
},
};

Expand Down
48 changes: 48 additions & 0 deletions src/shared/ui/Icons/CheckCircle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Box from '../Box';

type Props = {
color?: string;
strokeWidth?: number;

width?: string;
height?: string;
};

const CheckCircle = (props: Props) => {
return (
<Box
display="flex"
sx={{
'& path': {
strokeDasharray: 56,
strokeDashoffset: 56,
animation: 'draw 2s ease forwards',
},
'@keyframes draw': {
to: {
strokeDashoffset: 0,
},
},
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth || 1.5}
stroke={props.color || 'currentColor'}
width={props.width || '24px'}
height={props.height || '24px'}
className="checkmark-circle"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>
</Box>
);
};

export default CheckCircle;
1 change: 1 addition & 0 deletions src/shared/ui/Icons/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as ClockIcon } from './ClockIcon';
export { default as CheckCircle } from './CheckCircle';
15 changes: 0 additions & 15 deletions src/widgets/SurveyAnswerProcessing/ProcessingScreen.tsx

This file was deleted.

1 change: 1 addition & 0 deletions src/widgets/SurveyAnswerProcessing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as SurveyAnswerProcessingWidget } from './ui';
84 changes: 84 additions & 0 deletions src/widgets/SurveyAnswerProcessing/ui/ProcessingScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useCallback, useContext } from 'react';

import { ProgressBar } from './ProgressBar';

import { appletModel } from '~/entities/applet';
import { useBanners } from '~/entities/banner/model';
import { SurveyContext, SurveyLayout, SurveyManageButtons } from '~/features/PassSurvey';
import { Theme } from '~/shared/constants';
import Box from '~/shared/ui/Box';
import Text from '~/shared/ui/Text';
import { useCustomTranslation } from '~/shared/utils';

export const ProcessingScreen = () => {
const { t } = useCustomTranslation();

const context = useContext(SurveyContext);

const { addWarningBanner } = useBanners();

const { completeActivity, completeFlow } = appletModel.hooks.useEntityComplete({
activityId: context.activityId,
eventId: context.eventId,
appletId: context.appletId,
flow: context.flow,
flowId: context.flow?.id ?? null,
publicAppletKey: context.publicAppletKey,
});

const onFinish = useCallback(() => {
const canBeClosed = true; // TODO: Change on real one when the store will be ready

if (!canBeClosed) {
return addWarningBanner(t('answerProcessingScreen.processInProgress'));
}

return context.flow ? completeFlow({ forceComplete: true }) : completeActivity();
}, [addWarningBanner, completeActivity, completeFlow, context.flow, t]);

return (
<SurveyLayout
isSaveAndExitButtonShown={false}
title="Test Activity Or Flow Title" // TODO: Change on real one when the store will be ready
footerActions={
<SurveyManageButtons
isLoading={false}
isBackShown={false}
onNextButtonClick={onFinish}
nextButtonText={t('Consent.close')}
/>
}
>
<Box display="flex" flex={1} justifyContent="center" alignItems="center" paddingX="24px">
<Box>
<Box
display="flex"
justifyContent="center"
alignItems="center"
flexDirection="column"
gap="12px"
>
<Text variant="h4">{t('answerProcessingScreen.title')}</Text>
<Text variant="body1">{t('answerProcessingScreen.description')}</Text>
</Box>

<Box
padding="16px 8px"
marginTop="16px"
bgcolor={Theme.colors.light.primary012}
borderRadius="12px"
>
<ProgressBar
activityName={context.activity.name}
currentActivityIndex={0}
activitiesCount={10}
isCompleted={false} // TODO: Change on real one when the store will be ready
isNotStarted={true} // TODO: Change on real one when the store will be ready
isInProgress={false} // TODO: Change on real one when the store will be ready
/>
</Box>
</Box>
</Box>
</SurveyLayout>
);
};
Loading

0 comments on commit ab107b0

Please sign in to comment.