@aneuhold/be-ts-db-lib 2.0.78 → 2.0.79

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 🔖 [2.0.79] (2025-08-18)
9
+
10
+ ### ✅ Added
11
+
12
+ - Added `DemoAccountsService` and `DashboardDemoAccountsService` for seeding demo accounts and demo data for dashboard users
13
+ - Utilities to create, reset, and seed demo users with example tasks, collaborators, and user configs
14
+ - Shared and non-shared demo task creation logic for onboarding/testing
15
+
8
16
  ## 🔖 [2.0.78] (2025-07-04)
9
17
 
10
18
  ### 🏗️ Changed
@@ -38,6 +46,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
38
46
 
39
47
  <!-- Link References -->
40
48
 
49
+ [2.0.79]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v2.0.78...be-ts-db-lib-v2.0.79
41
50
  [2.0.78]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v2.0.77...be-ts-db-lib-v2.0.78
42
51
  [2.0.77]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v2.0.76...be-ts-db-lib-v2.0.77
43
52
  [2.0.76]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v2.0.75...be-ts-db-lib-v2.0.76
@@ -0,0 +1,72 @@
1
+ import { ObjectId } from 'bson';
2
+ /**
3
+ * Dashboard-specific demo seeding utilities.
4
+ */
5
+ export default class DashboardDemoAccountsService {
6
+ /**
7
+ * Default priorities for known demo tags. If a tag is not listed here,
8
+ * a fallback priority will be used.
9
+ */
10
+ private static readonly TAG_PRIORITY;
11
+ /**
12
+ * Seeds the dashboard demo data for two users. This will:
13
+ * - Ensure both users have configs and are collaborators of each other
14
+ * - Reset select config flags (enableDevMode, useConfettiForTasks, catImageOnHomePage)
15
+ * - Wipe all tasks owned by either user
16
+ * - Create example tasks (shared, nested, completed, with tags)
17
+ *
18
+ * Safe to re-run; will produce the same end state.
19
+ *
20
+ * @param demoUser1Id The first user
21
+ * @param demoUser2Id The second user
22
+ */
23
+ static seedDemoAccounts(demoUser1Id: ObjectId, demoUser2Id: ObjectId): Promise<void>;
24
+ /**
25
+ * Ensures user has a config with the collaborator, and resets specific flags.
26
+ *
27
+ * @param ownerId The config owner
28
+ * @param collaboratorId The collaborator to include
29
+ */
30
+ private static ensureCollaboratorsAndResetConfig;
31
+ /**
32
+ * Deletes all tasks owned by the provided users. Children are also removed
33
+ * since they share the same owner.
34
+ *
35
+ * @param userIds The users to wipe tasks for
36
+ */
37
+ private static wipeTasksForUsers;
38
+ /**
39
+ * Creates a task owned by the specified user. Because we wipe all tasks
40
+ * for the demo users prior to creation, we don't need to check for
41
+ * existing tasks and can always insert.
42
+ *
43
+ * @param ownerId The owning user ID
44
+ * @param title The task title
45
+ * @param opts Additional optional task properties
46
+ * @param opts.description Description text
47
+ * @param opts.completed Completion flag
48
+ * @param opts.parentTaskId Parent task reference for subtasks
49
+ * @param opts.sharedWith Users the task is shared with
50
+ * @param opts.assignedTo The assigned user
51
+ * @param opts.tags Tags for the owning user
52
+ * @param opts.startDate Optional start date
53
+ * @param opts.dueDate Optional due date
54
+ * @param opts.category System category
55
+ */
56
+ private static createTask;
57
+ /**
58
+ * Creates shared demo tasks visible to both users exactly once to avoid duplicates.
59
+ *
60
+ * @param user1Id The primary owner of some shared tasks
61
+ * @param user2Id The collaborator for shared tasks
62
+ */
63
+ private static createExampleTasks;
64
+ /**
65
+ * Creates non-shared demo tasks for each user.
66
+ *
67
+ * @param user1Id The first user
68
+ * @param user2Id The second user
69
+ */
70
+ private static createNonSharedTasks;
71
+ }
72
+ //# sourceMappingURL=DashboardDemoAccountsService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DashboardDemoAccountsService.d.ts","sourceRoot":"","sources":["../../../src/services/DemoAccountsService/DashboardDemoAccountsService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAIhC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,4BAA4B;IAC/C;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAUlC;IAEF;;;;;;;;;;;OAWG;WACU,gBAAgB,CAAC,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1F;;;;;OAKG;mBACkB,iCAAiC;IA4BtD;;;;;OAKG;mBACkB,iBAAiB;IAYtC;;;;;;;;;;;;;;;;;OAiBG;mBACkB,UAAU;IAgC/B;;;;;OAKG;mBACkB,kBAAkB;IAqCvC;;;;;OAKG;mBACkB,oBAAoB;CAwD1C"}
@@ -0,0 +1,234 @@
1
+ import { DashboardTask, DashboardUserConfig } from '@aneuhold/core-ts-db-lib';
2
+ import DashboardTaskRepository from '../../repositories/dashboard/DashboardTaskRepository.js';
3
+ import DashboardUserConfigRepository from '../../repositories/dashboard/DashboardUserConfigRepository.js';
4
+ /**
5
+ * Dashboard-specific demo seeding utilities.
6
+ */
7
+ export default class DashboardDemoAccountsService {
8
+ /**
9
+ * Default priorities for known demo tags. If a tag is not listed here,
10
+ * a fallback priority will be used.
11
+ */
12
+ static TAG_PRIORITY = {
13
+ planning: 9,
14
+ fun: 8,
15
+ booking: 7,
16
+ food: 6,
17
+ home: 5,
18
+ bbq: 4,
19
+ admin: 3,
20
+ paint: 2,
21
+ electric: 1
22
+ };
23
+ /**
24
+ * Seeds the dashboard demo data for two users. This will:
25
+ * - Ensure both users have configs and are collaborators of each other
26
+ * - Reset select config flags (enableDevMode, useConfettiForTasks, catImageOnHomePage)
27
+ * - Wipe all tasks owned by either user
28
+ * - Create example tasks (shared, nested, completed, with tags)
29
+ *
30
+ * Safe to re-run; will produce the same end state.
31
+ *
32
+ * @param demoUser1Id The first user
33
+ * @param demoUser2Id The second user
34
+ */
35
+ static async seedDemoAccounts(demoUser1Id, demoUser2Id) {
36
+ await this.ensureCollaboratorsAndResetConfig(demoUser1Id, demoUser2Id);
37
+ await this.ensureCollaboratorsAndResetConfig(demoUser2Id, demoUser1Id);
38
+ await this.wipeTasksForUsers([demoUser1Id, demoUser2Id]);
39
+ // Create shared tasks (visible to both users) exactly once
40
+ await this.createExampleTasks(demoUser1Id, demoUser2Id);
41
+ // Create non-shared tasks for each user
42
+ await this.createNonSharedTasks(demoUser1Id, demoUser2Id);
43
+ }
44
+ /**
45
+ * Ensures user has a config with the collaborator, and resets specific flags.
46
+ *
47
+ * @param ownerId The config owner
48
+ * @param collaboratorId The collaborator to include
49
+ */
50
+ static async ensureCollaboratorsAndResetConfig(ownerId, collaboratorId) {
51
+ const cfgRepo = DashboardUserConfigRepository.getRepo();
52
+ let cfg = await cfgRepo.getForUser(ownerId);
53
+ let isNew = false;
54
+ if (!cfg) {
55
+ cfg = new DashboardUserConfig(ownerId);
56
+ isNew = true;
57
+ }
58
+ // Ensure collaborator and reset flags
59
+ cfg.collaborators = [collaboratorId];
60
+ cfg.enableDevMode = true;
61
+ cfg.enabledFeatures.useConfettiForTasks = true;
62
+ cfg.enabledFeatures.catImageOnHomePage = false;
63
+ // Reset tag settings and seed priorities for demo tags
64
+ cfg.tagSettings = {};
65
+ for (const [tag, priority] of Object.entries(this.TAG_PRIORITY)) {
66
+ cfg.tagSettings[tag] = { priority };
67
+ }
68
+ if (isNew) {
69
+ await cfgRepo.insertNew(cfg);
70
+ }
71
+ else {
72
+ await cfgRepo.update(cfg);
73
+ }
74
+ }
75
+ /**
76
+ * Deletes all tasks owned by the provided users. Children are also removed
77
+ * since they share the same owner.
78
+ *
79
+ * @param userIds The users to wipe tasks for
80
+ */
81
+ static async wipeTasksForUsers(userIds) {
82
+ const taskRepo = DashboardTaskRepository.getRepo();
83
+ // Delete per-user using repo.deleteList to leverage child cleanup logic.
84
+ for (const userId of userIds) {
85
+ const allVisible = await taskRepo.getAllForUser(userId);
86
+ const owned = allVisible.filter((t) => t.userId.equals(userId));
87
+ if (owned.length === 0)
88
+ continue;
89
+ const ids = owned.map((t) => t._id);
90
+ await taskRepo.deleteList(ids);
91
+ }
92
+ }
93
+ /**
94
+ * Creates a task owned by the specified user. Because we wipe all tasks
95
+ * for the demo users prior to creation, we don't need to check for
96
+ * existing tasks and can always insert.
97
+ *
98
+ * @param ownerId The owning user ID
99
+ * @param title The task title
100
+ * @param opts Additional optional task properties
101
+ * @param opts.description Description text
102
+ * @param opts.completed Completion flag
103
+ * @param opts.parentTaskId Parent task reference for subtasks
104
+ * @param opts.sharedWith Users the task is shared with
105
+ * @param opts.assignedTo The assigned user
106
+ * @param opts.tags Tags for the owning user
107
+ * @param opts.startDate Optional start date
108
+ * @param opts.dueDate Optional due date
109
+ * @param opts.category System category
110
+ */
111
+ static async createTask(ownerId, title, opts) {
112
+ const taskRepo = DashboardTaskRepository.getRepo();
113
+ const task = new DashboardTask(ownerId);
114
+ task.title = title;
115
+ if (opts?.description)
116
+ task.description = opts.description;
117
+ if (opts?.completed !== undefined)
118
+ task.completed = opts.completed;
119
+ if (opts?.parentTaskId)
120
+ task.parentTaskId = opts.parentTaskId;
121
+ if (opts?.sharedWith)
122
+ task.sharedWith = opts.sharedWith;
123
+ if (opts?.assignedTo)
124
+ task.assignedTo = opts.assignedTo;
125
+ if (opts?.category)
126
+ task.category = opts.category;
127
+ if (opts?.startDate)
128
+ task.startDate = opts.startDate;
129
+ if (opts?.dueDate)
130
+ task.dueDate = opts.dueDate;
131
+ task.tags[ownerId.toString()] = opts?.tags ?? [];
132
+ const inserted = await taskRepo.insertNew(task);
133
+ return inserted ?? task;
134
+ }
135
+ /**
136
+ * Creates shared demo tasks visible to both users exactly once to avoid duplicates.
137
+ *
138
+ * @param user1Id The primary owner of some shared tasks
139
+ * @param user2Id The collaborator for shared tasks
140
+ */
141
+ static async createExampleTasks(user1Id, user2Id) {
142
+ // Shared parent task (owned by user1, shared with user2)
143
+ const parent = await this.createTask(user1Id, 'Plan weekend trip', {
144
+ description: 'Decide destination and plan activities for the weekend.',
145
+ sharedWith: [user2Id],
146
+ tags: ['planning', 'fun']
147
+ });
148
+ const child1 = await this.createTask(user1Id, 'Book hotel', {
149
+ parentTaskId: parent._id,
150
+ tags: ['booking']
151
+ });
152
+ await this.createTask(user1Id, 'Create itinerary', {
153
+ parentTaskId: parent._id,
154
+ tags: ['planning']
155
+ });
156
+ await this.createTask(user1Id, 'Pack bags', { parentTaskId: parent._id });
157
+ await this.createTask(user1Id, 'Buy snacks', {
158
+ parentTaskId: child1._id,
159
+ completed: true,
160
+ tags: ['food']
161
+ });
162
+ // Shared task assigned to user2 (owned by user1)
163
+ await this.createTask(user1Id, 'Buy groceries for BBQ', {
164
+ sharedWith: [user2Id],
165
+ assignedTo: user2Id,
166
+ tags: ['home', 'bbq']
167
+ });
168
+ // A shared planning task owned by user2
169
+ await this.createTask(user2Id, 'Plan potluck dinner', {
170
+ sharedWith: [user1Id],
171
+ assignedTo: user1Id,
172
+ tags: ['food', 'planning']
173
+ });
174
+ }
175
+ /**
176
+ * Creates non-shared demo tasks for each user.
177
+ *
178
+ * @param user1Id The first user
179
+ * @param user2Id The second user
180
+ */
181
+ static async createNonSharedTasks(user1Id, user2Id) {
182
+ const now = new Date();
183
+ const futureDue = new Date(now.getTime());
184
+ futureDue.setMonth(futureDue.getMonth() + 4);
185
+ const pastDue = new Date(now.getTime());
186
+ const pastDueStart = new Date(now.getTime());
187
+ pastDue.setDate(pastDue.getDate() - 7);
188
+ pastDueStart.setDate(pastDueStart.getDate() - 14);
189
+ // User 1: A completed solo task
190
+ await this.createTask(user1Id, 'Renew car registration', {
191
+ completed: true,
192
+ tags: ['admin']
193
+ });
194
+ // User 2: Own parent task with subtasks (not shared)
195
+ const u2Parent = await this.createTask(user2Id, 'Home improvement project', {
196
+ description: 'Small updates around the house.',
197
+ tags: ['home']
198
+ });
199
+ await this.createTask(user2Id, 'Paint living room walls', {
200
+ parentTaskId: u2Parent._id,
201
+ completed: true,
202
+ tags: ['paint']
203
+ });
204
+ await this.createTask(user2Id, 'Replace light fixtures', {
205
+ parentTaskId: u2Parent._id,
206
+ tags: ['electric']
207
+ });
208
+ // Root task with a future due date (>= 4 months out)
209
+ await this.createTask(user1Id, 'Schedule annual physical', {
210
+ description: 'Book an appointment and add to calendar.',
211
+ dueDate: futureDue,
212
+ tags: ['admin']
213
+ });
214
+ await this.createTask(user2Id, 'Schedule annual physical', {
215
+ description: 'Book an appointment and add to calendar.',
216
+ dueDate: futureDue,
217
+ tags: ['admin']
218
+ });
219
+ // Past-due task
220
+ await this.createTask(user1Id, 'Submit expense report', {
221
+ description: 'Collect receipts and submit to finance.',
222
+ startDate: pastDueStart,
223
+ dueDate: pastDue,
224
+ tags: ['admin']
225
+ });
226
+ await this.createTask(user2Id, 'Submit expense report', {
227
+ description: 'Collect receipts and submit to finance.',
228
+ startDate: pastDueStart,
229
+ dueDate: pastDue,
230
+ tags: ['admin']
231
+ });
232
+ }
233
+ }
234
+ //# sourceMappingURL=DashboardDemoAccountsService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DashboardDemoAccountsService.js","sourceRoot":"","sources":["../../../src/services/DemoAccountsService/DashboardDemoAccountsService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,257 @@
1
+ import { DashboardTask, DashboardUserConfig } from '@aneuhold/core-ts-db-lib';
2
+ import { ObjectId } from 'bson';
3
+ import DashboardTaskRepository from '../../repositories/dashboard/DashboardTaskRepository.js';
4
+ import DashboardUserConfigRepository from '../../repositories/dashboard/DashboardUserConfigRepository.js';
5
+
6
+ /**
7
+ * Dashboard-specific demo seeding utilities.
8
+ */
9
+ export default class DashboardDemoAccountsService {
10
+ /**
11
+ * Default priorities for known demo tags. If a tag is not listed here,
12
+ * a fallback priority will be used.
13
+ */
14
+ private static readonly TAG_PRIORITY: Record<string, number> = {
15
+ planning: 9,
16
+ fun: 8,
17
+ booking: 7,
18
+ food: 6,
19
+ home: 5,
20
+ bbq: 4,
21
+ admin: 3,
22
+ paint: 2,
23
+ electric: 1
24
+ };
25
+
26
+ /**
27
+ * Seeds the dashboard demo data for two users. This will:
28
+ * - Ensure both users have configs and are collaborators of each other
29
+ * - Reset select config flags (enableDevMode, useConfettiForTasks, catImageOnHomePage)
30
+ * - Wipe all tasks owned by either user
31
+ * - Create example tasks (shared, nested, completed, with tags)
32
+ *
33
+ * Safe to re-run; will produce the same end state.
34
+ *
35
+ * @param demoUser1Id The first user
36
+ * @param demoUser2Id The second user
37
+ */
38
+ static async seedDemoAccounts(demoUser1Id: ObjectId, demoUser2Id: ObjectId): Promise<void> {
39
+ await this.ensureCollaboratorsAndResetConfig(demoUser1Id, demoUser2Id);
40
+ await this.ensureCollaboratorsAndResetConfig(demoUser2Id, demoUser1Id);
41
+
42
+ await this.wipeTasksForUsers([demoUser1Id, demoUser2Id]);
43
+
44
+ // Create shared tasks (visible to both users) exactly once
45
+ await this.createExampleTasks(demoUser1Id, demoUser2Id);
46
+ // Create non-shared tasks for each user
47
+ await this.createNonSharedTasks(demoUser1Id, demoUser2Id);
48
+ }
49
+
50
+ /**
51
+ * Ensures user has a config with the collaborator, and resets specific flags.
52
+ *
53
+ * @param ownerId The config owner
54
+ * @param collaboratorId The collaborator to include
55
+ */
56
+ private static async ensureCollaboratorsAndResetConfig(
57
+ ownerId: ObjectId,
58
+ collaboratorId: ObjectId
59
+ ): Promise<void> {
60
+ const cfgRepo = DashboardUserConfigRepository.getRepo();
61
+ let cfg = await cfgRepo.getForUser(ownerId);
62
+ let isNew = false;
63
+ if (!cfg) {
64
+ cfg = new DashboardUserConfig(ownerId);
65
+ isNew = true;
66
+ }
67
+ // Ensure collaborator and reset flags
68
+ cfg.collaborators = [collaboratorId];
69
+ cfg.enableDevMode = true;
70
+ cfg.enabledFeatures.useConfettiForTasks = true;
71
+ cfg.enabledFeatures.catImageOnHomePage = false;
72
+ // Reset tag settings and seed priorities for demo tags
73
+ cfg.tagSettings = {};
74
+ for (const [tag, priority] of Object.entries(this.TAG_PRIORITY)) {
75
+ cfg.tagSettings[tag] = { priority };
76
+ }
77
+ if (isNew) {
78
+ await cfgRepo.insertNew(cfg);
79
+ } else {
80
+ await cfgRepo.update(cfg);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Deletes all tasks owned by the provided users. Children are also removed
86
+ * since they share the same owner.
87
+ *
88
+ * @param userIds The users to wipe tasks for
89
+ */
90
+ private static async wipeTasksForUsers(userIds: ObjectId[]): Promise<void> {
91
+ const taskRepo = DashboardTaskRepository.getRepo();
92
+ // Delete per-user using repo.deleteList to leverage child cleanup logic.
93
+ for (const userId of userIds) {
94
+ const allVisible = await taskRepo.getAllForUser(userId);
95
+ const owned = allVisible.filter((t) => t.userId.equals(userId));
96
+ if (owned.length === 0) continue;
97
+ const ids = owned.map((t) => t._id);
98
+ await taskRepo.deleteList(ids);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Creates a task owned by the specified user. Because we wipe all tasks
104
+ * for the demo users prior to creation, we don't need to check for
105
+ * existing tasks and can always insert.
106
+ *
107
+ * @param ownerId The owning user ID
108
+ * @param title The task title
109
+ * @param opts Additional optional task properties
110
+ * @param opts.description Description text
111
+ * @param opts.completed Completion flag
112
+ * @param opts.parentTaskId Parent task reference for subtasks
113
+ * @param opts.sharedWith Users the task is shared with
114
+ * @param opts.assignedTo The assigned user
115
+ * @param opts.tags Tags for the owning user
116
+ * @param opts.startDate Optional start date
117
+ * @param opts.dueDate Optional due date
118
+ * @param opts.category System category
119
+ */
120
+ private static async createTask(
121
+ ownerId: ObjectId,
122
+ title: string,
123
+ opts?: {
124
+ description?: string;
125
+ completed?: boolean;
126
+ parentTaskId?: ObjectId;
127
+ sharedWith?: ObjectId[];
128
+ assignedTo?: ObjectId;
129
+ tags?: string[];
130
+ startDate?: Date;
131
+ dueDate?: Date;
132
+ category?: string;
133
+ }
134
+ ): Promise<DashboardTask> {
135
+ const taskRepo = DashboardTaskRepository.getRepo();
136
+ const task = new DashboardTask(ownerId);
137
+ task.title = title;
138
+ if (opts?.description) task.description = opts.description;
139
+ if (opts?.completed !== undefined) task.completed = opts.completed;
140
+ if (opts?.parentTaskId) task.parentTaskId = opts.parentTaskId;
141
+ if (opts?.sharedWith) task.sharedWith = opts.sharedWith;
142
+ if (opts?.assignedTo) task.assignedTo = opts.assignedTo;
143
+ if (opts?.category) task.category = opts.category;
144
+ if (opts?.startDate) task.startDate = opts.startDate;
145
+ if (opts?.dueDate) task.dueDate = opts.dueDate;
146
+ task.tags[ownerId.toString()] = opts?.tags ?? [];
147
+
148
+ const inserted = await taskRepo.insertNew(task);
149
+ return inserted ?? task;
150
+ }
151
+
152
+ /**
153
+ * Creates shared demo tasks visible to both users exactly once to avoid duplicates.
154
+ *
155
+ * @param user1Id The primary owner of some shared tasks
156
+ * @param user2Id The collaborator for shared tasks
157
+ */
158
+ private static async createExampleTasks(user1Id: ObjectId, user2Id: ObjectId) {
159
+ // Shared parent task (owned by user1, shared with user2)
160
+ const parent = await this.createTask(user1Id, 'Plan weekend trip', {
161
+ description: 'Decide destination and plan activities for the weekend.',
162
+ sharedWith: [user2Id],
163
+ tags: ['planning', 'fun']
164
+ });
165
+ const child1 = await this.createTask(user1Id, 'Book hotel', {
166
+ parentTaskId: parent._id,
167
+ tags: ['booking']
168
+ });
169
+ await this.createTask(user1Id, 'Create itinerary', {
170
+ parentTaskId: parent._id,
171
+ tags: ['planning']
172
+ });
173
+ await this.createTask(user1Id, 'Pack bags', { parentTaskId: parent._id });
174
+ await this.createTask(user1Id, 'Buy snacks', {
175
+ parentTaskId: child1._id,
176
+ completed: true,
177
+ tags: ['food']
178
+ });
179
+
180
+ // Shared task assigned to user2 (owned by user1)
181
+ await this.createTask(user1Id, 'Buy groceries for BBQ', {
182
+ sharedWith: [user2Id],
183
+ assignedTo: user2Id,
184
+ tags: ['home', 'bbq']
185
+ });
186
+
187
+ // A shared planning task owned by user2
188
+ await this.createTask(user2Id, 'Plan potluck dinner', {
189
+ sharedWith: [user1Id],
190
+ assignedTo: user1Id,
191
+ tags: ['food', 'planning']
192
+ });
193
+ }
194
+
195
+ /**
196
+ * Creates non-shared demo tasks for each user.
197
+ *
198
+ * @param user1Id The first user
199
+ * @param user2Id The second user
200
+ */
201
+ private static async createNonSharedTasks(user1Id: ObjectId, user2Id: ObjectId) {
202
+ const now = new Date();
203
+ const futureDue = new Date(now.getTime());
204
+ futureDue.setMonth(futureDue.getMonth() + 4);
205
+ const pastDue = new Date(now.getTime());
206
+ const pastDueStart = new Date(now.getTime());
207
+ pastDue.setDate(pastDue.getDate() - 7);
208
+ pastDueStart.setDate(pastDueStart.getDate() - 14);
209
+
210
+ // User 1: A completed solo task
211
+ await this.createTask(user1Id, 'Renew car registration', {
212
+ completed: true,
213
+ tags: ['admin']
214
+ });
215
+
216
+ // User 2: Own parent task with subtasks (not shared)
217
+ const u2Parent = await this.createTask(user2Id, 'Home improvement project', {
218
+ description: 'Small updates around the house.',
219
+ tags: ['home']
220
+ });
221
+ await this.createTask(user2Id, 'Paint living room walls', {
222
+ parentTaskId: u2Parent._id,
223
+ completed: true,
224
+ tags: ['paint']
225
+ });
226
+ await this.createTask(user2Id, 'Replace light fixtures', {
227
+ parentTaskId: u2Parent._id,
228
+ tags: ['electric']
229
+ });
230
+
231
+ // Root task with a future due date (>= 4 months out)
232
+ await this.createTask(user1Id, 'Schedule annual physical', {
233
+ description: 'Book an appointment and add to calendar.',
234
+ dueDate: futureDue,
235
+ tags: ['admin']
236
+ });
237
+ await this.createTask(user2Id, 'Schedule annual physical', {
238
+ description: 'Book an appointment and add to calendar.',
239
+ dueDate: futureDue,
240
+ tags: ['admin']
241
+ });
242
+
243
+ // Past-due task
244
+ await this.createTask(user1Id, 'Submit expense report', {
245
+ description: 'Collect receipts and submit to finance.',
246
+ startDate: pastDueStart,
247
+ dueDate: pastDue,
248
+ tags: ['admin']
249
+ });
250
+ await this.createTask(user2Id, 'Submit expense report', {
251
+ description: 'Collect receipts and submit to finance.',
252
+ startDate: pastDueStart,
253
+ dueDate: pastDue,
254
+ tags: ['admin']
255
+ });
256
+ }
257
+ }
@@ -0,0 +1,17 @@
1
+ import { ObjectId } from 'bson';
2
+ /**
3
+ * Entry point for seeding demo accounts across projects.
4
+ *
5
+ * This class exposes static helpers that delegate to project-specific
6
+ * implementations (e.g., dashboard).
7
+ */
8
+ export default class DemoAccountsService {
9
+ /**
10
+ * Seeds demo accounts for the Dashboard project only.
11
+ *
12
+ * @param demoUser1Id The first demo user ID
13
+ * @param demoUser2Id The second demo user ID
14
+ */
15
+ static seedDashboardDemoAccounts(demoUser1Id: ObjectId, demoUser2Id: ObjectId): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=DemoAccountsService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DemoAccountsService.d.ts","sourceRoot":"","sources":["../../../src/services/DemoAccountsService/DemoAccountsService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAGhC;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IACtC;;;;;OAKG;WACU,yBAAyB,CACpC,WAAW,EAAE,QAAQ,EACrB,WAAW,EAAE,QAAQ,GACpB,OAAO,CAAC,IAAI,CAAC;CAGjB"}
@@ -0,0 +1,19 @@
1
+ import DashboardDemoAccountsService from './DashboardDemoAccountsService.js';
2
+ /**
3
+ * Entry point for seeding demo accounts across projects.
4
+ *
5
+ * This class exposes static helpers that delegate to project-specific
6
+ * implementations (e.g., dashboard).
7
+ */
8
+ export default class DemoAccountsService {
9
+ /**
10
+ * Seeds demo accounts for the Dashboard project only.
11
+ *
12
+ * @param demoUser1Id The first demo user ID
13
+ * @param demoUser2Id The second demo user ID
14
+ */
15
+ static async seedDashboardDemoAccounts(demoUser1Id, demoUser2Id) {
16
+ await DashboardDemoAccountsService.seedDemoAccounts(demoUser1Id, demoUser2Id);
17
+ }
18
+ }
19
+ //# sourceMappingURL=DemoAccountsService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DemoAccountsService.js","sourceRoot":"","sources":["../../../src/services/DemoAccountsService/DemoAccountsService.ts"],"names":[],"mappings":"AACA,OAAO,4BAA4B,MAAM,mCAAmC,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IACtC;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,yBAAyB,CACpC,WAAqB,EACrB,WAAqB;QAErB,MAAM,4BAA4B,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAChF,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ import { ObjectId } from 'bson';
2
+ import DashboardDemoAccountsService from './DashboardDemoAccountsService.js';
3
+
4
+ /**
5
+ * Entry point for seeding demo accounts across projects.
6
+ *
7
+ * This class exposes static helpers that delegate to project-specific
8
+ * implementations (e.g., dashboard).
9
+ */
10
+ export default class DemoAccountsService {
11
+ /**
12
+ * Seeds demo accounts for the Dashboard project only.
13
+ *
14
+ * @param demoUser1Id The first demo user ID
15
+ * @param demoUser2Id The second demo user ID
16
+ */
17
+ static async seedDashboardDemoAccounts(
18
+ demoUser1Id: ObjectId,
19
+ demoUser2Id: ObjectId
20
+ ): Promise<void> {
21
+ await DashboardDemoAccountsService.seedDemoAccounts(demoUser1Id, demoUser2Id);
22
+ }
23
+ }
@@ -0,0 +1,2 @@
1
+ export { default as DemoAccountsService } from './DemoAccountsService.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/DemoAccountsService/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default as DemoAccountsService } from './DemoAccountsService.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/DemoAccountsService/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1 @@
1
+ export { default as DemoAccountsService } from './DemoAccountsService.js';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@aneuhold/be-ts-db-lib",
3
3
  "author": "Anton G. Neuhold Jr.",
4
4
  "license": "MIT",
5
- "version": "2.0.78",
5
+ "version": "2.0.79",
6
6
  "description": "A backend database library meant to actually interact with various databases in personal projects",
7
7
  "packageManager": "pnpm@10.12.1",
8
8
  "type": "module",