💩(frontend) introduce an official footer

Based on Florian's feedbacks, this is mandatory as the project is
getting attention.

Bad code, needs a refactor.
This commit is contained in:
lebaudantoine
2024-12-03 00:13:48 +01:00
committed by aleb_the_flash
parent 819d3784f7
commit e7e7bb0d09
13 changed files with 402 additions and 26 deletions

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66667 4.00098V5.33431H3.33333V12.6676H10.6667V9.33431H12V13.3343C12 13.7025 11.7015 14.001 11.3333 14.001H2.66667C2.29848 14.001 2 13.7025 2 13.3343V4.66764C2 4.29945 2.29848 4.00098 2.66667 4.00098H6.66667ZM14 2.00098V7.33431H12.6667V4.27631L7.47133 9.47231L6.52867 8.52964L11.7233 3.33431H8.66667V2.00098H14Z" fill="#666666"/>
</svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -7,6 +7,7 @@ import { Center } from '@/styled-system/jsx'
export const LoadingScreen = ({
delay = 500,
header = undefined,
footer = undefined,
layout = 'centered',
}: {
delay?: number
@@ -15,7 +16,7 @@ export const LoadingScreen = ({
return (
<DelayedRender delay={delay}>
<Screen layout={layout} header={header}>
<Screen layout={layout} header={header} footer={footer}>
<CenteredContent>
<Center>
<p>{t('loading')}</p>

View File

@@ -21,7 +21,7 @@ export const QueryAware = ({
}
if (status === 'pending') {
return <LoadingScreen header={undefined} />
return <LoadingScreen header={undefined} footer={undefined} />
}
return children

View File

@@ -15,6 +15,6 @@ export const UserAware = ({ children }: { children: React.ReactNode }) => {
return isLoggedIn !== undefined ? (
children
) : (
<LoadingScreen header={false} delay={1000} />
<LoadingScreen header={false} footer={false} delay={1000} />
)
}

View File

@@ -99,7 +99,7 @@ export const Conference = ({
return (
<QueryAware status={isFetchError ? createStatus : fetchStatus}>
<Screen header={false}>
<Screen header={false} footer={false}>
<LiveKitRoom
room={room}
serverUrl={data?.livekit?.url}

View File

@@ -0,0 +1,276 @@
import { styled } from '@/styled-system/jsx'
import { css } from '@/styled-system/css'
import { A } from '@/primitives'
import { useTranslation } from 'react-i18next'
const StyledLi = styled('li', {
base: {},
variants: {
divider: {
true: {
_after: {
content: '""',
display: 'inline-block',
marginX: '.75rem',
verticalAlign: 'middle',
boxShadow: 'inset 0 0 0 1px #ddd',
height: '1rem',
width: '1px',
},
},
},
},
})
const InnerContainer = styled('div', {
base: {
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
margin: 'auto',
maxWidth: '1200px',
paddingX: { base: '0.5rem', xs: '1rem', sm: '2rem' },
},
})
const MainLinkList = styled('ul', {
base: {
display: 'flex',
gap: '0.5rem 1rem',
flexWrap: 'wrap',
flexBasis: { base: '100%', md: '50%' },
},
})
const FirstRow = styled('div', {
base: {
display: 'flex',
gap: '2rem',
flexWrap: { base: 'wrap', md: 'nowrap' },
justifyContent: 'space-between',
width: '100%',
alignItems: 'flex-start',
marginBottom: '1.5rem',
},
})
const SecondRow = styled('ul', {
base: {
display: 'flex',
borderTop: '1px solid rgb(217 217 217)',
paddingTop: '0.5rem',
width: '100%',
flexWrap: 'wrap',
alignItems: 'center',
},
})
const ThirdRow = styled('p', {
base: {
fontSize: '0.75rem',
color: 'rgb(77 77 77)',
fontFamily: 'Marianne',
textWrap: 'wrap',
lineHeight: '1rem',
marginTop: { base: '1rem', xs: '0.5rem' },
},
})
const Marianne = () => {
return (
<div
className={css({
_before: {
content: '""',
display: 'block',
backgroundRepeat: 'no-repeat',
backgroundSize: 'contain',
backgroundImage: 'url(/assets/marianne.svg)',
height: '1.25rem',
marginBottom: '.2rem',
width: '3rem',
},
_after: {
content: '""',
display: 'block',
backgroundImage: 'url(/assets/devise.svg)',
backgroundRepeat: 'no-repeat',
backgroundSize: 'contain',
height: '2.313rem',
marginTop: '.2rem',
width: '3.25rem',
},
})}
>
<p
className={css({
letterSpacing: '-.01em',
textTransform: 'uppercase',
fontWeight: '600',
fontFamily: 'Marianne',
fontSize: '1.25rem',
lineHeight: '1.75rem',
})}
>
gouvernement
</p>
</div>
)
}
export const Footer = () => {
const { t } = useTranslation('global', { keyPrefix: 'footer' })
return (
<footer
className={css({
borderTop: '2px solid rgb(0 0 145)',
paddingY: '2rem',
})}
>
<InnerContainer>
<FirstRow>
<div
className={css({
display: 'flex',
paddingBottom: '1.5rem',
paddingX: '1.5rem',
alignItems: 'center',
gap: '1.5rem',
})}
>
<Marianne />
<span
className={css({
height: '80px',
backgroundColor: 'rgb(77 77 77)',
width: '1px',
display: { base: 'none', xs: 'block' },
})}
/>
<p
className={css({
display: 'none',
fontWeight: '700',
fontFamily: 'Marianne',
xs: {
display: 'block',
fontSize: '0.75rem',
lineHeight: '1rem',
},
xsm: {
display: 'block',
fontSize: '1rem',
lineHeight: '1.5rem',
},
})}
>
Direction
<br />
interministérielle
<br />
du numérique
</p>
</div>
<MainLinkList>
<li>
<A
externalIcon
underline={false}
footer="important"
href="https://legifrance.gouv.fr"
aria-label={
t('links.legifrance') + ' - ' + t('links.ariaLabel')
}
>
{t('links.legifrance')}
</A>
</li>
<li>
<A
externalIcon
underline={false}
footer="important"
href="https://info.gouv.fr"
aria-label={t('links.infogouv') + ' - ' + t('links.ariaLabel')}
>
{t('links.infogouv')}
</A>
</li>
<li>
<A
externalIcon
underline={false}
footer="important"
href="https://www.service-public.fr/"
aria-label={
t('links.servicepublic') + ' - ' + t('links.ariaLabel')
}
>
{t('links.servicepublic')}
</A>
</li>
<li>
<A
externalIcon
underline={false}
footer="important"
href="https://data.gouv.fr"
aria-label={t('links.datagouv') + ' - ' + t('links.ariaLabel')}
>
{t('links.datagouv')}
</A>
</li>
</MainLinkList>
</FirstRow>
<SecondRow>
<StyledLi divider>
<A
externalIcon
underline={false}
footer="minor"
href="https://docs.numerique.gouv.fr/docs/f88a2eb0-7ce7-4016-b6ee-9f1fd1771951/"
aria-label={t('links.legalsTerms') + ' - ' + t('links.ariaLabel')}
>
{t('links.legalsTerms')}
</A>
</StyledLi>
<StyledLi divider>
<A
externalIcon
underline={false}
footer="minor"
href="https://docs.numerique.gouv.fr/docs/168d7e8e-3f09-462d-8bbc-ea95dedd3889/"
aria-label={t('links.data') + ' - ' + t('links.ariaLabel')}
>
{t('links.data')}
</A>
</StyledLi>
<StyledLi>
<A
externalIcon
underline={false}
footer="minor"
href="https://docs.numerique.gouv.fr/docs/94bd1e3b-a44d-4cf5-b7ee-708a5386a111/"
aria-label={
t('links.accessibility') + ' - ' + t('links.ariaLabel')
}
>
{t('links.accessibility')}
</A>
</StyledLi>
</SecondRow>
<ThirdRow>
{t('mentions')}{' '}
<A
externalIcon
footer="minor"
href="https://github.com/etalab/licence-ouverte/blob/master/LO.md"
>
{t('license')}
</A>
</ThirdRow>
</InnerContainer>
</footer>
)
}

View File

@@ -3,6 +3,7 @@ import { css } from '@/styled-system/css'
import { Header } from './Header'
import { layoutStore } from '@/stores/layout'
import { useSnapshot } from 'valtio'
import { Footer } from '@/layout/Footer'
export type Layout = 'fullpage' | 'centered'
@@ -15,28 +16,33 @@ export type Layout = 'fullpage' | 'centered'
export const Layout = ({ children }: { children: ReactNode }) => {
const layoutSnap = useSnapshot(layoutStore)
const showHeader = layoutSnap.showHeader
const showFooter = layoutSnap.showFooter
return (
<div
className={css({
height: '100%',
display: 'flex',
flexDirection: 'column',
backgroundColor: 'white',
color: 'default.text',
})}
>
{showHeader && <Header />}
<main
<>
<div
className={css({
flexGrow: 1,
overflow: 'auto',
height: '100%',
display: 'flex',
minHeight: 'fit-content',
flexDirection: 'column',
backgroundColor: 'white',
color: 'default.text',
})}
>
{children}
</main>
</div>
{showHeader && <Header />}
<main
className={css({
flexGrow: 1,
overflow: 'auto',
display: 'flex',
flexDirection: 'column',
})}
>
{children}
</main>
</div>
{showFooter && <Footer />}
</>
)
}

View File

@@ -13,18 +13,23 @@ export type ScreenProps = {
* True by default. Pass undefined to render the screen without modifying current header visibility
*/
header?: boolean
footer?: boolean
children: React.ReactNode
}
export const Screen = ({
layout = 'fullpage',
header = true,
footer = true,
children,
}: ScreenProps) => {
useEffect(() => {
if (header !== undefined) {
layoutStore.showHeader = header
}
}, [header])
if (footer !== undefined) {
layoutStore.showFooter = footer
}
}, [header, footer])
return layout === 'centered' ? <Centered>{children}</Centered> : children
}

View File

@@ -24,5 +24,19 @@
"notFound": {
"heading": ""
},
"submit": "OK"
"submit": "OK",
"footer": {
"links": {
"legifrance": "",
"infogouv": "",
"servicepublic": "",
"datagouv": "",
"legalsTerms": "",
"data": "",
"accessibility": "",
"ariaLabel": ""
},
"mentions": "",
"license": ""
}
}

View File

@@ -24,5 +24,19 @@
"notFound": {
"heading": "Page not found"
},
"submit": "OK"
"submit": "OK",
"footer": {
"links": {
"legifrance": "legifrance.gouv.fr",
"infogouv": "info.gouv.fr",
"servicepublic": "service-public.fr",
"datagouv": "data.gouv.fr",
"legalsTerms": "Legal Notice",
"data": "Personal Data and Cookies",
"accessibility": "Accessibility: audit in progress",
"ariaLabel": "new window"
},
"mentions": "Unless otherwise stated, the contents of this site are available under",
"license": "etalab 2.0 license"
}
}

View File

@@ -24,5 +24,19 @@
"notFound": {
"heading": "Page introuvable"
},
"submit": "OK"
"submit": "OK",
"footer": {
"links": {
"legifrance": "legifrance.gouv.fr",
"infogouv": "info.gouv.fr",
"servicepublic": "service-public.fr",
"datagouv": "data.gouv.fr",
"legalsTerms": "Mentions légales",
"data": "Données personnelles et cookie",
"accessibility": "Accessibilités : audit en cours",
"ariaLabel": "nouvelle fenêtre"
},
"mentions": "Sauf mention contraire, les contenus de ce site sont disponibles sous",
"license": "licence etalab 2.0"
}
}

View File

@@ -21,6 +21,36 @@ const link = cva({
textStyle: 'sm',
},
},
externalIcon: {
true: {
_after: {
content: 'url(/assets/link-grey.svg)',
verticalAlign: 'middle',
paddingLeft: '.25rem',
},
},
},
underline: {
false: {
textDecoration: 'none',
},
},
footer: {
important: {
fontSize: '0.8rem',
lineHeight: '1rem',
fontWeight: '700',
fontFamily: 'Marianne',
textWrap: 'nowrap',
},
minor: {
fontSize: '0.75rem',
color: 'rgb(77 77 77)',
fontFamily: 'Marianne',
textWrap: 'nowrap',
lineHeight: '1rem',
},
},
},
})
@@ -29,6 +59,17 @@ export type AProps = LinkProps & RecipeVariantProps<typeof link>
/**
* anchor component styled with underline. Used mostly for external links. Use Link for internal links
*/
export const A = ({ size, ...props }: AProps) => {
return <Link {...props} className={link({ size })} />
export const A = ({
size,
externalIcon,
underline,
footer,
...props
}: AProps) => {
return (
<Link
{...props}
className={link({ size, externalIcon, underline, footer })}
/>
)
}

View File

@@ -3,10 +3,12 @@ import { PanelId } from '@/features/rooms/livekit/hooks/useSidePanel'
type State = {
showHeader: boolean
showFooter: boolean
activePanelId: PanelId | null
}
export const layoutStore = proxy<State>({
showHeader: false,
showFooter: false,
activePanelId: null,
})