Affiche une bannière dans la section dev lors d'un recrutement

pull/1891/head
Maxime Quandalle 2021-12-13 15:16:34 +01:00 committed by Maxime Quandalle
parent bec524a411
commit fd02b0095f
11 changed files with 139 additions and 72 deletions

View File

@ -12,7 +12,10 @@
"engines": {
"node": ">=12.16.1"
},
"browserslist": ["> 1% in FR", "not ie < 11"],
"browserslist": [
"> 1% in FR",
"not ie < 11"
],
"scripts": {
"prepare": "node scripts/prepare.js",
"build": "yarn run build:prod && yarn run build:legacy",
@ -40,6 +43,7 @@
},
"dependencies": {
"@babel/runtime": "^7.3.4",
"@icons/material": "^0.4.1",
"@internationalized/number": "^3.0.3",
"@mui/material": "^5.0.4",
"@mui/styled-engine": "npm:@mui/styled-engine-sc@latest",
@ -188,6 +192,7 @@
"webpack-hot-middleware": "^2.24.2",
"workbox-webpack-plugin": "^6.0.2",
"worker-loader": "^2.0.0",
"xml2js": "^0.4.23",
"yaml-loader": "^0.5.0"
},
"optionalDependencies": {

View File

@ -0,0 +1,38 @@
// We publish our job offers on https://beta.gouv.fr/recrutement/. To augment
// their reach, we also publish a banner on our website automatically by using
// the beta.gouv.fr API.
require('isomorphic-fetch')
const xml2js = require('xml2js')
const util = require('util')
const { createDataDir, writeInDataDir } = require('./utils.js')
const parseXML = util.promisify(xml2js.parseString)
main()
async function main() {
createDataDir()
const jobOffers = await fetchJobOffers()
writeInDataDir('job-offers.json', jobOffers)
}
async function fetchJobOffers() {
const response = await fetch('https://beta.gouv.fr/jobs.xml')
const content = await response.text()
// The XML API isn't the most ergonomic, we ought to have a JSON API.
// cf. https://github.com/betagouv/beta.gouv.fr/issues/6343
const jobOffers = (await parseXML(content)).feed.entry
.map((entry) => ({
title: entry.title[0]['_'].trim(),
link: entry.link[0].$.href,
content: entry.content[0]['_'].trim(),
}))
.filter(({ title }) => title.includes('Offre de Mon-entreprise'))
.map(({ title, ...rest }) => ({
...rest,
title: title.replace(' - Offre de Mon-entreprise', ''),
}))
return jobOffers
}

View File

@ -1,2 +1,3 @@
require('./fetch-releases.js')
require('./fetch-stats.js')
require('./fetch-job-offers.js')

View File

@ -2,12 +2,11 @@ import { useLocalStorage, writeStorage } from '@rehooks/local-storage'
import { Appear } from 'Components/ui/animate'
import Emoji from 'Components/utils/Emoji'
import { SitePathsContext } from 'Components/utils/SitePathsContext'
import { Button } from 'DesignSystem/buttons'
import { GenericButtonOrLinkProps, Link } from 'DesignSystem/typography/link'
import lastRelease from 'Data/last-release.json'
import { Banner, HideButton, InnerBanner } from 'DesignSystem/banner'
import { Link } from 'DesignSystem/typography/link'
import { useContext, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import lastRelease from '../../data/last-release.json'
const localStorageKey = 'last-viewed-release'
@ -17,44 +16,6 @@ export const hideNewsBanner = () => {
export const determinant = (word: string) =>
/^[aeiouy]/i.exec(word) ? 'd' : 'de '
const Container = styled.div`
display: flex;
width: 100%;
margin: auto;
align-items: center;
justify-content: center;
font-family: ${({ theme }) => theme.fonts.main};
`
const InnerContainer = styled.div`
display: flex;
margin: auto;
align-items: center;
justify-content: center;
padding: 0.5rem 1rem;
background-color: ${({ theme }) => theme.colors.bases.primary[100]};
border: 2px solid;
border-color: ${({ theme }) => theme.colors.bases.primary[500]};
border-radius: 0.375rem;
`
const HideButton = styled(Button)<GenericButtonOrLinkProps>`
display: flex;
align-items: center;
justify-content: center;
height: 1.5rem;
width: 1.5rem;
padding: 0;
background: ${({ theme }) => theme.colors.extended.grey[100]};
color: ${({ theme }) => theme.colors.bases.primary[600]};
font-weight: bold;
margin-left: 1rem;
&:hover {
background: ${({ theme }) => theme.colors.bases.primary[300]};
}
`
export default function NewsBanner() {
const [lastViewedRelease] = useLocalStorage(localStorageKey)
const sitePaths = useContext(SitePathsContext)
@ -77,8 +38,8 @@ export default function NewsBanner() {
}
return (
<Appear>
<Container className="print-hidden">
<InnerContainer>
<Banner className="print-hidden">
<InnerBanner>
<span>
<Emoji emoji="✨" /> Découvrez les nouveautés{' '}
{determinant(lastRelease.name)}
@ -89,8 +50,8 @@ export default function NewsBanner() {
<HideButton onPress={hideNewsBanner} aria-label={t('Fermer')}>
&times;
</HideButton>
</InnerContainer>
</Container>
</InnerBanner>
</Banner>
</Appear>
)
}

View File

@ -0,0 +1,41 @@
import { Button } from 'DesignSystem/buttons'
import { GenericButtonOrLinkProps } from 'DesignSystem/typography/link'
import styled from 'styled-components'
export const Banner = styled.div`
display: flex;
width: 100%;
margin: auto;
align-items: center;
justify-content: center;
font-family: ${({ theme }) => theme.fonts.main};
`
export const InnerBanner = styled.div`
display: flex;
margin: auto;
align-items: center;
justify-content: center;
padding: 0.5rem 1rem;
background-color: ${({ theme }) => theme.colors.bases.primary[100]};
border: 2px solid;
border-color: ${({ theme }) => theme.colors.bases.primary[500]};
border-radius: 0.375rem;
`
export const HideButton = styled(Button)<GenericButtonOrLinkProps>`
display: flex;
align-items: center;
justify-content: center;
height: 1.5rem;
width: 1.5rem;
padding: 0;
background: ${({ theme }) => theme.colors.extended.grey[100]};
color: ${({ theme }) => theme.colors.bases.primary[600]};
font-weight: bold;
margin-left: 1rem;
&:hover {
background: ${({ theme }) => theme.colors.bases.primary[300]};
}
`

View File

@ -4,7 +4,7 @@ import { Li, Ul } from 'DesignSystem/typography/list'
import { Body } from 'DesignSystem/typography/paragraphs'
import { useState } from 'react'
import styled from 'styled-components'
import stats from '../../data/stats.json'
import stats from 'Data/stats.json'
export default function DemandeUtilisateurs() {
return (

View File

@ -13,7 +13,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
import { Trans } from 'react-i18next'
import { useHistory, useLocation } from 'react-router-dom'
import { toAtString } from '../../ATInternetTracking'
import statsJson from '../../data/stats.json'
import statsJson from 'Data/stats.json'
import { debounce } from '../../utils'
import { SimulateurCard } from '../Simulateurs/Home'
import useSimulatorsData, { SimulatorData } from '../Simulateurs/metadata'

View File

@ -9,10 +9,19 @@ import { TrackChapter } from '../../ATInternetTracking'
import Iframe from './Iframe'
import Library from './Library'
import Options from './Options'
import jobOffers from 'Data/job-offers.json'
import { Banner, InnerBanner } from 'DesignSystem/banner'
type JobOffer = {
title: string
link: string
content: string
}
export default function Integration() {
const sitePaths = useContext(SitePathsContext)
const { pathname } = useLocation()
const openJobOffer = (jobOffers as Array<JobOffer>)[0]
return (
<>
<TrackChapter chapter1="integration" />
@ -26,27 +35,19 @@ export default function Integration() {
<Trans>Outils pour les développeurs</Trans> <Emoji emoji="👨‍💻" />
</Link>
)}
{/* TODO: Nous pourrions automatiser la publication de cette bannière
de recrutement lorsqu'une annonce est postée sur beta.gouv.fr
https://github.com/betagouv/beta.gouv.fr/issues/6343 */}
{/* <div
className="ui__ card plain"
css={`
margin: 1rem 0;
transform: translateY(1rem);
text-align: center;
padding: 0.4rem;
`}
>
📯{' '}
<strong>
<a href="https://beta.gouv.fr/recrutement/2021/05/25/mon-entreprise-fr.recrute.js.html">
Mon-entreprise recrute !
</a>
</strong>{' '}
<small>Freelance Typescript / React pour 6 mois minimum</small>
</div> */}
{openJobOffer && (
<Banner>
<InnerBanner>
<span>
<Emoji emoji="📯" />{' '}
<strong>
<a href={openJobOffer.link}>Mon-entreprise recrute !</a>
</strong>{' '}
<small>{openJobOffer.title}</small>
</span>
</InnerBanner>
</Banner>
)}
<Switch>
<Route exact path={sitePaths.integration.index} component={Options} />
<Route path={sitePaths.integration.iframe} component={Iframe} />

View File

@ -18,7 +18,8 @@
"Reducers/*": ["reducers/*"],
"Selectors/*": ["selectors/*"],
"Types/*": ["types/*"],
"DesignSystem/*": ["design-system/*"]
"DesignSystem/*": ["design-system/*"],
"Data/*": ["data/*"]
},
"typeRoots": ["./types/", "./node_modules/@types"],
"noEmit": true,

View File

@ -113,6 +113,7 @@ module.exports.default = {
Types: path.resolve('source/types/'),
Images: path.resolve('source/static/images/'),
DesignSystem: path.resolve('source/design-system'),
Data: path.resolve('source/data'),
},
extensions: ['.js', '.ts', '.tsx'],
},

View File

@ -1243,6 +1243,11 @@
resolved "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz"
integrity sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==
"@icons/material@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.4.1.tgz#20ba0dff8d59a2b8749c7ad765faad52ae45b868"
integrity sha512-r4CuKUZv9GeAYvWc6WEVF0Xiw/IS4S50zna/M0/ISJOKe3RbpbHN3yBjX7ZnaPGqH/rm5SnDBv8FNOHLpM7OpQ==
"@internationalized/date@3.0.0-alpha.1":
version "3.0.0-alpha.1"
resolved "https://registry.npmjs.org/@internationalized/date/-/date-3.0.0-alpha.1.tgz"
@ -12557,7 +12562,7 @@ safe-regex@^1.1.0:
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@~1.2.1:
sax@>=0.6.0, sax@~1.2.1:
version "1.2.4"
resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
@ -14909,6 +14914,19 @@ xml-name-validator@^3.0.0:
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
xml2js@^0.4.23:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
xmlchars@^2.1.1, xmlchars@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz"