diff --git a/src/abstract/lib/constants.ts b/src/abstract/lib/constants.ts
index ae04607f1..d394aabfd 100644
--- a/src/abstract/lib/constants.ts
+++ b/src/abstract/lib/constants.ts
@@ -32,4 +32,5 @@ export const phrasalTemplateCompatibleResponseTypes = [
'multiSelectRows',
'singleSelectRows',
'sliderRows',
+ 'paragraphText',
];
diff --git a/src/entities/activity/ui/items/ActionPlan/ResponseSegment.tsx b/src/entities/activity/ui/items/ActionPlan/ResponseSegment.tsx
index 38594c018..c0ed1e306 100644
--- a/src/entities/activity/ui/items/ActionPlan/ResponseSegment.tsx
+++ b/src/entities/activity/ui/items/ActionPlan/ResponseSegment.tsx
@@ -24,7 +24,7 @@ const isAnswersSkipped = (answers: string[]): boolean => {
type FieldValueTransformer = (value: string) => string;
const identity: FieldValueTransformer = (value) => value;
-type FieldValuesJoiner = (values: string[]) => string;
+type FieldValuesJoiner = (values: string[]) => string | JSX.Element[];
const joinWithComma: FieldValuesJoiner = (values) => values.join(', ');
type ResponseSegmentProps = {
@@ -58,6 +58,9 @@ export const ResponseSegment = ({ phrasalData, field, isAtStart }: ResponseSegme
};
} else if (fieldPhrasalData.context.itemResponseType === 'timeRange') {
joinSentenceWords = (values) => values.join(' - ');
+ } else if (fieldPhrasalData.context.itemResponseType === 'paragraphText') {
+ joinSentenceWords = (values) =>
+ values.map((item, index) =>
{item || ' '}
);
}
let words: string[];
@@ -65,6 +68,12 @@ export const ResponseSegment = ({ phrasalData, field, isAtStart }: ResponseSegme
words = isAnswersSkipped(fieldPhrasalData.values)
? [t('questionSkipped')]
: fieldPhrasalData.values.map(transformValue);
+ } else if (fieldPhrasalDataType === 'paragraph') {
+ words = isAnswersSkipped(fieldPhrasalData.values)
+ ? [t('questionSkipped')]
+ : fieldPhrasalData.values
+ .flatMap((value) => value.split(/\r?\n/)) // Split each paragraph by newlines
+ .map(transformValue);
} else if (fieldPhrasalDataType === 'indexed-array') {
const indexedAnswers = fieldPhrasalData.values[field.itemIndex] || [];
words = isAnswersSkipped(indexedAnswers)
diff --git a/src/entities/activity/ui/items/ActionPlan/phrasalData.ts b/src/entities/activity/ui/items/ActionPlan/phrasalData.ts
index 50ba98406..cb8cba5f2 100644
--- a/src/entities/activity/ui/items/ActionPlan/phrasalData.ts
+++ b/src/entities/activity/ui/items/ActionPlan/phrasalData.ts
@@ -1,3 +1,4 @@
+/* eslint-disable prettier/prettier */
import { phrasalTemplateCompatibleResponseTypes } from '~/abstract/lib/constants';
import { ActivityItemType } from '~/entities/activity/lib';
import { ItemRecord } from '~/entities/applet/model';
@@ -23,6 +24,8 @@ type ActivityPhrasalBaseData<
type ActivityPhrasalArrayFieldData = ActivityPhrasalBaseData<'array', string[]>;
+type ActivityPhrasalParagraphFieldData = ActivityPhrasalBaseData<'paragraph', string[]>;
+
type ActivityPhrasalItemizedArrayValue = Record;
type ActivityPhrasalIndexedArrayFieldData = ActivityPhrasalBaseData<
@@ -45,7 +48,8 @@ type ActivityPhrasalMatrixFieldData = ActivityPhrasalBaseData<'matrix', Activity
type ActivityPhrasalData =
| ActivityPhrasalArrayFieldData
| ActivityPhrasalIndexedArrayFieldData
- | ActivityPhrasalMatrixFieldData;
+ | ActivityPhrasalMatrixFieldData
+ | ActivityPhrasalParagraphFieldData;
export type ActivitiesPhrasalData = Record;
@@ -92,6 +96,13 @@ export const extractActivitiesPhrasalData = (items: ItemRecord[]): ActivitiesPhr
context: fieldDataContext,
};
fieldData = dateFieldData;
+ } else if (item.responseType === 'paragraphText') {
+ const dateFieldData: ActivityPhrasalParagraphFieldData = {
+ type: 'paragraph',
+ values: item.answer.map((value) => value || ''),
+ context: fieldDataContext,
+ };
+ fieldData = dateFieldData;
} else if (item.responseType === 'singleSelect' || item.responseType === 'multiSelect') {
const dateFieldData: ActivityPhrasalArrayFieldData = {
type: 'array',
diff --git a/src/entities/user/model/hooks/useOnLogin.ts b/src/entities/user/model/hooks/useOnLogin.ts
index 83facc1d7..f256332d9 100644
--- a/src/entities/user/model/hooks/useOnLogin.ts
+++ b/src/entities/user/model/hooks/useOnLogin.ts
@@ -47,7 +47,7 @@ export const useOnLogin = (params: Params) => {
secureTokensStorage.setTokens(tokens);
if (params.backRedirectPath !== undefined) {
- navigate(params.backRedirectPath);
+ navigate(params.backRedirectPath, { replace: true });
} else {
Mixpanel.track({ action: MixpanelEventType.LoginSuccessful });
Mixpanel.login(user.id);
diff --git a/src/features/Logout/lib/useLogout.ts b/src/features/Logout/lib/useLogout.ts
index 6337afc5d..b8b7d55c1 100644
--- a/src/features/Logout/lib/useLogout.ts
+++ b/src/features/Logout/lib/useLogout.ts
@@ -1,5 +1,7 @@
import { useCallback } from 'react';
+import { useLocation } from 'react-router-dom';
+
import { appletModel } from '~/entities/applet';
import { useLogoutMutation, userModel } from '~/entities/user';
import { AutoCompletionModel } from '~/features/AutoCompletion';
@@ -19,6 +21,7 @@ type UseLogoutReturn = {
export const useLogout = (): UseLogoutReturn => {
const navigator = useCustomNavigation();
+ const location = useLocation();
const { clearUser } = userModel.hooks.useUserState();
const { clearStore } = appletModel.hooks.useClearStore();
@@ -42,8 +45,18 @@ export const useLogout = (): UseLogoutReturn => {
Mixpanel.track({ action: MixpanelEventType.Logout });
Mixpanel.logout();
FeatureFlags.logout();
- return navigator.navigate(ROUTES.login.path);
- }, [clearUser, clearStore, clearAutoCompletionState, navigator, logoutMutation]);
+
+ const backRedirectPath = `${location.pathname}${location.search}`;
+ return navigator.navigate(ROUTES.login.path, { state: { backRedirectPath } });
+ }, [
+ clearUser,
+ clearStore,
+ clearAutoCompletionState,
+ location.pathname,
+ location.search,
+ navigator,
+ logoutMutation,
+ ]);
return {
logout,
diff --git a/src/features/PassSurvey/ui/EntityTimer.tsx b/src/features/PassSurvey/ui/EntityTimer.tsx
index b3c426e44..6e7fc5714 100644
--- a/src/features/PassSurvey/ui/EntityTimer.tsx
+++ b/src/features/PassSurvey/ui/EntityTimer.tsx
@@ -12,6 +12,7 @@ import {
getMsFromHours,
getMsFromMinutes,
useAppSelector,
+ useCustomTranslation,
useTimer,
} from '~/shared/utils';
@@ -20,6 +21,7 @@ type Props = {
};
export const EntityTimer = ({ entityTimerSettings }: Props) => {
+ const { t } = useCustomTranslation();
const context = useContext(SurveyContext);
const [varForDeps, forceUpdate] = useState({});
@@ -64,7 +66,7 @@ export const EntityTimer = ({ entityTimerSettings }: Props) => {
return '00:00';
}
- return `${formatTimerTime(timeToLeft)} remaining`;
+ return t('timeRemaining', { time: formatTimerTime(timeToLeft) });
};
const checkLessThan10Mins = (): boolean => {
diff --git a/src/features/PassSurvey/ui/ItemTimerBar.tsx b/src/features/PassSurvey/ui/ItemTimerBar.tsx
index ad56bd3ac..d07d3785d 100644
--- a/src/features/PassSurvey/ui/ItemTimerBar.tsx
+++ b/src/features/PassSurvey/ui/ItemTimerBar.tsx
@@ -1,7 +1,7 @@
import { Theme } from '~/shared/constants';
import Box from '~/shared/ui/Box';
import Text from '~/shared/ui/Text';
-import { convertMillisecondsToMinSec } from '~/shared/utils';
+import { convertMillisecondsToMinSec, useCustomTranslation } from '~/shared/utils';
type Props = {
duration: number; // seconds
@@ -10,6 +10,7 @@ type Props = {
};
export const ItemTimerBar = ({ time, progress, duration }: Props) => {
+ const { t } = useCustomTranslation();
const getProgressBarShift = (progress: number) => {
return progress - 100;
};
@@ -70,7 +71,7 @@ export const ItemTimerBar = ({ time, progress, duration }: Props) => {
},
}}
>
- {`${convertMillisecondsToMinSec(timeMS)} remaining `}
+ {t('timeRemaining', { time: convertMillisecondsToMinSec(timeMS) })}
{
transition: '0.6s',
}}
>
- for this item
+ {` ${t('forItem')}`}
diff --git a/src/features/Signup/ui/SignupForm.tsx b/src/features/Signup/ui/SignupForm.tsx
index eaae94f6f..4db30fb00 100644
--- a/src/features/Signup/ui/SignupForm.tsx
+++ b/src/features/Signup/ui/SignupForm.tsx
@@ -129,9 +129,9 @@ export const SignupForm = ({ locationState }: SignupFormProps) => {
setTerms((prev) => !prev)}>
- I agree to the{' '}
+ {`${t('iAgreeTo')} `}
- Terms of Service
+ {t('termsOfService')}
diff --git a/src/features/TakeNow/ui/TakeNowSuccessModal.tsx b/src/features/TakeNow/ui/TakeNowSuccessModal.tsx
index 575ef5682..847664314 100644
--- a/src/features/TakeNow/ui/TakeNowSuccessModal.tsx
+++ b/src/features/TakeNow/ui/TakeNowSuccessModal.tsx
@@ -2,7 +2,6 @@ import { useContext } from 'react';
import { TakeNowSuccessModalProps } from '../lib/types';
-import { SurveyContext } from '~/features/PassSurvey';
import { MuiModal } from '~/shared/ui';
import {
addFeatureToEvent,
@@ -13,6 +12,7 @@ import {
ReturnToAdminAppEvent,
useCustomTranslation,
} from '~/shared/utils';
+import { AppletDetailsContext } from '~/widgets/ActivityGroups/lib';
export const TakeNowSuccessModal = ({
isOpen,
@@ -23,7 +23,7 @@ export const TakeNowSuccessModal = ({
submitId,
}: TakeNowSuccessModalProps) => {
const { t } = useCustomTranslation();
- const { applet } = useContext(SurveyContext);
+ const { applet } = useContext(AppletDetailsContext);
const handleReturnToAdminAppClick = () => {
const event: ReturnToAdminAppEvent = {
diff --git a/src/i18n/en/translation.json b/src/i18n/en/translation.json
index a07f7296d..c4530857d 100644
--- a/src/i18n/en/translation.json
+++ b/src/i18n/en/translation.json
@@ -173,8 +173,10 @@
"logIn": "Log In",
"success": "Registration completed successfully",
"or": "Or",
- "pleaseAgreeTerms": "*Please agree to the Terms of Service."
- },
+ "pleaseAgreeTerms": "*Please agree to the Terms of Service.",
+ "iAgreeTo": "I agree to the",
+ "termsOfService": "Terms of Service"
+ },
"ForgotPassword": {
"title": "Reset Password",
"formTitle": "Enter the email associated with your account",
@@ -329,6 +331,11 @@
"support": "Support",
"privacy": "Privacy",
"termsOfService": "Terms"
- }
+ },
+ "activity": "Activity",
+ "networkError": "Network Error",
+ "timeRemaining": "{{time}} remaining",
+ "forItem": "for this item",
+ "noApplets": "No applets"
}
}
diff --git a/src/i18n/fr/translation.json b/src/i18n/fr/translation.json
index a00e48695..2187d8f82 100644
--- a/src/i18n/fr/translation.json
+++ b/src/i18n/fr/translation.json
@@ -184,7 +184,9 @@
"account": "Vous avez déjà un compte?",
"logIn": "Connexion",
"success": "Inscription terminée avec succès",
- "pleaseAgreeTerms": "*Please agree to the Terms of Service."
+ "pleaseAgreeTerms": "*Please agree to the Terms of Service.",
+ "iAgreeTo": "J'accepte les",
+ "termsOfService": "Conditions d'Utilisation"
},
"ForgotPassword": {
"title": "réinitialiser le mot de passe",
@@ -348,6 +350,11 @@
"support": "Soutien",
"privacy": "Confidentialité",
"termsOfService": "Conditions"
- }
+ },
+ "activity": "Activité",
+ "networkError": "Erreur réseau",
+ "timeRemaining": "{{time}} restantes",
+ "forItem": "pour cet objet",
+ "noApplets": "Pas d'applets"
}
}
diff --git a/src/pages/AuthorizedRoutes.tsx b/src/pages/AuthorizedRoutes.tsx
index a89b01516..5eeb0ee17 100644
--- a/src/pages/AuthorizedRoutes.tsx
+++ b/src/pages/AuthorizedRoutes.tsx
@@ -5,6 +5,7 @@ import { Navigate, Route, Routes } from 'react-router-dom';
import AppletDetailsPage from './AppletDetailsPage';
import AppletListPage from './AppletListPage';
import AutoCompletion from './AutoCompletion';
+import LoginPage from './Login';
import ProfilePage from './Profile';
import PublicAutoCompletion from './PublicAutoCompletion';
import SettingsPage from './Settings';
@@ -48,10 +49,10 @@ function AuthorizedRoutes({ refreshToken }: Props) {
} />
} />
} />
-
- } />
+ } />
+ } />
);
diff --git a/src/shared/utils/hooks/useSessionBanners/useSessionBanners.ts b/src/shared/utils/hooks/useSessionBanners/useSessionBanners.ts
index 91cd43b2f..dd5d94fda 100644
--- a/src/shared/utils/hooks/useSessionBanners/useSessionBanners.ts
+++ b/src/shared/utils/hooks/useSessionBanners/useSessionBanners.ts
@@ -1,4 +1,4 @@
-import { useEffect, useRef } from 'react';
+import { useRef } from 'react';
import { useBanners } from '~/entities/banner/model';
import { userModel } from '~/entities/user';
@@ -9,11 +9,9 @@ export const useSessionBanners = () => {
const prevIsAuthorized = useRef(isAuthorized);
- useEffect(() => {
- if (prevIsAuthorized.current !== isAuthorized && !isAuthorized) {
- removeAllBanners();
- }
+ if (prevIsAuthorized.current !== isAuthorized && !isAuthorized) {
+ removeAllBanners();
+ }
- prevIsAuthorized.current = isAuthorized;
- }, [isAuthorized, removeAllBanners]);
+ prevIsAuthorized.current = isAuthorized;
};
diff --git a/src/widgets/AppletList/index.tsx b/src/widgets/AppletList/index.tsx
index 8c95268de..573e1cc96 100644
--- a/src/widgets/AppletList/index.tsx
+++ b/src/widgets/AppletList/index.tsx
@@ -3,8 +3,10 @@ import { userModel } from '~/entities/user';
import { Box } from '~/shared/ui';
import { Text } from '~/shared/ui';
import Loader from '~/shared/ui/Loader';
+import { useCustomTranslation } from '~/shared/utils';
export const AppletListWidget = () => {
+ const { t } = useCustomTranslation();
const { user } = userModel.hooks.useUserState();
const {
@@ -36,7 +38,7 @@ export const AppletListWidget = () => {
if (isAppletsEmpty) {
return (
- No applets
+ {t('noApplets')}
);
}
diff --git a/src/widgets/Survey/ui/ActivityMetaData.tsx b/src/widgets/Survey/ui/ActivityMetaData.tsx
index 1a300c2c3..0eb86b0f7 100644
--- a/src/widgets/Survey/ui/ActivityMetaData.tsx
+++ b/src/widgets/Survey/ui/ActivityMetaData.tsx
@@ -23,7 +23,7 @@ export const ActivityMetaData = ({ activityLength, isFlow, activityOrderInFlow }
variant="body1"
component="span"
testid="metadata-activity-serial-number"
- >{`Activity ${activityOrderInFlow} `}
+ >{`${t('activity')} ${activityOrderInFlow} `}
•