Skip to content

Commit

Permalink
feat: add Askem feedback buttons (HL-922)
Browse files Browse the repository at this point in the history
  • Loading branch information
mjturt committed Dec 5, 2023
1 parent 2dbc349 commit b880158
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 26 deletions.
3 changes: 3 additions & 0 deletions .env.benefit-applicant.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ NEXTJS_SENTRY_DEBUG=true
NEXTJS_SENTRY_TRACING=true
NEXT_PUBLIC_SENTRY_ATTACH_STACKTRACE=
NEXT_PUBLIC_SENTRY_MAX_BREADCRUMBS=

NEXT_PUBLIC_ASKEM_API_KEY=
NEXT_PUBLIC_SHOW_COOKIE_BANNER=0
1 change: 1 addition & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ ARG SENTRY_PROJECT
ARG SENTRY_AUTH_TOKEN
ARG SENTRY_URL
ARG SENTRY_RELEASE
ARG NEXT_PUBLIC_ASKEM_API_KEY

# Use non-root user
USER appuser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,23 @@ export const $SpinnerContainer = styled.div`
`;

export const $Empty = styled.div``;

export const $AskemContainer = styled.div`
display: flex;
flex-direction: column;
min-height: 129px;
margin: 0;
padding-left: ${(props) => props.theme.spacing.l};
padding-right: ${(props) => props.theme.spacing.l};
${respondAbove('sm')`
flex-direction: row;
`}
`;

export const $AskemItem = styled.div`
display: flex;
flex-flow: column;
${respondAbove('sm')`
min-width: 13%;
`}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import ApplicationFormStep3 from 'benefit/applicant/components/applications/form
import ApplicationFormStep4 from 'benefit/applicant/components/applications/forms/application/step4/ApplicationFormStep4';
import ApplicationFormStep5 from 'benefit/applicant/components/applications/forms/application/step5/ApplicationFormStep5';
import ApplicationFormStep6 from 'benefit/applicant/components/applications/forms/application/step6/ApplicationFormStep6';
import { $Hr } from 'benefit/applicant/components/pages/Pages.sc';
import { SUBMITTED_STATUSES } from 'benefit/applicant/constants';
import { useAskem } from 'benefit/applicant/hooks/useAnalytics';
import { IconInfoCircleFill, LoadingSpinner, Stepper } from 'hds-react';
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
Expand All @@ -24,6 +26,7 @@ import { useTheme } from 'styled-components';
import ErrorPage from '../../errorPage/ErrorPage';
import { $Notification } from '../../Notification/Notification.sc';
import NotificationView from '../../notificationView/NotificationView';
import { $AskemContainer, $AskemItem } from '../Applications.sc';
import { usePageContent } from './usePageContent';

const stepperCss = {
Expand All @@ -49,8 +52,12 @@ const PageContent: React.FC = () => {
} = usePageContent();

const theme = useTheme();

const router = useRouter();
const canShowAskem = useAskem(
router.locale,
isSubmittedApplication,
isLoading
);

useEffect(() => {
if (isReadOnly) document.title = t('common:pageTitles.viewApplication');
Expand Down Expand Up @@ -83,16 +90,30 @@ const PageContent: React.FC = () => {

if (isSubmittedApplication) {
return (
<NotificationView
title={t('common:notifications.applicationSubmitted.label')}
message={t('common:notifications.applicationSubmitted.message', {
applicationNumber: application?.applicationNumber,
applicantName: getFullName(
application?.employee?.firstName,
application?.employee?.lastName
),
})}
/>
<>
<NotificationView
title={t('common:notifications.applicationSubmitted.label')}
message={t('common:notifications.applicationSubmitted.message', {
applicationNumber: application?.applicationNumber,
applicantName: getFullName(
application?.employee?.firstName,
application?.employee?.lastName
),
})}
/>
{canShowAskem && (
<Container>
<$Hr />
<$AskemContainer>
<$AskemItem />
<$AskemItem>
<div className="rns" />
</$AskemItem>
</$AskemContainer>
<$Hr />
</Container>
)}
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import {
ASKEM_HOSTNAME,
SUPPORTED_LANGUAGES,
} from 'benefit/applicant/constants';
import useLocale from 'benefit/applicant/hooks/useLocale';
import { BackendEndpoint } from 'benefit-shared/backend-api/backend-api';
import { CookieModal } from 'hds-react';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import React from 'react';
import useBackendAPI from 'shared/hooks/useBackendAPI';

const CookieConsent: React.FC = () => {
const locale = useLocale();
const router = useRouter();
const { t } = useTranslation();
const { axios } = useBackendAPI();
const { pathname, asPath, query } = router;

const onLanguageChange = (newLanguage: 'fi' | 'sv' | 'en'): void => {
const onLanguageChange = (newLanguage: SUPPORTED_LANGUAGES): void => {
void axios.get(BackendEndpoint.USER_OPTIONS, {
params: { lang: newLanguage },
});
Expand All @@ -23,26 +25,63 @@ const CookieConsent: React.FC = () => {
});
};

type CookieTexts = {
title: { fi: string; sv: string; en: string };
askemDescription: { fi: string; sv: string; en: string };
};

const text: CookieTexts = {
title: {
fi: 'Helsinki-lisä',
sv: 'Helsingforstillägg',
en: 'Helsinki benefit',
},
askemDescription: {
fi: 'Askem-reaktionappien toimintaan liittyvä tietue.',
sv: 'En post relaterad till driften av reaktionsknappen Askem.',
en: 'A record related to the operation of the Askem react buttons.',
},
};

const askemDescription = text.askemDescription[locale];

const contentSource = {
siteName: t('common:appName'),
siteName: text.title[locale],
currentLanguage: locale,
optionalCookies: {
cookies: [
groups: [
{
commonGroup: 'statistics',
commonCookie: 'matomo',
cookies: [
{
id: 'rns',
name: 'rnsbid',
hostName: ASKEM_HOSTNAME,
description: askemDescription,
expiration: '-',
},
{
id: 'rnsbid_ts',
name: 'rnsbid_ts',
hostName: ASKEM_HOSTNAME,
description: askemDescription,
expiration: '-',
},
{
id: 'rns_reaction',
name: 'rns_reaction_*',
hostName: ASKEM_HOSTNAME,
description: askemDescription,
expiration: '-',
},
],
},
],
},
language: {
onLanguageChange,
},
focusTargetSelector: '#focused-element-after-cookie-consent-closed',
onAllConsentsGiven: (consents: { matomo: boolean }) => {
if (!consents.matomo) {
console.log('stop matomo');
}
},
};

return <CookieModal contentSource={contentSource} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import styled from 'styled-components';
export const $Notification = styled.div`
display: flex;
flex-direction: column;
margin: ${(props) => props.theme.spacing.xl} 0
${(props) => props.theme.spacing.xl} 0;
margin-top: ${(props) => props.theme.spacing.xl};
padding: ${(props) => props.theme.spacing.l};
background: ${(props) => props.theme.colors.fogLight};
${respondAbove('sm')`
flex-direction: row;
`}
`;

Expand Down
3 changes: 3 additions & 0 deletions frontend/benefit/applicant/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,6 @@ export enum LOCAL_STORAGE_KEYS {
IS_TERMS_OF_SERVICE_APPROVED = 'isTermsOfServiceApproved',
CSRF_TOKEN = 'csrfToken',
}

export const ASKEM_SCRIPT_URL = 'https://cdn.reactandshare.com/plugin/rns.js';
export const ASKEM_HOSTNAME = 'reactandshare.com';
34 changes: 34 additions & 0 deletions frontend/benefit/applicant/src/hooks/useAnalytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect } from 'react';

import { ASKEM_SCRIPT_URL } from '../constants';
import { canShowAskem } from '../utils/cookie';

export const useAskem = (
lang: string | undefined,
isSubmittedApplication: boolean,
isLoading: boolean
): boolean => {
const showAskem = canShowAskem(lang);
useEffect(() => {
if (!canShowAskem || isLoading) {
return () => {};
}

const script = document.createElement('script');
// eslint-disable-next-line scanjs-rules/assign_to_src
script.src = ASKEM_SCRIPT_URL;
script.type = 'text/javascript';

window.rnsData = {
apiKey: process.env.NEXT_PUBLIC_ASKEM_API_KEY,
title: 'Helsinki-lisä',
canonicalUrl: window.location.href,
};

document.body.append(script);
return () => {
script.remove();
};
}, [lang, isSubmittedApplication, isLoading, showAskem]);
return showAskem;
};
4 changes: 3 additions & 1 deletion frontend/benefit/applicant/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const App: React.FC<AppProps> = (appProps) => {

const { t } = useTranslation();

const showCookieBanner = process.env.NEXT_PUBLIC_SHOW_COOKIE_BANNER === '1';

useEffect(() => {
setAppLoaded();
switch (router.route) {
Expand Down Expand Up @@ -62,7 +64,7 @@ const App: React.FC<AppProps> = (appProps) => {
>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<CookieConsent />
{showCookieBanner && <CookieConsent />}
<BaseApp
layout={Layout}
title={!isServerSide() && document.title}
Expand Down
19 changes: 19 additions & 0 deletions frontend/benefit/applicant/src/types/common.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,22 @@ interface ErrorResponse {
interface ErrorData {
data?: Record<string, string[]>;
}

type RNSData = {
apiKey: string;
title: string;
canonicalUrl: string;
};

export type ConsentsCookie = {
'city-of-helsinki-cookie-consents': boolean;
rns: boolean;
rnsbid_ts: boolean;
rns_reaction: boolean;
};

declare global {
interface Window {
rnsData: RNSData;
}
}
19 changes: 19 additions & 0 deletions frontend/benefit/applicant/src/utils/cookie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getLastCookieValue } from 'shared/cookies/get-last-cookie-value';

import type { ConsentsCookie } from '../types/common';

const parseConsentsCookie = (): ConsentsCookie | undefined => {
const consentsCookieEncoded = getLastCookieValue(
'city-of-helsinki-cookie-consents'
);
if (!consentsCookieEncoded) {
return undefined;
}
const consentsCookieString = decodeURIComponent(consentsCookieEncoded);
return JSON.parse(consentsCookieString) as ConsentsCookie;
};

export const canShowAskem = (lang: string): boolean => {
const consentsCookie = parseConsentsCookie();
return consentsCookie && consentsCookie.rns && lang === 'fi';
};

0 comments on commit b880158

Please sign in to comment.