Skip to content

Commit

Permalink
feat: add recurring emissions #323
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreBresson committed Nov 28, 2021
1 parent e04beaf commit e85759d
Show file tree
Hide file tree
Showing 83 changed files with 1,382 additions and 549 deletions.
7 changes: 5 additions & 2 deletions app/components/EmissionListItem/EmissionListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { Ionicons } from "@expo/vector-icons";
import { FormattedNumber } from "react-native-globalize";

import { Colors } from "style";
import { Emission, EmissionType } from "interfaces";
import { RecurringEmission, Emission, EmissionType } from "interfaces";

import Text from "../Text";
import styles from "./EmissionListItem.styles";

interface EmissionListItemProps extends Partial<Emission> {
interface AnyEmission extends RecurringEmission, Emission {}

interface EmissionListItemProps extends Partial<AnyEmission> {
title: string;
co2value: number;
iconName: string;
Expand All @@ -18,6 +20,7 @@ interface EmissionListItemProps extends Partial<Emission> {
emissionType?: EmissionType;
value?: number;
creationDate?: string;
times?: number;
}

const EmissionListItem: React.FC<EmissionListItemProps> = ({
Expand Down
16 changes: 16 additions & 0 deletions app/constant/weekDays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { WeekDays } from "interfaces";

import { TranslationKeys } from "../utils/translations/resources";

export const WEEK_DAYS: {
dayIndex: number;
nameKey: keyof TranslationKeys;
}[] = [
{ dayIndex: WeekDays.monday, nameKey: "UI_MONDAY" },
{ dayIndex: WeekDays.tuesday, nameKey: "UI_TUESDAY" },
{ dayIndex: WeekDays.wednesday, nameKey: "UI_WEDNESDAY" },
{ dayIndex: WeekDays.thursday, nameKey: "UI_THURSDAY" },
{ dayIndex: WeekDays.friday, nameKey: "UI_FRIDAY" },
{ dayIndex: WeekDays.saturday, nameKey: "UI_SATURDAY" },
{ dayIndex: WeekDays.sunday, nameKey: "UI_SUNDAY" },
];
3 changes: 2 additions & 1 deletion app/ducks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import emissions from "./emissions";
import budget from "./budget";
import userPreferences from "./userPreferences";
import recurringEmissions from "./recurringEmissions";

export { emissions, budget, userPreferences };
export { emissions, recurringEmissions, budget, userPreferences };
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Emissions actions should export expected actions 1`] = `
Object {
"createRecurringEmission": [Function],
"deleteAllRecurringEmissions": [Function],
"deleteRecurringEmission": [Function],
"loadRecurringEmissionsFromImport": [Function],
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { TransportType, FoodType } from "carbon-footprint";

import { RecurringEmission, EmissionType, PeriodicityType, WeekDays } from "interfaces";

import recurringEmissions from "../";

describe("Emissions actions should", () => {
it("export expected actions", () => expect(recurringEmissions.actions).toMatchSnapshot());

it("be able to create a recurring emission", () => {
const recurringEmission: RecurringEmission = {
id: "123",
creationDate: "2020-01-26T11:04:55.334Z",
emissionType: EmissionType.transport,
emissionModelType: TransportType.boat,
value: 200,
periodType: PeriodicityType.monthly,
weekDays: [],
times: 1,
};

const expectedAction = {
type: recurringEmissions.actions.createRecurringEmission.toString(),
payload: recurringEmission,
};

expect(recurringEmissions.actions.createRecurringEmission(recurringEmission)).toEqual(
expectedAction
);
});

it("be able to delete a recurring emission", () => {
const expectedAction = {
type: recurringEmissions.actions.deleteRecurringEmission.toString(),
payload: "123",
};

expect(recurringEmissions.actions.deleteRecurringEmission("123")).toEqual(expectedAction);
});

it("be able to load emissions", () => {
const emissionsList: Array<RecurringEmission> = [
{
id: "1",
creationDate: "2020-01-26T11:04:55.334Z",
emissionType: EmissionType.transport,
emissionModelType: TransportType.boat,
value: 200,
periodType: PeriodicityType.monthly,
weekDays: [],
times: 1,
},
{
id: "2",
creationDate: "2020-01-26T11:04:55.334Z",
emissionType: EmissionType.food,
emissionModelType: FoodType.beans,
value: 200,
periodType: PeriodicityType.weekly,
weekDays: [WeekDays.friday],
times: 2,
},
];

const expectedAction = {
type: recurringEmissions.actions.loadRecurringEmissionsFromImport.toString(),
payload: emissionsList,
};

expect(recurringEmissions.actions.loadRecurringEmissionsFromImport(emissionsList)).toEqual(
expectedAction
);
});

it("be able to delete all recurring emissions", () => {
const expectedAction = {
type: recurringEmissions.actions.deleteAllRecurringEmissions.toString(),
payload: undefined,
};

expect(recurringEmissions.actions.deleteAllRecurringEmissions()).toEqual(expectedAction);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { FoodType } from "carbon-footprint";

import { RecurringEmission, EmissionType, PeriodicityType, WeekDays } from "interfaces";

import recurringEmissions from "../";

describe("RecurringEmissions reducer should", () => {
it("return the initial state", () => {
// TODO: fix eslint complains bellow
// eslint-disable-next-line
expect(recurringEmissions.reducer(undefined, {} as any)).toEqual([]);
});

it("handle recurring emission creation", () => {
const recurringEmission: RecurringEmission = {
id: "123",
creationDate: "2020-01-26T11:04:55.334Z",
emissionType: EmissionType.custom,
emissionModelType: FoodType.nuts,
value: 200,
periodType: PeriodicityType.monthly,
times: 1,
weekDays: [],
};
const action = {
type: recurringEmissions.actions.createRecurringEmission.toString(),
payload: recurringEmission,
};

expect(recurringEmissions.reducer(undefined, action)).toEqual([recurringEmission]);

expect(recurringEmissions.reducer([recurringEmission], action)).toEqual([
recurringEmission,
recurringEmission,
]);
});

it("handle emission delete", () => {
const recurringEmission: RecurringEmission = {
id: "123",
creationDate: "2020-01-26T11:04:55.334Z",
emissionType: EmissionType.custom,
emissionModelType: FoodType.nuts,
value: 200,
periodType: PeriodicityType.weekly,
weekDays: [WeekDays.friday],
times: 2,
};
const action = {
type: recurringEmissions.actions.deleteRecurringEmission.toString(),
payload: recurringEmission.id,
};

expect(recurringEmissions.reducer([recurringEmission], action)).toEqual([]);
});

it("handle delete all emissions", () => {
const emission: RecurringEmission = {
id: "123",
creationDate: "2020-01-26T11:04:55.334Z",
emissionType: EmissionType.custom,
emissionModelType: FoodType.nuts,
value: 200,
periodType: PeriodicityType.weekly,
weekDays: [WeekDays.friday],
times: 2,
};
const action = {
type: recurringEmissions.actions.deleteAllRecurringEmissions.toString(),
};

expect(recurringEmissions.reducer([emission], action)).toEqual([]);
});

it("load emissions", () => {
const recurringEmissionsList: Array<RecurringEmission> = [
{
id: "1",
creationDate: "2020-01-26T11:04:55.334Z",
emissionType: EmissionType.food,
emissionModelType: FoodType.beans,
value: 20,
periodType: PeriodicityType.monthly,
weekDays: [],
times: 2,
},
{
id: "2",
creationDate: "2020-02-26T11:04:55.334Z",
emissionType: EmissionType.food,
emissionModelType: FoodType.beef,
value: 100,
periodType: PeriodicityType.weekly,
weekDays: [WeekDays.friday],
times: 1,
},
];

const action = {
type: recurringEmissions.actions.loadRecurringEmissionsFromImport.toString(),
payload: recurringEmissionsList,
};

expect(recurringEmissions.reducer(undefined, action)).toEqual(recurringEmissionsList);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { FoodType, TransportType } from "carbon-footprint";

import { RecurringEmission, EmissionType, PeriodicityType, WeekDays } from "interfaces";

import recurringEmissions from "../";

let state;

const recurringEmissionFood: RecurringEmission = {
id: "1",
creationDate: "2020-01-26T11:04:55.334Z",
emissionModelType: FoodType.redMeat,
emissionType: EmissionType.food,
value: 10,
periodType: PeriodicityType.weekly,
weekDays: [WeekDays.friday],
times: 1,
};

const recurringEmissionTransport: RecurringEmission = {
id: "2",
creationDate: "2020-01-26T11:04:55.334Z",
emissionModelType: TransportType.bus,
emissionType: EmissionType.transport,
value: 13,
periodType: PeriodicityType.weekly,
weekDays: [WeekDays.friday],
times: 1,
};

describe("if there are recurring emissions", () => {
beforeEach(() => {
state = {
[recurringEmissions.namespace]: [recurringEmissionFood, recurringEmissionTransport],
};
});

test("`getRecurringEmissionById` should return recurring emission with corresponding id", () =>
expect(recurringEmissions.selectors.getRecurringEmissionById(state, "1")).toEqual(
recurringEmissionFood
));

test("`getAllRecurringEmissions` should return all recurring emissions", () =>
expect(recurringEmissions.selectors.getAllRecurringEmissions(state)).toEqual([
recurringEmissionFood,
recurringEmissionTransport,
]));
});

describe("if there are no recurring emissions", () => {
beforeEach(() => {
state = {
[recurringEmissions.namespace]: [],
};
});

test("`getEmissionById` should return no recurring emission", () =>
expect(recurringEmissions.selectors.getRecurringEmissionById(state, "1")).toEqual(undefined));

test("`getAllRecurringEmissions` should return no recurring emission", () =>
expect(recurringEmissions.selectors.getAllRecurringEmissions(state)).toEqual([]));
});
4 changes: 4 additions & 0 deletions app/ducks/recurringEmissions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { actions, namespace, reducer } from "./recurringEmissions.slice";
import selectors from "./recurringEmissions.selectors";

export default { actions, namespace, reducer, selectors };
13 changes: 13 additions & 0 deletions app/ducks/recurringEmissions/recurringEmissions.selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { propEq, find, pathOr } from "ramda";

import { namespace } from "./recurringEmissions.slice";

const getAllRecurringEmissions = pathOr([], [namespace]);

const getRecurringEmissionById = (state, id: string) => find(propEq("id", id))(state[namespace]);

export default {
getAllRecurringEmissions,
getRecurringEmissionById,
};
42 changes: 42 additions & 0 deletions app/ducks/recurringEmissions/recurringEmissions.slice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";

import { RecurringEmission } from "interfaces";

const initialState: RecurringEmission[] = [];

const recurringEmissions = createSlice({
name: "recurringEmissions",
initialState,
reducers: {
createRecurringEmission(state, action: PayloadAction<RecurringEmission>) {
state.push(action.payload);
},
deleteRecurringEmission(state, action: PayloadAction<string>) {
return (state = state.filter((item) => item.id !== action.payload));
},
deleteAllRecurringEmissions() {
return initialState;
},
loadRecurringEmissionsFromImport(state, action: PayloadAction<Array<RecurringEmission>>) {
return (state = action.payload);
},
},
});

const {
createRecurringEmission,
deleteRecurringEmission,
deleteAllRecurringEmissions,
loadRecurringEmissionsFromImport,
} = recurringEmissions.actions;

export const actions = {
createRecurringEmission,
deleteRecurringEmission,
deleteAllRecurringEmissions,
loadRecurringEmissionsFromImport,
};

export const namespace = recurringEmissions.name;

export const reducer = recurringEmissions.reducer;
7 changes: 1 addition & 6 deletions app/interfaces/emission/emission.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,4 @@ interface Emission extends EmissionPayload {
isMitigated: boolean;
}

enum PeriodicityType {
weekly = "weekly",
monthly = "monthly",
}

export { Emission, EmissionPayload, EmissionType, EmissionModelType, PeriodicityType };
export { Emission, EmissionPayload, EmissionType, EmissionModelType };
10 changes: 2 additions & 8 deletions app/interfaces/emission/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import {
Emission,
EmissionType,
EmissionPayload,
EmissionModelType,
PeriodicityType,
} from "./emission.interface";
import { Emission, EmissionType, EmissionPayload, EmissionModelType } from "./emission.interface";

export { Emission, EmissionType, EmissionPayload, EmissionModelType, PeriodicityType };
export { Emission, EmissionType, EmissionPayload, EmissionModelType };
Loading

0 comments on commit e85759d

Please sign in to comment.