diff --git a/components/defaultFunnelChart.js b/components/defaultFunnelChart.js
new file mode 100644
index 0000000..916dc8a
--- /dev/null
+++ b/components/defaultFunnelChart.js
@@ -0,0 +1,40 @@
+import React from "react"
+import { ResponsiveBar } from "@nivo/bar"
+
+const xAxisKey = "label"
+
+function findKeysFromData(data) {
+ return data.reduce((keys, row) => {
+ Object.keys(row).forEach((key) => {
+ if (key !== xAxisKey && !keys.includes(key)) {
+ keys.push(key)
+ }
+ })
+ return keys
+ }, [])
+}
+
+export const DefaultFunnelChart = ({ data, dataTestid }) => {
+ const keys = findKeysFromData(data)
+
+ return (
+
+
+
+ )
+}
diff --git a/components/header.js b/components/header.js
index c8621b1..79af7c0 100644
--- a/components/header.js
+++ b/components/header.js
@@ -6,6 +6,7 @@ const menu = [
{ url: "/survey", label: "Résultats de sondage" },
{ url: "/behaviours", label: "Comportements utilisateur" },
{ url: "/pages", label: "Statistiques de visite" },
+ { url: "/funnel", label: "Tunnel de conversion" },
]
function closeMenu() {
diff --git a/cypress/e2e/funnel.cy.js b/cypress/e2e/funnel.cy.js
new file mode 100644
index 0000000..ab55c6b
--- /dev/null
+++ b/cypress/e2e/funnel.cy.js
@@ -0,0 +1,19 @@
+import { interceptAidesJeunesStatistics } from "../support/utils.js"
+
+describe("Funnel Page", () => {
+ beforeEach(() => {
+ const interceptIdentifier = interceptAidesJeunesStatistics()
+ cy.visitFromHome("/funnel")
+ cy.wait(interceptIdentifier)
+ })
+
+ it("displays the menu and title", () => {
+ cy.checkMenu()
+ cy.checkTitle()
+ })
+
+ it("displays funnel results chart", () => {
+ cy.get(".responsive-chart").should("be.visible")
+ cy.checkGraph("funnel-visits", "Visites", "125821")
+ })
+})
diff --git a/cypress/e2e/survey.cy.js b/cypress/e2e/survey.cy.js
index ba989b0..a01a2a7 100644
--- a/cypress/e2e/survey.cy.js
+++ b/cypress/e2e/survey.cy.js
@@ -1,8 +1,8 @@
-import { interceptSurveyStatistics } from "../support/utils.js"
+import { interceptAidesJeunesStatistics } from "../support/utils.js"
describe("Survey Page", () => {
beforeEach(() => {
- const interceptIdentifier = interceptSurveyStatistics()
+ const interceptIdentifier = interceptAidesJeunesStatistics()
cy.visitFromHome("/survey")
cy.wait(interceptIdentifier)
})
diff --git a/cypress/fixtures/surveyStatistics.json b/cypress/fixtures/aidesJeunesStatistics.json
similarity index 85%
rename from cypress/fixtures/surveyStatistics.json
rename to cypress/fixtures/aidesJeunesStatistics.json
index bf9d782..52a900d 100644
--- a/cypress/fixtures/surveyStatistics.json
+++ b/cypress/fixtures/aidesJeunesStatistics.json
@@ -105,5 +105,18 @@
"nothing": 9
}
}
+ },
+ "funnel": {
+ "2023-06": {
+ "visits": 125821,
+ "nbUniqVisitors": 97525,
+ "simulationCount": 25,
+ "followupWithOptinCount": 16,
+ "followupWithoutOptinCount": 0,
+ "followupWithSurveyCount": 2,
+ "followupWithSurveyRepliedCount": 2,
+ "showAccompanimentCount": 66,
+ "clickAccompanimentCount": 37
+ }
}
}
diff --git a/cypress/support/utils.js b/cypress/support/utils.js
index 9199a67..009d026 100644
--- a/cypress/support/utils.js
+++ b/cypress/support/utils.js
@@ -16,10 +16,10 @@ export const interceptUsageStatistics = createFetchInterceptor(
"usageStatistics.json",
)
-export const interceptSurveyStatistics = createFetchInterceptor(
- "interceptSurveyStatistics",
- configuration.env.surveyStatisticsURL,
- "surveyStatistics.json",
+export const interceptAidesJeunesStatistics = createFetchInterceptor(
+ "interceptAidesJeunesStatistics",
+ configuration.env.aidesJeunesStatisticsURL,
+ "aidesJeunesStatistics.json",
)
export const interceptRecorderStatistics = createFetchInterceptor(
diff --git a/next.config.js b/next.config.js
index 5fe97e7..bea4735 100644
--- a/next.config.js
+++ b/next.config.js
@@ -13,7 +13,7 @@ const configuration = {
"https://stats.data.gouv.fr/index.php?date=2021-01-01,yesterday&expanded=1&filter_limit=100&force_api_session=1&format=JSON&format_metrics=1&idSite=165&method=API.get&module=API&period=month&token_auth=anonymous",
observatoryURL:
"https://observatoire.numerique.gouv.fr/Demarches/3135?view-mode=statistics&date-debut=2020-07-01&date-fin=",
- surveyStatisticsURL:
+ aidesJeunesStatisticsURL:
"https://mes-aides.1jeune1solution.beta.gouv.fr/documents/stats.json",
benefitsURL: `${aidesJeunesUrl}api/benefits`,
pagesStatsURL:
diff --git a/pages/funnel.js b/pages/funnel.js
new file mode 100644
index 0000000..15ff12d
--- /dev/null
+++ b/pages/funnel.js
@@ -0,0 +1,73 @@
+import { Component } from "react"
+
+import { fetchFunnelData } from "../services/funnelService.js"
+import { DefaultFunnelChart } from "../components/defaultFunnelChart.js"
+
+class Funnel extends Component {
+ state = {
+ chartsData: null,
+ loading: true,
+ selectedMonth: null,
+ }
+
+ async componentDidMount() {
+ await this.fetchData()
+ }
+
+ async fetchData() {
+ const { availableMonths, chartsData } = await fetchFunnelData()
+
+ const selectedMonth = availableMonths[0]
+
+ this.setState({
+ chartsData,
+ selectedMonth,
+ loading: false,
+ })
+ }
+
+ render() {
+ const { loading, selectedMonth, chartsData } = this.state
+
+ if (loading) {
+ return Chargement...
+ }
+
+ const { visitToRecap, surveyData, accompanimentData } =
+ chartsData[selectedMonth]
+
+ return (
+ <>
+ Metriques de parcours {selectedMonth}
+
+ <>
+
+
+
Visites - Emails Récapitulatifs
+ {visitToRecap && (
+
+ )}
+
+
+
+
Sondages Envoyés - Répondus
+ {surveyData && }
+
+
+
+
Accompagnement
+ {accompanimentData && (
+
+ )}
+
+
+ >
+ >
+ )
+ }
+}
+
+export default Funnel
diff --git a/public/static/style.css b/public/static/style.css
index 1aa1932..8cba4e7 100644
--- a/public/static/style.css
+++ b/public/static/style.css
@@ -396,3 +396,23 @@ table .sortable-asc:after {
display: none;
}
}
+
+.funnel-charts {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+}
+
+.funnel-charts .funnel-chart {
+ flex: 1;
+ max-width: calc(100% / 3);
+}
+
+@media screen and (max-width: 1024px) {
+ .funnel-charts {
+ flex-direction: column;
+ }
+ .funnel-charts .funnel-chart {
+ max-width: 100%;
+ }
+}
diff --git a/services/fetch.js b/services/fetch.js
index 4cff8b8..6bd6cea 100644
--- a/services/fetch.js
+++ b/services/fetch.js
@@ -6,7 +6,7 @@ export default class Fetch {
static async getSurveyStatistics() {
const surveyStatiastics = await this.getJSON(
- process.env.surveyStatisticsURL,
+ process.env.aidesJeunesStatisticsURL,
)
const { benefitInstitutionMapping, institutions } =
await this.getBenefitsAndInstitutions()
diff --git a/services/funnelService.js b/services/funnelService.js
new file mode 100644
index 0000000..40fe38c
--- /dev/null
+++ b/services/funnelService.js
@@ -0,0 +1,49 @@
+import Fetch from "./fetch"
+
+import configuration from "../next.config.js"
+
+function formatFunnelData(data) {
+ return {
+ visitToRecap: [
+ { label: "Visites", total: data.visits },
+ { label: "Visites Uniques", total: data.nbUniqVisitors },
+ { label: "Simulations terminées", total: data.simulationCount },
+ {
+ label: "Emails de recap envoyés",
+ "Avec recontact": data.followupWithOptinCount,
+ "Sans recontact": data.followupWithoutOptinCount,
+ },
+ ],
+ surveyData: [
+ {
+ label: "Email de sondage envoyés",
+ total: data.followupWithSurveyCount,
+ },
+ {
+ label: "Sondages répondus",
+ total: data.followupWithSurveyRepliedCount,
+ },
+ ],
+ accompanimentData: [
+ { label: "RDV affiché", total: data.showAccompanimentCount },
+ { label: "RDV cliqué", total: data.clickAccompanimentCount },
+ ],
+ }
+}
+
+export const fetchFunnelData = async () => {
+ const statsResponse = await Fetch.getJSON(
+ configuration.env.aidesJeunesStatisticsURL,
+ )
+
+ const funnelData = statsResponse.funnel
+ const chartsData = {}
+ for (const [month, data] of Object.entries(funnelData)) {
+ chartsData[month] = formatFunnelData(data)
+ }
+
+ return {
+ availableMonths: Object.keys(funnelData),
+ chartsData,
+ }
+}