Skip to content

Commit

Permalink
OLO disable Hotjar (#1707)
Browse files Browse the repository at this point in the history
* Create GlobalAppProps

* Don't display Hotjar on externally embedded sites

---------

Co-authored-by: Marek Bodinger <marek.bodinger@gmail.com>
  • Loading branch information
MarekBodingerBA and MarekBodinger authored Nov 8, 2024
1 parent a295677 commit e3b47cc
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 116 deletions.
13 changes: 10 additions & 3 deletions next/components/forms/segments/CookieConsent/CookieConsent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import React, { useCallback, useEffect, useState } from 'react'
import { isBrowser, isProductionDeployment } from '../../../../frontend/utils/general'
import logger from '../../../../frontend/utils/logger'

type CookiesAndTrackingProps = {
externallyEmbedded?: boolean
}

interface CookieOptions {
expires?: number | Date
path?: string
Expand All @@ -31,7 +35,7 @@ const pickConsents = (consents: any) => mapValues(pick(consents, availableConsen

// taken from bratislava.sk without much of a change
// also takes care of loading all the consented 3rd parties - TODO consider better component name ?
export const CookiesAndTracking = () => {
export const CookiesAndTracking = ({ externallyEmbedded }: CookiesAndTrackingProps) => {
const [consents, setConsentsState] = useState<Record<string, any> | null>(null)
// defaults to true so that it does not flash into being in the beginning
const [bannerDismissed, setBannerDismissed] = useState(true)
Expand Down Expand Up @@ -73,13 +77,16 @@ export const CookiesAndTracking = () => {

const { t } = useTranslation(['common'])

const thirdPartyScriptsAllowed = isProductionDeployment()
const hotjarAllowed = thirdPartyScriptsAllowed && consents?.statistics && !externallyEmbedded

return (
<>
{/* all 3rd party scrips loading here */}
{/* don't use any of the analytics/tracking in staging/dev - change this if you need testing */}
{isProductionDeployment() ? (
{thirdPartyScriptsAllowed ? (
<>
{consents?.statistics ? (
{hotjarAllowed ? (
<Script
id="hotjar"
strategy="afterInteractive"
Expand Down
10 changes: 8 additions & 2 deletions next/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ const inter = Inter({
subsets: ['latin', 'latin-ext'],
})

const MyApp = ({ Component, pageProps }: AppProps) => {
export type GlobalAppProps = {
appProps?: {
externallyEmbedded?: boolean
}
}

const MyApp = ({ Component, pageProps }: AppProps<GlobalAppProps>) => {
const [queryClient] = useState(() => new QueryClient())

return (
Expand Down Expand Up @@ -74,7 +80,7 @@ const MyApp = ({ Component, pageProps }: AppProps) => {
<div id="root">
<Component {...pageProps} />
</div>
<CookieConsent />
<CookieConsent externallyEmbedded={pageProps.appProps?.externallyEmbedded} />
</NavMenuContextProvider>
</PlausibleProvider>
</SnackbarProvider>
Expand Down
157 changes: 81 additions & 76 deletions next/pages/mestske-sluzby/[slug]/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getDefaultFormDataForFormDefinition } from '../../../frontend/utils/get
import { getInitialFormSignature } from '../../../frontend/utils/getInitialFormSignature'
import { redirectQueryParam } from '../../../frontend/utils/queryParamRedirect'
import { slovakServerSideTranslations } from '../../../frontend/utils/slovakServerSideTranslations'
import type { GlobalAppProps } from '../../_app'

const fetchStrapiForm = async (slug: string): Promise<FormBaseFragment | null | undefined> => {
const result = await strapiClient.FormBaseBySlug({ slug })
Expand All @@ -27,96 +28,100 @@ type Params = {
id: string
}

export const getServerSideProps = amplifyGetServerSideProps<FormPageWrapperProps, Params>(
async ({ context, getAccessToken, isSignedIn }) => {
if (!context.params) {
return { notFound: true }
}
export const getServerSideProps = amplifyGetServerSideProps<
FormPageWrapperProps & GlobalAppProps,
Params
>(async ({ context, getAccessToken, isSignedIn }) => {
if (!context.params) {
return { notFound: true }
}

const { slug, id: formId } = context.params
const formDefinition = getFormDefinitionBySlug(slug)
if (!formDefinition) {
const { slug, id: formId } = context.params
const formDefinition = getFormDefinitionBySlug(slug)
if (!formDefinition) {
return { notFound: true }
}

try {
// These promises cannot be run in parallel because the redirects in catch blocks depends on the error response of the first promise.
const { data: form } = await formsApi.nasesControllerGetForm(formId, {
accessToken: 'onlyAuthenticated',
accessTokenSsrGetFn: getAccessToken,
})
if (
!form ||
/* If there wouldn't be this check it would be possible to open the page with any slug in the URL. */
form.formDefinitionSlug !== slug
) {
return { notFound: true }
}

try {
// These promises cannot be run in parallel because the redirects in catch blocks depends on the error response of the first promise.
const { data: form } = await formsApi.nasesControllerGetForm(formId, {
const [{ data: files }, initialSignature, strapiForm] = await Promise.all([
formsApi.filesControllerGetFilesStatusByForm(formId, {
accessToken: 'onlyAuthenticated',
accessTokenSsrGetFn: getAccessToken,
})
if (
!form ||
/* If there wouldn't be this check it would be possible to open the page with any slug in the URL. */
form.formDefinitionSlug !== slug
) {
return { notFound: true }
}
}),
getInitialFormSignature(form.formDataBase64),
fetchStrapiForm(slug),
])

const [{ data: files }, initialSignature, strapiForm] = await Promise.all([
formsApi.filesControllerGetFilesStatusByForm(formId, {
accessToken: 'onlyAuthenticated',
accessTokenSsrGetFn: getAccessToken,
}),
getInitialFormSignature(form.formDataBase64),
fetchStrapiForm(slug),
])
const formSent = form.state !== GetFormResponseDtoStateEnum.Draft
// If the form was created by an unauthenticated user, a migration modal is displayed and form is not editable.
const formMigrationRequired = Boolean(isSignedIn && !form.userExternalId)

const formSent = form.state !== GetFormResponseDtoStateEnum.Draft
// If the form was created by an unauthenticated user, a migration modal is displayed and form is not editable.
const formMigrationRequired = Boolean(isSignedIn && !form.userExternalId)
const { success: embeddedSuccess, isEmbedded } = handleEmbeddedFormRequest(
formDefinition,
context,
)
if (!embeddedSuccess) {
return { notFound: true }
}

const { success: embeddedSuccess, isEmbedded } = handleEmbeddedFormRequest(
formDefinition,
context,
)
if (!embeddedSuccess) {
return {
props: {
formServerContext: {
formDefinition: makeSerializableFormDefinition(formDefinition),
formId,
initialFormDataJson:
form.formDataJson ?? getDefaultFormDataForFormDefinition(formDefinition),
initialServerFiles: files,
initialSignature,
formSent,
formMigrationRequired,
isEmbedded,
strapiForm: strapiForm ?? null,
},
appProps: {
externallyEmbedded: isEmbedded,
},
...(await slovakServerSideTranslations()),
},
}
} catch (error) {
if (isAxiosError(error)) {
const is401 = error.response?.status === 401
const is403 = error.response?.status === 403
const is404 = error.response?.status === 404

// If logged in user receives 403 for his/her form it's not theirs.
if (is404 || (is403 && isSignedIn)) {
return { notFound: true }
}

return {
props: {
formServerContext: {
formDefinition: makeSerializableFormDefinition(formDefinition),
formId,
initialFormDataJson:
form.formDataJson ?? getDefaultFormDataForFormDefinition(formDefinition),
initialServerFiles: files,
initialSignature,
formSent,
formMigrationRequired,
isEmbedded,
strapiForm: strapiForm ?? null,
// If logged out user receives 403 for his/her form it might be theirs.
if (is401 || (is403 && !isSignedIn)) {
return {
redirect: {
destination: `${ROUTES.LOGIN}?${redirectQueryParam}=${encodeURIComponent(
context.resolvedUrl,
)}`,
permanent: false,
},
...(await slovakServerSideTranslations()),
} satisfies FormPageWrapperProps,
}
} catch (error) {
if (isAxiosError(error)) {
const is401 = error.response?.status === 401
const is403 = error.response?.status === 403
const is404 = error.response?.status === 404

// If logged in user receives 403 for his/her form it's not theirs.
if (is404 || (is403 && isSignedIn)) {
return { notFound: true }
}
// If logged out user receives 403 for his/her form it might be theirs.
if (is401 || (is403 && !isSignedIn)) {
return {
redirect: {
destination: `${ROUTES.LOGIN}?${redirectQueryParam}=${encodeURIComponent(
context.resolvedUrl,
)}`,
permanent: false,
},
}
}
}

throw error
}
},
)

throw error
}
})

export default SsrAuthProviderHOC(FormPageWrapper)
75 changes: 40 additions & 35 deletions next/pages/mestske-sluzby/dev/[slug].ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { amplifyGetServerSideProps } from '../../../frontend/utils/amplifyServer
import { handleEmbeddedFormRequest } from '../../../frontend/utils/embeddedFormsHelpers'
import { getDefaultFormDataForFormDefinition } from '../../../frontend/utils/getDefaultFormDataForFormDefinition'
import { slovakServerSideTranslations } from '../../../frontend/utils/slovakServerSideTranslations'
import type { GlobalAppProps } from '../../_app'

type Params = {
slug: string
Expand All @@ -16,43 +17,47 @@ type Params = {
/**
* A route to preview forms in `forms-shared` folder. Backend functionality doesn't work. Works only in development.
*/
export const getServerSideProps = amplifyGetServerSideProps<FormPageWrapperProps, Params>(
async ({ context }) => {
if (!environment.featureToggles.developmentForms || !context.params) {
return { notFound: true }
}
export const getServerSideProps = amplifyGetServerSideProps<
FormPageWrapperProps & GlobalAppProps,
Params
>(async ({ context }) => {
if (!environment.featureToggles.developmentForms || !context.params) {
return { notFound: true }
}

const { slug } = context.params
const formDefinition = getFormDefinitionBySlugDev(slug)
if (!formDefinition) {
return { notFound: true }
}
const { slug } = context.params
const formDefinition = getFormDefinitionBySlugDev(slug)
if (!formDefinition) {
return { notFound: true }
}

const { success: embeddedSuccess, isEmbedded } = handleEmbeddedFormRequest(
formDefinition,
context,
)
if (!embeddedSuccess) {
return { notFound: true }
}
const { success: embeddedSuccess, isEmbedded } = handleEmbeddedFormRequest(
formDefinition,
context,
)
if (!embeddedSuccess) {
return { notFound: true }
}

return {
props: {
formServerContext: {
formDefinition: makeSerializableFormDefinition(formDefinition),
formId: '',
initialFormDataJson: getDefaultFormDataForFormDefinition(formDefinition),
initialServerFiles: [],
formSent: false,
formMigrationRequired: false,
isEmbedded,
isDevRoute: true,
strapiForm: { slug },
},
...(await slovakServerSideTranslations()),
} satisfies FormPageWrapperProps,
}
},
)
return {
props: {
formServerContext: {
formDefinition: makeSerializableFormDefinition(formDefinition),
formId: '',
initialFormDataJson: getDefaultFormDataForFormDefinition(formDefinition),
initialServerFiles: [],
formSent: false,
formMigrationRequired: false,
isEmbedded,
isDevRoute: true,
strapiForm: { slug },
},
appProps: {
externallyEmbedded: isEmbedded,
},
...(await slovakServerSideTranslations()),
},
}
})

export default SsrAuthProviderHOC(FormPageWrapper)

0 comments on commit e3b47cc

Please sign in to comment.