@aneuhold/core-ts-db-lib 1.0.24 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,10 +4,6 @@ import RequiredUserId from '../../schemas/required-refs/RequiredUserId';
4
4
  import { DocumentValidator } from '../../schemas/validators/DocumentValidator';
5
5
  import { RecurrenceInfo } from '../../embedded-types/dashboard/task/RecurrenceInfo';
6
6
  export declare const validateDashboardTask: DocumentValidator<DashboardTask>;
7
- /**
8
- * Gets all the children task IDs for the given parent task IDs.
9
- */
10
- export declare const getDashboardTaskChildrenIds: (allUserTasks: DashboardTask[], parentTaskIds: ObjectId[]) => ObjectId[];
11
7
  /**
12
8
  * When thinking about the logic of tasks, the following thoughts come to mind:
13
9
  *
@@ -50,16 +46,20 @@ export default class DashboardTask extends BaseDocumentWithType implements Requi
50
46
  */
51
47
  sharedWith: ObjectId[];
52
48
  /**
53
- * The recurrence info for this task if there is any. This is ignored
54
- * if {@link parentRecurringTask} is set.
49
+ * The recurrence info for this task if there is any.
55
50
  */
56
51
  recurrenceInfo?: RecurrenceInfo;
57
52
  /**
58
- * The ID of the parent recurring task if there is one. Users should not
59
- * be able to share the current task if this is set to a value. It would
60
- * get confusing for the user it was shared with.
53
+ * The recurring task info for the parent recurring task if there is one.
54
+ *
55
+ * If this is set, then the current tasks's recurrence info should be the
56
+ * same as the parent recurring task.
61
57
  */
62
- parentRecurringTaskId?: ObjectId;
58
+ parentRecurringTaskInfo?: {
59
+ taskId: ObjectId;
60
+ startDate?: Date;
61
+ dueDate?: Date;
62
+ };
63
63
  title: string;
64
64
  completed: boolean;
65
65
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../../src/documents/dashboard/Task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAChC,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,cAAc,MAAM,4CAA4C,CAAC;AAExE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAC/E,OAAO,EACL,cAAc,EAEf,MAAM,oDAAoD,CAAC;AAE5D,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,CAAC,aAAa,CAqBlE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,iBACxB,aAAa,EAAE,iBACd,QAAQ,EAAE,KACxB,QAAQ,EA2BV,CAAC;AAyBF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,OAAO,OAAO,aACnB,SAAQ,oBACR,YAAW,cAAc;IAEzB,MAAM,CAAC,OAAO,SAAU;IAExB,OAAO,SAAyB;IAEhC;;OAEG;IACH,MAAM,EAAE,QAAQ,CAAC;IAEjB;;;;;;;;;;;OAWG;IACH,UAAU,EAAE,QAAQ,EAAE,CAAM;IAE5B;;;OAGG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,QAAQ,CAAC;IAEjC,KAAK,SAAM;IAEX,SAAS,UAAS;IAElB;;OAEG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC;IAExB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,OAAc;IAEzB;;OAEG;IACH,eAAe,OAAc;IAE7B,SAAS,CAAC,EAAE,IAAI,CAAC;IAEjB,OAAO,CAAC,EAAE,IAAI,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAM;IAEpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAa;gBAEjB,OAAO,EAAE,QAAQ;CAI9B"}
1
+ {"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../../src/documents/dashboard/Task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAChC,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,cAAc,MAAM,4CAA4C,CAAC;AAExE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAC/E,OAAO,EACL,cAAc,EAEf,MAAM,oDAAoD,CAAC;AAE5D,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,CAAC,aAAa,CAsBlE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,OAAO,OAAO,aACnB,SAAQ,oBACR,YAAW,cAAc;IAEzB,MAAM,CAAC,OAAO,SAAU;IAExB,OAAO,SAAyB;IAEhC;;OAEG;IACH,MAAM,EAAE,QAAQ,CAAC;IAEjB;;;;;;;;;;;OAWG;IACH,UAAU,EAAE,QAAQ,EAAE,CAAM;IAE5B;;OAEG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE;QACxB,MAAM,EAAE,QAAQ,CAAC;QACjB,SAAS,CAAC,EAAE,IAAI,CAAC;QACjB,OAAO,CAAC,EAAE,IAAI,CAAC;KAChB,CAAC;IAEF,KAAK,SAAM;IAEX,SAAS,UAAS;IAElB;;OAEG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC;IAExB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,OAAc;IAEzB;;OAEG;IACH,eAAe,OAAc;IAE7B,SAAS,CAAC,EAAE,IAAI,CAAC;IAEjB,OAAO,CAAC,EAAE,IAAI,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAM;IAEpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAa;gBAEjB,OAAO,EAAE,QAAQ;CAI9B"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getDashboardTaskChildrenIds = exports.validateDashboardTask = void 0;
6
+ exports.validateDashboardTask = void 0;
7
7
  const bson_1 = require("bson");
8
8
  const BaseDocumentWithType_1 = __importDefault(require("../BaseDocumentWithType"));
9
9
  const ValidateUtil_1 = __importDefault(require("../../schemas/validators/ValidateUtil"));
@@ -23,50 +23,11 @@ const validateDashboardTask = (task) => {
23
23
  validate.object('lastUpdatedDate', exampleTask.lastUpdatedDate);
24
24
  validate.optionalObject('startDate');
25
25
  validate.optionalObject('dueDate');
26
+ validate.optionalObject('parentRecurringTaskInfo');
26
27
  (0, RecurrenceInfo_1.validateRecurrenceInfo)(task, errors);
27
28
  return { updatedDoc: task, errors };
28
29
  };
29
30
  exports.validateDashboardTask = validateDashboardTask;
30
- /**
31
- * Gets all the children task IDs for the given parent task IDs.
32
- */
33
- const getDashboardTaskChildrenIds = (allUserTasks, parentTaskIds) => {
34
- const parentToTaskIdsDict = {};
35
- const taskIdToTaskDict = {};
36
- allUserTasks.forEach((task) => {
37
- taskIdToTaskDict[task._id.toString()] = task;
38
- if (task.parentTaskId) {
39
- if (!parentToTaskIdsDict[task.parentTaskId.toString()]) {
40
- parentToTaskIdsDict[task.parentTaskId.toString()] = [];
41
- }
42
- parentToTaskIdsDict[task.parentTaskId.toString()].push(task._id.toString());
43
- }
44
- });
45
- const childrenIds = [];
46
- parentTaskIds.forEach((taskId) => {
47
- const task = taskIdToTaskDict[taskId.toString()];
48
- if (task) {
49
- const childrenTaskIds = getChildrenTaskIds(taskIdToTaskDict, parentToTaskIdsDict, taskId.toString());
50
- childrenIds.push(...childrenTaskIds.map((id) => new bson_1.ObjectId(id)));
51
- }
52
- });
53
- return childrenIds;
54
- };
55
- exports.getDashboardTaskChildrenIds = getDashboardTaskChildrenIds;
56
- function getChildrenTaskIds(taskIdToTaskDict, parentToTaskIdsDict, taskId) {
57
- const childrenIds = parentToTaskIdsDict[taskId];
58
- if (!childrenIds) {
59
- return [];
60
- }
61
- childrenIds.forEach((childId) => {
62
- const childTask = taskIdToTaskDict[childId];
63
- if (childTask) {
64
- const grandchildrenIds = getChildrenTaskIds(taskIdToTaskDict, parentToTaskIdsDict, childId);
65
- childrenIds.push(...grandchildrenIds);
66
- }
67
- });
68
- return childrenIds;
69
- }
70
31
  /**
71
32
  * When thinking about the logic of tasks, the following thoughts come to mind:
72
33
  *
@@ -109,16 +70,16 @@ class DashboardTask extends BaseDocumentWithType_1.default {
109
70
  */
110
71
  sharedWith = [];
111
72
  /**
112
- * The recurrence info for this task if there is any. This is ignored
113
- * if {@link parentRecurringTask} is set.
73
+ * The recurrence info for this task if there is any.
114
74
  */
115
75
  recurrenceInfo;
116
76
  /**
117
- * The ID of the parent recurring task if there is one. Users should not
118
- * be able to share the current task if this is set to a value. It would
119
- * get confusing for the user it was shared with.
77
+ * The recurring task info for the parent recurring task if there is one.
78
+ *
79
+ * If this is set, then the current tasks's recurrence info should be the
80
+ * same as the parent recurring task.
120
81
  */
121
- parentRecurringTaskId;
82
+ parentRecurringTaskInfo;
122
83
  title = '';
123
84
  completed = false;
124
85
  /**
package/lib/index.d.ts CHANGED
@@ -2,11 +2,12 @@ import BaseDocument from './documents/BaseDocument';
2
2
  import BaseDocumentWithType from './documents/BaseDocumentWithType';
3
3
  import ApiKey, { validateApiKey } from './documents/common/ApiKey';
4
4
  import User, { UserCTO, validateUser } from './documents/common/User';
5
- import DashboardTask, { getDashboardTaskChildrenIds, validateDashboardTask } from './documents/dashboard/Task';
5
+ import DashboardTask, { validateDashboardTask } from './documents/dashboard/Task';
6
6
  import DashboardUserConfig, { validateDashboardUserConfig } from './documents/dashboard/UserConfig';
7
7
  import { RecurrenceBasis, RecurrenceEffect, RecurrenceFrequency, RecurrenceFrequencyType, RecurrenceInfo } from './embedded-types/dashboard/task/RecurrenceInfo';
8
8
  import RequiredUserId from './schemas/required-refs/RequiredUserId';
9
9
  import { DocumentValidator } from './schemas/validators/DocumentValidator';
10
- export { User, validateUser, ApiKey, validateApiKey, DashboardUserConfig, validateDashboardUserConfig, DashboardTask, RecurrenceFrequencyType, RecurrenceBasis, RecurrenceEffect, validateDashboardTask, getDashboardTaskChildrenIds, BaseDocument, BaseDocumentWithType, RequiredUserId };
10
+ import DashboardTaskService from './services/dashboard/TaskService';
11
+ export { User, validateUser, ApiKey, validateApiKey, DashboardUserConfig, validateDashboardUserConfig, DashboardTask, RecurrenceFrequencyType, RecurrenceBasis, RecurrenceEffect, validateDashboardTask, DashboardTaskService, BaseDocument, BaseDocumentWithType, RequiredUserId };
11
12
  export type { DocumentValidator, UserCTO, RecurrenceInfo, RecurrenceFrequency };
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,0BAA0B,CAAC;AACpD,OAAO,oBAAoB,MAAM,kCAAkC,CAAC;AACpE,OAAO,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,IAAI,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,aAAa,EAAE,EACpB,2BAA2B,EAC3B,qBAAqB,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,mBAAmB,EAAE,EAC1B,2BAA2B,EAC5B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACf,MAAM,gDAAgD,CAAC;AACxD,OAAO,cAAc,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAG3E,OAAO,EACL,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,cAAc,EACd,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,2BAA2B,EAC3B,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACf,CAAC;AAGF,YAAY,EAAE,iBAAiB,EAAE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,0BAA0B,CAAC;AACpD,OAAO,oBAAoB,MAAM,kCAAkC,CAAC;AACpE,OAAO,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,IAAI,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,aAAa,EAAE,EACpB,qBAAqB,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,mBAAmB,EAAE,EAC1B,2BAA2B,EAC5B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACf,MAAM,gDAAgD,CAAC;AACxD,OAAO,cAAc,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,oBAAoB,MAAM,kCAAkC,CAAC;AAGpE,OAAO,EACL,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,cAAc,EACd,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACf,CAAC;AAGF,YAAY,EAAE,iBAAiB,EAAE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC"}
package/lib/index.js CHANGED
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.RequiredUserId = exports.BaseDocumentWithType = exports.BaseDocument = exports.getDashboardTaskChildrenIds = exports.validateDashboardTask = exports.RecurrenceEffect = exports.RecurrenceBasis = exports.RecurrenceFrequencyType = exports.DashboardTask = exports.validateDashboardUserConfig = exports.DashboardUserConfig = exports.validateApiKey = exports.ApiKey = exports.validateUser = exports.User = void 0;
29
+ exports.RequiredUserId = exports.BaseDocumentWithType = exports.BaseDocument = exports.DashboardTaskService = exports.validateDashboardTask = exports.RecurrenceEffect = exports.RecurrenceBasis = exports.RecurrenceFrequencyType = exports.DashboardTask = exports.validateDashboardUserConfig = exports.DashboardUserConfig = exports.validateApiKey = exports.ApiKey = exports.validateUser = exports.User = void 0;
30
30
  const BaseDocument_1 = __importDefault(require("./documents/BaseDocument"));
31
31
  exports.BaseDocument = BaseDocument_1.default;
32
32
  const BaseDocumentWithType_1 = __importDefault(require("./documents/BaseDocumentWithType"));
@@ -39,7 +39,6 @@ exports.User = User_1.default;
39
39
  Object.defineProperty(exports, "validateUser", { enumerable: true, get: function () { return User_1.validateUser; } });
40
40
  const Task_1 = __importStar(require("./documents/dashboard/Task"));
41
41
  exports.DashboardTask = Task_1.default;
42
- Object.defineProperty(exports, "getDashboardTaskChildrenIds", { enumerable: true, get: function () { return Task_1.getDashboardTaskChildrenIds; } });
43
42
  Object.defineProperty(exports, "validateDashboardTask", { enumerable: true, get: function () { return Task_1.validateDashboardTask; } });
44
43
  const UserConfig_1 = __importStar(require("./documents/dashboard/UserConfig"));
45
44
  exports.DashboardUserConfig = UserConfig_1.default;
@@ -50,3 +49,5 @@ Object.defineProperty(exports, "RecurrenceEffect", { enumerable: true, get: func
50
49
  Object.defineProperty(exports, "RecurrenceFrequencyType", { enumerable: true, get: function () { return RecurrenceInfo_1.RecurrenceFrequencyType; } });
51
50
  const RequiredUserId_1 = __importDefault(require("./schemas/required-refs/RequiredUserId"));
52
51
  exports.RequiredUserId = RequiredUserId_1.default;
52
+ const TaskService_1 = __importDefault(require("./services/dashboard/TaskService"));
53
+ exports.DashboardTaskService = TaskService_1.default;
@@ -0,0 +1,27 @@
1
+ import { ObjectId } from 'bson';
2
+ import DashboardTask from '../../documents/dashboard/Task';
3
+ import { RecurrenceFrequency } from '../../embedded-types/dashboard/task/RecurrenceInfo';
4
+ export default class DashboardTaskService {
5
+ /**
6
+ * Gets all the children task IDs for the given parent task IDs.
7
+ */
8
+ static getChildrenIds: (allUserTasks: DashboardTask[], parentTaskIds: ObjectId[]) => ObjectId[];
9
+ /**
10
+ * Gets the next frequency date from the provided basis date. Returns null
11
+ * if the provided frequency is in an invalid state.
12
+ */
13
+ static getNextFrequencyDate(basisDate: Date, frequency: RecurrenceFrequency): Date | null;
14
+ /**
15
+ * Moves the start and due date forward by one frequency.
16
+ *
17
+ * This does not take into account the recurrence effect. That should be
18
+ * handled on the frontend.
19
+ *
20
+ * Makes no changes if the state of the task is invalid for recurrence or
21
+ * there isn't recurrence info.
22
+ */
23
+ static updateDatesForRecurrence(task: DashboardTask): void;
24
+ private static getDiffForDateUpdate;
25
+ private static getChildrenTaskIds;
26
+ }
27
+ //# sourceMappingURL=TaskService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TaskService.d.ts","sourceRoot":"","sources":["../../../src/services/dashboard/TaskService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEhC,OAAO,aAAa,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EACL,mBAAmB,EAEpB,MAAM,oDAAoD,CAAC;AAE5D,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC;;OAEG;IACH,MAAM,CAAC,cAAc,iBACL,aAAa,EAAE,iBACd,QAAQ,EAAE,KACxB,QAAQ,EAAE,CA2BX;IAEF;;;OAGG;IACH,MAAM,CAAC,oBAAoB,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,mBAAmB;IAsE3E;;;;;;;;OAQG;IACH,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IA+D1D,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAcnC,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAsBlC"}
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const bson_1 = require("bson");
4
+ const core_ts_lib_1 = require("@aneuhold/core-ts-lib");
5
+ const RecurrenceInfo_1 = require("../../embedded-types/dashboard/task/RecurrenceInfo");
6
+ class DashboardTaskService {
7
+ /**
8
+ * Gets all the children task IDs for the given parent task IDs.
9
+ */
10
+ static getChildrenIds = (allUserTasks, parentTaskIds) => {
11
+ const parentToTaskIdsDict = {};
12
+ const taskIdToTaskDict = {};
13
+ allUserTasks.forEach((task) => {
14
+ taskIdToTaskDict[task._id.toString()] = task;
15
+ if (task.parentTaskId) {
16
+ if (!parentToTaskIdsDict[task.parentTaskId.toString()]) {
17
+ parentToTaskIdsDict[task.parentTaskId.toString()] = [];
18
+ }
19
+ parentToTaskIdsDict[task.parentTaskId.toString()].push(task._id.toString());
20
+ }
21
+ });
22
+ const childrenIds = [];
23
+ parentTaskIds.forEach((taskId) => {
24
+ const task = taskIdToTaskDict[taskId.toString()];
25
+ if (task) {
26
+ const childrenTaskIds = this.getChildrenTaskIds(taskIdToTaskDict, parentToTaskIdsDict, taskId.toString());
27
+ childrenIds.push(...childrenTaskIds.map((id) => new bson_1.ObjectId(id)));
28
+ }
29
+ });
30
+ return childrenIds;
31
+ };
32
+ /**
33
+ * Gets the next frequency date from the provided basis date. Returns null
34
+ * if the provided frequency is in an invalid state.
35
+ */
36
+ static getNextFrequencyDate(basisDate, frequency) {
37
+ // Last day of month
38
+ if (frequency.type === RecurrenceInfo_1.RecurrenceFrequencyType.lastDayOfMonth) {
39
+ return core_ts_lib_1.DateService.getLastDayOfGivenMonth(basisDate);
40
+ }
41
+ // Every X Time Unit
42
+ if (frequency.type === RecurrenceInfo_1.RecurrenceFrequencyType.everyXTimeUnit) {
43
+ if (!frequency.everyXTimeUnit) {
44
+ return null;
45
+ }
46
+ if (frequency.everyXTimeUnit.timeUnit === 'day') {
47
+ return core_ts_lib_1.DateService.addDays(basisDate, frequency.everyXTimeUnit.x);
48
+ }
49
+ if (frequency.everyXTimeUnit.timeUnit === 'week') {
50
+ return core_ts_lib_1.DateService.addDays(basisDate, frequency.everyXTimeUnit.x * 7);
51
+ }
52
+ if (frequency.everyXTimeUnit.timeUnit === 'month') {
53
+ return core_ts_lib_1.DateService.addMonths(basisDate, frequency.everyXTimeUnit.x);
54
+ }
55
+ if (frequency.everyXTimeUnit.timeUnit === 'year') {
56
+ return core_ts_lib_1.DateService.addYears(basisDate, frequency.everyXTimeUnit.x);
57
+ }
58
+ }
59
+ // Week Day Set
60
+ if (frequency.type === RecurrenceInfo_1.RecurrenceFrequencyType.weekDaySet) {
61
+ if (!frequency.weekDaySet) {
62
+ return null;
63
+ }
64
+ const newDate = new Date(basisDate);
65
+ let daysToAdd = 0;
66
+ while (daysToAdd < 7) {
67
+ newDate.setDate(newDate.getDate() + 1);
68
+ if (frequency.weekDaySet.includes(newDate.getDay())) {
69
+ return newDate;
70
+ }
71
+ daysToAdd += 1;
72
+ }
73
+ }
74
+ // Every X Weekday of Month
75
+ if (frequency.type === RecurrenceInfo_1.RecurrenceFrequencyType.everyXWeekdayOfMonth) {
76
+ if (!frequency.everyXWeekdayOfMonth) {
77
+ return null;
78
+ }
79
+ // Start by adding one day
80
+ let newDate = core_ts_lib_1.DateService.getWeekDayOfXWeekOfMonth(core_ts_lib_1.DateService.addDays(basisDate, 1), frequency.everyXWeekdayOfMonth.weekDay, frequency.everyXWeekdayOfMonth.weekOfMonth);
81
+ let monthsPassed = 0;
82
+ while (!newDate ||
83
+ newDate < basisDate ||
84
+ newDate.getTime() === basisDate.getTime()) {
85
+ monthsPassed += 1;
86
+ newDate = core_ts_lib_1.DateService.getWeekDayOfXWeekOfMonth(core_ts_lib_1.DateService.addMonths(basisDate, monthsPassed), frequency.everyXWeekdayOfMonth.weekDay, frequency.everyXWeekdayOfMonth.weekOfMonth);
87
+ if (monthsPassed > 11) {
88
+ return null;
89
+ }
90
+ }
91
+ return newDate;
92
+ }
93
+ return null;
94
+ }
95
+ /**
96
+ * Moves the start and due date forward by one frequency.
97
+ *
98
+ * This does not take into account the recurrence effect. That should be
99
+ * handled on the frontend.
100
+ *
101
+ * Makes no changes if the state of the task is invalid for recurrence or
102
+ * there isn't recurrence info.
103
+ */
104
+ static updateDatesForRecurrence(task) {
105
+ // Initial basic validation
106
+ if (!task.recurrenceInfo) {
107
+ return;
108
+ }
109
+ // Validation for dates based on parent
110
+ if (task.parentRecurringTaskInfo) {
111
+ if (
112
+ // No dates to move forward
113
+ (!task.dueDate && !task.startDate) ||
114
+ // Invalid start date recurrence basis
115
+ (task.recurrenceInfo.recurrenceBasis === 'startDate' &&
116
+ !task.parentRecurringTaskInfo.startDate) ||
117
+ // Invalid due date recurrence basis
118
+ (task.recurrenceInfo.recurrenceBasis === 'dueDate' &&
119
+ !task.parentRecurringTaskInfo.dueDate)) {
120
+ return;
121
+ }
122
+ // Validation for moving dates based on their own recurrence
123
+ }
124
+ else if (!task.recurrenceInfo ||
125
+ (task.recurrenceInfo.recurrenceBasis === 'startDate' &&
126
+ !task.startDate) ||
127
+ (task.recurrenceInfo.recurrenceBasis === 'dueDate' && !task.dueDate)) {
128
+ return;
129
+ }
130
+ let diff = 0;
131
+ if (task.parentRecurringTaskInfo) {
132
+ if (task.recurrenceInfo.recurrenceBasis === 'startDate') {
133
+ diff = this.getDiffForDateUpdate(task.parentRecurringTaskInfo.startDate, task.recurrenceInfo.frequency);
134
+ }
135
+ else {
136
+ diff = this.getDiffForDateUpdate(task.parentRecurringTaskInfo.dueDate, task.recurrenceInfo.frequency);
137
+ }
138
+ }
139
+ else if (task.recurrenceInfo.recurrenceBasis === 'startDate') {
140
+ diff = this.getDiffForDateUpdate(task.startDate, task.recurrenceInfo.frequency);
141
+ }
142
+ else {
143
+ diff = this.getDiffForDateUpdate(task.dueDate, task.recurrenceInfo.frequency);
144
+ }
145
+ if (task.startDate) {
146
+ task.startDate = new Date(task.startDate.getTime() + diff);
147
+ }
148
+ if (task.dueDate) {
149
+ task.dueDate = new Date(task.dueDate.getTime() + diff);
150
+ }
151
+ }
152
+ static getDiffForDateUpdate(basisDate, frequency) {
153
+ if (!basisDate) {
154
+ return 0;
155
+ }
156
+ const nextFrequencyDate = this.getNextFrequencyDate(basisDate, frequency);
157
+ if (!nextFrequencyDate) {
158
+ return 0;
159
+ }
160
+ return nextFrequencyDate.getTime() - basisDate.getTime();
161
+ }
162
+ static getChildrenTaskIds(taskIdToTaskDict, parentToTaskIdsDict, taskId) {
163
+ const childrenIds = parentToTaskIdsDict[taskId];
164
+ if (!childrenIds) {
165
+ return [];
166
+ }
167
+ childrenIds.forEach((childId) => {
168
+ const childTask = taskIdToTaskDict[childId];
169
+ if (childTask) {
170
+ const grandchildrenIds = this.getChildrenTaskIds(taskIdToTaskDict, parentToTaskIdsDict, childId);
171
+ childrenIds.push(...grandchildrenIds);
172
+ }
173
+ });
174
+ return childrenIds;
175
+ }
176
+ }
177
+ exports.default = DashboardTaskService;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=TaskService.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TaskService.spec.d.ts","sourceRoot":"","sources":["../../../src/services/dashboard/TaskService.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const bson_1 = require("bson");
7
+ const Task_1 = __importDefault(require("../../documents/dashboard/Task"));
8
+ const RecurrenceInfo_1 = require("../../embedded-types/dashboard/task/RecurrenceInfo");
9
+ const TaskService_1 = __importDefault(require("./TaskService"));
10
+ describe('DashboardTaskService', () => {
11
+ describe('getNextFrequencyDate', () => {
12
+ it('should return a valid date for lastDayOfMonth', () => {
13
+ const basisDate = new Date(2024, 0, 1);
14
+ const frequency = {
15
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.lastDayOfMonth
16
+ };
17
+ const result = TaskService_1.default.getNextFrequencyDate(basisDate, frequency);
18
+ expect(result).toEqual(new Date(2024, 0, 31));
19
+ });
20
+ describe('every X time unit', () => {
21
+ it('should return a valid date for every X Day', () => {
22
+ const basisDate = new Date(2024, 0, 1);
23
+ const frequency = {
24
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXTimeUnit,
25
+ everyXTimeUnit: {
26
+ timeUnit: 'day',
27
+ x: 2
28
+ }
29
+ };
30
+ const result = TaskService_1.default.getNextFrequencyDate(basisDate, frequency);
31
+ expect(result).toEqual(new Date(2024, 0, 3));
32
+ });
33
+ it('should return a valid date for every X Week', () => {
34
+ const basisDate = new Date(2024, 0, 1);
35
+ const frequency = {
36
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXTimeUnit,
37
+ everyXTimeUnit: {
38
+ timeUnit: 'week',
39
+ x: 2
40
+ }
41
+ };
42
+ const result = TaskService_1.default.getNextFrequencyDate(basisDate, frequency);
43
+ expect(result).toEqual(new Date(2024, 0, 15));
44
+ });
45
+ it('should return a valid date for every X Month', () => {
46
+ const basisDate = new Date(2024, 0, 1);
47
+ const frequency = {
48
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXTimeUnit,
49
+ everyXTimeUnit: {
50
+ timeUnit: 'month',
51
+ x: 2
52
+ }
53
+ };
54
+ const result = TaskService_1.default.getNextFrequencyDate(basisDate, frequency);
55
+ expect(result).toEqual(new Date(2024, 2, 1));
56
+ });
57
+ });
58
+ it('should return a valid date for weekDaySet', () => {
59
+ const basisDate = new Date(2024, 0, 1);
60
+ const frequency = {
61
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.weekDaySet,
62
+ weekDaySet: [0, 6]
63
+ };
64
+ const result = TaskService_1.default.getNextFrequencyDate(basisDate, frequency);
65
+ expect(result).toEqual(new Date(2024, 0, 6));
66
+ });
67
+ describe('Every X Weekday of Month', () => {
68
+ it('should return a valid date for every 2nd Sunday of Month', () => {
69
+ const basisDate = new Date(2024, 0, 1);
70
+ // Every second sunday
71
+ const frequency = {
72
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXWeekdayOfMonth,
73
+ everyXWeekdayOfMonth: {
74
+ weekDay: 0,
75
+ weekOfMonth: 2
76
+ }
77
+ };
78
+ const result = TaskService_1.default.getNextFrequencyDate(basisDate, frequency);
79
+ expect(result).toEqual(new Date(2024, 0, 14));
80
+ });
81
+ it('should return a valid date across year change', () => {
82
+ const basisDate = new Date(2023, 11, 30);
83
+ // Every 1st Saturday
84
+ const frequency = {
85
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXWeekdayOfMonth,
86
+ everyXWeekdayOfMonth: {
87
+ weekDay: 6,
88
+ weekOfMonth: 1
89
+ }
90
+ };
91
+ const result = TaskService_1.default.getNextFrequencyDate(basisDate, frequency);
92
+ expect(result).toEqual(new Date(2024, 0, 6));
93
+ });
94
+ it('should return a valid next date when the basis is the same as the recurrence', () => {
95
+ const basisDate = new Date(2024, 0, 14);
96
+ // Every second sunday
97
+ const frequency = {
98
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXWeekdayOfMonth,
99
+ everyXWeekdayOfMonth: {
100
+ weekDay: 0,
101
+ weekOfMonth: 2
102
+ }
103
+ };
104
+ const result = TaskService_1.default.getNextFrequencyDate(basisDate, frequency);
105
+ expect(result).toEqual(new Date(2024, 1, 11));
106
+ });
107
+ });
108
+ });
109
+ describe('updateDatesForRecurrence', () => {
110
+ describe('Start date basis', () => {
111
+ it('should update the start date correctly for a daily recurrence', () => {
112
+ const task = new Task_1.default(new bson_1.ObjectId());
113
+ task.startDate = new Date(2024, 0, 1);
114
+ task.recurrenceInfo = {
115
+ frequency: {
116
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXTimeUnit,
117
+ everyXTimeUnit: {
118
+ timeUnit: 'day',
119
+ x: 1
120
+ }
121
+ },
122
+ recurrenceBasis: RecurrenceInfo_1.RecurrenceBasis.startDate,
123
+ recurrenceEffect: RecurrenceInfo_1.RecurrenceEffect.rollOnBasis
124
+ };
125
+ TaskService_1.default.updateDatesForRecurrence(task);
126
+ expect(task.startDate).toEqual(new Date(2024, 0, 2));
127
+ });
128
+ it('should update the start date correctly for daily recurrence on subtask', () => {
129
+ const task = new Task_1.default(new bson_1.ObjectId());
130
+ task.startDate = new Date(2024, 0, 8);
131
+ task.dueDate = new Date(2024, 0, 13);
132
+ task.recurrenceInfo = {
133
+ frequency: {
134
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXTimeUnit,
135
+ everyXTimeUnit: {
136
+ timeUnit: 'day',
137
+ x: 1
138
+ }
139
+ },
140
+ recurrenceBasis: RecurrenceInfo_1.RecurrenceBasis.startDate,
141
+ recurrenceEffect: RecurrenceInfo_1.RecurrenceEffect.rollOnBasis
142
+ };
143
+ task.parentRecurringTaskInfo = {
144
+ taskId: new bson_1.ObjectId(),
145
+ startDate: new Date(2024, 0, 1)
146
+ };
147
+ TaskService_1.default.updateDatesForRecurrence(task);
148
+ expect(task.startDate).toEqual(new Date(2024, 0, 9));
149
+ expect(task.dueDate).toEqual(new Date(2024, 0, 14));
150
+ });
151
+ it('should update the start date correctly for a weekly recurrence', () => {
152
+ const task = new Task_1.default(new bson_1.ObjectId());
153
+ task.startDate = new Date(2024, 0, 1);
154
+ task.recurrenceInfo = {
155
+ frequency: {
156
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXTimeUnit,
157
+ everyXTimeUnit: {
158
+ timeUnit: 'week',
159
+ x: 1
160
+ }
161
+ },
162
+ recurrenceBasis: RecurrenceInfo_1.RecurrenceBasis.startDate,
163
+ recurrenceEffect: RecurrenceInfo_1.RecurrenceEffect.rollOnBasis
164
+ };
165
+ TaskService_1.default.updateDatesForRecurrence(task);
166
+ expect(task.startDate).toEqual(new Date(2024, 0, 8));
167
+ });
168
+ it('should update the start and due date correctly for a weekDaySet reccurence', () => {
169
+ const task = new Task_1.default(new bson_1.ObjectId());
170
+ task.startDate = new Date(2024, 0, 1, 11);
171
+ task.dueDate = new Date(2024, 0, 4, 23, 59);
172
+ task.recurrenceInfo = {
173
+ frequency: {
174
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.weekDaySet,
175
+ weekDaySet: [0, 5]
176
+ },
177
+ recurrenceBasis: RecurrenceInfo_1.RecurrenceBasis.startDate,
178
+ recurrenceEffect: RecurrenceInfo_1.RecurrenceEffect.rollOnBasis
179
+ };
180
+ TaskService_1.default.updateDatesForRecurrence(task);
181
+ expect(task.startDate).toEqual(new Date(2024, 0, 5, 11));
182
+ expect(task.dueDate).toEqual(new Date(2024, 0, 8, 23, 59));
183
+ });
184
+ it('should update the start and due date correctly for a everyXWeekdayOfMonth reccurence', () => {
185
+ const task = new Task_1.default(new bson_1.ObjectId());
186
+ task.startDate = new Date(2024, 0, 1, 11);
187
+ task.dueDate = new Date(2024, 0, 4, 23, 59);
188
+ task.recurrenceInfo = {
189
+ frequency: {
190
+ type: RecurrenceInfo_1.RecurrenceFrequencyType.everyXWeekdayOfMonth,
191
+ everyXWeekdayOfMonth: {
192
+ weekDay: 0,
193
+ weekOfMonth: 1
194
+ }
195
+ },
196
+ recurrenceBasis: RecurrenceInfo_1.RecurrenceBasis.startDate,
197
+ recurrenceEffect: RecurrenceInfo_1.RecurrenceEffect.rollOnBasis
198
+ };
199
+ TaskService_1.default.updateDatesForRecurrence(task);
200
+ expect(task.startDate).toEqual(new Date(2024, 0, 7, 11));
201
+ expect(task.dueDate).toEqual(new Date(2024, 0, 10, 23, 59));
202
+ });
203
+ });
204
+ });
205
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aneuhold/core-ts-db-lib",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "description": "A core database library used for personal projects",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -26,12 +26,15 @@
26
26
  "build": "tsc",
27
27
  "watch": "tsc -w",
28
28
  "link:local": "cd lib && yarn link",
29
+ "link:core-ts-lib": "yarn link @aneuhold/core-ts-lib",
29
30
  "unlink:local": "cd lib && yarn unlink",
31
+ "unlink:core-ts-lib": "yarn unlink @aneuhold/core-ts-lib && yarn install --force",
30
32
  "upgrade:all": "yarn upgrade --latest",
31
33
  "upgrade:core": "yarn upgrade --latest --pattern @aneuhold",
32
34
  "test": "jest"
33
35
  },
34
36
  "dependencies": {
37
+ "@aneuhold/core-ts-lib": "^1.1.10",
35
38
  "bson": "^6.2.0"
36
39
  },
37
40
  "devDependencies": {