diff --git a/doc/changelog.md b/doc/changelog.md index 1c38bb7fd..d710c5702 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -110,6 +110,7 @@ Syntax: `- short text describing the change _(Your Name)_` - Added Meeting Agenda&Notes for 11.03.2024 9:00 _(Markus Raab, Jannis)_ - Added Meeting Agenda&Notes for 25.03.2024 9:00 _(Markus Raab, Moritz)_ - Added Meeting Agenda&Notes for 02.04.2024 9:00 _(Markus Raab, Lukas)_ +- _()_ - Added Meeting Agenda&Notes for 08.04.2024 9:00 _(Markus Raab, Filip)_ - Add 'Christoph Schreiner' as team member _(Christoph Schreiner)_ - Migrate from Jest to Vitest, update Vite to v5, update Node to 20, .env should be .env.local _(Paul)_ @@ -128,6 +129,9 @@ Syntax: `- short text describing the change _(Your Name)_` - Improve documentation of schema.rs patch workflow. _(Jannis @horenso, Christoph @chr_schr)_ - Prevent propagating enft key on markdown editor _(Daniel Steinkogler)_ - Enable deletion of selected plants via DEL shortcut _(Daniel Steinkogler)_ +- _()_ +- Refactoring of timeline state and add unit tests _(Daniel Steinkogler)_ +- _()_ - Fix link to Plant image for Openstreetmap _(Andrei Dinu)_ - Add key combinations for map geometry _(Daniel Steinkogler)_ - Add documentation for adding a new field to an entity _(Christoph Schreiner)_ diff --git a/frontend/src/features/map_planning/components/timeline/TimelineDatePicker.test.tsx b/frontend/src/features/map_planning/components/timeline/TimelineDatePicker.test.tsx index 1758a8a34..30914d43b 100644 --- a/frontend/src/features/map_planning/components/timeline/TimelineDatePicker.test.tsx +++ b/frontend/src/features/map_planning/components/timeline/TimelineDatePicker.test.tsx @@ -1,8 +1,7 @@ import { render } from '@testing-library/react'; import ReactTestUtils, { act } from 'react-dom/test-utils'; import { createDays, createYearsAndMonths } from '../../hooks/useGetTimelineData'; -import useMapStore from '../../store/MapStore'; -import { UNTRACKED_DEFAULT_STATE, UntrackedMapSlice } from '../../store/MapStoreTypes'; +import { useTimelineStore } from '../../store/TimeLineStore'; import TimelineDatePicker from './TimelineDatePicker'; const onSelectChange = vi.fn(); @@ -184,21 +183,18 @@ describe('handleYearItemChange', () => { }); function setupTimeline() { - useMapStore.setState(createStoreWithTimelineData()); + useTimelineStore.setState(createStoreWithTimelineData()); } -function createStoreWithTimelineData(): Pick { +function createStoreWithTimelineData() { const { years, months } = createYearsAndMonths(2000, 2021, {}, {}); const days = createDays(2000, 2021, {}); return { - untrackedState: { - ...UNTRACKED_DEFAULT_STATE, - timeLineEvents: { - yearly: years, - monthly: months, - daily: days, - }, + timeLineEvents: { + yearly: years, + monthly: months, + daily: days, }, }; } diff --git a/frontend/src/features/map_planning/components/timeline/TimelineDatePicker.tsx b/frontend/src/features/map_planning/components/timeline/TimelineDatePicker.tsx index d8ad0b9e5..93d8e3fe2 100644 --- a/frontend/src/features/map_planning/components/timeline/TimelineDatePicker.tsx +++ b/frontend/src/features/map_planning/components/timeline/TimelineDatePicker.tsx @@ -1,11 +1,11 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import useMapStore from '../../store/MapStore'; import { TimelineDailyEvent, TimelineMonthlyEvent, TimelineYearlyEvent, -} from '../../store/MapStoreTypes'; + useTimelineStore, +} from '../../store/TimeLineStore'; import { getShortMonthNameFromNumber } from '../../utils/date-utils'; import ItemSliderPicker from './ItemSliderPicker'; @@ -33,9 +33,9 @@ type TimelineDatePickerProps = { const TimelineDatePicker = ({ onSelectDate, onLoading, defaultDate }: TimelineDatePickerProps) => { const { i18n } = useTranslation(); - const daySliderItems = useMapStore((state) => state.untrackedState.timeLineEvents.daily); - const monthSliderItems = useMapStore((state) => state.untrackedState.timeLineEvents.monthly); - const yearSliderItems = useMapStore((state) => state.untrackedState.timeLineEvents.yearly); + const daySliderItems = useTimelineStore((state) => state.timeLineEvents.daily); + const monthSliderItems = useTimelineStore((state) => state.timeLineEvents.monthly); + const yearSliderItems = useTimelineStore((state) => state.timeLineEvents.yearly); const defaulttDateObject = new Date(defaultDate); const defaultYear = defaulttDateObject.getFullYear(); @@ -257,14 +257,11 @@ const TimelineDatePicker = ({ onSelectDate, onLoading, defaultDate }: TimelineDa }; const updateTimeLineVisibleYears = (from: number, to: number) => { - useMapStore.setState((state) => ({ + useTimelineStore.setState((state) => ({ ...state, - untrackedState: { - ...state.untrackedState, - timeLineVisibleYears: { - from, - to, - }, + timeLineVisibleYears: { + from, + to, }, })); }; @@ -330,7 +327,7 @@ const TimelineDatePicker = ({ onSelectDate, onLoading, defaultDate }: TimelineDa }, [selectedDayItem]); const updateVisibleYears = useCallback(() => { - const timeLineVisibleYears = useMapStore.getState().untrackedState.timeLineVisibleYears; + const timeLineVisibleYears = useTimelineStore.getState().timeLineVisibleYears; if (yearLeftBoundReached.current) { updateTimeLineVisibleYears(timeLineVisibleYears.from - 100, timeLineVisibleYears.to - 100); yearLeftBoundReached.current = false; diff --git a/frontend/src/features/map_planning/layers/plant/actions.ts b/frontend/src/features/map_planning/layers/plant/actions.ts index 55b5c2dfb..b18af809f 100644 --- a/frontend/src/features/map_planning/layers/plant/actions.ts +++ b/frontend/src/features/map_planning/layers/plant/actions.ts @@ -23,12 +23,7 @@ import updateAddDatePlanting, { } from '../../api/plantingApi'; import useMapStore from '../../store/MapStore'; import { Action, TrackedMapState } from '../../store/MapStoreTypes'; -import { - decreaseAddedPlantsForDate, - increaseAddedPlantsForDate, - timlineEventsUpdateAdedDate, - timlineEventsUpdateRemoveDate, -} from '../../utils/TimelineEventsHelper'; +import { useTimelineStore } from '../../store/TimeLineStore'; import { filterVisibleObjects } from '../../utils/filterVisibleObjects'; export class CreatePlantAction @@ -53,7 +48,7 @@ export class CreatePlantAction apply(state: TrackedMapState): TrackedMapState { const timelineDate = useMapStore.getState().untrackedState.timelineDate; - increaseAddedPlantsForDate(this._data[0].addDate || timelineDate); + useTimelineStore.getState().increaseAddedEventsForDate(this._data[0].addDate || timelineDate); return { ...state, @@ -108,7 +103,10 @@ export class DeletePlantAction (obj) => obj.id === deleteActionPayload.id, ); if (plant?.addDate) { - decreaseAddedPlantsForDate(plant.addDate); + useTimelineStore.getState().decreaseAddedEventsForDate(plant.addDate); + } + if (plant?.removeDate) { + useTimelineStore.getState().decreaseRemovedEventsForDate(plant.removeDate); } } @@ -313,7 +311,9 @@ export class UpdateAddDatePlantAction (obj) => obj.id === addDateActionPayload.id, ); if (plant?.addDate && addDateActionPayload.addDate) { - timlineEventsUpdateAdedDate(plant.addDate, addDateActionPayload.addDate); + useTimelineStore + .getState() + .timelineEventsUpdateAddedDate(plant.addDate, addDateActionPayload.addDate); } } @@ -453,7 +453,10 @@ export class UpdateRemoveDatePlantAction const plant = state.layers.plants.loadedObjects.find( (obj) => obj.id === removeDateActionPayload.id, ); - timlineEventsUpdateRemoveDate(plant?.removeDate, removeDateActionPayload.removeDate); + + useTimelineStore + .getState() + .timelineEventsUpdateRemoveDate(plant?.removeDate, removeDateActionPayload.removeDate); } return { diff --git a/frontend/src/features/map_planning/routes/MapWrapper.tsx b/frontend/src/features/map_planning/routes/MapWrapper.tsx index d0b51ff68..a0983a87b 100644 --- a/frontend/src/features/map_planning/routes/MapWrapper.tsx +++ b/frontend/src/features/map_planning/routes/MapWrapper.tsx @@ -20,6 +20,7 @@ import useGetTimeLineData from '../hooks/useGetTimelineData'; import { useMapId } from '../hooks/useMapId'; import useMapStore from '../store/MapStore'; import { handleRemoteAction } from '../store/RemoteActions'; +import { useTimelineStore } from '../store/TimeLineStore'; import { mapEditorSteps, tourOptions } from '../utils/EditorTour'; import { ReadOnlyModeContextProvider } from '../utils/ReadOnlyModeContext'; @@ -31,7 +32,7 @@ function getDefaultLayer(layerType: LayerType, layers?: LayerDto[]) { } const useInitializeTimeline = (mapId: number) => { - const timeLineVisibleYears = useMapStore((state) => state.untrackedState.timeLineVisibleYears); + const timeLineVisibleYears = useTimelineStore((state) => state.timeLineVisibleYears); const timeLineEvents = useGetTimeLineData( mapId, timeLineVisibleYears.from, @@ -40,15 +41,12 @@ const useInitializeTimeline = (mapId: number) => { useEffect(() => { if (timeLineEvents) { - useMapStore.setState((state) => ({ + useTimelineStore.setState((state) => ({ ...state, - untrackedState: { - ...state.untrackedState, - timeLineEvents: { - daily: timeLineEvents.daily, - monthly: timeLineEvents.monthly, - yearly: timeLineEvents.yearly, - }, + timeLineEvents: { + daily: timeLineEvents.daily, + monthly: timeLineEvents.monthly, + yearly: timeLineEvents.yearly, }, })); } diff --git a/frontend/src/features/map_planning/store/MapStoreTypes.ts b/frontend/src/features/map_planning/store/MapStoreTypes.ts index dd35086b1..399c1c393 100644 --- a/frontend/src/features/map_planning/store/MapStoreTypes.ts +++ b/frontend/src/features/map_planning/store/MapStoreTypes.ts @@ -317,15 +317,6 @@ export const UNTRACKED_DEFAULT_STATE: UntrackedMapState = { }), {} as UntrackedLayers, ), - timeLineEvents: { - daily: [], - monthly: [], - yearly: [], - }, - timeLineVisibleYears: { - from: new Date().getFullYear() - 100, - to: new Date().getFullYear() + 100, - }, }; /** @@ -394,36 +385,6 @@ export type TrackedPlantLayerState = { loadedObjects: PlantingDto[]; }; -export type TimelineDailyEvent = { - key: number; - year: number; - month: number; - day: number; - added: number; - removed: number; -}; - -export type TimelineMonthlyEvent = { - key: number; - year: number; - month: number; - added: number; - removed: number; -}; - -export type TimelineYearlyEvent = { - key: number; - year: number; - added: number; - removed: number; -}; - -export type TimeLineEvents = { - daily: TimelineDailyEvent[]; - monthly: TimelineMonthlyEvent[]; - yearly: TimelineYearlyEvent[]; -}; - export type TrackedBaseLayerState = { id: number; layerId: number; @@ -521,11 +482,6 @@ export type UntrackedMapState = { tooltipPosition: { x: number; y: number }; bottomStatusPanelContent: React.ReactNode | null; layers: UntrackedLayers; - timeLineEvents: TimeLineEvents; - timeLineVisibleYears: { - from: number; - to: number; - }; }; /** diff --git a/frontend/src/features/map_planning/store/TimeLineStore.test.ts b/frontend/src/features/map_planning/store/TimeLineStore.test.ts new file mode 100644 index 000000000..bf1de351a --- /dev/null +++ b/frontend/src/features/map_planning/store/TimeLineStore.test.ts @@ -0,0 +1,299 @@ +import { useTimelineStore } from './TimeLineStore'; + +// mock the axios api configuration, so that we don't actually send requests to the backend +vi.mock('@/config/axios'); + +describe('TimeLineStore', () => { + it('increase added events from store', () => { + initializeTimeLineEvents(); + + useTimelineStore.getState().increaseAddedEventsForDate('2022-01-01'); + const timeLineState = useTimelineStore.getState(); + + // Check that the added events for 2022-01-01 have been increased + expect(timeLineState.timeLineEvents.daily[0].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.daily[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.daily[0].added).toEqual(1); + + expect(timeLineState.timeLineEvents.monthly[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.monthly[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.monthly[0].added).toEqual(1); + + expect(timeLineState.timeLineEvents.yearly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.yearly[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.yearly[0].added).toEqual(1); + + // Check that the added events for 2023-01-01 have not been changed + expect(timeLineState.timeLineEvents.daily[1].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.daily[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.daily[1].added).toEqual(10); + + expect(timeLineState.timeLineEvents.monthly[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.monthly[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.monthly[1].added).toEqual(10); + + expect(timeLineState.timeLineEvents.yearly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.yearly[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.yearly[1].added).toEqual(10); + }); + + it('increase removed events from store', () => { + initializeTimeLineEvents(); + + useTimelineStore.getState().increaseRemovedEventsForDate('2022-01-01'); + const timeLineState = useTimelineStore.getState(); + + // Check that the added events for 2022-01-01 have been increased + expect(timeLineState.timeLineEvents.daily[0].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.daily[0].removed).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].added).toEqual(0); + + expect(timeLineState.timeLineEvents.monthly[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.monthly[0].removed).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[0].added).toEqual(0); + + expect(timeLineState.timeLineEvents.yearly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.yearly[0].removed).toEqual(1); + expect(timeLineState.timeLineEvents.yearly[0].added).toEqual(0); + + // Check that the added events for 2023-01-01 have not been changed + expect(timeLineState.timeLineEvents.daily[1].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.daily[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.daily[1].added).toEqual(10); + + expect(timeLineState.timeLineEvents.monthly[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.monthly[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.monthly[1].added).toEqual(10); + + expect(timeLineState.timeLineEvents.yearly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.yearly[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.yearly[1].added).toEqual(10); + }); + + it('decrease added events from store', () => { + initializeTimeLineEvents(); + + useTimelineStore.getState().decreaseAddedEventsForDate('2023-01-01'); + const timeLineState = useTimelineStore.getState(); + + // Check that the added events for 2023-01-01 have been decreased + expect(timeLineState.timeLineEvents.daily[1].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.daily[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.daily[1].added).toEqual(9); + + expect(timeLineState.timeLineEvents.monthly[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.monthly[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.monthly[1].added).toEqual(9); + + expect(timeLineState.timeLineEvents.yearly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.yearly[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.yearly[1].added).toEqual(9); + + // Check that the added events for 2022-01-01 has not been changed + expect(timeLineState.timeLineEvents.daily[0].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.daily[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.daily[0].added).toEqual(0); + + expect(timeLineState.timeLineEvents.monthly[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.monthly[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.monthly[0].added).toEqual(0); + + expect(timeLineState.timeLineEvents.yearly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.yearly[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.yearly[0].added).toEqual(0); + }); + + it('decrease removed events from store', () => { + initializeTimeLineEvents(); + + useTimelineStore.getState().decreaseRemovedEventsForDate('2023-01-01'); + const timeLineState = useTimelineStore.getState(); + + // Check that the added events for 2023-01-01 have been decreased + expect(timeLineState.timeLineEvents.daily[1].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.daily[1].removed).toEqual(9); + expect(timeLineState.timeLineEvents.daily[1].added).toEqual(10); + + expect(timeLineState.timeLineEvents.monthly[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.monthly[1].removed).toEqual(9); + expect(timeLineState.timeLineEvents.monthly[1].added).toEqual(10); + + expect(timeLineState.timeLineEvents.yearly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.yearly[1].removed).toEqual(9); + expect(timeLineState.timeLineEvents.yearly[1].added).toEqual(10); + + // Check that the added events for 2022-01-01 has not been changed + expect(timeLineState.timeLineEvents.daily[0].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.daily[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.daily[0].added).toEqual(0); + + expect(timeLineState.timeLineEvents.monthly[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.monthly[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.monthly[0].added).toEqual(0); + + expect(timeLineState.timeLineEvents.yearly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.yearly[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.yearly[0].added).toEqual(0); + }); + + it('update added date', () => { + initializeTimeLineEvents(); + + useTimelineStore.getState().timelineEventsUpdateAddedDate('2023-01-01', '2022-01-01'); + const timeLineState = useTimelineStore.getState(); + + // Check that the added events for 2023-01-01 have been decreased + expect(timeLineState.timeLineEvents.daily[1].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.daily[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.daily[1].added).toEqual(9); + + expect(timeLineState.timeLineEvents.monthly[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.monthly[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.monthly[1].added).toEqual(9); + + expect(timeLineState.timeLineEvents.yearly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.yearly[1].removed).toEqual(10); + expect(timeLineState.timeLineEvents.yearly[1].added).toEqual(9); + + // Check that the added events for 2022-01-01 have been increased + expect(timeLineState.timeLineEvents.daily[0].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.daily[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.daily[0].added).toEqual(1); + + expect(timeLineState.timeLineEvents.monthly[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.monthly[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.monthly[0].added).toEqual(1); + + expect(timeLineState.timeLineEvents.yearly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.yearly[0].removed).toEqual(0); + expect(timeLineState.timeLineEvents.yearly[0].added).toEqual(1); + }); +}); + +it('update removed date', () => { + initializeTimeLineEvents(); + + useTimelineStore.getState().timelineEventsUpdateRemoveDate('2023-01-01', '2022-01-01'); + const timeLineState = useTimelineStore.getState(); + + // Check that the added events for 2023-01-01 have been decreased + expect(timeLineState.timeLineEvents.daily[1].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.daily[1].removed).toEqual(9); + expect(timeLineState.timeLineEvents.daily[1].added).toEqual(10); + + expect(timeLineState.timeLineEvents.monthly[1].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.monthly[1].removed).toEqual(9); + expect(timeLineState.timeLineEvents.monthly[1].added).toEqual(10); + + expect(timeLineState.timeLineEvents.yearly[1].year).toEqual(2023); + expect(timeLineState.timeLineEvents.yearly[1].removed).toEqual(9); + expect(timeLineState.timeLineEvents.yearly[1].added).toEqual(10); + + // Check that the added events for 2022-01-01 have been increased + expect(timeLineState.timeLineEvents.daily[0].day).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.daily[0].removed).toEqual(1); + expect(timeLineState.timeLineEvents.daily[0].added).toEqual(0); + + expect(timeLineState.timeLineEvents.monthly[0].month).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.monthly[0].removed).toEqual(1); + expect(timeLineState.timeLineEvents.monthly[0].added).toEqual(0); + + expect(timeLineState.timeLineEvents.yearly[0].year).toEqual(2022); + expect(timeLineState.timeLineEvents.yearly[0].removed).toEqual(1); + expect(timeLineState.timeLineEvents.yearly[0].added).toEqual(0); +}); + +function initializeTimeLineEvents() { + useTimelineStore.setState( + (state) => ({ + ...state, + timeLineEvents: { + daily: [ + { + key: 1, + day: 1, + month: 1, + year: 2022, + added: 0, + removed: 0, + }, + { + key: 2, + day: 1, + month: 1, + year: 2023, + added: 10, + removed: 10, + }, + ], + monthly: [ + { + key: 11, + month: 1, + year: 2022, + added: 0, + removed: 0, + }, + { + key: 12, + month: 1, + year: 2023, + added: 10, + removed: 10, + }, + ], + yearly: [ + { + key: 111, + year: 2022, + added: 0, + removed: 0, + }, + { + key: 112, + year: 2023, + added: 10, + removed: 10, + }, + ], + }, + }), + false, + ); +} diff --git a/frontend/src/features/map_planning/store/TimeLineStore.ts b/frontend/src/features/map_planning/store/TimeLineStore.ts new file mode 100644 index 000000000..9b18b6e39 --- /dev/null +++ b/frontend/src/features/map_planning/store/TimeLineStore.ts @@ -0,0 +1,187 @@ +import { create } from 'zustand'; + +export type TimelineDailyEvent = { + key: number; + year: number; + month: number; + day: number; + added: number; + removed: number; +}; + +export type TimelineMonthlyEvent = { + key: number; + year: number; + month: number; + added: number; + removed: number; +}; + +export type TimelineYearlyEvent = { + key: number; + year: number; + added: number; + removed: number; +}; + +export type TimeLineEvents = { + daily: TimelineDailyEvent[]; + monthly: TimelineMonthlyEvent[]; + yearly: TimelineYearlyEvent[]; +}; + +interface TimelineState { + timeLineEvents: TimeLineEvents; + timeLineVisibleYears: { + from: number; + to: number; + }; + decreaseRemovedEventsForDate: (date: string) => void; + increaseRemovedEventsForDate: (date: string) => void; + decreaseAddedEventsForDate: (date: string) => void; + increaseAddedEventsForDate: (date: string) => void; + timelineEventsUpdateRemoveDate: (oldRemoveDate?: string, newRemoveDate?: string) => void; + timelineEventsUpdateAddedDate: (oldAddedDate: string, newAddedDate: string) => void; +} + +export const useTimelineStore = create((set, get) => { + return { + timeLineEvents: { + daily: [], + monthly: [], + yearly: [], + }, + timeLineVisibleYears: { + from: new Date().getFullYear() - 100, + to: new Date().getFullYear() + 100, + }, + decreaseAddedEventsForDate: (date: string) => { + const parsedDate = new Date(date); + const day = parsedDate.getDate(); + const month = parsedDate.getMonth() + 1; + const year = parsedDate.getFullYear(); + + set((state) => ({ + timeLineEvents: { + ...state.timeLineEvents, + yearly: state.timeLineEvents.yearly.map((yearItem) => { + if (yearItem.year === year) { + return { ...yearItem, added: yearItem.added - 1 }; + } + return yearItem; + }), + monthly: state.timeLineEvents.monthly.map((monthItem) => { + if (monthItem.year === year && monthItem.month === month) { + return { ...monthItem, added: monthItem.added - 1 }; + } + return monthItem; + }), + daily: state.timeLineEvents.daily.map((dayItem) => { + if (dayItem.year === year && dayItem.month === month && dayItem.day === day) { + return { ...dayItem, added: dayItem.added - 1 }; + } + return dayItem; + }), + }, + })); + }, + increaseAddedEventsForDate: (date: string) => { + const parsedDate = new Date(date); + const day = parsedDate.getDate(); + const month = parsedDate.getMonth() + 1; + const year = parsedDate.getFullYear(); + + set((state) => ({ + timeLineEvents: { + ...state.timeLineEvents, + yearly: state.timeLineEvents.yearly.map((yearItem) => { + if (yearItem.year === year) { + return { ...yearItem, added: yearItem.added + 1 }; + } + return yearItem; + }), + monthly: state.timeLineEvents.monthly.map((monthItem) => { + if (monthItem.year === year && monthItem.month === month) { + return { ...monthItem, added: monthItem.added + 1 }; + } + return monthItem; + }), + daily: state.timeLineEvents.daily.map((dayItem) => { + if (dayItem.year === year && dayItem.month === month && dayItem.day === day) { + return { ...dayItem, added: dayItem.added + 1 }; + } + return dayItem; + }), + }, + })); + }, + decreaseRemovedEventsForDate: (date: string) => { + const parsedDate = new Date(date); + const day = parsedDate.getDate(); + const month = parsedDate.getMonth() + 1; + const year = parsedDate.getFullYear(); + + set((state) => ({ + timeLineEvents: { + ...state.timeLineEvents, + yearly: state.timeLineEvents.yearly.map((yearItem) => { + if (yearItem.year === year) { + return { ...yearItem, removed: yearItem.removed - 1 }; + } + return yearItem; + }), + monthly: state.timeLineEvents.monthly.map((monthItem) => { + if (monthItem.year === year && monthItem.month === month) { + return { ...monthItem, removed: monthItem.removed - 1 }; + } + return monthItem; + }), + daily: state.timeLineEvents.daily.map((dayItem) => { + if (dayItem.year === year && dayItem.month === month && dayItem.day === day) { + return { ...dayItem, removed: dayItem.removed - 1 }; + } + return dayItem; + }), + }, + })); + }, + increaseRemovedEventsForDate: (date: string) => { + const parsedDate = new Date(date); + const day = parsedDate.getDate(); + const month = parsedDate.getMonth() + 1; + const year = parsedDate.getFullYear(); + + set((state) => ({ + timeLineEvents: { + ...state.timeLineEvents, + yearly: state.timeLineEvents.yearly.map((yearItem) => { + if (yearItem.year === year) { + return { ...yearItem, removed: yearItem.removed + 1 }; + } + return yearItem; + }), + monthly: state.timeLineEvents.monthly.map((monthItem) => { + if (monthItem.year === year && monthItem.month === month) { + return { ...monthItem, removed: monthItem.removed + 1 }; + } + return monthItem; + }), + daily: state.timeLineEvents.daily.map((dayItem) => { + if (dayItem.year === year && dayItem.month === month && dayItem.day === day) { + return { ...dayItem, removed: dayItem.removed + 1 }; + } + return dayItem; + }), + }, + })); + }, + timelineEventsUpdateAddedDate: (oldAddedDate: string, newAddedDate: string) => { + get().decreaseAddedEventsForDate(oldAddedDate); + get().increaseAddedEventsForDate(newAddedDate); + }, + timelineEventsUpdateRemoveDate: (oldRemoveDate?: string, newRemoveDate?: string) => { + if (oldRemoveDate) get().decreaseRemovedEventsForDate(oldRemoveDate); + if (newRemoveDate) get().increaseRemovedEventsForDate(newRemoveDate); + }, + }; +}); diff --git a/frontend/src/features/map_planning/utils/TimelineEventsHelper.ts b/frontend/src/features/map_planning/utils/TimelineEventsHelper.ts deleted file mode 100644 index be1f86569..000000000 --- a/frontend/src/features/map_planning/utils/TimelineEventsHelper.ts +++ /dev/null @@ -1,174 +0,0 @@ -import useMapStore from '../store/MapStore'; - -export function timlineEventsUpdateRemoveDate(oldRemoveDate?: string, newRemoveDate?: string) { - if (oldRemoveDate) decreaseRemovedPlantsForDate(oldRemoveDate); - if (newRemoveDate) increaseRemovedPlantsForDate(newRemoveDate); -} - -export function timlineEventsUpdateAdedDate(oldAddedDate: string, newAddedDate: string) { - decreaseAddedPlantsForDate(oldAddedDate); - increaseAddedPlantsForDate(newAddedDate); -} - -export function decreaseRemovedPlantsForDate(date: string) { - const parsedDate = new Date(date); - const day = parsedDate.getDate(); - const month = parsedDate.getMonth() + 1; - const year = parsedDate.getFullYear(); - - const timelineEvents = useMapStore.getState().untrackedState.timeLineEvents; - - const updatedYearly = timelineEvents.yearly.map((yearItem) => { - if (yearItem.year === year) { - return { ...yearItem, removed: yearItem.removed - 1 }; - } - return yearItem; - }); - - const updatedMonthly = timelineEvents.monthly.map((monthItem) => { - if (monthItem.year === year && monthItem.month === month) { - return { ...monthItem, removed: monthItem.removed - 1 }; - } - return monthItem; - }); - - const updatedDaily = timelineEvents.daily.map((dayItem) => { - if (dayItem.year === year && dayItem.month === month && dayItem.day === day) { - return { ...dayItem, removed: dayItem.removed - 1 }; - } - return dayItem; - }); - - useMapStore.setState({ - untrackedState: { - ...useMapStore.getState().untrackedState, - timeLineEvents: { - yearly: updatedYearly, - monthly: updatedMonthly, - daily: updatedDaily, - }, - }, - }); -} - -export function increaseRemovedPlantsForDate(date: string) { - const parsedDate = new Date(date); - const day = parsedDate.getDate(); - const month = parsedDate.getMonth() + 1; - const year = parsedDate.getFullYear(); - - const timelineEvents = useMapStore.getState().untrackedState.timeLineEvents; - - const updatedYearly = timelineEvents.yearly.map((yearItem) => { - if (yearItem.year === year) { - return { ...yearItem, removed: yearItem.removed + 1 }; - } - return yearItem; - }); - - const updatedMonthly = timelineEvents.monthly.map((monthItem) => { - if (monthItem.year === year && monthItem.month === month) { - return { ...monthItem, removed: monthItem.removed + 1 }; - } - return monthItem; - }); - - const updatedDaily = timelineEvents.daily.map((dayItem) => { - if (dayItem.year === year && dayItem.month === month && dayItem.day === day) { - return { ...dayItem, removed: dayItem.removed + 1 }; - } - return dayItem; - }); - - useMapStore.setState({ - untrackedState: { - ...useMapStore.getState().untrackedState, - timeLineEvents: { - yearly: updatedYearly, - monthly: updatedMonthly, - daily: updatedDaily, - }, - }, - }); -} -export function increaseAddedPlantsForDate(date: string) { - const parsedDate = new Date(date); - const day = parsedDate.getDate(); - const month = parsedDate.getMonth() + 1; - const year = parsedDate.getFullYear(); - - const timelineEvents = useMapStore.getState().untrackedState.timeLineEvents; - - const updatedYearly = timelineEvents.yearly.map((yearItem) => { - if (yearItem.year === year) { - return { ...yearItem, added: yearItem.added + 1 }; - } - return yearItem; - }); - - const updatedMonthly = timelineEvents.monthly.map((monthItem) => { - if (monthItem.year === year && monthItem.month === month) { - return { ...monthItem, added: monthItem.added + 1 }; - } - return monthItem; - }); - - const updatedDaily = timelineEvents.daily.map((dayItem) => { - if (dayItem.year === year && dayItem.month === month && dayItem.day === day) { - return { ...dayItem, added: dayItem.added + 1 }; - } - return dayItem; - }); - - useMapStore.setState({ - untrackedState: { - ...useMapStore.getState().untrackedState, - timeLineEvents: { - yearly: updatedYearly, - monthly: updatedMonthly, - daily: updatedDaily, - }, - }, - }); -} - -export function decreaseAddedPlantsForDate(date: string) { - const parsedDate = new Date(date); - const day = parsedDate.getDate(); - const month = parsedDate.getMonth() + 1; - const year = parsedDate.getFullYear(); - - const timelineEvents = useMapStore.getState().untrackedState.timeLineEvents; - - const updatedYearly = timelineEvents.yearly.map((yearItem) => { - if (yearItem.year === year) { - return { ...yearItem, added: yearItem.added - 1 }; - } - return yearItem; - }); - - const updatedMonthly = timelineEvents.monthly.map((monthItem) => { - if (monthItem.year === year && monthItem.month === month) { - return { ...monthItem, added: monthItem.added - 1 }; - } - return monthItem; - }); - - const updatedDaily = timelineEvents.daily.map((dayItem) => { - if (dayItem.year === year && dayItem.month === month && dayItem.day === day) { - return { ...dayItem, added: dayItem.added - 1 }; - } - return dayItem; - }); - - useMapStore.setState({ - untrackedState: { - ...useMapStore.getState().untrackedState, - timeLineEvents: { - yearly: updatedYearly, - monthly: updatedMonthly, - daily: updatedDaily, - }, - }, - }); -}