From 451c98bc5c1f25bc5439c51d297774e899a17c6e Mon Sep 17 00:00:00 2001
From: Bastien DUMONT <bdumont@grandlyon.com>
Date: Tue, 8 Oct 2024 11:23:57 +0200
Subject: [PATCH] Reapply "feat(water): solidarity pricing"

This reverts commit fca2f831e5b8af04120ad360b26e4fd703682091.
---
 src/assets/icons/ico/euro-crossed.svg         |   7 +
 .../ico/{euro-icon.svg => euro-gold.svg}      |   0
 src/assets/icons/ico/euro.svg                 |   6 +
 .../ProfileComparatorRow.tsx                  |   2 +-
 .../Consumption/ConsumptionView.spec.tsx      |   3 +
 .../Consumption/ConsumptionView.tsx           |   4 +
 .../WaterPricing/WaterPricing.scss            | 192 ++++++++++++++++++
 .../WaterPricing/WaterPricing.spec.tsx        |  29 +++
 .../Consumption/WaterPricing/WaterPricing.tsx | 164 +++++++++++++++
 .../WaterPricing/WaterPricingModal.scss       |  13 ++
 .../WaterPricing/WaterPricingModal.tsx        |  49 +++++
 .../__snapshots__/WaterPricing.spec.tsx.snap  | 153 ++++++++++++++
 src/locales/fr.json                           |  15 +-
 13 files changed, 635 insertions(+), 2 deletions(-)
 create mode 100644 src/assets/icons/ico/euro-crossed.svg
 rename src/assets/icons/ico/{euro-icon.svg => euro-gold.svg} (100%)
 create mode 100644 src/assets/icons/ico/euro.svg
 create mode 100644 src/components/Consumption/WaterPricing/WaterPricing.scss
 create mode 100644 src/components/Consumption/WaterPricing/WaterPricing.spec.tsx
 create mode 100644 src/components/Consumption/WaterPricing/WaterPricing.tsx
 create mode 100644 src/components/Consumption/WaterPricing/WaterPricingModal.scss
 create mode 100644 src/components/Consumption/WaterPricing/WaterPricingModal.tsx
 create mode 100644 src/components/Consumption/WaterPricing/__snapshots__/WaterPricing.spec.tsx.snap

diff --git a/src/assets/icons/ico/euro-crossed.svg b/src/assets/icons/ico/euro-crossed.svg
new file mode 100644
index 000000000..1118c616a
--- /dev/null
+++ b/src/assets/icons/ico/euro-crossed.svg
@@ -0,0 +1,7 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect x="0.5" y="0.5" width="11" height="11" rx="5.5" stroke="#1B1C22" />
+  <path d="M9.5 8.5L4 3" stroke="#1B1C22" stroke-linecap="round" />
+  <path fill-rule="evenodd" clip-rule="evenodd"
+    d="M4.29445 4.70867C4.2492 4.80317 4.20873 4.90039 4.17333 5H3.33333C3.15 5 3 5.15 3 5.33333C3 5.51667 3.15 5.66667 3.33333 5.66667H4.02C4.00667 5.77667 4 5.88667 4 6C4 6.11333 4.00667 6.22333 4.02 6.33333H3.33333C3.15 6.33333 3 6.48333 3 6.66667C3 6.85 3.15 7 3.33333 7H4.17333C4.58667 8.16333 5.69333 9 7 9C7.46345 9 7.90137 8.89571 8.29333 8.70755L7.65291 8.06712C7.44768 8.13205 7.22878 8.16667 7 8.16667C6.16333 8.16667 5.44 7.69333 5.08 7H6.58579L5.91912 6.33333H4.86C4.84333 6.22333 4.83333 6.11333 4.83333 6C4.83333 5.88667 4.84333 5.77667 4.86 5.66667H5.25245L4.29445 4.70867ZM6.34784 3.93363C6.5538 3.86847 6.77294 3.83333 7 3.83333C7.41667 3.83333 7.80667 3.95333 8.14 4.15667C8.30667 4.26 8.52333 4.24333 8.66333 4.10333C8.85667 3.91 8.81333 3.59333 8.58 3.45C8.12 3.16667 7.57667 3 7 3C6.53657 3 6.0983 3.10524 5.70719 3.29298L6.34784 3.93363Z"
+    fill="#1B1C22" />
+</svg>
\ No newline at end of file
diff --git a/src/assets/icons/ico/euro-icon.svg b/src/assets/icons/ico/euro-gold.svg
similarity index 100%
rename from src/assets/icons/ico/euro-icon.svg
rename to src/assets/icons/ico/euro-gold.svg
diff --git a/src/assets/icons/ico/euro.svg b/src/assets/icons/ico/euro.svg
new file mode 100644
index 000000000..e09b58243
--- /dev/null
+++ b/src/assets/icons/ico/euro.svg
@@ -0,0 +1,6 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect x="0.5" y="0.5" width="11" height="11" rx="5.5" stroke="currentColor" />
+  <path
+    d="M7 8.16667C6.16333 8.16667 5.44 7.69333 5.08 7H6.66667C6.85 7 7 6.85 7 6.66667C7 6.48333 6.85 6.33333 6.66667 6.33333H4.86C4.84333 6.22333 4.83333 6.11333 4.83333 6C4.83333 5.88667 4.84333 5.77667 4.86 5.66667H6.66667C6.85 5.66667 7 5.51667 7 5.33333C7 5.15 6.85 5 6.66667 5H5.08C5.44 4.30667 6.16667 3.83333 7 3.83333C7.41667 3.83333 7.80667 3.95333 8.14 4.15667C8.30667 4.26 8.52333 4.24333 8.66333 4.10333C8.85667 3.91 8.81333 3.59333 8.58 3.45C8.12 3.16667 7.57667 3 7 3C5.69333 3 4.58667 3.83667 4.17333 5H3.33333C3.15 5 3 5.15 3 5.33333C3 5.51667 3.15 5.66667 3.33333 5.66667H4.02C4.00667 5.77667 4 5.88667 4 6C4 6.11333 4.00667 6.22333 4.02 6.33333H3.33333C3.15 6.33333 3 6.48333 3 6.66667C3 6.85 3.15 7 3.33333 7H4.17333C4.58667 8.16333 5.69333 9 7 9C7.58 9 8.12 8.83667 8.58 8.55C8.81 8.40667 8.85333 8.08667 8.66 7.89333C8.52 7.75333 8.30333 7.73667 8.13667 7.84333C7.80667 8.05 7.42 8.16667 7 8.16667Z"
+    fill="currentColor" />
+</svg>
\ No newline at end of file
diff --git a/src/components/Analysis/ProfileComparator/ProfileComparatorRow.tsx b/src/components/Analysis/ProfileComparator/ProfileComparatorRow.tsx
index a4bc59be3..7b47c580b 100644
--- a/src/components/Analysis/ProfileComparator/ProfileComparatorRow.tsx
+++ b/src/components/Analysis/ProfileComparator/ProfileComparatorRow.tsx
@@ -1,4 +1,4 @@
-import EuroIcon from 'assets/icons/ico/euro-icon.svg'
+import EuroIcon from 'assets/icons/ico/euro-gold.svg'
 import classNames from 'classnames'
 import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
 import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
diff --git a/src/components/Consumption/ConsumptionView.spec.tsx b/src/components/Consumption/ConsumptionView.spec.tsx
index df06f2717..ba2d74b7e 100644
--- a/src/components/Consumption/ConsumptionView.spec.tsx
+++ b/src/components/Consumption/ConsumptionView.spec.tsx
@@ -56,6 +56,9 @@ mockFluidStatus[FluidType.ELECTRICITY].status = FluidState.DONE
 const mockChartStateShowOffline = { ...mockChartState, showOfflineData: true }
 
 describe('ConsumptionView component', () => {
+  beforeEach(() => {
+    jest.clearAllMocks()
+  })
   it('should be rendered correctly', async () => {
     const store = createMockEcolyoStore({
       chart: {
diff --git a/src/components/Consumption/ConsumptionView.tsx b/src/components/Consumption/ConsumptionView.tsx
index 816cbc96c..3e3cbe47d 100644
--- a/src/components/Consumption/ConsumptionView.tsx
+++ b/src/components/Consumption/ConsumptionView.tsx
@@ -33,6 +33,7 @@ import {
 } from 'utils/utils'
 import ConsumptionDetails from './ConsumptionDetails/ConsumptionDetails'
 import FluidButtons from './FluidButtons/FluidButtons'
+import { WaterPricing } from './WaterPricing/WaterPricing'
 
 /**
  * http://ecolyo.cozy.tools:8080/#/consumption
@@ -210,6 +211,9 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
             <ConsumptionDetails fluidType={fluidType} />
           </>
         )}
+
+        {fluidType === FluidType.WATER && showOfflineData && <WaterPricing />}
+
         {!isMulti && <KonnectorViewerCard fluidType={fluidType} />}
 
         {isMulti && !showOfflineData && <KonnectorViewerList />}
diff --git a/src/components/Consumption/WaterPricing/WaterPricing.scss b/src/components/Consumption/WaterPricing/WaterPricing.scss
new file mode 100644
index 000000000..769c7e523
--- /dev/null
+++ b/src/components/Consumption/WaterPricing/WaterPricing.scss
@@ -0,0 +1,192 @@
+@import 'src/styles/base/color';
+@import 'src/styles/base/breakpoint';
+
+$price-free: #99cfff;
+$price-regular: #3a98ec;
+$price-double: #3793ff;
+$price-background: #383941;
+
+.pricing-root {
+  margin: 0 auto;
+  margin-bottom: 1rem;
+  max-width: 45.75rem;
+  width: 100%;
+  box-sizing: border-box;
+  @media #{$large-phone} {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .pricing-container {
+    background: $grey-linear-gradient-background;
+    border-radius: 4px;
+    display: flex;
+    flex-direction: column;
+    gap: 16px;
+    padding: 1rem;
+    p {
+      margin: 0;
+    }
+
+    .row {
+      display: flex;
+      flex-direction: row;
+      justify-content: space-between;
+      align-items: center;
+
+      .year {
+        color: $white;
+      }
+    }
+
+    .gauges {
+      display: grid;
+      grid-template-columns: 1fr auto 3fr auto 1fr;
+      gap: 4px;
+
+      .separator {
+        border-right: 1px solid $soft-grey;
+        height: 130%;
+      }
+    }
+
+    .limit-container {
+      margin-top: 4px;
+      display: grid;
+      grid-template-columns: 1fr auto 3fr auto 1fr;
+      span {
+        color: $soft-grey;
+      }
+      .limit12 {
+        grid-column: 2;
+      }
+      .limit180 {
+        grid-column: 4;
+      }
+    }
+
+    // Colors
+    .gauge-container.free {
+      .gauge-border {
+        border-color: $price-free;
+        background-image: linear-gradient(
+          45deg,
+          $price-free 11.11%,
+          $price-background 11.11%,
+          $price-background 50%,
+          $price-free 50%,
+          $price-free 61.11%,
+          $price-background 61.11%,
+          $price-background 100%
+        );
+      }
+      .gauge-content {
+        background-color: $price-free;
+      }
+    }
+    .gauge-container.regular {
+      .gauge-border {
+        border-radius: 0;
+        border-color: $price-regular;
+        background-image: linear-gradient(
+          45deg,
+          $price-regular 11.11%,
+          $price-background 11.11%,
+          $price-background 50%,
+          $price-regular 50%,
+          $price-regular 61.11%,
+          $price-background 61.11%,
+          $price-background 100%
+        );
+      }
+      .gauge-content {
+        background-color: $price-regular;
+      }
+    }
+
+    .gauge-container.double {
+      .gauge-border {
+        border-radius: 0 20px 20px 0;
+        border-color: $price-double;
+        background-image: linear-gradient(
+          45deg,
+          $price-double 11.11%,
+          $price-background 11.11%,
+          $price-background 50%,
+          $price-double 50%,
+          $price-double 61.11%,
+          $price-background 61.11%,
+          $price-background 100%
+        );
+      }
+      .gauge-content {
+        background-color: $price-double;
+      }
+    }
+
+    .gauge-container.no-color {
+      .gauge-border {
+        border-color: transparent;
+        background-color: $price-background;
+        background-image: none;
+      }
+      .gauge-content {
+        background-color: transparent;
+      }
+    }
+
+    .gauge-container {
+      .gauge-border {
+        height: 16px;
+        box-sizing: border-box;
+        border: 1px solid;
+        background-size: 9px 9px;
+        border-radius: 20px 0 0 20px;
+        overflow: hidden;
+        position: relative;
+        .gauge-content {
+          position: absolute;
+          transition: all 0.5s ease;
+          height: 17px;
+          width: 100%;
+          &.rounded {
+            border-radius: 0 20px 20px 0;
+          }
+        }
+      }
+    }
+
+    .iconFree,
+    .iconRegular,
+    .iconDouble {
+      z-index: 10;
+      position: absolute;
+      top: 1px;
+      left: 1px;
+      &.filled {
+        color: #1b1c22;
+      }
+    }
+    .iconDouble:nth-of-type(2) {
+      left: 15px;
+    }
+
+    .pricing {
+      margin-bottom: 4px;
+      &.free {
+        color: $price-free;
+      }
+      &.regular {
+        color: $price-regular;
+      }
+      &.double {
+        color: $price-double;
+      }
+    }
+
+    .consumption span {
+      color: $white;
+      font-weight: 700;
+    }
+  }
+}
diff --git a/src/components/Consumption/WaterPricing/WaterPricing.spec.tsx b/src/components/Consumption/WaterPricing/WaterPricing.spec.tsx
new file mode 100644
index 000000000..14ac6b98f
--- /dev/null
+++ b/src/components/Consumption/WaterPricing/WaterPricing.spec.tsx
@@ -0,0 +1,29 @@
+import { render, screen } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import React from 'react'
+import { Provider } from 'react-redux'
+import { createMockEcolyoStore } from 'tests/__mocks__/store'
+import { WaterPricing } from './WaterPricing'
+
+const store = createMockEcolyoStore()
+
+describe('WaterPricing component', () => {
+  it('should be rendered correctly', () => {
+    const { container } = render(
+      <Provider store={store}>
+        <WaterPricing />
+      </Provider>
+    )
+    expect(container).toMatchSnapshot()
+  })
+
+  it('should open modal when click on button', async () => {
+    render(
+      <Provider store={store}>
+        <WaterPricing />
+      </Provider>
+    )
+    userEvent.click(screen.getByRole('button'))
+    expect(await screen.findByRole('dialog')).toBeInTheDocument()
+  })
+})
diff --git a/src/components/Consumption/WaterPricing/WaterPricing.tsx b/src/components/Consumption/WaterPricing/WaterPricing.tsx
new file mode 100644
index 000000000..91107bbfc
--- /dev/null
+++ b/src/components/Consumption/WaterPricing/WaterPricing.tsx
@@ -0,0 +1,164 @@
+import { Button } from '@material-ui/core'
+import euroCrossedIcon from 'assets/icons/ico/euro-crossed.svg'
+import euroIcon from 'assets/icons/ico/euro.svg'
+import classNames from 'classnames'
+import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
+import { useClient } from 'cozy-client'
+import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
+import { FluidType, TimeStep } from 'enums'
+import { DateTime } from 'luxon'
+import React, { useEffect, useState } from 'react'
+import ConsumptionService from 'services/consumption.service'
+import { useAppSelector } from 'store/hooks'
+import './WaterPricing.scss'
+import { WaterPricingModal } from './WaterPricingModal'
+
+// In m³
+const MAX_FREE = 12
+const MAX_REGULAR = 180
+
+export const WaterPricing = () => {
+  const { t } = useI18n()
+  const client = useClient()
+  const { selectedDate } = useAppSelector(state => state.ecolyo.chart)
+  const [showModal, setShowModal] = useState(false)
+  const [consumption, setConsumption] = useState(0)
+
+  const pricing =
+    consumption > 180 ? 'double' : consumption > 12 ? 'regular' : 'free'
+  const isFreeExceeded = consumption >= MAX_FREE
+  const isRegularExceeded = consumption >= MAX_REGULAR
+
+  const freePercentage = Math.min((consumption / MAX_FREE) * 100, 100)
+  // threshold of 30% to display icon
+  const freeWithThreshold = Math.max(30, freePercentage)
+
+  const regularPercentage = Math.min((consumption / MAX_REGULAR) * 100, 100)
+  const regularWithThreshold = Math.max(10, regularPercentage)
+
+  const year = Number(selectedDate.toFormat('y'))
+
+  useEffect(() => {
+    async function fetchData() {
+      const cs = new ConsumptionService(client)
+      const startDate = DateTime.local(year, 1, 1)
+      const endDate = DateTime.local(year, 12, 31)
+
+      const dataLoad = await cs.getGraphData({
+        fluidTypes: [FluidType.WATER],
+        timeStep: TimeStep.YEAR,
+        timePeriod: { startDate, endDate },
+      })
+
+      if (!dataLoad?.actualData) return null
+
+      const rounded = Math.ceil(dataLoad.actualData[0].value / 100) / 10
+      setConsumption(rounded)
+    }
+    fetchData()
+  }, [client, year])
+
+  return (
+    <div className="pricing-root">
+      <div className="pricing-container">
+        <div className="row">
+          <span className="year text-16-bold">
+            {t('consumption.water_pricing.year', { year })}
+          </span>
+          <Button className="btnText" onClick={() => setShowModal(true)}>
+            {t('consumption.water_pricing.more')}
+          </Button>
+        </div>
+
+        <div>
+          <div className="gauges">
+            <div className="gauge-container free">
+              <div className="gauge-border">
+                <StyledIcon
+                  className="iconFree"
+                  icon={euroCrossedIcon}
+                  size={12}
+                />
+                <div
+                  className={classNames('gauge-content', {
+                    rounded: !isFreeExceeded,
+                  })}
+                  style={{ right: `${100 - freeWithThreshold}%` }}
+                />
+              </div>
+            </div>
+
+            <div className="separator" />
+
+            <div
+              className={classNames('gauge-container regular', {
+                'no-color': !isFreeExceeded,
+              })}
+            >
+              <div className="gauge-border">
+                <StyledIcon
+                  className={`iconRegular ${!isFreeExceeded ? '' : 'filled'}`}
+                  icon={euroIcon}
+                  size={12}
+                />
+                <div
+                  className={classNames('gauge-content', {
+                    rounded: !isRegularExceeded,
+                  })}
+                  style={{ right: `${100 - regularWithThreshold}%` }}
+                />
+              </div>
+            </div>
+
+            <div className="separator" />
+
+            <div
+              className={classNames('gauge-container double', {
+                'no-color': !isRegularExceeded,
+              })}
+            >
+              <div className="gauge-border">
+                <StyledIcon
+                  className={`iconDouble ${!isRegularExceeded ? '' : 'filled'}`}
+                  icon={euroIcon}
+                  size={12}
+                />
+                <StyledIcon
+                  className={`iconDouble ${!isRegularExceeded ? '' : 'filled'}`}
+                  icon={euroIcon}
+                  size={12}
+                />
+                <div
+                  className="gauge-content rounded"
+                  style={{ right: `${40}%` }}
+                />
+              </div>
+            </div>
+          </div>
+
+          <div className="limit-container">
+            <span className="limit12">{MAX_FREE}m³</span>
+            <span className="limit180">{MAX_REGULAR}m³</span>
+          </div>
+        </div>
+
+        <div>
+          <p className={`pricing ${pricing} text-14`}>
+            {t(`consumption.water_pricing.${pricing}`)}
+          </p>
+          <p
+            className="consumption text-14"
+            dangerouslySetInnerHTML={{
+              __html: t('consumption.water_pricing.consumption', {
+                consumption,
+              }),
+            }}
+          />
+        </div>
+        {showModal && (
+          <WaterPricingModal handleCloseClick={() => setShowModal(false)} />
+        )}
+      </div>
+    </div>
+  )
+}
diff --git a/src/components/Consumption/WaterPricing/WaterPricingModal.scss b/src/components/Consumption/WaterPricing/WaterPricingModal.scss
new file mode 100644
index 000000000..71c4d0c34
--- /dev/null
+++ b/src/components/Consumption/WaterPricing/WaterPricingModal.scss
@@ -0,0 +1,13 @@
+@import 'src/styles/base/color';
+
+.waterPricingModal {
+  text-align: center;
+
+  h1 {
+    color: $water-color;
+  }
+
+  p {
+    color: $grey-bright;
+  }
+}
diff --git a/src/components/Consumption/WaterPricing/WaterPricingModal.tsx b/src/components/Consumption/WaterPricing/WaterPricingModal.tsx
new file mode 100644
index 000000000..1caad1470
--- /dev/null
+++ b/src/components/Consumption/WaterPricing/WaterPricingModal.tsx
@@ -0,0 +1,49 @@
+import { Dialog } from '@material-ui/core'
+import Button from '@material-ui/core/Button'
+import CloseIcon from 'assets/icons/ico/close.svg'
+import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton'
+import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
+import React from 'react'
+import './WaterPricingModal.scss'
+
+export const WaterPricingModal = ({
+  handleCloseClick,
+}: {
+  handleCloseClick: () => void
+}) => {
+  const { t } = useI18n()
+  return (
+    <Dialog
+      open={true}
+      disableEscapeKeyDown
+      onClose={(event, reason): void => {
+        event && reason !== 'backdropClick' && handleCloseClick()
+      }}
+      classes={{
+        root: 'modal-root',
+        paper: 'modal-paper',
+      }}
+    >
+      <StyledIconButton
+        icon={CloseIcon}
+        onClick={handleCloseClick}
+        aria-label={t('feedback.accessibility.button_close')}
+        className="modal-paper-close-button"
+      />
+      <div className="waterPricingModal">
+        <h1 className="text-20-bold">
+          {t('consumption.water_pricing.modal.title')}
+        </h1>
+        <p
+          dangerouslySetInnerHTML={{
+            __html: t('consumption.water_pricing.modal.details'),
+          }}
+        />
+
+        <Button onClick={handleCloseClick} className="btnPrimary">
+          {t('consumption.water_pricing.modal.understood')}
+        </Button>
+      </div>
+    </Dialog>
+  )
+}
diff --git a/src/components/Consumption/WaterPricing/__snapshots__/WaterPricing.spec.tsx.snap b/src/components/Consumption/WaterPricing/__snapshots__/WaterPricing.spec.tsx.snap
new file mode 100644
index 000000000..d36d9c0a7
--- /dev/null
+++ b/src/components/Consumption/WaterPricing/__snapshots__/WaterPricing.spec.tsx.snap
@@ -0,0 +1,153 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`WaterPricing component should be rendered correctly 1`] = `
+<div>
+  <div
+    class="pricing-root"
+  >
+    <div
+      class="pricing-container"
+    >
+      <div
+        class="row"
+      >
+        <span
+          class="year text-16-bold"
+        >
+          consumption.water_pricing.year
+        </span>
+        <button
+          class="MuiButtonBase-root MuiButton-root MuiButton-text btnText"
+          tabindex="0"
+          type="button"
+        >
+          <span
+            class="MuiButton-label"
+          >
+            consumption.water_pricing.more
+          </span>
+          <span
+            class="MuiTouchRipple-root"
+          />
+        </button>
+      </div>
+      <div>
+        <div
+          class="gauges"
+        >
+          <div
+            class="gauge-container free"
+          >
+            <div
+              class="gauge-border"
+            >
+              <svg
+                aria-hidden="true"
+                class="iconFree styles__icon___23x3R"
+                height="12"
+                width="12"
+              >
+                <use
+                  xlink:href="#test-file-stub"
+                />
+              </svg>
+              <div
+                class="gauge-content rounded"
+                style="right: 70%;"
+              />
+            </div>
+          </div>
+          <div
+            class="separator"
+          />
+          <div
+            class="gauge-container regular no-color"
+          >
+            <div
+              class="gauge-border"
+            >
+              <svg
+                aria-hidden="true"
+                class="iconRegular  styles__icon___23x3R"
+                height="12"
+                width="12"
+              >
+                <use
+                  xlink:href="#test-file-stub"
+                />
+              </svg>
+              <div
+                class="gauge-content rounded"
+                style="right: 90%;"
+              />
+            </div>
+          </div>
+          <div
+            class="separator"
+          />
+          <div
+            class="gauge-container double no-color"
+          >
+            <div
+              class="gauge-border"
+            >
+              <svg
+                aria-hidden="true"
+                class="iconDouble  styles__icon___23x3R"
+                height="12"
+                width="12"
+              >
+                <use
+                  xlink:href="#test-file-stub"
+                />
+              </svg>
+              <svg
+                aria-hidden="true"
+                class="iconDouble  styles__icon___23x3R"
+                height="12"
+                width="12"
+              >
+                <use
+                  xlink:href="#test-file-stub"
+                />
+              </svg>
+              <div
+                class="gauge-content rounded"
+                style="right: 40%;"
+              />
+            </div>
+          </div>
+        </div>
+        <div
+          class="limit-container"
+        >
+          <span
+            class="limit12"
+          >
+            12
+            m³
+          </span>
+          <span
+            class="limit180"
+          >
+            180
+            m³
+          </span>
+        </div>
+      </div>
+      <div>
+        <p
+          class="pricing free text-14"
+        >
+          consumption.water_pricing.free
+        </p>
+        <p
+          class="consumption text-14"
+        >
+          consumption.water_pricing.consumption
+        </p>
+      </div>
+    </div>
+  </div>
+</div>
+`;
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 3339a628e..5748f6503 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -368,7 +368,20 @@
       "additional_text": "La visualisation et/ou la connexion à vos données de consommation peut s'en trouver affectée.<br /><br /><i>Merci pour votre patience en attendant un retour à la normale :)</i>",
       "ok": "Ok"
     },
-    "compared": "Comparé"
+    "compared": "Comparé",
+    "water_pricing": {
+      "year": "Année %{year}",
+      "consumption": "Consommation : <span>%{consumption}m³</span>",
+      "free": "Gratuit",
+      "regular": "Tarif normal",
+      "double": "Tarif double",
+      "more": "En savoir plus",
+      "modal": {
+        "title": "A partir du 1er janvier 2025, une tarification solidaire et environnementale de l’eau est mise en place.",
+        "details": "Cette jauge vous permet de garder un œil tout au long de l’année sur votre consommation d’eau afin de voir dans quelle tranche vous vous situez en tant que particulier.<br><br> Cette information vous est donnée à titre informatif, l'application définitive des tranches sera assurée par Eau Publique du Grand Lyon sur vos factures d'eau à compter du 01/01/2025 en fonction de vos consommations.",
+        "understood": "J'ai compris"
+      }
+    }
   },
   "consumption_details": {
     "detail": "Détail par fluide",
-- 
GitLab