From 4adc820e71b354a206dff3a97bd5b7bd30524896 Mon Sep 17 00:00:00 2001 From: Yoan VALLET <ext.sopra.yvallet@grandlyon.com> Date: Thu, 3 Dec 2020 02:01:38 +0100 Subject: [PATCH] feat: implement duel screens --- src/assets/icons/visu/boss/BOSS001.svg | 24 +++++ src/assets/icons/visu/boss/default.svg | 24 +++++ src/assets/icons/visu/duel/captionAverage.svg | 3 + .../icons/visu/duel/captionConsumption.svg | 3 + .../icons/visu/duel/captionIncoming.svg | 11 +++ src/components/Duel/DuelDetails.tsx | 17 ---- src/components/Duel/DuelError.tsx | 2 +- src/components/Duel/DuelOngoing.tsx | 67 ++++++++++++++ src/components/Duel/DuelUnlocked.tsx | 89 +++++++++++++++++++ src/components/Duel/DuelView.tsx | 23 +++-- src/components/Duel/duelOngoing.scss | 46 ++++++++++ .../{duelDetails.scss => duelUnlocked.scss} | 0 src/components/Duel/duelView.scss | 9 +- src/components/Season/SeasonCard.tsx | 40 ++++----- src/components/Season/SeasonCardBoss.tsx | 14 --- src/components/Season/SeasonCardOnGoing.tsx | 33 +++---- src/components/Season/SeasonCardUnlocked.tsx | 19 ++-- src/components/Season/seasonCardBoss.scss | 2 - src/db/bossEntity.json | 4 +- src/db/seasonEntity.json | 6 +- src/enum/updateUserSeason.enum.ts | 3 +- src/locales/fr.json | 9 +- src/services/boss.service.ts | 13 ++- src/services/season.service.ts | 16 +++- src/styles/base/_typography.scss | 7 ++ test/__mocks__/bossData.mock.ts | 5 +- 26 files changed, 380 insertions(+), 109 deletions(-) create mode 100644 src/assets/icons/visu/boss/BOSS001.svg create mode 100644 src/assets/icons/visu/boss/default.svg create mode 100644 src/assets/icons/visu/duel/captionAverage.svg create mode 100644 src/assets/icons/visu/duel/captionConsumption.svg create mode 100644 src/assets/icons/visu/duel/captionIncoming.svg delete mode 100644 src/components/Duel/DuelDetails.tsx create mode 100644 src/components/Duel/DuelOngoing.tsx create mode 100644 src/components/Duel/DuelUnlocked.tsx create mode 100644 src/components/Duel/duelOngoing.scss rename src/components/Duel/{duelDetails.scss => duelUnlocked.scss} (100%) delete mode 100644 src/components/Season/SeasonCardBoss.tsx delete mode 100644 src/components/Season/seasonCardBoss.scss diff --git a/src/assets/icons/visu/boss/BOSS001.svg b/src/assets/icons/visu/boss/BOSS001.svg new file mode 100644 index 000000000..7054c2862 --- /dev/null +++ b/src/assets/icons/visu/boss/BOSS001.svg @@ -0,0 +1,24 @@ +<svg width="219" height="219" viewBox="0 0 219 219" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M94.17 91.9805H89.79V109.5C89.79 115.338 92.3277 120.583 96.36 124.192V117.401C94.9698 115.094 94.17 112.391 94.17 109.5V91.9805Z" fill="#FFDDBD"/> +<path d="M124.83 91.9805H129.21V109.5C129.21 115.338 126.672 120.583 122.64 124.192V117.401C124.03 115.094 124.83 112.391 124.83 109.5V91.9805Z" fill="#FFDDBD"/> +<path d="M109.5 78.8403H96.1596C91.5427 78.8403 90.5507 83.5072 89.3418 89.1943C89.1942 89.8885 89.0434 90.5979 88.8824 91.3159C88.4666 93.17 88.0188 94.8349 87.6001 96.3603H96.1596L97.2182 118.26H109.5V78.8403Z" fill="#488D4F"/> +<path d="M109.5 118.26H121.782L122.841 96.3603H131.4C130.981 94.8349 130.534 93.17 130.118 91.3159C129.957 90.5983 129.806 89.8892 129.659 89.1954C128.45 83.5082 127.457 78.8403 122.841 78.8403H109.5V118.26Z" fill="#4BBA56"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M109.5 122.64H96.3601L101.91 201.48H107.133V144.713L109.5 136.711V122.64Z" fill="#9A5624"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M109.5 122.64H122.64L117.09 201.48H111.867V144.713L109.5 136.711V122.64Z" fill="#B67D55"/> +<path d="M105.12 70.0801H113.88V82.7201C113.88 82.9962 113.656 83.2201 113.38 83.2201H105.62C105.344 83.2201 105.12 82.9962 105.12 82.7201V70.0801Z" fill="#FFDDBD"/> +<path d="M105.12 70.0801H113.88V81.0301C113.88 82.2396 112.9 83.2201 111.69 83.2201H107.31C106.101 83.2201 105.12 82.2396 105.12 81.0301V70.0801Z" fill="#FFDDBD"/> +<path d="M100.74 78.8403V81.0303C100.74 84.6588 103.681 87.6003 107.31 87.6003H111.69C115.318 87.6003 118.26 84.6588 118.26 81.0303V78.8403H113.88V81.0303C113.88 82.2398 112.899 83.2203 111.69 83.2203H107.31C106.1 83.2203 105.12 82.2398 105.12 81.0303V78.8403H100.74Z" fill="#488D4F"/> +<rect x="105.12" y="74.4604" width="8.76" height="4.38" fill="#F4BA84"/> +<path d="M107.379 201.48H100.945V206.066H96.3601C91.5221 206.066 87.6001 209.488 87.6001 212.636C87.6001 215.291 93.6369 215.025 97.0056 214.877C97.6309 214.849 98.1643 214.826 98.5501 214.826C99.782 214.826 100.261 214.278 100.74 213.731C101.219 213.183 101.698 212.636 102.93 212.636C104.14 212.636 105.12 213.616 105.12 214.826H107.379V201.48Z" fill="#545F7C"/> +<path d="M111.621 201.48H118.055V206.066H122.64C127.478 206.066 131.4 209.488 131.4 212.636C131.4 215.291 126.009 215.025 122.64 214.877C122.015 214.849 121.481 214.826 121.095 214.826C119.863 214.826 119.384 214.278 118.905 213.731C118.426 213.183 117.947 212.636 116.715 212.636C115.506 212.636 114.525 213.616 114.525 214.826H111.621V201.48Z" fill="#6E7B9D"/> +<path d="M70.08 13.1401H148.92C151.339 13.1401 153.3 15.1011 153.3 17.5201V61.3201C153.3 63.7391 151.339 65.7001 148.92 65.7001H140.16V70.0801H118.26V65.7001H70.08C67.6609 65.7001 65.7 63.7391 65.7 61.3201V17.5201C65.7 15.1011 67.6609 13.1401 70.08 13.1401Z" fill="#8A8A8A"/> +<path d="M70.08 21.8999H148.92C151.339 21.8999 153.3 23.8609 153.3 26.2799V70.0799C153.3 72.4989 151.339 74.4599 148.92 74.4599H70.08C67.6609 74.4599 65.7 72.4989 65.7 70.0799V26.2799C65.7 23.8609 67.6609 21.8999 70.08 21.8999Z" fill="#C4C4C4"/> +<rect x="70.0798" y="29.5649" width="52.56" height="35.04" rx="1" fill="#96C5CF"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M118.26 31.7549H74.4598C73.2503 31.7549 72.2698 32.7354 72.2698 33.9449V62.5518C72.2698 63.7613 73.2503 64.7418 74.4598 64.7418H118.26C119.469 64.7418 120.45 63.7613 120.45 62.5518V33.9449C120.45 32.7354 119.469 31.7549 118.26 31.7549ZM74.4598 29.5649C72.0408 29.5649 70.0798 31.5259 70.0798 33.9449V62.5518C70.0798 64.9708 72.0408 66.9318 74.4598 66.9318H118.26C120.679 66.9318 122.64 64.9708 122.64 62.5518V33.9449C122.64 31.5259 120.679 29.5649 118.26 29.5649H74.4598Z" fill="#43595E"/> +<path opacity="0.85" d="M90.1028 31.8921H96.36L80.7171 64.6052H74.46L90.1028 31.8921Z" fill="white"/> +<path opacity="0.85" d="M102.2 31.8921H105.12L90.5199 64.6052H87.5999L102.2 31.8921Z" fill="white"/> +<ellipse cx="142.35" cy="58.0353" rx="6.57" ry="6.57" fill="#464646"/> +<path d="M146.418 58.5825C146.576 58.2133 146.662 57.8159 146.662 57.402C146.662 55.446 144.747 53.8604 142.384 53.8604C140.022 53.8604 138.107 55.446 138.107 57.402C138.107 57.8159 138.193 58.2133 138.35 58.5825C138.938 57.207 140.522 56.2214 142.384 56.2214C144.247 56.2214 145.831 57.207 146.418 58.5825Z" fill="#C4C4C4"/> +<rect x="135.78" y="30.6602" width="13.14" height="8.76" rx="0.5" fill="black"/> +<rect x="128.115" y="13.1401" width="2.19002" height="61.32" fill="#8A8A8A"/> +</svg> diff --git a/src/assets/icons/visu/boss/default.svg b/src/assets/icons/visu/boss/default.svg new file mode 100644 index 000000000..7054c2862 --- /dev/null +++ b/src/assets/icons/visu/boss/default.svg @@ -0,0 +1,24 @@ +<svg width="219" height="219" viewBox="0 0 219 219" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M94.17 91.9805H89.79V109.5C89.79 115.338 92.3277 120.583 96.36 124.192V117.401C94.9698 115.094 94.17 112.391 94.17 109.5V91.9805Z" fill="#FFDDBD"/> +<path d="M124.83 91.9805H129.21V109.5C129.21 115.338 126.672 120.583 122.64 124.192V117.401C124.03 115.094 124.83 112.391 124.83 109.5V91.9805Z" fill="#FFDDBD"/> +<path d="M109.5 78.8403H96.1596C91.5427 78.8403 90.5507 83.5072 89.3418 89.1943C89.1942 89.8885 89.0434 90.5979 88.8824 91.3159C88.4666 93.17 88.0188 94.8349 87.6001 96.3603H96.1596L97.2182 118.26H109.5V78.8403Z" fill="#488D4F"/> +<path d="M109.5 118.26H121.782L122.841 96.3603H131.4C130.981 94.8349 130.534 93.17 130.118 91.3159C129.957 90.5983 129.806 89.8892 129.659 89.1954C128.45 83.5082 127.457 78.8403 122.841 78.8403H109.5V118.26Z" fill="#4BBA56"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M109.5 122.64H96.3601L101.91 201.48H107.133V144.713L109.5 136.711V122.64Z" fill="#9A5624"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M109.5 122.64H122.64L117.09 201.48H111.867V144.713L109.5 136.711V122.64Z" fill="#B67D55"/> +<path d="M105.12 70.0801H113.88V82.7201C113.88 82.9962 113.656 83.2201 113.38 83.2201H105.62C105.344 83.2201 105.12 82.9962 105.12 82.7201V70.0801Z" fill="#FFDDBD"/> +<path d="M105.12 70.0801H113.88V81.0301C113.88 82.2396 112.9 83.2201 111.69 83.2201H107.31C106.101 83.2201 105.12 82.2396 105.12 81.0301V70.0801Z" fill="#FFDDBD"/> +<path d="M100.74 78.8403V81.0303C100.74 84.6588 103.681 87.6003 107.31 87.6003H111.69C115.318 87.6003 118.26 84.6588 118.26 81.0303V78.8403H113.88V81.0303C113.88 82.2398 112.899 83.2203 111.69 83.2203H107.31C106.1 83.2203 105.12 82.2398 105.12 81.0303V78.8403H100.74Z" fill="#488D4F"/> +<rect x="105.12" y="74.4604" width="8.76" height="4.38" fill="#F4BA84"/> +<path d="M107.379 201.48H100.945V206.066H96.3601C91.5221 206.066 87.6001 209.488 87.6001 212.636C87.6001 215.291 93.6369 215.025 97.0056 214.877C97.6309 214.849 98.1643 214.826 98.5501 214.826C99.782 214.826 100.261 214.278 100.74 213.731C101.219 213.183 101.698 212.636 102.93 212.636C104.14 212.636 105.12 213.616 105.12 214.826H107.379V201.48Z" fill="#545F7C"/> +<path d="M111.621 201.48H118.055V206.066H122.64C127.478 206.066 131.4 209.488 131.4 212.636C131.4 215.291 126.009 215.025 122.64 214.877C122.015 214.849 121.481 214.826 121.095 214.826C119.863 214.826 119.384 214.278 118.905 213.731C118.426 213.183 117.947 212.636 116.715 212.636C115.506 212.636 114.525 213.616 114.525 214.826H111.621V201.48Z" fill="#6E7B9D"/> +<path d="M70.08 13.1401H148.92C151.339 13.1401 153.3 15.1011 153.3 17.5201V61.3201C153.3 63.7391 151.339 65.7001 148.92 65.7001H140.16V70.0801H118.26V65.7001H70.08C67.6609 65.7001 65.7 63.7391 65.7 61.3201V17.5201C65.7 15.1011 67.6609 13.1401 70.08 13.1401Z" fill="#8A8A8A"/> +<path d="M70.08 21.8999H148.92C151.339 21.8999 153.3 23.8609 153.3 26.2799V70.0799C153.3 72.4989 151.339 74.4599 148.92 74.4599H70.08C67.6609 74.4599 65.7 72.4989 65.7 70.0799V26.2799C65.7 23.8609 67.6609 21.8999 70.08 21.8999Z" fill="#C4C4C4"/> +<rect x="70.0798" y="29.5649" width="52.56" height="35.04" rx="1" fill="#96C5CF"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M118.26 31.7549H74.4598C73.2503 31.7549 72.2698 32.7354 72.2698 33.9449V62.5518C72.2698 63.7613 73.2503 64.7418 74.4598 64.7418H118.26C119.469 64.7418 120.45 63.7613 120.45 62.5518V33.9449C120.45 32.7354 119.469 31.7549 118.26 31.7549ZM74.4598 29.5649C72.0408 29.5649 70.0798 31.5259 70.0798 33.9449V62.5518C70.0798 64.9708 72.0408 66.9318 74.4598 66.9318H118.26C120.679 66.9318 122.64 64.9708 122.64 62.5518V33.9449C122.64 31.5259 120.679 29.5649 118.26 29.5649H74.4598Z" fill="#43595E"/> +<path opacity="0.85" d="M90.1028 31.8921H96.36L80.7171 64.6052H74.46L90.1028 31.8921Z" fill="white"/> +<path opacity="0.85" d="M102.2 31.8921H105.12L90.5199 64.6052H87.5999L102.2 31.8921Z" fill="white"/> +<ellipse cx="142.35" cy="58.0353" rx="6.57" ry="6.57" fill="#464646"/> +<path d="M146.418 58.5825C146.576 58.2133 146.662 57.8159 146.662 57.402C146.662 55.446 144.747 53.8604 142.384 53.8604C140.022 53.8604 138.107 55.446 138.107 57.402C138.107 57.8159 138.193 58.2133 138.35 58.5825C138.938 57.207 140.522 56.2214 142.384 56.2214C144.247 56.2214 145.831 57.207 146.418 58.5825Z" fill="#C4C4C4"/> +<rect x="135.78" y="30.6602" width="13.14" height="8.76" rx="0.5" fill="black"/> +<rect x="128.115" y="13.1401" width="2.19002" height="61.32" fill="#8A8A8A"/> +</svg> diff --git a/src/assets/icons/visu/duel/captionAverage.svg b/src/assets/icons/visu/duel/captionAverage.svg new file mode 100644 index 000000000..ff0033c06 --- /dev/null +++ b/src/assets/icons/visu/duel/captionAverage.svg @@ -0,0 +1,3 @@ +<svg width="27" height="2" viewBox="0 0 27 2" fill="none" xmlns="http://www.w3.org/2000/svg"> +<line x1="26" y1="1" x2="1" y2="1" stroke="#61F0F2" stroke-width="2" stroke-linecap="round" stroke-dasharray="3 6"/> +</svg> diff --git a/src/assets/icons/visu/duel/captionConsumption.svg b/src/assets/icons/visu/duel/captionConsumption.svg new file mode 100644 index 000000000..dd86a1cb1 --- /dev/null +++ b/src/assets/icons/visu/duel/captionConsumption.svg @@ -0,0 +1,3 @@ +<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M0 4C0 1.79086 1.79086 0 4 0H20C22.2091 0 24 1.79086 24 4V25H0V4Z" fill="#E3B82A"/> +</svg> diff --git a/src/assets/icons/visu/duel/captionIncoming.svg b/src/assets/icons/visu/duel/captionIncoming.svg new file mode 100644 index 000000000..96587ac88 --- /dev/null +++ b/src/assets/icons/visu/duel/captionIncoming.svg @@ -0,0 +1,11 @@ +<svg width="26" height="19" viewBox="0 0 26 19" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g clip-path="url(#clip0)"> +<path d="M1 5C1 2.79086 2.79086 1 5 1H21C23.2091 1 25 2.79086 25 5V49H1V5Z" fill="#E3B82A" fill-opacity="0.1"/> +<path d="M25 49V5C25 2.79086 23.2091 1 21 1H5C2.79086 1 1 2.79086 1 5V49" stroke="#E3B82A" stroke-width="0.5" stroke-linecap="round" stroke-dasharray="4 6"/> +</g> +<defs> +<clipPath id="clip0"> +<rect width="26" height="19" fill="white"/> +</clipPath> +</defs> +</svg> diff --git a/src/components/Duel/DuelDetails.tsx b/src/components/Duel/DuelDetails.tsx deleted file mode 100644 index fbfcab799..000000000 --- a/src/components/Duel/DuelDetails.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { useState } from 'react' -import './duelDetails.scss' -import { useSelector } from 'react-redux' -import { EcolyoState } from 'store' -import { UserSeason } from 'models' - -interface DuelDetailsProps { - userSeason: UserSeason -} - -const DuelDetails: React.FC<DuelDetailsProps> = ({ - userSeason, -}: DuelDetailsProps) => { - return <div></div> -} - -export default DuelDetails diff --git a/src/components/Duel/DuelError.tsx b/src/components/Duel/DuelError.tsx index d9a1a604b..cac039c8f 100644 --- a/src/components/Duel/DuelError.tsx +++ b/src/components/Duel/DuelError.tsx @@ -17,7 +17,7 @@ const DuelError: React.FC = () => { <div className="duel-error-message">{t('duel.global_error')}</div> <div className="duel-error-button"> <StyledStopButton color="secondary" onClick={goBack}> - Retour + {t('duel.error_go_back')} </StyledStopButton> </div> </div> diff --git a/src/components/Duel/DuelOngoing.tsx b/src/components/Duel/DuelOngoing.tsx new file mode 100644 index 000000000..69c76af28 --- /dev/null +++ b/src/components/Duel/DuelOngoing.tsx @@ -0,0 +1,67 @@ +import React, { useCallback, useEffect, useState } from 'react' +import './duelOngoing.scss' +import { Client, useClient } from 'cozy-client' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { useDispatch, useSelector } from 'react-redux' +import { EcolyoState } from 'store' +import { UserSeason } from 'models' + +import StyledIcon from 'components/CommonKit/Icon/StyledIcon' +import CaptionAverageIcon from 'assets/icons/visu/duel/captionAverage.svg' +import CaptionConsumptionIcon from 'assets/icons/visu/duel/captionConsumption.svg' +import CaptionIncomingIcon from 'assets/icons/visu/duel/captionIncoming.svg' + +interface DuelOngoingProps { + userSeason: UserSeason +} + +const DuelOngoing: React.FC<DuelOngoingProps> = ({ + userSeason, +}: DuelOngoingProps) => { + const client: Client = useClient() + const dispatch = useDispatch() + const { t } = useI18n() + const boss = userSeason.boss + + return ( + <div className="duel-ongoing-container"> + <div className="boss-title text-16-normal">{boss.title}</div> + <div className="boss-goal text-18-normal"> + {t('duel.goal', { title: boss.title })} + </div> + <div className="boss-consumption text-28-normal"> + <span className="consumption">{boss.userConsumption}</span> + {` / ${boss.threshold} €`} + </div> + <div className="boss-chart">CHART</div> + <div className="boss-chart-caption text-15-normal"> + <div className="boss-caption"> + <StyledIcon + className="caption-icon" + icon={CaptionAverageIcon} + size={25} + /> + <div className="caption-label">{t('duel.caption_average')}</div> + </div> + <div className="boss-caption"> + <StyledIcon + className="caption-icon" + icon={CaptionConsumptionIcon} + size={25} + /> + <div className="caption-label">{t('duel.caption_consumption')}</div> + </div> + <div className="boss-caption"> + <StyledIcon + className="caption-icon" + icon={CaptionIncomingIcon} + size={25} + /> + <div className="caption-label">{t('duel.caption_incoming')}</div> + </div> + </div> + </div> + ) +} + +export default DuelOngoing diff --git a/src/components/Duel/DuelUnlocked.tsx b/src/components/Duel/DuelUnlocked.tsx new file mode 100644 index 000000000..eea277168 --- /dev/null +++ b/src/components/Duel/DuelUnlocked.tsx @@ -0,0 +1,89 @@ +import React, { useCallback, useEffect, useState } from 'react' +import './duelUnlocked.scss' +import { Client, useClient } from 'cozy-client' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { useDispatch, useSelector } from 'react-redux' +import { EcolyoState } from 'store' +import { UserSeason } from 'models' +import { updateUserSeasonList } from 'store/season/season.actions' +import SeasonService from 'services/season.service' + +import defaultIcon from 'assets/icons/visu/boss/default.svg' +import StyledIcon from 'components/CommonKit/Icon/StyledIcon' +import StyledStopButton from 'components/CommonKit/Button/StyledStopButton' +import SeasonNoFluidModal from 'components/Season/SeasonNoFluidModal' +import { UpdateUserSeason } from 'enum/updateUserSeason.enum' + +interface DuelUnlockedProps { + userSeason: UserSeason +} + +const DuelUnlocked: React.FC<DuelUnlockedProps> = ({ + userSeason, +}: DuelUnlockedProps) => { + const client: Client = useClient() + const dispatch = useDispatch() + const { t } = useI18n() + + const [bossIcon, setBossIcon] = useState(defaultIcon) + const [openNoFluidModal, setopenNoFluidModal] = useState(false) + const { fluidTypes } = useSelector((state: EcolyoState) => state.global) + + const importBossIcon = useCallback(async (id: string) => { + // Les svg doivent être au format id.svg + let importedBossIcon + try { + importedBossIcon = await import( + /* webpackMode: "eager" */ `assets/icons/visu/boss/${id}.svg` + ) + } catch (e) {} + if (importedBossIcon) { + setBossIcon(importedBossIcon.default) + } + }, []) + + const toggleNoFluidModal = useCallback(() => { + setopenNoFluidModal(prev => !prev) + }, []) + + const launchBoss = useCallback(async () => { + if (fluidTypes.length > 0) { + const seasonService = new SeasonService(client) + const updatedSeason = await seasonService.updateUserSeason( + userSeason, + UpdateUserSeason.BOSS_START + ) + dispatch(updateUserSeasonList(updatedSeason)) + } else { + return toggleNoFluidModal() + } + }, [client, dispatch, userSeason, fluidTypes, toggleNoFluidModal]) + + useEffect(() => { + if (userSeason) { + importBossIcon(userSeason.id) + } + }, [userSeason, importBossIcon]) + + return ( + <div className="duel-unlocked-container"> + <StyledIcon className="boss-icon" icon={bossIcon} size={219} /> + <div className="boss-title">{userSeason.boss.title}</div> + <div className="boss-description">{userSeason.boss.description}</div> + <div className="boss-description"> + Sachant que votre moyenne actuelle est de #CONSUMPTION €, relevez-vous + le defi? + </div> + <StyledStopButton color="secondary" onClick={launchBoss}> + Allons-y ! + </StyledStopButton> + + <SeasonNoFluidModal + open={openNoFluidModal} + handleCloseClick={toggleNoFluidModal} + ></SeasonNoFluidModal> + </div> + ) +} + +export default DuelUnlocked diff --git a/src/components/Duel/DuelView.tsx b/src/components/Duel/DuelView.tsx index 4c7ab2883..785fe74cb 100644 --- a/src/components/Duel/DuelView.tsx +++ b/src/components/Duel/DuelView.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useCallback, useState } from 'react' import './duelView.scss' import { useSelector } from 'react-redux' import { EcolyoState } from 'store' @@ -8,8 +8,10 @@ import Header from 'components/Header/Header' import { UserSeasonState } from 'enum/userSeason.enum' import DuelError from 'components/Duel/DuelError' -import DuelDetails from 'components/Duel/DuelDetails' +import DuelUnlocked from 'components/Duel/DuelUnlocked' import { UserBossState } from 'enum/userBoss.enum' +import { UserSeason } from 'models' +import DuelOngoing from './DuelOngoing' const DuelView: React.FC = () => { const [headerHeight, setHeaderHeight] = useState<number>(0) @@ -18,7 +20,16 @@ const DuelView: React.FC = () => { const defineHeaderHeight = (height: number) => { setHeaderHeight(height) } - + const renderDuel = useCallback((season: UserSeason) => { + switch (season.boss.state) { + case UserBossState.UNLOCKED: + return <DuelUnlocked userSeason={season} /> + case UserBossState.ONGOING: + return <DuelOngoing userSeason={season} /> + default: + return <DuelError /> + } + }, []) return ( <> <CozyBar titleKey={'COMMON.APP_DUEL_TITLE'} displayBackArrow={true} /> @@ -29,10 +40,8 @@ const DuelView: React.FC = () => { ></Header> <Content height={headerHeight}> <div className="duel-view-container"> - {currentSeason && - currentSeason.state === UserSeasonState.BOSS && - currentSeason.boss.state === UserBossState.ONGOING ? ( - <DuelDetails userSeason={currentSeason} /> + {currentSeason && currentSeason.state === UserSeasonState.BOSS ? ( + renderDuel(currentSeason) ) : ( <DuelError /> )} diff --git a/src/components/Duel/duelOngoing.scss b/src/components/Duel/duelOngoing.scss new file mode 100644 index 000000000..9a72fcbff --- /dev/null +++ b/src/components/Duel/duelOngoing.scss @@ -0,0 +1,46 @@ +@import '../../styles/base/typography'; + +.duel-ongoing-container{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.boss-title{ + color: $soft-grey; + margin-top: 1rem; +} +.boss-goal{ + color: $grey-bright; + margin: 1rem 3rem; + text-align: center; +} +.boss-consumption{ + color: $grey-bright; + margin: 1rem auto; + .consumption{ + color: $gold-light; + + } +} +.boss-chart{ + height: 15.625rem; + display: flex; + align-items: center; + justify-content: center; +} +.boss-chart-caption{ + display: flex; + flex-direction: column; + align-self: flex-start; + .boss-caption{ + display: flex; + margin-top: 0.75rem; + .caption-icon{ + margin: auto 1.5rem; + } + .caption-label{ + color: $grey-bright; + } + } +} \ No newline at end of file diff --git a/src/components/Duel/duelDetails.scss b/src/components/Duel/duelUnlocked.scss similarity index 100% rename from src/components/Duel/duelDetails.scss rename to src/components/Duel/duelUnlocked.scss diff --git a/src/components/Duel/duelView.scss b/src/components/Duel/duelView.scss index 8c5948843..6708d11d3 100644 --- a/src/components/Duel/duelView.scss +++ b/src/components/Duel/duelView.scss @@ -1,8 +1 @@ -@import '../../styles/base/typography'; - -.duel-view-container{ - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} \ No newline at end of file +@import '../../styles/base/typography'; \ No newline at end of file diff --git a/src/components/Season/SeasonCard.tsx b/src/components/Season/SeasonCard.tsx index 6b9bd1577..6e7f46784 100644 --- a/src/components/Season/SeasonCard.tsx +++ b/src/components/Season/SeasonCard.tsx @@ -1,7 +1,6 @@ import { UserSeasonState } from 'enum/userSeason.enum' import { UserSeason } from 'models' -import React from 'react' -import SeasonCardBoss from './SeasonCardBoss' +import React, { useCallback } from 'react' import SeasonCardDone from './SeasonCardDone' import SeasonCardLocked from './SeasonCardLocked' import SeasonCardOnGoing from './SeasonCardOnGoing' @@ -20,23 +19,24 @@ const SeasonCard: React.FC<SeasonCardProps> = ({ index, cardWitdh, }: SeasonCardProps) => { - const renderCard = () => { - const { state } = userSeason - - switch (state) { - case UserSeasonState.LOCKED: - return <SeasonCardLocked userSeason={userSeason} /> - case UserSeasonState.UNLOCKED: - return <SeasonCardUnlocked userSeason={userSeason} /> - case UserSeasonState.DONE: - return <SeasonCardDone userSeason={userSeason} /> - case UserSeasonState.ONGOING: - case UserSeasonState.BOSS: - return <SeasonCardOnGoing userSeason={userSeason} /> - default: - return <SeasonCardLocked userSeason={userSeason} /> - } - } + const renderCard = useCallback( + (state: UserSeasonState) => { + switch (state) { + case UserSeasonState.LOCKED: + return <SeasonCardLocked userSeason={userSeason} /> + case UserSeasonState.UNLOCKED: + return <SeasonCardUnlocked userSeason={userSeason} /> + case UserSeasonState.DONE: + return <SeasonCardDone userSeason={userSeason} /> + case UserSeasonState.ONGOING: + case UserSeasonState.BOSS: + return <SeasonCardOnGoing userSeason={userSeason} /> + default: + return <SeasonCardLocked userSeason={userSeason} /> + } + }, + [userSeason] + ) return ( <div className={indexSlider === index ? 'slide active' : 'slide'} @@ -45,7 +45,7 @@ const SeasonCard: React.FC<SeasonCardProps> = ({ maxWidth: `${cardWitdh}px`, }} > - {renderCard()} + {renderCard(userSeason.state)} </div> ) } diff --git a/src/components/Season/SeasonCardBoss.tsx b/src/components/Season/SeasonCardBoss.tsx deleted file mode 100644 index 0ea3cc766..000000000 --- a/src/components/Season/SeasonCardBoss.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import './seasonCardBoss.scss' -import { UserSeason } from 'models' - -interface SeasonCardBossProps { - userSeason: UserSeason -} -const SeasonCardBoss: React.FC<SeasonCardBossProps> = ({ - userSeason, -}: SeasonCardBossProps) => { - return <></> -} - -export default SeasonCardBoss diff --git a/src/components/Season/SeasonCardOnGoing.tsx b/src/components/Season/SeasonCardOnGoing.tsx index 8c0f6df57..6c4ae0e4e 100644 --- a/src/components/Season/SeasonCardOnGoing.tsx +++ b/src/components/Season/SeasonCardOnGoing.tsx @@ -28,27 +28,28 @@ const SeasonCardOnGoing: React.FC<SeasonCardOnGoingProps> = ({ const winStarts = useCallback(async () => { const seasonService = new SeasonService(client) - userSeason.progress + 5 >= userSeason.target - ? (userSeason.progress = userSeason.target) - : (userSeason.progress += 5) + let progress = 0 + let updateType: UpdateUserSeason + if (userSeason.progress + 5 >= userSeason.target) { + progress = userSeason.target + updateType = UpdateUserSeason.BOSS_UNLOCK + } else { + progress = userSeason.progress + 5 + updateType = UpdateUserSeason.SEASON + } + console.log(userSeason) const updatedSeason = await seasonService.updateUserSeason( - userSeason, - UpdateUserSeason.SEASON + { ...userSeason, progress: progress }, + updateType ) dispatch(updateUserSeasonList(updatedSeason)) }, [client, dispatch, userSeason]) - const launchBoss = useCallback(async () => { - const seasonService = new SeasonService(client) - const updatedSeason = await seasonService.updateUserSeason( - userSeason, - UpdateUserSeason.BOSS_START - ) - dispatch(updateUserSeasonList(updatedSeason)) + const goDuel = useCallback(async () => { history.push('/challenges/duel') - }, [client, dispatch, userSeason, history]) + }, [history]) - const resetProgress = async () => { + const resetProgress = useCallback(async () => { const seasonService = new SeasonService(client) userSeason.progress = 0 const updatedSeason = await seasonService.updateUserSeason( @@ -56,7 +57,7 @@ const SeasonCardOnGoing: React.FC<SeasonCardOnGoingProps> = ({ UpdateUserSeason.SEASON ) dispatch(updateUserSeasonList(updatedSeason)) - } + }, [client, dispatch, userSeason]) return ( <div className="cardContent onGoing"> @@ -107,7 +108,7 @@ const SeasonCardOnGoing: React.FC<SeasonCardOnGoingProps> = ({ ? 'smallCard' : 'smallCard finished' } - onClick={launchBoss} + onClick={goDuel} > {t('season.card.ongoing.duel')} </button> diff --git a/src/components/Season/SeasonCardUnlocked.tsx b/src/components/Season/SeasonCardUnlocked.tsx index eb9df5d76..40547e08b 100644 --- a/src/components/Season/SeasonCardUnlocked.tsx +++ b/src/components/Season/SeasonCardUnlocked.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useState } from 'react' import { Client, useClient } from 'cozy-client' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { useDispatch, useSelector } from 'react-redux' import { updateUserSeasonList } from 'store/season/season.actions' import './seasonCardUnlocked.scss' @@ -9,8 +10,7 @@ import StyledButtonValid from 'components/CommonKit/Button/StyledButtonValid' import SeasonNoFluidModal from './SeasonNoFluidModal' import { EcolyoState } from 'store' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' -import def from 'assets/icons/visu/season/seasonLocked.svg' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import defaultIcon from 'assets/icons/visu/season/seasonLocked.svg' interface SeasonCardUnlockedProps { userSeason: UserSeason @@ -25,9 +25,9 @@ const SeasonCardUnlocked: React.FC<SeasonCardUnlockedProps> = ({ const [openNoFluidModal, setopenNoFluidModal] = useState(false) const { fluidTypes } = useSelector((state: EcolyoState) => state.global) - const [seasonIcon, setSeasonIcon] = useState(def) + const [seasonIcon, setSeasonIcon] = useState(defaultIcon) - async function importSeasonIcon(id: string) { + const importSeasonIcon = useCallback(async (id: string) => { // Les svg doivent être au format id.svg let importedSeasonIcon try { @@ -38,10 +38,11 @@ const SeasonCardUnlocked: React.FC<SeasonCardUnlockedProps> = ({ if (importedSeasonIcon) { setSeasonIcon(importedSeasonIcon.default) } - } - const toggleNoFluidModal = () => { + }, []) + + const toggleNoFluidModal = useCallback(() => { setopenNoFluidModal(prev => !prev) - } + }, []) const launchSeason = useCallback(async () => { if (fluidTypes.length > 0) { @@ -51,13 +52,13 @@ const SeasonCardUnlocked: React.FC<SeasonCardUnlockedProps> = ({ } else { return toggleNoFluidModal() } - }, [client, dispatch, userSeason, fluidTypes]) + }, [client, dispatch, userSeason, fluidTypes, toggleNoFluidModal]) useEffect(() => { if (userSeason) { importSeasonIcon(userSeason.id) } - }, [userSeason]) + }, [userSeason, importSeasonIcon]) return ( <> diff --git a/src/components/Season/seasonCardBoss.scss b/src/components/Season/seasonCardBoss.scss deleted file mode 100644 index e4300007a..000000000 --- a/src/components/Season/seasonCardBoss.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '../../styles/base/typography'; - diff --git a/src/db/bossEntity.json b/src/db/bossEntity.json index 57c1d4c28..9217cd8cf 100644 --- a/src/db/bossEntity.json +++ b/src/db/bossEntity.json @@ -1,8 +1,8 @@ [ { - "_id": "BO001", + "_id": "BOSS001", "title": "Nicolas Hublot", - "description": "foobar", + "description": "Je parie un ours polaire que vous ne pouvez pas consommer moins que #CONSUMPTION € en 1 semaine", "duration": { "days": 7 }, "threshold": 40 } diff --git a/src/db/seasonEntity.json b/src/db/seasonEntity.json index 2f755459a..830b34260 100644 --- a/src/db/seasonEntity.json +++ b/src/db/seasonEntity.json @@ -6,7 +6,7 @@ "target": 15, "relationships": { "boss": { - "data": { "_id": "BO001", "_type": "com.grandlyon.ecolyo.boss" } + "data": { "_id": "BOSS001", "_type": "com.grandlyon.ecolyo.boss" } } }, "quizType": "Culture Générale" @@ -18,7 +18,7 @@ "target": 15, "relationships": { "boss": { - "data": { "_id": "BO002", "_type": "com.grandlyon.ecolyo.boss" } + "data": { "_id": "BOSS002", "_type": "com.grandlyon.ecolyo.boss" } } }, "quizType": "Culture Générale" @@ -30,7 +30,7 @@ "target": 15, "relationships": { "boss": { - "data": { "_id": "BO003", "_type": "com.grandlyon.ecolyo.boss" } + "data": { "_id": "BOSS003", "_type": "com.grandlyon.ecolyo.boss" } } }, "quizType": "Culture Générale" diff --git a/src/enum/updateUserSeason.enum.ts b/src/enum/updateUserSeason.enum.ts index 85d0c8dcf..f5e4b72e3 100644 --- a/src/enum/updateUserSeason.enum.ts +++ b/src/enum/updateUserSeason.enum.ts @@ -1,6 +1,7 @@ export enum UpdateUserSeason { SEASON = 0, - BOSS_START = 10, + BOSS_UNLOCK = 10, + BOSS_START = 11, QUIZ = 20, MISSION = 30, } diff --git a/src/locales/fr.json b/src/locales/fr.json index 17151fec7..079edd221 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -345,6 +345,13 @@ } }, "duel": { - "global_error": "Oups.. Une erreur est parvenue. Veuillez retourner à l'écran des défis" + "global_error": "Oups.. Une erreur est parvenue. Veuillez retourner à l'écran des défis", + "error_go_back": "Retour", + "average_info": "Sachant que votre moyenne actuelle est de ${threshold} €, relevez-vous le défi ?", + "start": "Allons-y !", + "goal": "Faites moins que votre moyenne hebdomadaire pour batte ${title} !", + "caption_average": "Votre moyenne à titre indicatif", + "caption_consumption": "Votre consommation", + "caption_incoming": "Données à venir" } } diff --git a/src/services/boss.service.ts b/src/services/boss.service.ts index f93f7f44f..ccbfa6fad 100644 --- a/src/services/boss.service.ts +++ b/src/services/boss.service.ts @@ -90,14 +90,21 @@ export default class BossService { } } + public async unlockUserBoss(userBoss: Boss): Promise<Boss> { + const updatedUserBoss: Boss = { + ...userBoss, + state: UserBossState.UNLOCKED, + } + return updatedUserBoss + } + public async startUserBoss(userBoss: Boss): Promise<Boss> { - // const updatedThreshold: number = await this.getConsumptionInfo(userBoss) + const updatedThreshold: number = await this.getConsumptionInfo(userBoss) const updatedUserBoss: Boss = { ...userBoss, state: UserBossState.ONGOING, startDate: DateTime.local(), - // TODO add calculation of threshold - threshold: await this.getConsumptionInfo(userBoss), + threshold: updatedThreshold, } return updatedUserBoss } diff --git a/src/services/season.service.ts b/src/services/season.service.ts index 9f3992bd8..51acdc1bd 100644 --- a/src/services/season.service.ts +++ b/src/services/season.service.ts @@ -9,6 +9,7 @@ import { UpdateUserSeason } from 'enum/updateUserSeason.enum' import { DateTime } from 'luxon' import { Relation } from 'models' import { getRelationship } from 'utils/utils' +import { UserBossState } from 'enum/userBoss.enum' export default class SeasonService { private readonly _client: Client @@ -161,14 +162,23 @@ export default class SeasonService { userSeason: UserSeason, flag: UpdateUserSeason ): Promise<UserSeason> { - let updatedUserSeason + let updatedUserSeason = userSeason + let updatedBoss = userSeason.boss + const bossService = new BossService(this._client) switch (flag) { case UpdateUserSeason.SEASON: updatedUserSeason = userSeason break + case UpdateUserSeason.BOSS_UNLOCK: + updatedBoss = await bossService.unlockUserBoss(userSeason.boss) + updatedUserSeason = { + ...userSeason, + state: UserSeasonState.BOSS, + boss: updatedBoss, + } + break case UpdateUserSeason.BOSS_START: - const bossService = new BossService(this._client) - const updatedBoss = await bossService.startUserBoss(userSeason.boss) + updatedBoss = await bossService.startUserBoss(userSeason.boss) updatedUserSeason = { ...userSeason, state: UserSeasonState.BOSS, diff --git a/src/styles/base/_typography.scss b/src/styles/base/_typography.scss index f80e3e5b9..0e74941f5 100644 --- a/src/styles/base/_typography.scss +++ b/src/styles/base/_typography.scss @@ -168,6 +168,13 @@ p { font-size: 1.5rem; line-height: 120%; } +.text-28-normal { + font-family: $text-font; + font-style: normal; + font-weight: normal; + font-size: 1.75rem; + line-height: 120%; +} /* Button */ .button-primary-text { diff --git a/test/__mocks__/bossData.mock.ts b/test/__mocks__/bossData.mock.ts index 4cd363906..2136e1dce 100644 --- a/test/__mocks__/bossData.mock.ts +++ b/test/__mocks__/bossData.mock.ts @@ -5,10 +5,11 @@ import { UserBossState } from 'enum/userBoss.enum' export const bossData: Boss = { id: 'BOSS001', title: 'NicolasHublot', - description: 'desccc', + description: + 'Je parie un ours polaire que vous ne pouvez pas consommer moins que #CONSUMPTION € en 1 semaine', duration: Duration.fromObject({ days: 30 }), threshold: 1, state: UserBossState.LOCKED, startDate: null, - consumption: 1000, + userConsumption: 1000, } -- GitLab