💶 Création d'une page /budget

pull/977/head
Maxime Quandalle 2020-02-27 09:32:40 +01:00
parent f854dc6b81
commit 5003014398
12 changed files with 362 additions and 13 deletions

View File

@ -96,7 +96,7 @@ export default function Provider({
onStoreCreated?.(store)
// Remove loader
const css = document.createElement('style')
var css = document.createElement('style')
css.type = 'text/css'
css.innerHTML = `
#js {

View File

@ -0,0 +1,86 @@
import React, { useContext } from 'react'
import emoji from 'react-easy-emoji'
import { useTranslation } from 'react-i18next'
import { Link, useLocation } from 'react-router-dom'
import { icons } from './ui/SocialIcon'
import { SitePathsContext } from './utils/withSitePaths'
export default function MoreInfosOnUs() {
const { pathname } = useLocation()
const sitePaths = useContext(SitePathsContext)
const { language } = useTranslation().i18n
if (language !== 'fr') {
return null
}
return (
<section
className="ui__ full-width light-bg center-flex"
css={`
margin-top: 3rem;
`}
>
<h3 style={{ textAlign: 'center', width: '100%' }}>
Plus d'infos sur mon-entreprise.fr
</h3>
{!pathname.startsWith(sitePaths.nouveautés) && (
<Link className="ui__ interactive card box" to={sitePaths.nouveautés}>
<div className="ui__ big box-icon">{emoji('✨')}</div>
<h3>Les nouveautés</h3>
<p className="ui__ notice" css="flex: 1">
Qu'avons-nous mis en production ces derniers mois ?
</p>
<div className="ui__ small simple button">Découvrir</div>
</Link>
)}
<a
href="https://mon-entreprise.fr/stats"
className="ui__ interactive card box"
>
<div className="ui__ big box-icon">{emoji('📊')}</div>
<h3>Les statistiques</h3>
<p className="ui__ notice" css="flex: 1">
Quel est notre impact ?
</p>
<div className="ui__ small simple button">Découvrir</div>
</a>
{!pathname.startsWith(sitePaths.budget) && (
<Link className="ui__ interactive card box" to={sitePaths.budget}>
<div className="ui__ big box-icon">{emoji('💶')}</div>
<h3>Le budget</h3>
<p className="ui__ notice" css="flex: 1">
Quelles sont nos ressources et comment sont-elles employées ?
</p>
<div className="ui__ small simple button">Découvrir</div>
</Link>
)}
<a
href="https://github.com/betagouv/mon-entreprise"
target="_blank"
className="ui__ interactive card box"
>
<div className="ui__ big box-icon">
{' '}
<svg
viewBox="15 15 34 34"
style={{
width: '3rem',
height: '3rem',
margin: 0
}}
>
<g>
<path d={icons['github'].icon} />
</g>
</svg>
</div>
<h3>Le code source</h3>
<p className="ui__ notice" css="flex: 1">
Nos travaux sont ouverts et libres de droit, ça se passe sur GitHub
</p>
<div className="ui__ small simple button">Découvrir</div>
</a>
</section>
)
}

View File

@ -137,3 +137,18 @@ span.ui__.enumeration:not(:last-of-type)::after {
.no-scroll {
overflow: hidden;
}
.markdown table {
text-align: left;
margin-bottom: 1rem;
width: 100%;
}
.markdown table tbody tr:nth-child(2n + 1) {
background-color: var(--lighterColor);
}
.markdown table td,
.markdown table th {
padding: 5px 10px;
}

View File

@ -61,7 +61,7 @@ Gérant minoritaire: Managing director
Habituellement: Usually
Imprimer: Print
Impôts: Taxes
"Indemnité chômage partiel prise en charge par l'état :": 'State-paid short-time working allowance :'
'Indemnité chômage partiel prise en charge par l''état :': 'State-paid short-time working allowance :'
Indépendant: Independent
International: International
Intégrer l'interface de simulation: Integrate the simulation interface
@ -78,7 +78,7 @@ Mon entreprise: My company
Mon revenu: My income
Montant: Amount
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'
@ -143,7 +143,7 @@ Taux: Rate
Taux calculé: Calculated rate
Taux moyen: Average rate
Total des retenues: Total withheld
"Total payé par l'entreprise :": 'Total paid by the company :'
'Total payé par l''entreprise :': 'Total paid by the company :'
Tout effacer: Delete all
Tranche de l'assiette: Scale bracket
Un seul associé: Only one partner
@ -891,6 +891,7 @@ pages:
module: What module?
par: per
path:
budget: /budget
coronavirus: /coronavirus
créer:
après: /after-registration

View File

@ -23,6 +23,7 @@ import './App.css'
import Footer from './layout/Footer/Footer'
import Header from './layout/Header'
import trackSimulatorActions from './middlewares/trackSimulatorActions'
import Budget from './pages/Budget/Budget'
import Coronavirus from './pages/Coronavirus'
import Créer from './pages/Créer'
import IntegrationTest from './pages/Dev/IntegrationTest'
@ -127,6 +128,7 @@ const App = () => {
<Route path={sitePaths.integration.index} component={Integration} />
<Route path={sitePaths.nouveautés} component={Nouveautés} />
<Route path={sitePaths.coronavirus} component={Coronavirus} />
<Route path={sitePaths.budget} component={Budget} />
<Route exact path="/dev/sitemap" component={Sitemap} />
<Route
exact

View File

@ -65,11 +65,13 @@ const Footer = () => {
<>
{' • '}
<Link to={sitePaths.nouveautés}>Nouveautés</Link>
{' • '}
<a href="https://mon-entreprise.fr/stats">Stats</a>
{' • '}
<Link to={sitePaths.budget}>Budget</Link>
</>
)}
{' • '}
<a href="https://mon-entreprise.fr/stats">Stats</a>
{' • '}
<Link to={sitePaths.integration.index}>
<Trans>Intégrer nos simulateurs</Trans>
</Link>

View File

@ -0,0 +1,126 @@
import MoreInfosOnUs from 'Components/MoreInfosOnUs'
import { Markdown } from 'Components/utils/markdown'
import { ScrollToTop } from 'Components/utils/Scroll'
import { formatCurrency } from 'Engine/format'
import { sum, uniq } from 'ramda'
import React, { useState } from 'react'
import emoji from 'react-easy-emoji'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import prose from './budget.md'
import budget from './budget.yaml'
// Splitting the markdown file to insert React components in-between is a bit
// arcane, we may consider MDX in the future https://github.com/mdx-js/mdx.
const [
intro,
ressources2019,
ressources2020,
ressourcesDescription
] = prose.split(/\r?\n-{3,}\r?\n/)
const ressources = {
2019: ressources2019,
2020: ressources2020
}
export default function Budget() {
const [selectedYear, setSelectedYear] = useState('2020')
const years = ['2019', '2020']
const quarters = ['T1', 'T2', 'T3', 'T4']
const categories = uniq(
quarters
.map(q => Object.keys(budget[2020][q] ?? {}))
.reduce((acc, curr) => [...acc, ...curr], [])
)
const { language } = useTranslation().i18n
return (
<>
<ScrollToTop />
<h1>Budget {emoji('💶')}</h1>
<Markdown source={intro} />
<label>
{emoji('📅')} Année{' '}
<select
value={selectedYear}
onChange={event => setSelectedYear(event.target.value)}
>
{years.map(year => (
<option key={year}>{year}</option>
))}
</select>
</label>
<Markdown source={ressources[selectedYear]} />
{selectedYear !== '2019' && (
<>
<h2>Emploi des ressources</h2>
<RessourcesAllocationTable>
<thead>
<tr>
<td>2020</td>
{quarters.map(q => (
<td key={q}>{q}</td>
))}
</tr>
</thead>
<tbody>
{categories.map(label => (
<tr key={label}>
<td>{label}</td>
{quarters.map(q => {
const value = budget[2020]?.[q]?.[label]
return (
<td key={q}>
{value ? formatCurrency(value, language) : '-'}
</td>
)
})}
</tr>
))}
</tbody>
<tfoot>
<tr>
<td>Total</td>
<td>
{formatCurrency(
sum(Object.values(budget[2020]['T1'])),
language
)}
</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
</tfoot>
</RessourcesAllocationTable>
<Markdown source={ressourcesDescription} />
</>
)}
<MoreInfosOnUs />
</>
)
}
const RessourcesAllocationTable = styled.table`
width: 100%;
text-align: left;
td {
padding: 6px;
}
td:not(:first-child) {
width: 100px;
text-align: right;
}
tbody tr:nth-child(2n + 1),
tfoot tr {
background: var(--lighterColor);
}
thead,
tfoot {
font-weight: bold;
}
`

View File

@ -0,0 +1,102 @@
**Mon-Entreprise.fr** est une start-up d'État financée par de l'argent public,
c'est pourquoi nous sommes transparents sur les ressources allouées et la
manière dont elles sont employées.
## Principes
Notre organisation souscrit aux principes établis dans le [manifeste des startup
d'État](https://beta.gouv.fr/incubateurs) que nous rappelons ici :
> ### Considère les besoins des usagers avant ceux de ladministration
> Il cible ses investissements sur des sujets qui en valent la peine,
> cest-à-dire où existe un réel irritant supporté par des milliers ou des
> millions de personnes. Il ne soutient pas dinvestissement qui nait obtenu de
> plébiscite usagers au-delà de 6 mois, il incite donc à la confrontation la
> plus rapide au terrain.
> ### Pilote ses équipes par la finalité plus que par les moyens
> Son mode de gestion repose sur la confiance. Une autonomie maximale est
> concédée aux équipes, pilotées uniquement par leurs objectifs dimpact et non
> par leurs moyens. Il veille en particulier à ne leur imposer aucune des
> contraintes inhérentes à la structure (comitologie, communication, achat,
> standard technologique…).
> ### Saméliore en continu plus quil nobéit à un plan
> Il simpose à lui-même la méthode frugale et incrémentale quil promeut. En
> particulier, son objectif initial est de lancer le plus rapidement possible
> une première startup puis daméliorer en continu ses méthodes, ses produits et
> les compétences de ses membres. Dans son portefeuille, rien nest en
> maintenance, tout est soit en vie, soit stoppé.
---
## Budget
En 2019, le projet dispose d'un budget de **250 000 € HT** pris en charge :
- Par la DINSIC (ancien nom de la [DINUM](https://www.numerique.gouv.fr/dinum/)) à hauteur de 150 000 € HT
- Par l'[ACOSS](https://www.acoss.fr) à hauteur de 100 000 € HT
[➡ Voir la convention](https://static.data.gouv.fr/resources/conventions-de-partenariat/20190423-181035/convention-du-15-avril-2019.pdf)
| | |
| -------------------------- | ---------------- |
| Financement DINSIC | 150 000 € HT |
| Financement Acoss initial | 100 000 € HT |
| Rallonge Acoss fin d'année | 12 500 € HT |
| **Total** | **262 500 € HT** |
En fin d'année une rallonge de a été attribuée pour la réalisation d'un nouveau
simulateur et une expérimentation sur la paie.
---
## Budget provisionnel
En 2020, le budget de **250 000 € HT** est reconduit. Il est pris en charge
à 100% par l[ACOSS](https://www.acoss.fr).
De plus, une enveloppe de **41 667 € HT** est allouée pour expérimenter la
transformation de notre moteur de simulations en moteur de paie complète.
| | |
| --------------------------- | ---------------- |
| Budget mon-entreprise | 250 000 € HT |
| Budget expérimentation paie | 41 667 € HT |
| **Total** | **291 667 € HT** |
En plus de cette contribution financière, l'ACOSS fournit des ressources métier
expertes provenant du réseau des URSSAF.
---
### Description des catégories
- **Développement 👨‍💻**
Les coûts de développement répresentent la grande majorité de notre budget.
Nous utilisons une petite équipe de développeurs freelances, qui sont
pluridisciplinaires aussi bien sur les aspects techniques, stratégiques et
métiers.
- **Logiciels et hébergement 💻**
Notre modèle open-source nous permet d'accéder gratuitement à la majorité des
outils que nous utilisions (hébergement de code, serveurs de tests, etc.). Le
site est hebergé sur [Netlify](https://www.netlify.com).
- **Déplacements 🚅**
Le réseau des URSSAF est présent dans toutes la France. Nous organisions
plusieurs fois par an des ateliers avec des experts en région sur des
thématiques particulières. Sont aussi inclus dans cette catégories la prise en
charge des frais de déplacements des développeurs qui ne sont pas situés en
région parisienne.
- **Documentation 📖**
Nous achetons de la documentation spécialisée à destination des professionnels
du droit afin de faciliter l'implémentation des dipositifs législatifs.

View File

@ -0,0 +1,11 @@
# Ces données sont optenues avec la commande suivante depuis le repo privé
# `compa-mon-entreprise` :
#
# $ hledger bal charges -p 2020 -Q -B --depth 2
2020:
T1:
Développement: 73500
Logiciels et hébergement: 170
Déplacements: 742
Documentation: 286

View File

@ -1,3 +1,4 @@
import MoreInfosOnUs from 'Components/MoreInfosOnUs'
import { Markdown } from 'Components/utils/markdown'
import { ScrollToTop } from 'Components/utils/Scroll'
import { SitePathsContext } from 'Components/utils/withSitePaths'
@ -50,20 +51,17 @@ export default function Nouveautés() {
les{' '}
{selectedRelease === 0
? 'dernières nouveautés'
: `nouveautés de ${data[selectedRelease].name.toLowerCase()}`}{' '}
:
: `nouveautés de ${data[selectedRelease].name.toLowerCase()}`}
&nbsp;:
</p>
<SmallScreenSelect
value={selectedRelease}
onChange={evt => {
history.push(getPath(Number(evt.target.value)))
}}
>
{data.map(({ name }, index) => (
<option
key={index}
value={index}
selected={index === selectedRelease}
>
<option key={index} value={index}>
{name}
</option>
))}
@ -100,6 +98,7 @@ export default function Nouveautés() {
</NavigationButtons>
</MainBlock>
</NewsSection>
<MoreInfosOnUs />
</>
)
}

View File

@ -126,6 +126,7 @@ export const constructLocalizedSitePath = (language: string) => {
)
},
nouveautés: t('path.nouveautés', '/nouveautés'),
budget: t('path.budget', '/budget'),
documentation: {
index: t('path.documentation.index', '/documentation'),
rule: (dottedName: DottedName) => '/' + encodeRuleName(dottedName)

View File

@ -0,0 +1,4 @@
declare module '*.md' {
const content: string
export default content
}