Commits (21)
This diff is collapsed.
......@@ -3,7 +3,7 @@
"slug": "ecolyo",
"icon": "icon.svg",
"categories": ["energy"],
"version": "1.3.0",
"version": "1.4.0",
"licence": "AGPL-3.0",
"editor": "Métropole de Lyon",
"default_locale": "fr",
......
{
"name": "ecolyo",
"version": "1.4.0",
"version": "1.4.1",
"scripts": {
"tx": "tx pull --all || true",
"lint": "yarn lint:js && yarn lint:styles",
......
......@@ -82,7 +82,7 @@ const AnalysisConsumption: React.FC<AnalysisConsumptionProps> = ({
analysisDate.year
)
const monthlyForecast: MonthlyForecast = await profileTypeService.getMonthlyForecast(
analysisDate.month - 1
analysisDate.minus({ month: 1 }).startOf('month').month
)
if (subscribed) {
setForecast(monthlyForecast)
......
......@@ -49,11 +49,19 @@ const BarChart: React.FC<BarChartProps> = ({
const maxCompare = chartData.comparisonData
? Math.max(...chartData.comparisonData.map(d => d.value))
: 0
let max = chartData.actualData
const max = chartData.actualData
? Math.max(...chartData.actualData.map(d => d.value))
: 0
max = max <= 0 ? 15 : max
return showCompare ? Math.max(max, maxCompare) : max
if (showCompare) {
if (max <= 0 && maxCompare <= 0) return 15
else return Math.max(max, maxCompare)
} else {
if (max <= 0) {
return 15
} else return max
}
}
const xScale: ScaleBand<string> = scaleBand()
......
......@@ -24,7 +24,6 @@ const LastDataConsumptionVisualizer: React.FC<LastDataConsumptionVisualizerProps
)
const moveToDate = () => {
console.log(fluidType)
if (lastDataDate) {
const dateChartService = new DateChartService()
const updatedIndex: number = dateChartService.defineDateIndex(
......
......@@ -8,7 +8,7 @@ import { DateTime } from 'luxon'
import classNames from 'classnames'
import DateChartService from 'services/dateChart.service'
import { isLastDateReached, isLastPeriodReached } from 'utils/date'
import { isLastDateReached } from 'utils/date'
import DateNavigatorFormat from 'components/DateNavigator/DateNavigatorFormat'
import LeftArrowIcon from 'assets/icons/ico/left-arrow.svg'
......@@ -43,10 +43,7 @@ const DateNavigator: React.FC<DateNavigatorProps> = ({
const disableNext: boolean = currentAnalysisDate
? isLastDateReached(currentAnalysisDate, TimeStep.MONTH)
: isLastDateReached(selectedDate, currentTimeStep)
const disableNextSlide: boolean = isLastPeriodReached(
selectedDate,
currentTimeStep
)
const dateChartService = new DateChartService()
const handleClickMove = (increment: number) => {
......@@ -79,21 +76,21 @@ const DateNavigator: React.FC<DateNavigatorProps> = ({
selectedDate,
currentIndex
)
handleClickMove(increment)
if (currentAnalysisDate) {
handleClickMove(-1)
} else handleClickMove(increment)
}
}
const handleChangeNextIndex = () => {
console.log(!isKonnectorActive(fluidStatus, FluidType.MULTIFLUID))
if (
!disableNextSlide &&
isKonnectorActive(fluidStatus, FluidType.MULTIFLUID)
) {
if (!disableNext && isKonnectorActive(fluidStatus, FluidType.MULTIFLUID)) {
const increment: number = dateChartService.defineIncrementForNextIndex(
currentTimeStep,
selectedDate,
currentIndex
)
handleClickMove(increment)
if (currentAnalysisDate) {
handleClickMove(1)
} else handleClickMove(increment)
}
}
......
......@@ -15,7 +15,6 @@ import StyledSwitch from 'components/CommonKit/Switch/StyledSwitch'
import TimeStepSelector from 'components/TimeStepSelector/TimeStepSelector'
import ActivateHalfHourLoad from 'components/ActivateHalfHourLoad/ActivateHalfHourLoad'
import FluidChartSwipe from './FluidChartSwipe'
import ConsumptionVisualizer from 'components/ConsumptionVisualizer/ConsumptionVisualizer'
import { UsageEventType } from 'enum/usageEvent.enum'
import UsageEventService from 'services/usageEvent.service'
......
......@@ -37,6 +37,13 @@ const ConsumptionView: React.FC<ConsumptionViewProps> = ({
)
const [active, setActive] = useState<boolean>(false)
/* eslint-disable @typescript-eslint/no-non-null-assertion */
const lastDataDate =
fluidType !== FluidType.MULTIFLUID && fluidStatus[fluidType].lastDataDate
? fluidStatus[fluidType].lastDataDate!.toLocaleString()
: ''
const defineHeaderHeight = useCallback((height: number) => {
setHeaderHeight(height)
}, [])
......@@ -74,7 +81,11 @@ const ConsumptionView: React.FC<ConsumptionViewProps> = ({
['--hidden']: loading,
})}
>
<FluidChart fluidType={fluidType} setActive={setActive} />
<FluidChart
fluidType={fluidType}
setActive={setActive}
key={lastDataDate}
/>
<ConsumptionDetails fluidType={fluidType} />
</div>
{!isMulti && (
......
......@@ -5,7 +5,11 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import Lottie from 'react-lottie'
import * as loadingData from 'assets/anims/bounceloading.json'
import { ERROR_EVENT } from 'cozy-harvest-lib/dist/models/flowEvents'
import {
ERROR_EVENT,
SUCCESS_EVENT,
LOGIN_SUCCESS_EVENT,
} from 'cozy-harvest-lib/dist/models/flowEvents'
import { FluidType } from 'enum/fluid.enum'
import Icon from 'cozy-ui/transpiled/react/Icon'
......@@ -127,7 +131,33 @@ const KonnectorModal: React.FC<KonnectorModalProps> = ({
) : (
<>
<div className="kmodal-info">
{state === ERROR_EVENT ? (
{state === LOGIN_SUCCESS_EVENT && (
<>
<Lottie
options={loadingOptions}
height={50}
width={50}
speed={2}
/>
<div className="kc-wait text-16-bold">
{t(`konnector_modal.loading_data`)}
</div>
<div className="kmodal-waiting-text text-18-italic">
{shuffledWaitingTexts.map((text, idx) => (
<div
key={idx}
className={classNames('waiting-text', {
['show']: idx === index % shuffledWaitingTexts.length,
})}
>
<p>{text.first}</p>
<p>{text.second}</p>
</div>
))}
</div>
</>
)}
{state === ERROR_EVENT && (
<>
{error === 'LOGIN_FAILED' ? (
<div className="konnector-config">
......@@ -162,7 +192,8 @@ const KonnectorModal: React.FC<KonnectorModalProps> = ({
</div>
)}
</>
) : (
)}
{state === SUCCESS_EVENT && (
<div className="konnector-config">
<Icon icon={successIcon} size={48} />
<div className="kcs-picto-txt text-20-bold">
......@@ -175,16 +206,18 @@ const KonnectorModal: React.FC<KonnectorModalProps> = ({
)}
</div>
)}
<Button
aria-label={t('konnector_modal.accessibility.button_close')}
onClick={() => handleCloseClick()}
classes={{
root: 'btn-highlight',
label: 'text-16-bold',
}}
>
<div>{t('konnector_modal.button_validate')}</div>
</Button>
{state !== LOGIN_SUCCESS_EVENT && (
<Button
aria-label={t('konnector_modal.accessibility.button_close')}
onClick={() => handleCloseClick()}
classes={{
root: 'btn-highlight',
label: 'text-16-bold',
}}
>
<div>{t('konnector_modal.button_validate')}</div>
</Button>
)}
</div>
</>
)}
......
......@@ -33,7 +33,7 @@ import Icon from 'cozy-ui/transpiled/react/Icon'
import ExpansionPanel from '@material-ui/core/ExpansionPanel'
import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'
import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'
import failurePicto from 'assets/png/picto/picto-failure.png'
import ErrorNotif from 'assets/icons/ico/notif_error.svg'
import ConnectionNotFound from 'components/Connection/ConnectionNotFound'
import ConnectionForm from 'components/Connection/ConnectionForm'
import ConnectionResult from 'components/Connection/ConnectionResult'
......@@ -49,6 +49,7 @@ import {
import { DateTime } from 'luxon'
import { setSelectedDate } from 'store/chart/chart.actions'
import DateChartService from 'services/dateChart.service'
import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
interface KonnectorViewerCardProps {
fluidStatus: FluidStatus
......@@ -189,7 +190,11 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({
const getConnectionCard = useCallback(() => {
if (fluidState === FluidState.KONNECTOR_NOT_FOUND && !isUpdating) {
return <ConnectionNotFound konnectorSlug={fluidSlug} />
} else if (account && fluidState !== FluidState.ERROR_LOGIN_FAILED) {
} else if (
account &&
fluidState !== FluidState.ERROR_LOGIN_FAILED &&
fluidStatus.status !== FluidState.NOT_CONNECTED
) {
return (
<ConnectionResult
fluidStatus={fluidStatus}
......@@ -204,8 +209,9 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({
fluidState,
isUpdating,
account,
fluidSlug,
openModal,
fluidStatus,
fluidSlug,
handleAccountDeletion,
fluidType,
])
......@@ -313,12 +319,12 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({
) : (
<Icon icon={iconAddType} size={49} />
)}
{fluidStatus.status === FluidState.ERROR ? (
<img
{fluidStatus.status === FluidState.ERROR || isOutdatedData ? (
<StyledIcon
icon={ErrorNotif}
size={24}
className="konnector-state-picto"
src={failurePicto}
alt={t('konnector_options.accessibility.label_ko_status')}
></img>
/>
) : null}
</div>
<div
......
......@@ -173,7 +173,7 @@ const SplashRoot = ({
) {
dispatch(toggleChallengeExplorationNotification(true))
}
// Set Notification if action state is notification
// Set action to notifcation if action is accomplished
if (
filteredCurrentOngoingChallenge[0] &&
filteredCurrentOngoingChallenge[0].action.state ===
......@@ -185,9 +185,16 @@ const SplashRoot = ({
)
if (updatedUserChallenge) {
dispatch(updateUserChallengeList(updatedUserChallenge))
dispatch(toggleChallengeActionNotification(true))
}
}
// Set Notification if action state is notification
if (
filteredCurrentOngoingChallenge[0] &&
filteredCurrentOngoingChallenge[0].action.state ===
UserActionState.NOTIFICATION
) {
dispatch(toggleChallengeActionNotification(true))
}
const filteredCurrentDuelChallenge = userChallengeList.filter(
challenge => challenge.state === UserChallengeState.DUEL
)
......
......@@ -76,7 +76,6 @@ const TimeStepSelector: React.FC<TimeStepSelectorProps> = ({
dispatch(setCurrentTimeStep(targetTimestep))
dispatch(setCurrentIndex(index))
}
return (
<div className={'timestep-selector'}>
<Button
......@@ -89,7 +88,10 @@ const TimeStepSelector: React.FC<TimeStepSelectorProps> = ({
{t('timestep.today')}
</Button>
<div className={'timestep-container'}>
<ul className={'timestep-bar'}>
<ul
className={`timestep-bar ${fluidType === FluidType.ELECTRICITY &&
'elec-bar'}`}
>
{timeStepArray.map((step, key) => {
return (
<React.Fragment key={key}>
......
......@@ -25,12 +25,15 @@
width: 100%;
}
.timestep-bar {
margin: 0 2.344rem;
margin: 0 1rem 0 1.7rem;
padding: 0;
display: flex;
justify-content: space-evenly;
align-items: center;
height: 3rem;
&.elec-bar {
margin-left: 1rem;
}
.circle {
cursor: pointer;
position: relative;
......@@ -53,14 +56,17 @@
}
}
.text {
opacity: 0;
position: relative;
display: block;
top: 15px;
left: -20px;
text-align: center;
color: $grey-bright;
color: $grey-dark;
width: 50px;
overflow: visible;
@media only screen and (max-width: 355px) {
opacity: 0;
}
}
.bar {
width: 100%;
......@@ -81,7 +87,8 @@
height: 10px;
.text {
opacity: 1;
overflow: visible;
top: 16px;
color: white;
transition: 300ms ease;
}
}
......
......@@ -79,7 +79,7 @@ describe('TotalConsumption component', () => {
.find('.euro-value')
.first()
.text()
).toEqual('20,23')
).toEqual('20,38')
})
it('should format multifluid value', async () => {
const component = mount(
......
......@@ -27,7 +27,7 @@
Bonjour {{username}},
</mj-text>
{{#if consumptionTextExist }}
<mj-text color="white" font-weight="400" font-size="18px">Ce mois ci vous avez consommé {{{consumptionText}}} par rapport au mois de {{previousMonth}}. <br /><br /> Retrouvez le détail de vos consommations et plus d'informations dans votre bilan Ecolyo.</mj-text>
<mj-text color="white" font-weight="400" font-size="18px">Ce mois-ci vous avez consommé {{{consumptionText}}} par rapport au mois de {{previousMonth}}. <br /><br /> Retrouvez le détail de vos consommations et plus d'informations dans votre bilan Ecolyo.</mj-text>
{{/if}}
{{#unless consumptionTextExist }}
<mj-text color="white" font-weight="400" font-size="18px">Retrouvez le détail de vos consommations et plus d'informations dans votre bilan Ecolyo.</mj-text>
......
......@@ -35,14 +35,14 @@ jest.mock('./ecogesture.service', () => {
describe('Action Service', () => {
const actionService = new ActionService(mockClient)
it('shoud return the default actions', async () => {
it('should return the default actions', async () => {
mockgetAllEcogestures.mockResolvedValueOnce(AllEcogestureData)
mockgetEcogesturesByIds.mockResolvedValueOnce(defaultEcogestureData)
const result: Ecogesture[] = await actionService.getDefaultActions()
expect(result).toEqual(defaultEcogestureData)
})
it('shoud return the Available Action List', async () => {
it('should return the Available Action List', async () => {
mockgetAllUserChallengeEntities.mockResolvedValueOnce(userChallengeData)
mockgetAllEcogestures.mockResolvedValueOnce(AllEcogestureData)
mockgetEcogesturesByIds.mockResolvedValueOnce([
......@@ -58,7 +58,7 @@ describe('Action Service', () => {
AllEcogestureData[4],
])
})
it('shoud return winter ecogestures', () => {
it('should return winter ecogestures', () => {
jest.spyOn(utils, 'getSeason').mockReturnValueOnce(Season.WINTER)
const result: Ecogesture[] = actionService.filterBySeason(AllEcogestureData)
expect(result).toEqual([
......@@ -67,13 +67,13 @@ describe('Action Service', () => {
AllEcogestureData[5],
])
})
it('shoud return no season ecogestures', () => {
it('should return no season ecogestures', () => {
jest.spyOn(utils, 'getSeason').mockReturnValueOnce(Season.NONE)
const result: Ecogesture[] = actionService.filterBySeason(AllEcogestureData)
expect(result).toEqual([AllEcogestureData[3], AllEcogestureData[4]])
})
it('shoud return summer ecogestures', () => {
it('should return summer ecogestures', () => {
jest.spyOn(utils, 'getSeason').mockReturnValueOnce(Season.SUMMER)
const result: Ecogesture[] = actionService.filterBySeason(AllEcogestureData)
......@@ -84,7 +84,7 @@ describe('Action Service', () => {
])
})
it('shoud launch an action', () => {
it('should launch an action', () => {
jest
.spyOn(DateTime, 'local')
.mockReturnValueOnce(
......@@ -119,7 +119,7 @@ describe('Action Service', () => {
})
describe('getCustomActions function', () => {
it('shoud return filtered and sorted ecogestures and not complete the list', async () => {
it('should return filtered and sorted ecogestures and not complete the list', async () => {
jest.spyOn(utils, 'getSeason').mockReturnValueOnce(Season.WINTER)
mockgetAllUserChallengeEntities.mockResolvedValueOnce(userChallengeData)
mockgetAllEcogestures.mockResolvedValueOnce(AllEcogestureData)
......@@ -139,7 +139,7 @@ describe('Action Service', () => {
AllEcogestureData[2],
])
})
it('shoud return filtered and sorted ecogestures and complete the list with default ecogesture', async () => {
it('should return filtered and sorted ecogestures and complete the list with default ecogesture', async () => {
jest.spyOn(utils, 'getSeason').mockReturnValueOnce(Season.WINTER)
mockgetAllUserChallengeEntities.mockResolvedValueOnce(userChallengeData)
mockgetAllEcogestures.mockResolvedValueOnce(AllEcogestureData)
......
......@@ -35,7 +35,7 @@ describe('Connection service', () => {
const connectionService = new ConnectionService(mockClient)
describe('connectNewUser method', () => {
it('shoud return created Trigger', async () => {
it('should return created Trigger', async () => {
mockGetKonnector.mockResolvedValueOnce(konnectorsData[0])
mockCreateAccount.mockResolvedValueOnce(accountsData[0])
mockCreateTrigger.mockResolvedValueOnce(triggersData[0])
......@@ -48,7 +48,7 @@ describe('Connection service', () => {
expect(result).toEqual(mockResult)
})
it('shoud throw error when konnector is not found', async () => {
it('should throw error when konnector is not found', async () => {
let error
try {
await connectionService.connectNewUser(
......@@ -64,7 +64,7 @@ describe('Connection service', () => {
)
})
it('shoud throw error when account is not created', async () => {
it('should throw error when account is not created', async () => {
mockGetKonnector.mockResolvedValueOnce(konnectorsData[0])
let error
try {
......@@ -79,7 +79,7 @@ describe('Connection service', () => {
expect(error).toEqual(new Error(`Error during account creation`))
})
it('shoud throw error when trigger is not created', async () => {
it('should throw error when trigger is not created', async () => {
mockGetKonnector.mockResolvedValueOnce(konnectorsData[0])
mockCreateAccount.mockResolvedValueOnce(accountsData[0])
let error
......
......@@ -66,7 +66,7 @@ describe('Consumption service', () => {
)
expect(result).toBeNull()
})
it('shoud return a mapped data for one fluid', async () => {
it('should return a mapped data for one fluid', async () => {
mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual)
mockFetchFluidData.mockResolvedValueOnce(mockFetchDataComparison)
const mockResult = {
......@@ -126,21 +126,13 @@ describe('Consumption service', () => {
actualData: [
{
date: DateTime.fromISO('2020-10-01T00:00:00.000Z'),
value: 69.18029999999999,
valueDetail: [
45.127739999999996,
0.9048899999999999,
23.147669999999998,
],
value: 79.131171,
valueDetail: [45.478019999999994, 0.931161, 32.72199],
},
{
date: DateTime.fromISO('2020-10-02T00:00:00.000Z'),
value: 61.65554999999999,
valueDetail: [
40.21918999999999,
0.8064649999999999,
20.629894999999998,
],
value: 70.5240635,
valueDetail: [40.531369999999995, 0.8298785, 29.162815],
},
{
date: DateTime.fromISO('2020-10-03T00:00:00.000Z'),
......@@ -151,13 +143,13 @@ describe('Consumption service', () => {
comparisonData: [
{
date: DateTime.fromISO('2020-09-01T00:00:00.000Z'),
value: 54.090509999999995,
valueDetail: [35.284358, 0.707513, 18.098639],
value: 61.8708707,
valueDetail: [35.558234, 0.7280537, 25.584583],
},
{
date: DateTime.fromISO('2020-09-02T00:00:00.000Z'),
value: 56.57427,
valueDetail: [36.904565999999996, 0.740001, 18.929703],
value: 64.7118939,
valueDetail: [37.191018, 0.7614849, 26.759391],
},
{
date: DateTime.fromISO('2020-09-03T00:00:00.000Z'),
......@@ -180,21 +172,13 @@ describe('Consumption service', () => {
actualData: [
{
date: DateTime.fromISO('2020-10-01T00:00:00.000Z'),
value: 69.18029999999999,
valueDetail: [
45.127739999999996,
0.9048899999999999,
23.147669999999998,
],
value: 79.131171,
valueDetail: [45.478019999999994, 0.931161, 32.72199],
},
{
date: DateTime.fromISO('2020-10-02T00:00:00.000Z'),
value: 61.65554999999999,
valueDetail: [
40.21918999999999,
0.8064649999999999,
20.629894999999998,
],
value: 70.5240635,
valueDetail: [40.531369999999995, 0.8298785, 29.162815],
},
{
date: DateTime.fromISO('2020-10-03T00:00:00.000Z'),
......@@ -241,12 +225,12 @@ describe('Consumption service', () => {
})
})
describe('getMaxLoad method', () => {
it('shoud return the maxed value for a time period for the home', async () => {
it('should return the maxed value for a time period for the home', async () => {
for (let i = 0; i < fluidTypes.length; i++) {
mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual)
mockFetchFluidData.mockResolvedValueOnce(mockFetchDataComparison)
}
const expectedResult = 69.18029999999999
const expectedResult = 79.131171
const result = await consumptionDataManager.getMaxLoad(
mockTimePeriod,
TimeStep.DAY,
......@@ -256,7 +240,7 @@ describe('Consumption service', () => {
)
expect(result).toEqual(expectedResult)
})
it('shoud return the maxed value for a time period', async () => {
it('should return the maxed value for a time period', async () => {
const mockFluidTypes = [1]
const expectedResult = 63.1254
mockFetchFluidMaxData.mockResolvedValueOnce(expectedResult)
......@@ -271,7 +255,7 @@ describe('Consumption service', () => {
})
})
describe('getPerformanceIndicators method', () => {
it('shoud return the performance indicator', async () => {
it('should return the performance indicator', async () => {
mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual)
mockFetchFluidData.mockResolvedValueOnce(mockFetchDataComparison)
mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual)
......@@ -321,7 +305,7 @@ describe('Consumption service', () => {
})
})
describe('fetchLastDateData method', () => {
it('shoud return the latest date data of one fluid', async () => {
it('should return the latest date data of one fluid', async () => {
const mockFluidTypes = [0]
const expectedResult = DateTime.fromISO('2020-09-03T23:59:59.999Z')
mockGetLastDateData.mockResolvedValueOnce(
......@@ -332,7 +316,7 @@ describe('Consumption service', () => {
)
expect(result).toEqual(expectedResult)
})
it('shoud return the latest date data of multiple fluid', async () => {
it('should return the latest date data of multiple fluid', async () => {
const mockFluidTypes = [0, 2]
mockGetLastDateData.mockResolvedValueOnce(
DateTime.fromISO('2020-09-03T23:59:59.999Z')
......@@ -346,7 +330,7 @@ describe('Consumption service', () => {
)
expect(result).toEqual(expectedResult)
})
it('shoud return the latest date data of all fluids', async () => {
it('should return the latest date data of all fluids', async () => {
mockGetLastDateData.mockResolvedValueOnce(
DateTime.fromISO('2020-09-02T23:59:59.999Z')
)
......@@ -365,7 +349,7 @@ describe('Consumption service', () => {
})
})
describe('fetchAllLastDateData method', () => {
it('shoud return the latest date data of one fluid', async () => {
it('should return the latest date data of one fluid', async () => {
const mockFluidTypes = [0]
const expectedResult = [DateTime.fromISO('2020-09-03T23:59:59.999Z')]
mockGetLastDateData.mockResolvedValueOnce(
......@@ -376,7 +360,7 @@ describe('Consumption service', () => {
)
expect(result).toEqual(expectedResult)
})
it('shoud return the latest date data of All fluid', async () => {
it('should return the latest date data of All fluid', async () => {
mockGetLastDateData.mockResolvedValueOnce(
DateTime.fromISO('2020-09-02T23:59:59.999Z')
)
......@@ -398,7 +382,7 @@ describe('Consumption service', () => {
})
})
describe('checkDoctypeEntries method', () => {
it('shoud return a boolean if doctype are correct', async () => {
it('should return a boolean if doctype are correct', async () => {
let fluidType = 2
mockGetEntries.mockResolvedValueOnce({ data: [1] })
let result = await consumptionDataManager.checkDoctypeEntries(
......