🎨 Fusion page choix de couleur et choix du module

* Correction du choix de la couleur qui modifiait la couleur de tout le
  site
* Retire les titres sur les intégrations (comme sur le simulateur
  salarié)
pull/888/head
Maxime Quandalle 2020-02-10 18:22:06 +01:00
parent fd025f36d0
commit 234e9a2b82
13 changed files with 284 additions and 175 deletions

View File

@ -1,21 +0,0 @@
import { Component } from 'react'
class SetCSSColor extends Component {
updateCSSColor = () => {
Object.entries(this.props.colors).forEach(([key, value]) => {
document.body.style.setProperty(`--${key}`, value)
}, this.props.colors)
}
constructor(props) {
super(props)
this.updateCSSColor()
}
componentDidUpdate() {
this.updateCSSColor()
}
render() {
return null
}
}
export default SetCSSColor

View File

@ -1,6 +1,5 @@
import convert from 'color-convert'
import SetCssColor from 'Components/utils/SetCssColor'
import React, { createContext } from 'react'
import React, { createContext, useEffect, useRef } from 'react'
/*
Hex to RGB conversion:
@ -101,10 +100,17 @@ type ProviderProps = {
export function ThemeColorsProvider({ color, children }: ProviderProps) {
const colors = generateTheme(color)
const divRef = useRef<HTMLDivElement>(null)
useEffect(() => {
Object.entries(colors).forEach(([key, value]) => {
if (typeof value === 'string') {
divRef.current?.style.setProperty(`--${key}`, value)
}
}, colors)
}, [colors])
return (
<ThemeColorsContext.Provider value={colors}>
<SetCssColor colors={colors} />
{children}
<div ref={divRef}>{children}</div>
</ThemeColorsContext.Provider>
)
}

View File

@ -17,6 +17,7 @@ Changer: Change
Chercher dans la documentation: Search the documentation
Choisir la forme juridique: Choose your legal status
Choisir plus tard: Choose later
Code d'intégration: Integration Code
Commencer: Get started
'Commerçant, artisan, ou libéral ?': 'Trader, craftsman, or liberal?'
Continuer: Continue
@ -62,7 +63,7 @@ Modifier mes réponses: Change my answers
Mon entreprise: My company
Mon revenu: My income
Montant des cotisations: Amount of contributions
"Nom de l'entreprise ou SIREN ": Company name or SIREN code
'Nom de l''entreprise ou SIREN ': Company name or SIREN code
Non: 'No'
Nous n'avons rien trouvé: We didn't find any matching registered company.
Oui: 'Yes'
@ -74,12 +75,16 @@ Part salarié: Employee share
Pas en auto-entrepreneur: Not in auto-entrepreneur
Pas implémenté: Not implemented
Passer: Skip
Personnalisez l'integration: Customize the integration
Plein écran: Fullscreen
Plus d'informations: More information (fr)
Plusieurs associés: Several partners
Prochaines questions: Next questions
Protection sociale: Social security
Précédent: Previous
Prévisualisation: Preview
Quel module ?: What module?
Quelle couleur ?: What color?
Quelques exemples de salaires: Some salary exemples
Quelques intégrations: Some integrations
Recherche en cours...: Searching...
@ -115,6 +120,7 @@ Total des retenues: Total withheld
Tout effacer: Delete all
Tranche de l'assiette: Scale bracket
Un seul associé: Only one partner
Une idée&nbsp;?<1></1>Contactez-nous&nbsp;!: Any ideas?<1></1>Contact us!
Vie privée: Privacy
Voir la répartition des cotisations: View contribution breakdown
Voir le code source: See the source code
@ -780,10 +786,14 @@ pages:
bibliothèque: "<0>Integrate our calculation library</0><1>If you think that your site or service would benefit from displaying salary calculations, for example switching from gross salary to net salary, good news: all the contribution and tax calculations behind my-company.fr are free and <2>can be integrated in the form of an <2>NPM library</2></2>.</1><2>Put simply, your team's developers are able to integrate the calculation into your interface in 5 minutes{emoji('⌚')}, without having to deal with the complexity of payroll and the regular updating of calculation rules.</2><3>This library is a common digital library developed by the State and ACOSS. It is based on a new programming language, <2>publicodes</2>.</3><4>How to use it?</4><5>The following examples show you how to use the library on a very simple ReactJs site.</5><6>1) Make a very simple calculation: from gross to net</6><7><0></0></7><8>2) Browse online documentation</8><9>As you can see from the previous example, the recipe for a calculation is simple: input variables (gross wage), one or more output variables (net wage).</9><10>All these variables are listed and explained in our <2>online documentation</2>.</10><11>Use the search engine to find the right variable, then click on \"View source code\" to get all the documentation: default value, possible values when it's an enumeration of choice, unit when it's a number, description, associated user question, etc.</11><12>Let's run a calculation closer to a payslip: Here is a description of the input situation with links to the corresponding pages of the documentation :</12><13> An <3>executive</3> earning <7>€3,400 gross</7>, who benefits from the<10> bicycle mileage allowance</10> and works in a company with <14>12 employees</14>.</13><14>The calculation for this more complete example is as follows:</14><15><0></0></15><16>{emoji(' ')} Note that in the previous example we have to specify the transport payment rate ourselves.</16><17>Whereas in the <2>employee</2> simulator, it is sufficient to fill in the municipality and the corresponding rate is automatically determined. It's intentional: to keep the library (and the site) light, we use two online APIs. The<4> Geo API - communes</4> to switch from the commune name to the common code. Then the<7> transport payout API</7>, developed and maintained by us, which is not documented but its use is very simple and understandable <10>in this React component that calls it</10>, a component that also uses the common API.</17><18>Making economic charts{emoji(' \U0001F4C8')}</18><19>It is also possible to use the library for economic or political analysis calculations. Here, the price of labour and the net wage is plotted against the gross wage.</19><20>We can see the progressiveness of the total wage, which is in percent lower for a minimum wage than for a high income. In other words, high-wage earners pay part of the social security contributions of low-wage earners.</20><21>{emoji('⚠️ ')}Beware, this example does a lot of calculations in one go, which can block your browser for a few seconds. To overcome this problem, you would have to call the library in a Web Worker, but this is not possible for the <3>moment</3> in these demos.</21><22><0></0></22>"
développeurs:
choice:
github: >-
<0>Contribute to GitHub</0><1>All our tools are open and publicly
developed on GitHub.</1>
library: >-
<0>Using the calculation engine</0><1>The entire socio-fiscal
calculation engine developed by URSSAF, freely available in the form of
an NPM library.</1>
code à copier: 'Here is the code to copy and paste on your site:'
home:
choice:
iframe: >-
@ -799,13 +809,12 @@ pages:
users.
titre: Integrate social security law at the heart of your tools
iframe: >-
<0>Integrate the Web module</0><1>By adding a line to your web page
:</1><2></2><3>You can <2>choose the main color of the module</2> to blend
it into the visual theme of your page: just change the <4>data-color</4>
value above. To choose it, use our <7>interactive tool</7>.</3><4>The
<0>Integrate the Web module</0><1>Our simulators can be seamlessly
integrated by adding a simple line of code to your web page.</1><2>You can
choose the simulator to integrate and <2>customize the main color of the
module</2> to blend it into the visual theme of your page.</2><3>The
attribute <1>data-lang="en"</1> allows you to choose English as the
default language of the simulator (it will remain modifiable by the
user).</4>
simulator language.</3>
par: per
path:
créer:

View File

@ -25,7 +25,6 @@ import Footer from './layout/Footer/Footer'
import Header from './layout/Header'
import trackSimulatorActions from './middlewares/trackSimulatorActions'
import Créer from './pages/Créer'
import Couleur from './pages/Dev/Couleur'
import IntegrationTest from './pages/Dev/IntegrationTest'
import Personas from './pages/Dev/Personas'
import Sitemap from './pages/Dev/Sitemap'
@ -128,7 +127,6 @@ const App = () => {
path="/dev/integration-test"
component={IntegrationTest}
/>
<Route exact path="/dev/couleur" component={Couleur} />
<Route exact path="/dev/personas" component={Personas} />
<Route component={Route404} />

View File

@ -3,11 +3,9 @@ import { ChromePicker } from 'react-color'
export default function ColorPicker({ color, onChange }) {
return (
<div style={{ display: 'flex', justifyContent: 'center', margin: '1rem' }}>
<ChromePicker
color={color}
onChangeComplete={color => onChange(color.hex)}
/>
</div>
<ChromePicker
color={color}
onChangeComplete={color => onChange(color.hex)}
/>
)
}

View File

@ -1,33 +0,0 @@
import {
ThemeColorsContext,
ThemeColorsProvider
} from 'Components/utils/colors'
import React, { Suspense, useContext, useState } from 'react'
import Home from '../Iframes/SimulateurEmbauche'
let LazyColorPicker = React.lazy(() => import('./ColorPicker'))
export default function Couleur() {
const { color: defaultColor } = useContext(ThemeColorsContext)
const [color, setColor] = useState(defaultColor)
return (
<>
<h1>Changez la couleur de l'integration </h1>
<p className="indication">
Visualisez sur cette page lapparence du module pour différentes
couleurs principales.
</p>
<Suspense fallback={<div>Chargement...</div>}>
<LazyColorPicker color={color} onChange={setColor} />
</Suspense>
<p className="indication">
La couleur sélectionnée, à déclarer comme attribut
&quot;data-couleur&quot; du script sur votre page est : <b>{color}</b>
</p>
<div className="ui__ card">
<ThemeColorsProvider color={color}>
<Home />
</ThemeColorsProvider>
</div>
</>
)
}

View File

@ -1,7 +1,7 @@
import React, { Suspense } from 'react'
let LazyColorPicker = React.lazy(() => import('./ColorPicker'))
const integrableModuleNames = [
export const integrableModuleNames = [
'simulateur-embauche',
'simulateur-autoentrepreneur',
'simulateur-independant',
@ -56,70 +56,13 @@ export default function IntegrationTest() {
</button>
<div
style={{ border: '2px dashed blue' }}
css={`
display: ${version > 0 ? 'block' : 'none'};
`}
>
<p>Code d'intégration </p>
<IntegrationCode color={color} module={currentModule} />
<div style={{ border: '2px dashed blue' }}>
<div ref={domNode} />
</div>
<div ref={domNode} />
</div>
</>
)
}
export let IntegrationCode = ({
module = 'simulateur-embauche',
color = '#2975D1'
}) => (
<code
css={`
display: block;
font-size: 80%;
width: 90%;
padding: 1em;
background: #f8f8f8;
margin: auto;
margin-bottom: 1em;
overflow: auto;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05),
-1px 1px 1px rgba(0, 0, 0, 0.02);
em {
font-weight: 300;
color: black;
}
:before {
content: '';
position: absolute;
top: 0;
right: 0;
border-width: 0 16px 16px 0;
border-style: solid;
border-color: #e8e8e8 white;
}
#scriptColor {
color: #2975d1;
}
`}
>
<span>{'<'}</span>
<em>
script
<br />
id
</em>
="script-simulateur-embauche"
<em>data-module</em>="
<span>{module}</span>"<em>data-couleur</em>="
<span id="scriptColor">{color}</span>" <em>src</em>
="https://mon-entreprise.fr/simulateur-iframe-integration.js">
<span>{'<'}</span>
<span>/</span>
<em>script</em>
<span>></span>
</code>
)

View File

@ -1,6 +1,7 @@
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import React from 'react'
import { Route } from 'react-router'
import { inIframe } from '../../../../utils'
import SimulateurArtisteAuteur from '../Simulateurs/ArtisteAuteur'
import SimulateurAssimiléSalarié from '../Simulateurs/AssimiléSalarié'
import SimulateurAutoEntrepreneur from '../Simulateurs/AutoEntrepreneur'
@ -36,7 +37,7 @@ export default function Iframes() {
path="/iframes/simulateur-artiste-auteur"
component={SimulateurArtisteAuteur}
/>
<IframeFooter />
{inIframe() && <IframeFooter />}
</div>
</IsEmbeddedContext.Provider>
)

View File

@ -4,6 +4,7 @@ import RuleLink from 'Components/RuleLink'
import SimulateurWarning from 'Components/SimulateurWarning'
import config from 'Components/simulationConfigs/artiste-auteur.yaml'
import 'Components/TargetSelection.css'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import { formatValue } from 'Engine/format'
import InputComponent from 'Engine/RuleInput'
import { getRuleFromAnalysis } from 'Engine/rules'
@ -41,14 +42,17 @@ export default function ArtisteAuteur() {
const dispatch = useDispatch()
dispatch(setSimulationConfig(config))
const initialRender = useInitialRender()
const inIframe = useContext(IsEmbeddedContext)
return (
<>
<h1>
<Trans i18nKey="simulateurs.artiste-auteur.titre">
Estimer mes cotisations dartiste-auteur
</Trans>
</h1>
{!inIframe && (
<h1>
<Trans i18nKey="simulateurs.artiste-auteur.titre">
Estimer mes cotisations dartiste-auteur
</Trans>
</h1>
)}
<SimulateurWarning simulateur="artiste-auteur" />
<section className="ui__ light card">
<div id="targetSelection">

View File

@ -3,7 +3,8 @@ import SalaryExplanation from 'Components/SalaryExplanation'
import Warning from 'Components/SimulateurWarning'
import Simulation from 'Components/Simulation'
import assimiléConfig from 'Components/simulationConfigs/assimilé.yaml'
import React from 'react'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import React, { useContext } from 'react'
import { Helmet } from 'react-helmet'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
@ -13,8 +14,8 @@ export default function AssimiléSalarié() {
const dispatch = useDispatch()
const location = useLocation()
dispatch(setSimulationConfig(assimiléConfig, location.state?.fromGérer))
const { t } = useTranslation()
const inIframe = useContext(IsEmbeddedContext)
return (
<>
@ -33,11 +34,13 @@ export default function AssimiléSalarié() {
)}
/>
</Helmet>
<h1>
<Trans i18nKey="simulateurs.assimilé-salarié.titre">
Simulateur de revenus assimilé salarié
</Trans>
</h1>
{!inIframe && (
<h1>
<Trans i18nKey="simulateurs.assimilé-salarié.titre">
Simulateur de revenus assimilé salarié
</Trans>
</h1>
)}
<Warning simulateur="assimilé-salarié" />
<Simulation explanations={<SalaryExplanation />} />
</>

View File

@ -4,6 +4,7 @@ import Simulation from 'Components/Simulation'
import autoEntrepreneurConfig from 'Components/simulationConfigs/auto-entrepreneur.yaml'
import StackedBarChart from 'Components/StackedBarChart'
import { ThemeColorsContext } from 'Components/utils/colors'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import { getRuleFromAnalysis } from 'Engine/rules'
import { default as React, useContext } from 'react'
import { Helmet } from 'react-helmet'
@ -15,6 +16,7 @@ import { analysisWithDefaultsSelector } from 'Selectors/analyseSelectors'
export default function AutoEntrepreneur() {
const dispatch = useDispatch()
const location = useLocation()
const inIframe = useContext(IsEmbeddedContext)
dispatch(
setSimulationConfig(autoEntrepreneurConfig, location.state?.fromGérer)
)
@ -38,11 +40,13 @@ export default function AutoEntrepreneur() {
)}
/>
</Helmet>
<h1>
<Trans i18nKey="simulateurs.auto-entrepreneur.titre">
Simulateur de revenus auto-entrepreneur
</Trans>
</h1>
{!inIframe && (
<h1>
<Trans i18nKey="simulateurs.auto-entrepreneur.titre">
Simulateur de revenus auto-entrepreneur
</Trans>
</h1>
)}
<Warning simulateur="auto-entrepreneur" />
<Simulation explanations={<ExplanationSection />} />
</>

View File

@ -4,6 +4,7 @@ import Simulation from 'Components/Simulation'
import indépendantConfig from 'Components/simulationConfigs/indépendant.yaml'
import StackedBarChart from 'Components/StackedBarChart'
import { ThemeColorsContext } from 'Components/utils/colors'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import { getRuleFromAnalysis } from 'Engine/rules'
import { default as React, useContext } from 'react'
import { Helmet } from 'react-helmet'
@ -17,6 +18,8 @@ export default function Indépendant() {
const location = useLocation()
dispatch(setSimulationConfig(indépendantConfig, location.state?.fromGérer))
const { t } = useTranslation()
const inIframe = useContext(IsEmbeddedContext)
return (
<>
<Helmet>
@ -34,11 +37,13 @@ export default function Indépendant() {
)}
/>
</Helmet>
<h1>
<Trans i18nKey="simulateurs.indépendant.titre">
Simulateur de revenus pour indépendants
</Trans>
</h1>
{!inIframe && (
<h1>
<Trans i18nKey="simulateurs.indépendant.titre">
Simulateur de revenus pour indépendants
</Trans>
</h1>
)}
<Warning simulateur="indépendant" />
<Simulation explanations={<ExplanationSection />} />
</>

View File

@ -1,15 +1,118 @@
import { ThemeColorsProvider } from 'Components/utils/colors'
import { ScrollToTop } from 'Components/utils/Scroll'
import urssafLogo from 'Images/urssaf.svg'
import React from 'react'
import React, { Suspense, useRef, useState } from 'react'
import { Trans } from 'react-i18next'
import { Link } from 'react-router-dom'
import { IntegrationCode } from '../Dev/IntegrationTest'
import { MemoryRouter } from 'react-router-dom'
import { integrableModuleNames } from '../Dev/IntegrationTest'
import Iframes from '../Iframes'
import './iframe.css'
import apecLogo from './images/apec.png'
import cciLogo from './images/cci.png'
import minTraLogo from './images/min-tra.jpg'
import poleEmploiLogo from './images/pole-emploi.png'
let LazyColorPicker = React.lazy(() => import('../Dev/ColorPicker'))
function IntegrationCustomizer() {
const [currentModule, setCurrentModule] = React.useState(
integrableModuleNames[0]
)
const [color, setColor] = useState()
return (
<section>
<h2>
<Trans>Personnalisez l'integration</Trans>
</h2>
<div
className="ui__ full-width"
css={`
background-color: var(--lightestColor);
`}
>
<div
css={`
display: flex;
padding: 20px 5%;
max-width: 1400px;
margin: auto;
.ui__.left-side {
width: 40%;
padding-right: 25px;
margin-right: 35px;
border-right: 2px solid var(--lighterColor);
select {
padding: 7px;
}
}
.ui__.right-side {
width: 60%;
}
@media (max-width: 800px) {
flex-direction: column;
.ui__.left-side,
.ui__.right-side {
border: none;
width: 100%;
}
}
`}
>
<div className="ui__ left-side">
<h3>
<Trans>Quel module ?</Trans>
</h3>
<select onChange={event => setCurrentModule(event.target.value)}>
{integrableModuleNames.map(name => (
<option key={name}>{name}</option>
))}
</select>
<h3>
<Trans>Quelle couleur ?</Trans>
</h3>
<Suspense fallback={<div>Chargement...</div>}>
<LazyColorPicker color={color} onChange={setColor} />
</Suspense>
<h3>
<Trans>Code d'intégration</Trans>
</h3>
<p>
<Trans i18nKey="pages.développeurs.code à copier">
Voici le code à copier-coller sur votre site :
</Trans>
</p>
<IntegrationCode color={color} module={currentModule} />
</div>
<div
className="ui__ right-side"
css={`
overflow: hidden;
`}
>
<h3>
<Trans>Prévisualisation</Trans>
</h3>
<MemoryRouter
key={currentModule}
initialEntries={[`/iframes/${currentModule}`]}
>
<ThemeColorsProvider color={color}>
<Iframes />
</ThemeColorsProvider>
</MemoryRouter>
</div>
</div>
</div>
</section>
)
}
export default function Integration() {
return (
<>
@ -17,26 +120,26 @@ export default function Integration() {
<div>
<Trans i18nKey="pages.développeurs.iframe">
<h1>Intégrez le module Web</h1>
<p>En ajoutant une ligne à votre page Web :</p>
<IntegrationCode />
<p>
Vous pouvez <strong>choisir la couleur principale du module</strong>{' '}
pour le fondre dans le thème visuel de votre page : changez
simplement la valeur de <i>data-couleur</i> ci-dessus. Pour la
choisir, utilisez notre{' '}
<Link to="/dev/couleur">outil interactif</Link>.
Nous simulateurs sont intégrables de manière transparante en
ajoutant une simple ligne de code à votre page Web.
</p>
<p>
Vous pouvez choisir le simulateur à intégrer et{' '}
<strong>personnaliser la couleur principale du module</strong> pour
le fondre dans le thème visuel de votre page.
</p>
<p>
L'attribut <i>data-lang="en"</i> vous permet quand à lui de choisir
l'anglais comme langue par défaut du simulateur (elle restera
modifiable par l'utilisateur).
l'anglais comme langue du simulateur.
</p>
</Trans>
</div>
<div className="blocks" id="integrations">
<h1>
<IntegrationCustomizer />
<section className="blocks" id="integrations">
<h2>
<Trans>Quelques intégrations</Trans>
</h1>
</h2>
<div id="integrationList">
<article>
<a href="https://www.urssaf.fr/portail/home/utile-et-pratique/estimateur-de-cotisations-2019.html?ut=estimateurs">
@ -72,14 +175,103 @@ export default function Integration() {
<a href="mailto:contact@mon-entreprise.beta.gouv.fr?subject=Proposition de réutilisation">
<span className="question-mark">?</span>
<h2>
Une idée&nbsp;?
<br />
Contactez-nous&nbsp;!
<Trans>
Une idée&nbsp;?
<br />
Contactez-nous&nbsp;!
</Trans>
</h2>
</a>
</article>
</div>
</div>
</section>
</>
)
}
type IntegrationCodeProps = {
module?: string
color?: string
}
function IntegrationCode({
module = 'simulateur-embauche',
color
}: IntegrationCodeProps) {
const codeRef = useRef<HTMLDivElement>(null)
const [secondClick, setSecondClick] = useState(false)
const selectAllCode = () => {
if (codeRef.current && !secondClick) {
const range = document.createRange()
range.selectNode(codeRef.current)
window.getSelection()?.removeAllRanges()
window.getSelection()?.addRange(range)
setSecondClick(true)
}
if (secondClick) {
setSecondClick(false)
}
}
return (
<code
ref={codeRef}
onClick={selectAllCode}
css={`
display: block;
font-size: 80%;
padding: 1em;
background: #f8f8f8;
margin: auto;
margin-bottom: 1em;
overflow: auto;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05),
-1px 1px 1px rgba(0, 0, 0, 0.02);
em {
font-weight: 300;
color: black;
}
:before {
content: '';
position: absolute;
top: 0;
right: 0;
border-width: 0 16px 16px 0;
border-style: solid;
border-color: #e8e8e8 white;
}
#scriptColor {
color: #2975d1;
}
`}
>
<span>{'<'}</span>
<em>
script
<br />
id
</em>
="script-simulateur-embauche"
<br />
<em>data-module</em>="
<span>{module}</span>"
{color ? (
<>
<br />
<em>data-couleur</em>="
<span id="scriptColor">{color}</span>"
</>
) : (
''
)}
<br />
<em>src</em>
="https://mon-entreprise.fr/simulateur-iframe-integration.js">
<span>{'<'}</span>
<span>/</span>
<em>script</em>
<span>></span>
</code>
)
}