diff --git a/src/components/Report/ChallengeReport.tsx b/src/components/Report/ChallengeReport.tsx new file mode 100644 index 0000000000000000000000000000000000000000..578f0d0f1d65661e286fb2bd8bcc0f027c1c73a0 --- /dev/null +++ b/src/components/Report/ChallengeReport.tsx @@ -0,0 +1,95 @@ +import React, { useEffect, useState } from 'react' +import './challengereport.scss' +import { NavLink } from 'react-router-dom' + +import { UserChallenge } from 'models' + +import StyledChallengeCard from 'components/CommonKit/Card/StyledChallengeCard' +import StyledIcon from 'components/CommonKit/Icon/StyledIcon' +import BlackArrowIcon from 'assets/icons/ico/black-arrow.svg' + +interface ChallengeReportProps { + userChallenges: UserChallenge[] +} + +const ChallengeReport: React.FC<ChallengeReportProps> = ({ + userChallenges, +}: ChallengeReportProps) => { + const [badgeSources, setBadgeSources] = useState<string[]>([]) + + async function importRightBadge( + id: string, + badgeStatus: number, + index: number + ) { + if (badgeStatus !== -1) { + // Les png doivent être au format idchallenge-badgestate.png + const importedBadge = + id === 'CHA00000001' + ? await import( + /* webpackMode: "eager" */ `assets/png/badges/${id}-1.png` + ) + : await import( + /* webpackMode: "eager" */ `assets/png/badges/${id}-${badgeStatus}.png` + ) + const updatedList = [...badgeSources] + updatedList[index] = importedBadge.default + setBadgeSources(updatedList) + } + } + + useEffect(() => { + userChallenges.forEach((userChallenge, index) => { + if (userChallenge.challengeType && userChallenge.badge) { + importRightBadge( + userChallenge.challengeType.id, + userChallenge.badge, + index + ) + } + }) + }, []) + + return ( + <div className="challenge-root"> + {userChallenges.map((userChallenge, index) => { + return ( + <NavLink + key={index} + className="challenge-card-link" + to={{ + pathname: `challenges/finished`, + state: userChallenge, + }} + > + <StyledChallengeCard displayBorder={true}> + <div className="challenge-card"> + <div className="challenge-card-content-left"> + <div className="challenge-card-content-badge"> + {badgeSources && badgeSources[index] && ( + <img + className="cp-win-badge" + src={badgeSources[index]} + width={72} + /> + )} + </div> + <div className="challenge-card-content-title text-18-normal"> + {userChallenge.challengeType + ? userChallenge.challengeType.title + : null} + </div> + </div> + <div className="challenge-card-content-right"> + <StyledIcon icon={BlackArrowIcon} size={18} /> + </div> + </div> + </StyledChallengeCard> + </NavLink> + ) + })} + </div> + ) +} + +export default ChallengeReport diff --git a/src/components/Report/MonthlyReport.tsx b/src/components/Report/MonthlyReport.tsx index a08d9a79ed1a5b1b2af4107625d9bdfe0bdf5b07..660cdc4e74b574b63204d1ee88e8bd252dd5cb64 100644 --- a/src/components/Report/MonthlyReport.tsx +++ b/src/components/Report/MonthlyReport.tsx @@ -8,18 +8,20 @@ import './monthlyreport.scss' import { TimeStep } from 'enum/timeStep.enum' import { fluidTypeState } from 'atoms/fluidState.state' import { userProfileState } from 'atoms/userProfile.state' -import { PerformanceIndicator } from 'models' +import { PerformanceIndicator, UserChallenge } from 'models' import ConsumptionService from 'services/consumption.service' import PerformanceIndicatorService from 'services/performanceIndicator.service' import ConfigService from 'services/fluidConfig.service' +import ChallengeService from 'services/challenge.service' import { convertDateToMonthString } from 'utils/date' import PerformanceIndicatorContent from 'components/PerformanceIndicator/PerformanceIndicatorContent' import FluidPerformanceIndicator from 'components/PerformanceIndicator/FluidPerformanceIndicator' import DateNavigator from 'components/DateNavigator/DateNavigator' import ChartReport from 'components/Report/ChartReport' +import ChallengeReport from 'components/Report/ChallengeReport' -const MonthlyReport = () => { +const MonthlyReport: React.FC = () => { const { t } = useI18n() const client = useClient() const fluidTypes = useRecoilValue(fluidTypeState) @@ -38,10 +40,14 @@ const MonthlyReport = () => { compareValue: 0, percentageVariation: 0, }) + const [userChallenges, setUserChallenges] = useState<UserChallenge[] | null>( + null + ) const [isLoaded, setIsLoaded] = useState<boolean>(false) const consumptionService = new ConsumptionService(client) const performanceIndicatorService = new PerformanceIndicatorService() + const challengeService = new ChallengeService(client) const configService = new ConfigService() const fluidConfig = configService.getFluidConfig() const timeStep = TimeStep.MONTH @@ -53,25 +59,28 @@ const MonthlyReport = () => { useEffect(() => { let subscribed = true - async function populatePerformanceIndicators() { + async function populateData() { + const periods = { + timePeriod: { + startDate: reportDate.minus({ month: 1 }).startOf('month'), + endDate: reportDate.minus({ month: 1 }).endOf('month'), + }, + comparisonTimePeriod: { + startDate: reportDate.minus({ month: 2 }).startOf('month'), + endDate: reportDate.minus({ month: 2 }).endOf('month'), + }, + } + const fetchedPerformanceIndicators = await consumptionService.getPerformanceIndicators( + periods.timePeriod, + timeStep, + fluidTypes, + periods.comparisonTimePeriod + ) + const userChallengesDone = await challengeService.getFinishedUserChallengesOnMonth( + reportDate.minus({ month: 1 }) + ) + console.log(userChallengesDone) if (subscribed) { - const periods = { - timePeriod: { - startDate: reportDate.minus({ month: 1 }).startOf('month'), - endDate: reportDate.minus({ month: 1 }).endOf('month'), - }, - comparisonTimePeriod: { - startDate: reportDate.minus({ month: 2 }).startOf('month'), - endDate: reportDate.minus({ month: 2 }).endOf('month'), - }, - } - const fetchedPerformanceIndicators = await consumptionService.getPerformanceIndicators( - periods.timePeriod, - timeStep, - fluidTypes, - periods.comparisonTimePeriod - ) - if (fetchedPerformanceIndicators) { setPerformanceIndicators(fetchedPerformanceIndicators) setAggregatedPerformanceIndicators( @@ -80,10 +89,13 @@ const MonthlyReport = () => { ) ) } + if (userChallengesDone) { + setUserChallenges(userChallengesDone) + } + setIsLoaded(true) } - setIsLoaded(true) } - populatePerformanceIndicators() + populateData() return () => { subscribed = false } @@ -92,55 +104,75 @@ const MonthlyReport = () => { return ( <> {isLoaded ? ( - <div className="report-root"> - <div className="report-content"> - <DateNavigator - timeStep={timeStep} - date={reportDate} - handleClickMove={handleClickMove} - indexDisplayed={0} - offset={{ month: -1 }} - /> - <div className="report-header text-16-normal-uppercase"> - {t('report.reportDate')}{' '} - {convertDateToMonthString(reportDate.plus({ month: -1 }))} + <div> + <div className="report-root black"> + <div className="report-content"> + <DateNavigator + timeStep={timeStep} + date={reportDate} + handleClickMove={handleClickMove} + indexDisplayed={0} + offset={{ month: -1 }} + /> + <div> + <div className="report-header text-16-normal-uppercase"> + {t('report.reportDate')}{' '} + {convertDateToMonthString(reportDate.plus({ month: -1 }))} + </div> + <PerformanceIndicatorContent + performanceIndicator={aggregatedPerformanceIndicators} + timeStep={timeStep} + /> + <ChartReport + chartData={[ + { + date: reportDate.plus({ month: -2 }), + value: aggregatedPerformanceIndicators.compareValue + ? aggregatedPerformanceIndicators.compareValue + : -1, + }, + { + date: reportDate.plus({ month: -1 }), + value: aggregatedPerformanceIndicators.value + ? aggregatedPerformanceIndicators.value + : -1, + }, + ]} + /> + </div> </div> - <PerformanceIndicatorContent - performanceIndicator={aggregatedPerformanceIndicators} - timeStep={timeStep} - /> - <ChartReport - chartData={[ - { - date: reportDate.plus({ month: -2 }), - value: aggregatedPerformanceIndicators.compareValue - ? aggregatedPerformanceIndicators.compareValue - : -1, - }, - { - date: reportDate.plus({ month: -1 }), - value: aggregatedPerformanceIndicators.value - ? aggregatedPerformanceIndicators.value - : -1, - }, - ]} - /> - <div> - <span className="report-header text-16-normal-uppercase "> - {t('report.detail')} - {convertDateToMonthString(reportDate.plus({ month: -1 }))} - </span> - {fluidConfig.map((fluid, index) => { - return fluidTypes.includes(fluid.fluidTypeId) ? ( - <FluidPerformanceIndicator - key={index} - fluidType={fluid.fluidTypeId} - performanceIndicator={ - performanceIndicators[fluid.fluidTypeId] - } - /> - ) : null - })} + </div> + <div className="report-root"> + <div className="report-content"> + <div> + <div className="report-header text-16-normal-uppercase "> + {t('report.detail')} + {convertDateToMonthString(reportDate.plus({ month: -1 }))} + </div> + {fluidConfig.map((fluid, index) => { + return fluidTypes.includes(fluid.fluidTypeId) ? ( + <FluidPerformanceIndicator + key={index} + fluidType={fluid.fluidTypeId} + performanceIndicator={ + performanceIndicators[fluid.fluidTypeId] + } + /> + ) : null + })} + </div> + <div> + <div className="report-header text-16-normal-uppercase "> + {t('report.challenge') + + ' ' + + reportDate + .plus({ month: -1 }) + .toLocaleString({ locale: 'fr-FR', month: 'long' })} + </div> + {userChallenges && ( + <ChallengeReport userChallenges={userChallenges} /> + )} + </div> </div> </div> </div> diff --git a/src/components/Report/ReportView.tsx b/src/components/Report/ReportView.tsx index 90e3383235cbe4dccbdc1eecb883e25c7cfcf526..30c7b154d734ede145ffadb8876d746baf0774c5 100644 --- a/src/components/Report/ReportView.tsx +++ b/src/components/Report/ReportView.tsx @@ -57,7 +57,7 @@ const ReportView: React.FC = () => { setHeaderHeight={defineHeaderHeight} desktopTitleKey={'report.viewTitle'} ></Header> - <Content height={headerHeight} background="var(--darkLight2)"> + <Content height={headerHeight}> <MonthlyReport /> </Content> </> diff --git a/src/components/Report/challengereport.scss b/src/components/Report/challengereport.scss new file mode 100644 index 0000000000000000000000000000000000000000..fe485d3f382f769b669a47e000ff8efc3a1568a3 --- /dev/null +++ b/src/components/Report/challengereport.scss @@ -0,0 +1,40 @@ +@import '../../styles/base/color'; +@import '../../styles/base/breakpoint'; + + +.challenge-root { + margin-bottom: 1rem; + .challenge-card-link { + color: black; + text-decoration: none; + } + .challenge-card { + cursor: pointer; + display: flex; + flex-direction: row; + width: 100%; + .challenge-card-content-left { + flex: 1; + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + width: inherit; + .challenge-card-content-badge { + height: 4.5rem; + width: 4.5rem; + } + .challenge-card-content-title { + margin-left: 1rem; + color: $white; + } + } + .challenge-card-content-right { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + align-self: center; + } + } +} \ No newline at end of file diff --git a/src/components/Report/monthlyreport.scss b/src/components/Report/monthlyreport.scss index 3e68f64f12934b686576478930bbce0976cb7713..c45f49d574c4a61d81bb1bc99c167adb37c0fa6a 100644 --- a/src/components/Report/monthlyreport.scss +++ b/src/components/Report/monthlyreport.scss @@ -8,12 +8,16 @@ align-items: center; justify-content: center; padding: 0.5rem 1.5rem; + &.black{ + background: var(--darkLight2); + } @media #{$large-phone} { margin-bottom: 0; } .report-content { min-height: 23.875rem; width: 45.75rem; + @media #{$large-phone} { width: 100%; } diff --git a/src/locales/fr.json b/src/locales/fr.json index a17ac42c07898ffba3e39dbbfec7d3431704c41f..bb053805f30607170a61b8ebbcf9ca41d1ab0a92 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -310,6 +310,7 @@ "report": { "viewTitle": "Bilan", "reportDate": "Bilan du mois", - "detail": "Détail du mois" + "detail": "Détail du mois", + "challenge": "Défis terminés en" } } diff --git a/src/services/challenge.service.ts b/src/services/challenge.service.ts index 2cfd3e7c37734bb6aea226654a472245001519cf..16bb0f4d4cf40eb68cae8bc30707e24965bf2547 100644 --- a/src/services/challenge.service.ts +++ b/src/services/challenge.service.ts @@ -92,7 +92,9 @@ export default class ChallengeService { const maxEnergy = await this.getMaxEnergy(challenge, challengeFluidTypes) if (maxEnergy && maxEnergy > 0) { try { - await this.updateChallenge(challenge.id, { maxEnergy: maxEnergy }) + await this.updateChallenge(challenge.id, { + maxEnergy: maxEnergy, + }) return maxEnergy } catch (error) { console.log(error) @@ -150,7 +152,9 @@ export default class ChallengeService { ) if (spentEnergy && spentEnergy >= 0) { try { - await this.updateChallenge(challenge.id, { currentEnergy: spentEnergy }) + await this.updateChallenge(challenge.id, { + currentEnergy: spentEnergy, + }) return spentEnergy } catch (error) { @@ -506,6 +510,61 @@ export default class ChallengeService { return userChallenges } + public async getFinishedUserChallengesOnMonth( + date: DateTime + ): Promise<UserChallenge[]> { + try { + const startDate = date.startOf('month') + const endDate = date.endOf('month') + const resultUserChallenge = await this._client.query( + this._client + .find(USERCHALLENGE_DOCTYPE) + .include(['challengeType', 'selectedEcogestures']) + .where({ + state: { + $eq: ChallengeState.FINISHED, + }, + endingDate: { + $gte: startDate.toISO(), + $lte: endDate.toISO(), + }, + }) + .sortBy([{ endingDate: 'desc' }]) + ) + + if (resultUserChallenge && !resultUserChallenge.data[0]) { + return [] + } + + const userChallengeEntitites: + | UserChallengeEntity[] + | null = resultUserChallenge.data ? resultUserChallenge.data : null + + const userChallengeEntityRelationships: + | object + | null = resultUserChallenge.included + ? resultUserChallenge.included + : null + + if (!userChallengeEntitites || userChallengeEntitites.length === 0) + return [] + const userChallenges: UserChallenge[] = [] + + userChallengeEntitites.forEach(userChallengeEntitity => { + userChallenges.push( + this._challengeMapper.mapToUserChallenge( + userChallengeEntitity, + userChallengeEntityRelationships + ) + ) + }) + return userChallenges + } catch (error) { + console.log(error) + throw error + } + } + public async getCurrentChallenge( withEcogestures = true ): Promise<UserChallenge | null> { @@ -546,7 +605,9 @@ export default class ChallengeService { public async cancelChallenge(id: string): Promise<boolean> { try { - await this.updateChallenge(id, { state: ChallengeState.ABANDONED }) + await this.updateChallenge(id, { + state: ChallengeState.ABANDONED, + }) return true } catch (err) { console.log(err)