feat(dueDate): make day changes work as expected

This commit is contained in:
Johannes Millan 2025-05-08 13:31:13 +02:00
parent a1c4d527d2
commit fa93e83a91
10 changed files with 143 additions and 18 deletions

View file

@ -13,12 +13,12 @@ import { TODAY_TAG } from '../../tag/tag.const';
import { IssueProvider } from '../../issue/issue.model';
import { Project } from '../../project/project.model';
import { selectAllProjects } from '../../project/store/project.selectors';
import { getWorklogStr } from '../../../util/get-work-log-str';
import {
selectTagFeatureState,
selectTodayTagTaskIds,
} from '../../tag/store/tag.reducer';
import { isToday } from '../../../util/is-today.util';
import { selectTodayStr } from '../../../root-store/app-state/app-state.selectors';
const mapSubTasksToTasks = (tasksIN: any[]): TaskWithSubTasks[] => {
return tasksIN
@ -117,18 +117,22 @@ export const selectStartableTasks = createSelector(
},
);
export const selectOverdueTasks = createSelector(selectTaskFeatureState, (s): Task[] => {
const today = new Date(getWorklogStr());
const todayStart = new Date(today);
todayStart.setHours(0, 0, 0, 0);
return s.ids
.map((id) => s.entities[id] as Task)
.filter(
(task) =>
(task.dueDay && new Date(task.dueDay) < today) ||
(task.dueWithTime && task.dueWithTime < todayStart.getTime()),
);
});
export const selectOverdueTasks = createSelector(
selectTaskFeatureState,
selectTodayStr,
(s, todayStr): Task[] => {
const today = new Date(todayStr);
const todayStart = new Date(today);
todayStart.setHours(0, 0, 0, 0);
return s.ids
.map((id) => s.entities[id] as Task)
.filter(
(task) =>
(task.dueDay && new Date(task.dueDay) < today) ||
(task.dueWithTime && task.dueWithTime < todayStart.getTime()),
);
},
);
export const selectOverdueTasksOnToday = createSelector(
selectOverdueTasks,
@ -180,8 +184,9 @@ export const selectOverdueTasksWithSubTasks = createSelector(
export const selectAllTasksDueAndOverdue = createSelector(
selectTaskFeatureState,
selectTagFeatureState,
(s, tagState): Task[] => {
const today = new Date(getWorklogStr());
selectTodayStr,
(s, tagState, todayStr): Task[] => {
const today = new Date(todayStr);
return s.ids
.map((id) => s.entities[id] as Task)
.filter(
@ -462,9 +467,8 @@ export const selectAllTasksWithDueDay = createSelector(
export const selectAllTasksDueToday = createSelector(
selectTaskFeatureState,
(taskState): (TaskWithDueTime | TaskWithDueDay)[] => {
const todayStr = getWorklogStr();
selectTodayStr,
(taskState, todayStr): (TaskWithDueTime | TaskWithDueDay)[] => {
const allDueDayTasks = Object.values(taskState.entities).filter(
(task) => !!task && !!(task as TaskWithDueTime).dueDay && todayStr === task.dueDay,
) as TaskWithDueDay[];

View file

@ -0,0 +1,9 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { createActionGroup, props } from '@ngrx/store';
export const AppStateActions = createActionGroup({
source: 'AppState',
events: {
'Set Today String': props<{ todayStr: string }>(),
},
});

View file

@ -0,0 +1,25 @@
// import { TestBed } from '@angular/core/testing';
// import { provideMockActions } from '@ngrx/effects/testing';
// import { Observable } from 'rxjs';
//
// import { AppStateEffects } from './app-state.effects';
//
// describe('AppStateEffects', () => {
// let actions$: Observable<any>;
// let effects: AppStateEffects;
//
// beforeEach(() => {
// TestBed.configureTestingModule({
// providers: [
// AppStateEffects,
// provideMockActions(() => actions$)
// ]
// });
//
// effects = TestBed.inject(AppStateEffects);
// });
//
// it('should be created', () => {
// expect(effects).toBeTruthy();
// });
// });

View file

@ -0,0 +1,18 @@
import { inject, Injectable } from '@angular/core';
import { createEffect } from '@ngrx/effects';
import { map, skip } from 'rxjs/operators';
import { AppStateActions } from './app-state.actions';
import { GlobalTrackingIntervalService } from '../../core/global-tracking-interval/global-tracking-interval.service';
@Injectable()
export class AppStateEffects {
private _globalTimeTrackingIntervalService = inject(GlobalTrackingIntervalService);
setTodayStr$ = createEffect(() => {
return this._globalTimeTrackingIntervalService.todayDateStr$.pipe(
skip(1), // skip first since it should be already the default value
map((todayStr) => AppStateActions.setTodayString({ todayStr })),
);
});
}

View file

@ -0,0 +1,13 @@
// import { reducer, initialState } from './app-state.reducer';
//
// describe('AppState Reducer', () => {
// describe('an unknown action', () => {
// it('should return the previous state', () => {
// const action = {} as any;
//
// const result = reducer(initialState, action);
//
// expect(result).toBe(initialState);
// });
// });
// });

View file

@ -0,0 +1,26 @@
import { createFeature, createReducer, on } from '@ngrx/store';
import { AppStateActions } from './app-state.actions';
import { getWorklogStr } from '../../util/get-work-log-str';
export const appStateFeatureKey = 'appState';
export interface AppState {
todayStr: string;
}
export const appStateInitialState: AppState = {
todayStr: getWorklogStr(),
};
export const appStateReducer = createReducer(
appStateInitialState,
on(AppStateActions.setTodayString, (state, { todayStr }) => ({
...state,
todayStr,
})),
);
export const appStateFeature = createFeature({
name: appStateFeatureKey,
reducer: appStateReducer,
});

View file

@ -0,0 +1,12 @@
// import * as fromAppState from './app-state.reducer';
// import { selectAppStateState } from './app-state.selectors';
//
// describe('AppState Selectors', () => {
// it('should select the feature state', () => {
// const result = selectAppStateState({
// [fromAppState.appStateFeatureKey]: {}
// });
//
// expect(result).toEqual({});
// });
// });

View file

@ -0,0 +1,11 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromAppState from './app-state.reducer';
export const selectAppStateState = createFeatureSelector<fromAppState.AppState>(
fromAppState.appStateFeatureKey,
);
export const selectTodayStr = createSelector(
selectAppStateState,
(state: fromAppState.AppState) => state.todayStr,
);

View file

@ -94,10 +94,15 @@ import { timeTrackingFeature } from '../features/time-tracking/store/time-tracki
import { TimeTrackingEffects } from '../features/time-tracking/store/time-tracking.effects';
import { plannerFeature } from '../features/planner/store/planner.reducer';
import { PlannerEffects } from '../features/planner/store/planner.effects';
import { AppStateEffects } from './app-state/app-state.effects';
import { appStateFeature } from './app-state/app-state.reducer';
@NgModule({
declarations: [],
imports: [
StoreModule.forFeature(appStateFeature),
EffectsModule.forFeature([AppStateEffects]),
StoreModule.forFeature(LAYOUT_FEATURE_NAME, layoutReducer),
EffectsModule.forFeature([LayoutEffects]),

View file

@ -17,6 +17,7 @@ import {
} from '../features/boards/store/boards.reducer';
import * as fromPlanner from '../features/planner/store/planner.reducer';
import { PlannerState } from '../features/planner/store/planner.reducer';
import { AppState, appStateFeatureKey } from './app-state/app-state.reducer';
export interface RootState {
[TASK_FEATURE_NAME]: TaskState;
@ -28,4 +29,5 @@ export interface RootState {
[CONFIG_FEATURE_NAME]: GlobalConfigState;
[BOARDS_FEATURE_NAME]: BoardsState;
[fromPlanner.plannerFeatureKey]: PlannerState;
[appStateFeatureKey]: AppState;
}