Skip to content

File tree

11 files changed

+180
-167
lines changed

11 files changed

+180
-167
lines changed
 
Loading

‎public/static/images/LOGO_Powered_by_UNLY_monochrome_WHITE.svg

-54
This file was deleted.

‎src/airtableSchema.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { AirtableSchema } from './types/airtableDataset/AirtableSchema';
22
import { GenericPostConsolidationTransformationValueInputProps } from './types/airtableDataset/FieldSchema';
3-
import { Product } from './types/data/Product';
43

54
type Props = {}
65

@@ -53,7 +52,12 @@ export const getAirtableSchema = (props?: Props): AirtableSchema => {
5352
},
5453
},
5554
serviceLabel: {},
56-
privacyDescription: {},
55+
termsDescription: {
56+
isI18n: true,
57+
},
58+
privacyDescription: {
59+
isI18n: true,
60+
},
5761
},
5862
},
5963
Product: {
@@ -71,7 +75,7 @@ export const getAirtableSchema = (props?: Props): AirtableSchema => {
7175
const { sanitizedRecord: product } = props;
7276
return product?.imagesTitle?.split(', ') || [];
7377
},
74-
}
78+
},
7579
},
7680
description: {
7781
isI18n: true,
@@ -95,10 +99,11 @@ export const getAirtableSchema = (props?: Props): AirtableSchema => {
9599
onSurfaceColor: {},
96100
errorColor: {},
97101
onErrorColor: {},
102+
fonts: {},
98103
logo: {
99104
transformations: {
100105
asSingleRecord: true,
101-
}
106+
},
102107
},
103108
logoTitle: {},
104109
},

‎src/components/appBootstrap/MultiversalAppBootstrap.tsx

+16-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { detectLightHouse } from '../../utils/quality/lighthouse';
3838
import { detectCypress } from '../../utils/testing/cypress';
3939
import Loader from '../animations/Loader';
4040
import DefaultErrorLayout from '../errors/DefaultErrorLayout';
41+
import ErrorDebug from '../errors/ErrorDebug';
4142
import BrowserPageBootstrap, { BrowserPageBootstrapProps } from './BrowserPageBootstrap';
4243
import MultiversalGlobalStyles from './MultiversalGlobalStyles';
4344
import ServerPageBootstrap, { ServerPageBootstrapProps } from './ServerPageBootstrap';
@@ -95,12 +96,26 @@ const MultiversalAppBootstrap: React.FunctionComponent<Props> = (props): JSX.Ele
9596
}: SSGPageProps | SSRPageProps = pageProps;
9697
configureSentryI18n(lang, locale);
9798

99+
if (typeof serializedDataset !== 'string') {
100+
return (
101+
<ErrorDebug
102+
error={new Error(`Fatal error - Unexpected "serializedDataset" passed as page props.\n
103+
Expecting string, but got "${typeof serializedDataset}".\n
104+
This error is often caused by returning an invalid "serializedDataset" from a getStaticProps/getServerSideProps.\n
105+
Make sure you return a correct value, using "serializeSafe".`)}
106+
context={{
107+
pageProps,
108+
}}
109+
/>
110+
);
111+
}
112+
98113
if (process.env.NEXT_PUBLIC_APP_STAGE !== 'production') {
99114
// XXX It's too cumbersome to do proper typings when type changes
100115
// The "customer" was forwarded as a JSON-ish string (using Flatten) in order to avoid circular dependencies issues (SSG/SSR)
101116
// It now being converted back into an object to be actually usable on all pages
102117
// eslint-disable-next-line no-console
103-
console.debug('pageProps.serializedDataset length (bytes)', (serializedDataset as unknown as string).length);
118+
console.debug('pageProps.serializedDataset length (bytes)', (serializedDataset as unknown as string)?.length);
104119
// console.debug('serializedDataset', serializedDataset);
105120
}
106121

‎src/components/pageLayouts/Footer.tsx

+135-96
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ import { css } from '@emotion/core';
22
import { useTheme } from 'emotion-theming';
33
import React from 'react';
44
import { useTranslation } from 'react-i18next';
5-
import {
6-
Col,
7-
Row,
8-
} from 'reactstrap';
9-
import { NRN_CO_BRANDING_LOGO_URL } from '../../constants';
105

6+
import { NRN_CO_BRANDING_LOGO_URL } from '../../constants';
117
import useCustomer from '../../hooks/useCustomer';
128
import useUserSession, { UserSession } from '../../hooks/useUserSession';
9+
import { CSSStyles } from '../../types/CSSStyles';
1310
import { AirtableRecord } from '../../types/data/AirtableRecord';
1411
import { Asset } from '../../types/data/Asset';
1512
import { Customer } from '../../types/data/Customer';
@@ -21,135 +18,177 @@ import I18nBtnChangeLocale from '../i18n/I18nBtnChangeLocale';
2118
import I18nLink from '../i18n/I18nLink';
2219
import DisplayOnBrowserMount from '../rehydration/DisplayOnBrowserMount';
2320

24-
type Props = {};
21+
type Props = {
22+
style?: CSSStyles;
23+
};
2524

26-
const Footer: React.FunctionComponent<Props> = () => {
25+
const Footer: React.FunctionComponent<Props> = (props) => {
26+
const {
27+
style,
28+
} = props;
2729
const { t } = useTranslation();
2830
const { deviceId }: UserSession = useUserSession();
2931
const customer: Customer = useCustomer();
32+
const { availableLanguages } = customer;
33+
const shouldDisplayI18nButton = availableLanguages?.length > 1;
3034
const theme = useTheme<CustomerTheme>();
31-
const { primaryColor, logo: logoAirtable } = theme;
32-
const logo = logoAirtable as AirtableRecord<Asset>;
35+
const { backgroundColor, onBackgroundColor, logo } = theme;
3336
const logoSizesMultipliers = [
3437
{
3538
size: SIZE_XS,
3639
multiplier: 1, // We wanna keep the logos in the footer big and visible even on small devices, we've got enough space
3740
},
3841
];
39-
40-
// Resolve values, handle multiple fallback levels
4142
const copyrightOwner = customer?.label;
4243
const currentYear = (new Date()).getFullYear();
4344

4445
return (
4546
<div
4647
id="footer"
47-
className={'footer align-items-center center'}
48+
className={'footer'}
49+
style={style}
4850
css={css`
49-
background: ${primaryColor};
51+
color: ${onBackgroundColor};
52+
background-color: ${backgroundColor};
5053
padding: 20px 50px;
51-
color: white;
54+
display: inline-flex;
55+
flex-direction: row;
56+
justify-content: space-between;
57+
align-items: center;
58+
width: 100%;
5259
53-
a {
54-
color: white;
60+
@media (max-width: 991.98px) {
61+
flex-direction: column;
62+
padding: 20px;
63+
height: 40vh;
5564
}
65+
}
5666
57-
img {
58-
max-width: 100%;
59-
}
67+
img {
68+
max-width: 300px;
69+
}
70+
71+
.column-center {
72+
align-self: flex-end;
73+
}
6074
61-
.column-center {
62-
align-self: flex-end;
75+
.column-right {
76+
display: flex;
77+
flex-direction: column;
78+
justify-content: space-between;
79+
}
80+
81+
.credits {
82+
opacity: 0.35;
83+
margin-left: 20px
84+
}
85+
86+
.credits-container {
87+
display: flex;
88+
align-items: center;
89+
justify-content: center;
90+
91+
@media (max-width: 991.98px) {
92+
display: block;
93+
align-items: center;
94+
justify-content: center;
95+
text-align: center;
6396
}
97+
}
6498
65-
.column-right {
66-
display: flex;
67-
flex-direction: column;
68-
justify-content: space-between;
99+
.links {
100+
color: ${onBackgroundColor};
101+
102+
@media (max-width: 991.98px) {
103+
align-items: center;
104+
justify-content: center;
105+
text-align: center;
69106
}
107+
}
70108
`}
71109
>
72-
<Row className={'justify-content-end'}>
73-
<Col md={4} xs={12} className={'column-left text-md-left mt-4'}>
74-
<AirtableAsset
75-
id={'footer-logo'}
76-
asset={logo}
77-
linkOverride={{ id: 'link-footer-logo' }}
78-
/>
79-
</Col>
80-
<Col md={4} xs={12} className={'column-center align-items-end mt-4'}>
110+
<section className="credits-container">
111+
<AirtableAsset
112+
id={'footer-logo'}
113+
asset={logo as AirtableRecord<Asset>}
114+
linkOverride={{ id: 'link-footer-logo' }}
115+
/>
116+
<div className={'credits'}>
81117
<p className={'m-0'}>
82118
{copyrightOwner} - {currentYear}
83119
<br />
84120
{t('footer.terms.text', 'Tous droits réservés')}
85121
</p>
86-
<I18nLink
87-
href={`/terms`}
88-
>
89-
<div
90-
css={{
91-
marginTop: '20px',
92-
textDecoration: 'underline',
93-
cursor: 'pointer',
94-
color: 'white',
95-
}}
96-
>
97-
{t('footer.terms.link', 'Conditions générales d\'utilisation')}
98-
</div>
99-
</I18nLink>
100-
<div
101-
css={css`
102-
margin-top: 10px;
103-
104-
code {
105-
color: white;
106-
}
107-
108-
i {
109-
cursor: help;
110-
}
111-
`}
112-
>
113-
<i
114-
title={'This is only informational, your activity on this website is being tracked for analytics purposes and demonstration on how to perform analytics with Next.js and Amplitude (this uses userSessionContext store provider)'}
115-
>
116-
Device id (analytics):<br />
117-
<DisplayOnBrowserMount
118-
// When using SSR, we want to render the deviceId immediately because we have access to it through server cookies
119-
// When using SSG, we need to wait for the browser render because we don't have access to the cookies when generating the static page
120-
// To test the different behaviours, refresh the both /examples and /products page with JS disabled
121-
// and notice how the deviceId is properly included in the HTML with SSR (/products), unlike SSG (/examples) where it's empty
122-
123-
// XXX This example showcase this complex behaviour. You may want to do something similar for a "Profile" section in <Nav>,
124-
// that can be rendered using both SSG/SSR depending on the page, where SSR should render the component but SSG should wait for browser re-render
125-
deps={[deviceId]}
126-
>
127-
<code>{deviceId}</code>
128-
</DisplayOnBrowserMount>
129-
</i>
122+
</div>
123+
</section>
124+
125+
<section className={'links'}>
126+
<I18nLink
127+
href={`/terms`}
128+
>
129+
<div>
130+
{t('footer.terms.link', 'Conditions générales d\'utilisation')}
130131
</div>
131-
</Col>
132-
<Col md={4} xs={12} className={'column-right text-md-right mt-4'}>
132+
</I18nLink>
133+
<I18nLink
134+
href={`/privacy`}
135+
>
133136
<div>
134-
<I18nBtnChangeLocale id={'footer-btn-change-locale'} />
137+
{t('footer.privacy.link', 'Politique de confidentialité')}
135138
</div>
139+
</I18nLink>
140+
<div
141+
css={css`
142+
margin-top: 10px;
143+
144+
i {
145+
cursor: help;
146+
}
147+
`}
148+
>
149+
<i
150+
title={'This is only informational, your activity on this website is being tracked for analytics purposes and demonstration on how to perform analytics with Next.js and Amplitude (this uses userSessionContext store provider)'}
151+
>
152+
Device id (analytics):<br />
153+
<DisplayOnBrowserMount
154+
// When using SSR, we want to render the deviceId immediately because we have access to it through server cookies
155+
// When using SSG, we need to wait for the browser render because we don't have access to the cookies when generating the static page
156+
// To test the different behaviours, refresh the both /examples and /products page with JS disabled
157+
// and notice how the deviceId is properly included in the HTML with SSR (/products), unlike SSG (/examples) where it's empty
158+
159+
// XXX This example showcase this complex behaviour. You may want to do something similar for a "Profile" section in <Nav>,
160+
// that can be rendered using both SSG/SSR depending on the page, where SSR should render the component but SSG should wait for browser re-render
161+
deps={[deviceId]}
162+
>
163+
<code>{deviceId}</code>
164+
</DisplayOnBrowserMount>
165+
</i>
166+
</div>
167+
</section>
168+
{
169+
shouldDisplayI18nButton && (
136170
<div>
137-
<Logo
138-
id={'footer-logo-unly-brand'}
139-
logo={{
140-
url: NRN_CO_BRANDING_LOGO_URL,
141-
link: {
142-
url: 'https://github.com/unlyEd',
143-
target: '_blank',
144-
},
145-
} as unknown as Asset}
146-
width={100}
147-
height={50}
148-
sizesMultipliers={logoSizesMultipliers}
149-
/>
171+
<I18nBtnChangeLocale id={'footer-btn-change-locale'} />
150172
</div>
151-
</Col>
152-
</Row>
173+
)
174+
}
175+
<section>
176+
<div>
177+
<Logo
178+
id={'footer-logo-unly-brand'}
179+
logo={{
180+
url: NRN_CO_BRANDING_LOGO_URL,
181+
link: {
182+
url: 'https://github.com/unlyEd',
183+
target: '_blank',
184+
},
185+
} as unknown as Asset}
186+
width={100}
187+
height={50}
188+
sizesMultipliers={logoSizesMultipliers}
189+
/>
190+
</div>
191+
</section>
153192
</div>
154193
);
155194
};

‎src/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const NRN_DEFAULT_SERVICE_LABEL = process.env.NEXT_PUBLIC_APP_STAGE === '
66
/**
77
* Co-branding logo displayed in the footer ("powered by Unly")
88
*/
9-
export const NRN_CO_BRANDING_LOGO_URL = '/static/images/LOGO_Powered_by_UNLY_monochrome_WHITE.svg';
9+
export const NRN_CO_BRANDING_LOGO_URL = '/static/images/LOGO_Powered_by_UNLY_BLACK_BLUE.svg';
1010

1111
/**
1212
* Fallback fonts used until our own fonts have been loaded by the browser.

‎src/pages/404.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ const NotFound404Page: NextPage<Props> = (props): JSX.Element => {
119119
text-align: center;
120120
`}
121121
>
122-
<FontAwesomeIcon icon="exclamation-triangle" size={'8x'} />
122+
<FontAwesomeIcon icon="exclamation-triangle" size={'4x'} />
123123
{content}
124124

125125
<I18nLink

‎src/types/data/Customer.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ export type Customer = {
2222
availableLanguages: string[];
2323
products?: AirtableRecord<Product>[];
2424
theme: AirtableRecord<Theme>;
25-
terms?: I18nMarkdown;
26-
termsEN?: Markdown;
27-
termsFR?: Markdown;
2825
serviceLabel?: string;
29-
termsDescription?: Markdown;
30-
privacyDescription?: Markdown;
26+
termsDescription?: I18nMarkdown;
27+
termsDescriptionEN?: Markdown;
28+
termsDescriptionFR?: Markdown;
29+
privacyDescription?: I18nMarkdown;
30+
privacyDescriptionEN?: Markdown;
31+
privacyDescriptionFR?: Markdown;
3132
};

‎src/types/data/CustomerTheme.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { AirtableAttachment } from './AirtableAttachment';
21
import { Theme } from './Theme';
32

43
/**
54
* Simplified version of the Theme.
65
*
76
* CustomerTheme is what's really used for theming, and doesn't include useless properties from the Theme entity.
7+
* Also, all properties defined in Theme will be set in CustomerTheme, because fallback values will have been applied.
8+
*
9+
* Inspired by MaterialUI Design system.
10+
*
11+
* @see https://material.io/design/color/the-color-system.html#color-theme-creation
812
*/
9-
export type CustomerTheme = Omit<Theme, 'ref' | 'id' | '__typename'> & {
10-
logo: AirtableAttachment;
11-
fonts: string;
12-
};
13+
export type CustomerTheme = Required<Omit<Theme, 'ref' | 'id' | '__typename'>> & {};

‎src/types/data/Theme.ts

+1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ export type Theme = {
2424
errorColor?: string;
2525
onErrorColor?: string;
2626
logo?: AirtableAttachment;
27+
fonts?: string;
2728
} & AirtableRecordBaseFields;

‎src/utils/icons/font-awesome.ts

+4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import { faTimesCircle } from '@fortawesome/free-regular-svg-icons';
88
import {
99
faArrowCircleLeft,
1010
faArrowCircleRight,
11+
faArrowRight,
1112
faBook,
1213
faBookReader,
1314
faCoffee,
1415
faHome,
1516
faQuestionCircle,
1617
faUserCog,
18+
faExclamationTriangle,
1719
} from '@fortawesome/free-solid-svg-icons';
1820

1921
// See https://github.com/FortAwesome/react-fontawesome#integrating-with-other-tools-and-frameworks
@@ -46,10 +48,12 @@ library.add(
4648
library.add(
4749
faArrowCircleLeft,
4850
faArrowCircleRight,
51+
faArrowRight,
4952
faBook,
5053
faBookReader,
5154
faCoffee,
5255
faHome,
5356
faQuestionCircle,
5457
faUserCog,
58+
faExclamationTriangle,
5559
);

1 commit comments

Comments
 (1)
Please sign in to comment.