Skip to content

Commit

Permalink
Merge pull request #1649 from BaseAdresseNationale/feat/add-new-page-…
Browse files Browse the repository at this point in the history
…stats-and-budget

Feat/add new page stats and budget
  • Loading branch information
nkokla authored Nov 14, 2023
2 parents eec84ff + 0cd7767 commit a5261f6
Show file tree
Hide file tree
Showing 21 changed files with 1,793 additions and 5 deletions.
25 changes: 25 additions & 0 deletions components/charts/axis-tick-by-date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import PropTypes from 'prop-types'

function AxisTickByDate({x, y, payload}) {
const [year, month, day] = payload.value.split('-')

return (
<g transform={`translate(${x},${y})`}>
<text x={0} y={0} dy={16} textAnchor='end' fill='#666' transform='rotate(-35)'>
{
day ?
`${day}/${month}/${year}` :
(new Date(year, month - 1, day || 1)).toLocaleDateString('fr-FR', {year: 'numeric', month: 'short'})
}
</text>
</g>
)
}

AxisTickByDate.propTypes = {
x: PropTypes.number,
y: PropTypes.number,
payload: PropTypes.object,
}

export default AxisTickByDate
140 changes: 140 additions & 0 deletions components/charts/chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import PropTypes from 'prop-types'

import {
AreaChart,
Area,
BarChart,
Bar,
LineChart,
Line,
ScatterChart,
Scatter,
CartesianGrid,
XAxis,
YAxis,
LabelList,
ResponsiveContainer,
Tooltip,
Legend
} from 'recharts'

import {defaultTheme} from './color-theme'
import AxisTickByDate from './axis-tick-by-date'

const renderColorfulLegendText = (value, entry) => {
const {color} = entry
return <span style={{color}}>{value}</span>
}

const yAxisTickFormatter = value => {
return Number.isNaN(value) ?
value :
`${value.toLocaleString(
undefined, {minimumFractionDigits: 0}
).replace(/000$/g, 'K')}`
}

const defaultArea = {
type: 'monotone',
dataKey: 'download BAL',
stackId: '1',
strokeWidth: 0.5,
}

const typeComponents = {
area: {
chart: AreaChart,
axis: Area,
},
bar: {
chart: BarChart,
axis: Bar,
},
line: {
chart: LineChart,
axis: Line,
},
scatter: {
chart: ScatterChart,
axis: Scatter,
},
}

function CartesianChart({type, data, axisDef, yAxisMaxKeyName, totalKeyName}) {
const dataList = Object.entries(axisDef).map(([dataKey, areaItem], index) => ({
...defaultArea,
dataKey,
stroke: defaultTheme[index]?.[0],
fill: defaultTheme[index]?.[1],
...(areaItem || {}),
}))

const {chart: Chart, axis: Axis} = typeComponents[type]

return (
<ResponsiveContainer width='100%' height='100%'>
<Chart
data={data}
outerRadius={90}
margin={{
top: 0,
right: 0,
left: 0,
bottom: 50
}}
>
<CartesianGrid strokeDasharray='3 3' />
<XAxis
dataKey='period'
angle={-45}
padding={{
left: 0,
right: 0
}}
tick={<AxisTickByDate />}
/>
<YAxis
dataKey={yAxisMaxKeyName}
tickFormatter={yAxisTickFormatter}
/>

<Tooltip />

<>
{dataList.map((areaItem, index, arr) => (
<Axis
key={areaItem.dataKey}
{...(areaItem || {})}
>
{totalKeyName && index === arr.length - 1 && <LabelList dataKey={totalKeyName} position='top' />}
</Axis>
))}
</>

<Legend
layout='horizontal'
verticalAlign='bottom'
align='left'
wrapperStyle={{
position: 'absolute',
paddingBottom: '20px',
paddingTop: '50px',
}}
iconType='circle'
formatter={renderColorfulLegendText}
/>
</Chart>

</ResponsiveContainer>
)
}

CartesianChart.propTypes = {
type: PropTypes.oneOf(['area', 'bar', 'line', 'scatter']),
data: PropTypes.array,
axisDef: PropTypes.object,
yAxisMaxKeyName: PropTypes.string,
totalKeyName: PropTypes.string,
}

export default CartesianChart
51 changes: 51 additions & 0 deletions components/charts/color-theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export const defaultTheme = [
['#6C6C6C', '#b3b3b3'],
['#5C5C5C', '#a6a6a6'],
['#4F4F4F', '#999999'],
['#444444', '#8c8c8c'],
]

export const colorthemes = {
ecume: [
['#869ECE', '#ced6ea'],
['#465F9D', '#8b9eda'],
['#273962', '#5576c2'],
['#222940', '#536190'],
],
wisteria: [
['#A695E7', '#e2d8f5'],
['#8C7CE5', '#c6bdf0'],
['#6F5FD5', '#a99ee6'],
['#4D3DBD', '#7d6fc6'],
],
azure: [
['#3C91E6', '#cbe0f7'],
['#1D7ECC', '#8dc0f3'],
['#0E6AA2', '#6aa8e9'],
['#0A4C6A', '#4e8ad5'],
],
forest: [
['#6DBA8E', '#c8e5d6'],
['#3FAE6B', '#9ed8b9'],
['#2D8E53', '#6cc39b'],
['#1E673C', '#4da77f'],
],
emerald: [
['#4DBA87', '#c8e5d6'],
['#2FAE64', '#9ed8b9'],
['#1D8E4F', '#6cc39b'],
['#0E673A', '#4da77f'],
],
glicyne: [
['#F5C242', '#f9e7c7'],
['#F3B824', '#f9d99f'],
['#F1A806', '#f9cb77'],
['#F09A00', '#f9c05e'],
],
rubi: [
['#E65A5A', '#f7c6c6'],
['#CC3F3F', '#f3a8a8'],
['#A22D2D', '#e98b8b'],
['#6A1E1E', '#d56f6f'],
],
}
2 changes: 2 additions & 0 deletions components/charts/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export {default as CartesianChart} from './chart'
export {defaultTheme, colorthemes} from './color-theme'
34 changes: 34 additions & 0 deletions components/color-line.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import PropTypes from 'prop-types'

function ColorLine({color, className}) {
return (
<>
<span className={`color-line-wrapper ${className || ''}`}>
<i className='color-line' />
</span>
<style jsx>{`
.color-line-wrapper {
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
font-size: 1.5rem;
margin: 0 0.2rem;
${color ? `color: ${color};` : ''}
}
.color-line {
width: 1.5rem;
height: 0.5rem;
background-color: ${color};
border-radius: 0.5rem;
}
`}</style>
</>
)
}

ColorLine.propTypes = {
color: PropTypes.string,
className: PropTypes.string,
}

export default ColorLine
2 changes: 2 additions & 0 deletions components/key-numbers-block/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export {default} from './key-numbers-block'
export {default as KeyNumberItem} from './key-number-item'
60 changes: 60 additions & 0 deletions components/key-numbers-block/key-number-item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import PropTypes from 'prop-types'

function KeyNumberItem({value = '', unit, label = '', description, className}) {
return (
<>
<div className={`key-number-item ${className || ''}`}>
<span className='key-number' data-unit={unit}>{value}</span>
<span className='number-label'>{label}</span>
{description && <span className='number-description'>{description}</span>}
</div>

<style jsx>{`
.key-number-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
/* position: relative; */
}
.key-number {
font-size: 2.5em;
font-weight: bold;
line-height: 1em;
}
.key-number::after {
content: attr(data-unit);
font-size: 0.6em;
padding-left: .1em;
}
.number-label {
font-size: 0.8em;
text-transform: uppercase;
line-height: 1.5;
padding-top: .5em;
margin-right: 4em;
}
.number-description {
font-size: 0.8em;
line-height: 1.5;
padding-top: .25em;
margin-right: 4em;
opacity: .8;
}
`}</style>
</>
)
}

export const KeyNumberItemPropTypes = {
value: PropTypes.string.isRequired,
unit: PropTypes.string,
label: PropTypes.string.isRequired,
description: PropTypes.string,
className: PropTypes.string,
}

KeyNumberItem.propTypes = KeyNumberItemPropTypes

export default KeyNumberItem
Loading

0 comments on commit a5261f6

Please sign in to comment.