Ajoute la repartition des visites par page
parent
28e29fb7b9
commit
ce0e957a35
|
@ -4,6 +4,9 @@ on:
|
|||
types: [opened, synchronize]
|
||||
env:
|
||||
GITHUB_API_SECRET: ${{ secrets.GITHUB_TOKEN }}
|
||||
ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }}
|
||||
ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }}
|
||||
ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }}
|
||||
INSEE_SIRENE_API_SECRET: ${{ secrets.INSEE_SIRENE_API_TOKEN }}
|
||||
MATOMO_TOKEN: ${{ secrets.MATOMO_TOKEN }}
|
||||
EN_SITE: "${path}?s=m"
|
||||
|
|
|
@ -265,4 +265,6 @@ async function main() {
|
|||
})
|
||||
}
|
||||
|
||||
main()
|
||||
main().catch((e) => {
|
||||
throw new Error(e)
|
||||
})
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -173,6 +173,7 @@ Taux moyen: Average rate
|
|||
Total des retenues: Total withheld
|
||||
"Total payé par l'entreprise :": "Total paid by the company :"
|
||||
Tout effacer: Delete all
|
||||
Tout le site: The whole site
|
||||
Tranche de l'assiette: Scale bracket
|
||||
Un seul associé: Only one partner
|
||||
Une idée ?<1></1>Contactez-nous !: Any ideas?<1></1>Contact us!
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
import { ThemeColorsContext } from 'Components/utils/colors'
|
||||
import { formatValue } from 'publicodes'
|
||||
import { groupWith } from 'ramda'
|
||||
import React, { useContext } from 'react'
|
||||
import React, { Fragment, useContext } from 'react'
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
Bar,
|
||||
BarChart,
|
||||
Brush,
|
||||
CartesianGrid,
|
||||
ComposedChart,
|
||||
Legend,
|
||||
Line,
|
||||
LineChart,
|
||||
ReferenceArea,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
|
@ -39,6 +37,11 @@ const weekEndDays = groupWith(
|
|||
|
||||
type VisitsChartProps = {
|
||||
period: Period
|
||||
sync?: boolean
|
||||
stack?: boolean
|
||||
grid?: boolean
|
||||
colored?: boolean
|
||||
layout?: 'horizontal' | 'vertical'
|
||||
onDateChange?: ({
|
||||
startIndex,
|
||||
endIndex,
|
||||
|
@ -50,28 +53,65 @@ type VisitsChartProps = {
|
|||
endIndex?: number
|
||||
data: Data
|
||||
}
|
||||
const Palette = [
|
||||
'#1ea0f5',
|
||||
'#697ad5',
|
||||
'#b453b6',
|
||||
'#ff2d96',
|
||||
'#fd667f',
|
||||
'#fc9e67',
|
||||
'#fad750',
|
||||
'#bed976',
|
||||
'#82da9d',
|
||||
'#46dcc3',
|
||||
]
|
||||
|
||||
export default function VisitsChart({
|
||||
period,
|
||||
data,
|
||||
onDateChange,
|
||||
sync = true,
|
||||
layout = 'horizontal',
|
||||
grid = true,
|
||||
stack = false,
|
||||
colored = false,
|
||||
startIndex,
|
||||
endIndex,
|
||||
}: VisitsChartProps) {
|
||||
const { color, lightColor, lighterColor } = useContext(ThemeColorsContext)
|
||||
const { darkColor, lightColor, lighterColor } = useContext(ThemeColorsContext)
|
||||
if (!data.length) {
|
||||
return null
|
||||
}
|
||||
const isStacked = typeof data[0].nombre !== 'number'
|
||||
const isBarChart = data.length <= 3
|
||||
const dataKeys = isStacked ? Object.keys(data[0].nombre) : ['nombre']
|
||||
const flattenData = data.map((d) => (isStacked ? { ...d, ...d.nombre } : d))
|
||||
const flattenData = (data as any).map((d: any) =>
|
||||
isStacked ? { ...d, ...d.nombre } : d
|
||||
)
|
||||
|
||||
const Chart = isBarChart ? BarChart : isStacked ? AreaChart : LineChart
|
||||
const AxeA: any = layout === 'horizontal' ? XAxis : YAxis
|
||||
const AxeB: any = layout === 'horizontal' ? YAxis : XAxis
|
||||
|
||||
function getColor(i: number): string {
|
||||
if (!colored) {
|
||||
return [lighterColor, lightColor, darkColor][i % 3]
|
||||
}
|
||||
return Palette[i % Palette.length]
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
<Chart data={flattenData} syncId="1">
|
||||
<div
|
||||
css={`
|
||||
svg {
|
||||
overflow: visible;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<ResponsiveContainer width="100%" height={500}>
|
||||
<ComposedChart
|
||||
layout={layout}
|
||||
data={flattenData}
|
||||
syncId={sync ? '1' : undefined}
|
||||
>
|
||||
{data.length > 1 && onDateChange && (
|
||||
<Brush
|
||||
startIndex={startIndex}
|
||||
|
@ -81,65 +121,59 @@ export default function VisitsChart({
|
|||
tickFormatter={period === 'jours' ? formatDay : formatMonth}
|
||||
/>
|
||||
)}
|
||||
<CartesianGrid />
|
||||
|
||||
<XAxis
|
||||
{grid && <CartesianGrid />}
|
||||
<Legend />
|
||||
<AxeA
|
||||
dataKey="date"
|
||||
type="category"
|
||||
tickFormatter={period === 'jours' ? formatDay : formatMonth}
|
||||
/>
|
||||
|
||||
<YAxis
|
||||
domain={[0, 'auto']}
|
||||
<AxeB
|
||||
dataKey={dataKeys[0]}
|
||||
tickFormatter={formatValue}
|
||||
type="number"
|
||||
/>
|
||||
|
||||
<Tooltip
|
||||
content={<CustomTooltip period={period} dataKeys={dataKeys} />}
|
||||
/>
|
||||
{weekEndDays
|
||||
.filter((days) => days.length === 2)
|
||||
.map((days) => (
|
||||
<ReferenceArea
|
||||
key={days[0]}
|
||||
x1={days[0]}
|
||||
x2={days[1]}
|
||||
strokeOpacity={0.3}
|
||||
/>
|
||||
))}
|
||||
|
||||
{dataKeys.map((k, i) =>
|
||||
isBarChart ? (
|
||||
<Bar
|
||||
layout={layout}
|
||||
key={k}
|
||||
dataKey={k}
|
||||
maxBarSize={50}
|
||||
fill={
|
||||
i % 3 === 2 ? color : i % 3 === 1 ? lightColor : lighterColor
|
||||
}
|
||||
name={formatLegend(k)}
|
||||
barSize={20}
|
||||
stackId={stack ? 1 : undefined}
|
||||
fill={getColor(i)}
|
||||
/>
|
||||
) : isStacked ? (
|
||||
<Area
|
||||
key={k}
|
||||
dataKey={k}
|
||||
name={formatLegend(k)}
|
||||
stackId={stack ? 1 : undefined}
|
||||
type="monotone"
|
||||
stroke={color}
|
||||
fill={
|
||||
i % 3 === 2 ? color : i % 3 === 1 ? lightColor : lighterColor
|
||||
}
|
||||
stroke={getColor(i)}
|
||||
fill={getColor(i)}
|
||||
/>
|
||||
) : (
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey={k}
|
||||
stroke={color}
|
||||
name={formatLegend(k)}
|
||||
stroke={getColor(i + 1)}
|
||||
strokeWidth={3}
|
||||
animationDuration={500}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Chart>
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -150,6 +184,14 @@ function formatDay(date: string | Date) {
|
|||
})
|
||||
}
|
||||
|
||||
function formatDayLong(date: string | Date) {
|
||||
return new Date(date).toLocaleString('default', {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
})
|
||||
}
|
||||
|
||||
function formatMonth(date: string | Date) {
|
||||
return new Date(date).toLocaleString('default', {
|
||||
month: 'short',
|
||||
|
@ -170,30 +212,33 @@ const CustomTooltip = ({
|
|||
payload,
|
||||
dataKeys,
|
||||
}: CustomTooltipProps) => {
|
||||
if (!active) {
|
||||
if (!active || !payload) {
|
||||
return null
|
||||
}
|
||||
|
||||
const data = payload[0].payload
|
||||
return (
|
||||
<p className="ui__ card">
|
||||
<small>
|
||||
{period === 'jours' ? formatDay(data.date) : formatMonth(data.date)}
|
||||
{period === 'jours' ? formatDayLong(data.date) : formatMonth(data.date)}
|
||||
</small>
|
||||
<br />
|
||||
{dataKeys.map((key: string) => (
|
||||
<>
|
||||
<Fragment key={key}>
|
||||
<strong>{formatValue(data[key])}</strong>{' '}
|
||||
{dataKeys.length > 1 &&
|
||||
(key === 'accueil'
|
||||
? 'visites'
|
||||
: key === 'simulation_commencee'
|
||||
? 'simulation commencée'
|
||||
: key === 'simulation_terminee'
|
||||
? 'simulation terminée'
|
||||
: key.replaceAll('_', ' '))}
|
||||
{dataKeys.length > 1 && formatLegend(key)}
|
||||
<br />
|
||||
</>
|
||||
</Fragment>
|
||||
))}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
const formatLegend = (key: string) =>
|
||||
key === 'accueil'
|
||||
? 'visites'
|
||||
: key === 'simulation_commencee'
|
||||
? 'simulation commencée'
|
||||
: key === 'simulation_terminee'
|
||||
? 'simulation terminée'
|
||||
: key.replaceAll('_', ' ')
|
||||
|
|
|
@ -30,11 +30,11 @@ export default function SatisfactionChart({ data }: SatisfactionChartProps) {
|
|||
const flattenData = data.map((d) => ({ ...d, ...toPercentage(d.nombre) }))
|
||||
return (
|
||||
<>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
<BarChart data={flattenData}>
|
||||
<XAxis dataKey="date" tickFormatter={formatMonth} />
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
<Bar dataKey="mauvais" stackId="1" fill="#ffcccb" maxBarSize={50}>
|
||||
<Bar dataKey="mauvais" stackId="1" fill="#fd667f" maxBarSize={50}>
|
||||
<LabelList dataKey="mauvais" content={() => '🙁'} position="left" />
|
||||
</Bar>
|
||||
<Bar dataKey="moyen" stackId="1" maxBarSize={50} fill={lighterColor}>
|
||||
|
|
|
@ -37,15 +37,17 @@ const isPAM = (name: string | undefined) =>
|
|||
'auxiliaire_medical',
|
||||
'sage_femme',
|
||||
].includes(name)
|
||||
|
||||
type RawData = Array<{
|
||||
date: string
|
||||
page_chapter1?: string
|
||||
page_chapter2: string
|
||||
page_chapter3?: string
|
||||
page?: string
|
||||
click?: string
|
||||
nombre: number
|
||||
}>
|
||||
const filterByChapter2 = (
|
||||
data: Array<{
|
||||
date: string
|
||||
page_chapter2: string
|
||||
page_chapter3?: string
|
||||
page?: string
|
||||
click?: string
|
||||
}>,
|
||||
data: RawData,
|
||||
chapter2: Chapter2
|
||||
): Array<{ date: string; nombre: Record<string, number> }> => {
|
||||
return toPairs(
|
||||
|
@ -68,6 +70,25 @@ const filterByChapter2 = (
|
|||
}))
|
||||
}
|
||||
|
||||
function groupByDate(data: RawData) {
|
||||
return toPairs(
|
||||
groupBy(
|
||||
(p) => p.date,
|
||||
data.filter((d) => d.page === 'accueil')
|
||||
)
|
||||
).map(([date, values]) => ({
|
||||
date,
|
||||
nombre: Object.fromEntries(
|
||||
Object.entries(
|
||||
groupBy((x) => x.page_chapter1 + ' / ' + x.page_chapter2, values)
|
||||
)
|
||||
.map(([k, v]) => [k, v.map((v) => v.nombre).reduce(add, 0)] as const)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 7)
|
||||
),
|
||||
}))
|
||||
}
|
||||
|
||||
const computeTotals = (data: Data): number | Record<string, number> => {
|
||||
if (typeof data[0].nombre === 'number') {
|
||||
return (data as Data & { nombre: number }[])
|
||||
|
@ -89,6 +110,11 @@ export default function Stats() {
|
|||
return filterByChapter2(rawData.pages, chapter2)
|
||||
}, [period, chapter2])
|
||||
|
||||
const repartition = useMemo(() => {
|
||||
const rawData = stats.visitesMois
|
||||
return groupByDate(rawData.pages)
|
||||
}, [])
|
||||
|
||||
const satisfaction = useMemo(() => {
|
||||
return filterByChapter2(stats.satisfaction, chapter2)
|
||||
}, [chapter2])
|
||||
|
@ -182,18 +208,14 @@ export default function Stats() {
|
|||
<h2>Visites</h2>
|
||||
|
||||
<Chart
|
||||
key={period + visites.length}
|
||||
period={period}
|
||||
data={visites}
|
||||
onDateChange={handleDateChange}
|
||||
startIndex={startDateIndex}
|
||||
endIndex={endDateIndex}
|
||||
/>
|
||||
{period === 'mois' && !!satisfaction.length && (
|
||||
<>
|
||||
<h2>Satisfaction</h2>
|
||||
<SatisfactionChart key={chapter2} data={satisfaction} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<h2>
|
||||
Cumuls pour la période{' '}
|
||||
{period === 'jours'
|
||||
|
@ -240,6 +262,24 @@ export default function Stats() {
|
|||
</>
|
||||
)}
|
||||
</Indicators>
|
||||
{period === 'mois' && !!satisfaction.length && (
|
||||
<>
|
||||
<h2>Satisfaction</h2>
|
||||
<SatisfactionChart key={chapter2} data={satisfaction} />
|
||||
</>
|
||||
)}
|
||||
{chapter2 === '' && period === 'mois' && (
|
||||
<>
|
||||
<h2>Principales pages</h2>
|
||||
<Chart
|
||||
colored
|
||||
period={'mois'}
|
||||
data={repartition}
|
||||
grid={false}
|
||||
layout={'vertical'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
css={`
|
||||
|
@ -310,7 +350,7 @@ function getChapter2(s: SimulatorData[keyof SimulatorData]): Chapter2 | '' {
|
|||
}
|
||||
function SelectedSimulator(props: { chapter2: Chapter2 }) {
|
||||
const simulateur = Object.values(useSimulatorsData()).find(
|
||||
(s) => getChapter2(s) === props.chapter2 && !s.tracking.chapter3
|
||||
(s) => getChapter2(s) === props.chapter2 && !(s.tracking as any).chapter3
|
||||
)
|
||||
if (!simulateur) {
|
||||
return null
|
||||
|
|
|
@ -14,6 +14,9 @@ declare namespace NodeJS {
|
|||
GITHUB_API_SECRET: string
|
||||
DEEPL_API_SECRET: string
|
||||
INSEE_SIRENE_API_SECRET: string
|
||||
ATINTERNET_API_ACCESS_KEY: string
|
||||
ATINTERNET_API_SECRET_KEY: string
|
||||
ZAMMAD_API_SECRET_KEY: string
|
||||
}
|
||||
|
||||
interface Global {
|
||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -4708,11 +4708,16 @@ core-js@^0.8.3:
|
|||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-0.8.4.tgz#c22665f1e0d1b9c3c5e1b08dabd1f108695e4fcf"
|
||||
integrity sha1-wiZl8eDRucPF4bCNq9HxCGleT88=
|
||||
|
||||
core-js@^2.4.0, core-js@^2.6.10:
|
||||
core-js@^2.4.0:
|
||||
version "2.6.11"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
|
||||
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
|
||||
|
||||
core-js@^2.6.10:
|
||||
version "2.6.12"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||
|
||||
core-js@^3.2.1:
|
||||
version "3.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
|
||||
|
@ -11180,9 +11185,9 @@ react-signature-pad-wrapper@^1.2.11:
|
|||
throttle-debounce "^2.1.0"
|
||||
|
||||
react-smooth@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.5.tgz#94ae161d7951cdd893ccb7099d031d342cb762ad"
|
||||
integrity sha512-eW057HT0lFgCKh8ilr0y2JaH2YbNcuEdFpxyg7Gf/qDKk9hqGMyXryZJ8iMGJEuKH0+wxS0ccSsBBB3W8yCn8w==
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.6.tgz#18b964f123f7bca099e078324338cd8739346d0a"
|
||||
integrity sha512-B2vL4trGpNSMSOzFiAul9kFAsxTukL9Wyy9EXtkQy3GJr6sZqW9e1nShdVOJ3hRYamPZ94O17r3Q0bjSw3UYtg==
|
||||
dependencies:
|
||||
lodash "~4.17.4"
|
||||
prop-types "^15.6.0"
|
||||
|
|
Loading…
Reference in New Issue