@flusys/nestjs-event-manager 3.0.1

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.
Files changed (89) hide show
  1. package/README.md +1328 -0
  2. package/adapters/event-manager.adapter.d.ts +12 -0
  3. package/adapters/index.d.ts +1 -0
  4. package/cjs/adapters/event-manager.adapter.js +71 -0
  5. package/cjs/adapters/index.js +18 -0
  6. package/cjs/config/event-manager.constants.js +13 -0
  7. package/cjs/config/index.js +18 -0
  8. package/cjs/controllers/event-participant.controller.js +271 -0
  9. package/cjs/controllers/event.controller.js +229 -0
  10. package/cjs/controllers/index.js +19 -0
  11. package/cjs/docs/event-manager-swagger.config.js +135 -0
  12. package/cjs/docs/index.js +24 -0
  13. package/cjs/dtos/calendar-query.dto.js +186 -0
  14. package/cjs/dtos/event-participant.dto.js +226 -0
  15. package/cjs/dtos/event.dto.js +291 -0
  16. package/cjs/dtos/index.js +20 -0
  17. package/cjs/entities/event-participant.entity.js +96 -0
  18. package/cjs/entities/event-with-company.entity.js +56 -0
  19. package/cjs/entities/event.entity.js +143 -0
  20. package/cjs/entities/index.js +55 -0
  21. package/cjs/index.js +27 -0
  22. package/cjs/interfaces/event-manager-module.interface.js +4 -0
  23. package/cjs/interfaces/event.interface.js +4 -0
  24. package/cjs/interfaces/index.js +19 -0
  25. package/cjs/modules/event-manager.module.js +133 -0
  26. package/cjs/modules/index.js +18 -0
  27. package/cjs/services/event-manager-config.service.js +76 -0
  28. package/cjs/services/event-manager-datasource.provider.js +181 -0
  29. package/cjs/services/event-manager-helper.service.js +253 -0
  30. package/cjs/services/event-participant.service.js +164 -0
  31. package/cjs/services/event.service.js +421 -0
  32. package/cjs/services/index.js +22 -0
  33. package/config/event-manager.constants.d.ts +1 -0
  34. package/config/index.d.ts +1 -0
  35. package/controllers/event-participant.controller.d.ts +31 -0
  36. package/controllers/event.controller.d.ts +24 -0
  37. package/controllers/index.d.ts +2 -0
  38. package/docs/event-manager-swagger.config.d.ts +3 -0
  39. package/docs/index.d.ts +2 -0
  40. package/dtos/calendar-query.dto.d.ts +23 -0
  41. package/dtos/event-participant.dto.d.ts +31 -0
  42. package/dtos/event.dto.d.ts +37 -0
  43. package/dtos/index.d.ts +3 -0
  44. package/entities/event-participant.entity.d.ts +8 -0
  45. package/entities/event-with-company.entity.d.ts +4 -0
  46. package/entities/event.entity.d.ts +14 -0
  47. package/entities/index.d.ts +11 -0
  48. package/fesm/adapters/event-manager.adapter.js +61 -0
  49. package/fesm/adapters/index.js +1 -0
  50. package/fesm/config/event-manager.constants.js +3 -0
  51. package/fesm/config/index.js +1 -0
  52. package/fesm/controllers/event-participant.controller.js +261 -0
  53. package/fesm/controllers/event.controller.js +219 -0
  54. package/fesm/controllers/index.js +2 -0
  55. package/fesm/docs/event-manager-swagger.config.js +128 -0
  56. package/fesm/docs/index.js +4 -0
  57. package/fesm/dtos/calendar-query.dto.js +168 -0
  58. package/fesm/dtos/event-participant.dto.js +199 -0
  59. package/fesm/dtos/event.dto.js +270 -0
  60. package/fesm/dtos/index.js +3 -0
  61. package/fesm/entities/event-participant.entity.js +86 -0
  62. package/fesm/entities/event-with-company.entity.js +46 -0
  63. package/fesm/entities/event.entity.js +133 -0
  64. package/fesm/entities/index.js +24 -0
  65. package/fesm/index.js +10 -0
  66. package/fesm/interfaces/event-manager-module.interface.js +3 -0
  67. package/fesm/interfaces/event.interface.js +1 -0
  68. package/fesm/interfaces/index.js +2 -0
  69. package/fesm/modules/event-manager.module.js +123 -0
  70. package/fesm/modules/index.js +1 -0
  71. package/fesm/services/event-manager-config.service.js +66 -0
  72. package/fesm/services/event-manager-datasource.provider.js +130 -0
  73. package/fesm/services/event-manager-helper.service.js +243 -0
  74. package/fesm/services/event-participant.service.js +154 -0
  75. package/fesm/services/event.service.js +411 -0
  76. package/fesm/services/index.js +5 -0
  77. package/index.d.ts +9 -0
  78. package/interfaces/event-manager-module.interface.d.ts +20 -0
  79. package/interfaces/event.interface.d.ts +28 -0
  80. package/interfaces/index.d.ts +2 -0
  81. package/modules/event-manager.module.d.ts +8 -0
  82. package/modules/index.d.ts +1 -0
  83. package/package.json +89 -0
  84. package/services/event-manager-config.service.d.ts +13 -0
  85. package/services/event-manager-datasource.provider.d.ts +23 -0
  86. package/services/event-manager-helper.service.d.ts +27 -0
  87. package/services/event-participant.service.d.ts +27 -0
  88. package/services/event.service.d.ts +49 -0
  89. package/services/index.d.ts +5 -0
@@ -0,0 +1,411 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _ts_decorate(decorators, target, key, desc) {
15
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
19
+ }
20
+ function _ts_metadata(k, v) {
21
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
+ }
23
+ function _ts_param(paramIndex, decorator) {
24
+ return function(target, key) {
25
+ decorator(target, key, paramIndex);
26
+ };
27
+ }
28
+ import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
29
+ import { RecurrenceType } from '@flusys/nestjs-shared/enums';
30
+ import { UtilsService } from '@flusys/nestjs-shared/modules';
31
+ import { applyCompanyFilter } from '@flusys/nestjs-shared/utils';
32
+ import { Inject, Injectable, NotFoundException, Scope } from '@nestjs/common';
33
+ import { In, IsNull } from 'typeorm';
34
+ import { EventManagerConfigService } from './event-manager-config.service';
35
+ import { Event, EventWithCompany, EventParticipant } from '../entities';
36
+ import { EventManagerDataSourceProvider } from './event-manager-datasource.provider';
37
+ export class EventService extends RequestScopedApiService {
38
+ // Abstract implementations
39
+ resolveEntity() {
40
+ return this.eventConfig.isCompanyFeatureEnabled() ? EventWithCompany : Event;
41
+ }
42
+ getDataSourceProvider() {
43
+ return this.dataSourceProvider;
44
+ }
45
+ // Override methods
46
+ async convertSingleDtoToEntity(dto, user) {
47
+ let entity = {};
48
+ if ('id' in dto && dto.id && typeof dto.id === 'string') {
49
+ const existing = await this.repository.findOne({
50
+ where: {
51
+ id: dto.id
52
+ }
53
+ });
54
+ if (!existing) throw new NotFoundException('Event not found');
55
+ entity = existing;
56
+ }
57
+ const merged = {
58
+ ...entity,
59
+ title: dto.title ?? entity.title,
60
+ description: dto.description ?? entity.description,
61
+ meetingLink: dto.meetingLink ?? entity.meetingLink,
62
+ startDateTime: dto.startDateTime ?? entity.startDateTime,
63
+ endDateTime: dto.endDateTime ?? entity.endDateTime,
64
+ isAllDay: dto.isAllDay ?? entity.isAllDay ?? false,
65
+ recurrenceType: dto.recurrenceType ?? entity.recurrenceType ?? RecurrenceType.NONE,
66
+ recurrenceEndDate: dto.recurrenceEndDate ?? entity.recurrenceEndDate,
67
+ color: dto.color ?? entity.color ?? this.eventConfig.getDefaultColor(),
68
+ isActive: dto.isActive ?? entity.isActive ?? true,
69
+ metadata: dto.metadata ?? entity.metadata
70
+ };
71
+ if (this.eventConfig.isCompanyFeatureEnabled()) {
72
+ merged.companyId = user?.companyId ?? null;
73
+ }
74
+ if ('participantIds' in dto) {
75
+ this.pendingParticipantsList.push(dto.participantIds ?? []);
76
+ }
77
+ return merged;
78
+ }
79
+ async getFilterQuery(query, filter, _user) {
80
+ for (const [key, value] of Object.entries(filter)){
81
+ if (key === 'isActive' && typeof value === 'boolean') {
82
+ query.andWhere(`${this.entityName}.isActive = :isActive`, {
83
+ isActive: value
84
+ });
85
+ } else if (key === 'recurrenceType') {
86
+ query.andWhere(`${this.entityName}.recurrenceType = :recurrenceType`, {
87
+ recurrenceType: value
88
+ });
89
+ } else if (key === 'participantUserId' && value) {
90
+ const eventIds = await this.getParticipantEventIds(value);
91
+ if (eventIds.length > 0) {
92
+ query.andWhere(`${this.entityName}.id IN (:...participantEventIds)`, {
93
+ participantEventIds: eventIds
94
+ });
95
+ } else {
96
+ query.andWhere('1 = 0');
97
+ }
98
+ } else {
99
+ query.andWhere(`${this.entityName}.${key} = :value`, {
100
+ value
101
+ });
102
+ }
103
+ }
104
+ return {
105
+ query,
106
+ isRaw: false
107
+ };
108
+ }
109
+ async getExtraManipulateQuery(query, filterDto, user) {
110
+ const result = await super.getExtraManipulateQuery(query, filterDto, user);
111
+ this.applyEventCompanyFilter(query, user);
112
+ return result;
113
+ }
114
+ async afterInsertOperation(entities, user, _qr) {
115
+ for(let i = 0; i < entities.length; i++){
116
+ const participantIds = this.pendingParticipantsList[i] ?? [];
117
+ await this.addCreatorAndParticipants(entities[i].id, participantIds, user);
118
+ }
119
+ this.pendingParticipantsList = [];
120
+ }
121
+ // Public API methods
122
+ async getEventsForCalendarRange(queryDto, user) {
123
+ await this.ensureRepositoryInitialized();
124
+ const query = this.repository.createQueryBuilder('event').where('event.deletedAt IS NULL').andWhere('event.isActive = :isActive', {
125
+ isActive: queryDto.activeOnly !== false
126
+ });
127
+ query.andWhere('(event.startDateTime <= :endDate AND event.endDateTime >= :startDate)', {
128
+ startDate: queryDto.startDate,
129
+ endDate: queryDto.endDate
130
+ });
131
+ query.orWhere('event.deletedAt IS NULL AND event.recurrenceType != :none AND event.startDateTime <= :endDate AND (event.recurrenceEndDate IS NULL OR event.recurrenceEndDate >= :startDate)', {
132
+ none: RecurrenceType.NONE,
133
+ startDate: queryDto.startDate,
134
+ endDate: queryDto.endDate
135
+ });
136
+ this.applyEventCompanyFilter(query, user);
137
+ if (queryDto.userId) {
138
+ const eventIds = await this.getParticipantEventIds(queryDto.userId);
139
+ if (eventIds.length > 0) {
140
+ query.andWhere('event.id IN (:...eventIds)', {
141
+ eventIds
142
+ });
143
+ } else {
144
+ return [];
145
+ }
146
+ }
147
+ const events = await query.getMany();
148
+ if (queryDto.includeParticipants && events.length > 0) {
149
+ await this.loadParticipantsForEvents(events);
150
+ }
151
+ return this.expandRecurringEvents(events, queryDto.startDate, queryDto.endDate);
152
+ }
153
+ async getEventWithParticipants(id, user) {
154
+ await this.ensureRepositoryInitialized();
155
+ const query = this.repository.createQueryBuilder('event').where('event.id = :id', {
156
+ id
157
+ }).andWhere('event.deletedAt IS NULL');
158
+ this.applyEventCompanyFilter(query, user);
159
+ const event = await query.getOne();
160
+ if (event) {
161
+ const participantRepo = await this.ensureParticipantRepositoryInitialized();
162
+ event.participants = await participantRepo.find({
163
+ where: {
164
+ eventId: id,
165
+ deletedAt: IsNull()
166
+ }
167
+ });
168
+ }
169
+ return event;
170
+ }
171
+ async addParticipants(eventId, userIds, _user) {
172
+ await this.ensureRepositoryInitialized();
173
+ const participantRepo = await this.ensureParticipantRepositoryInitialized();
174
+ const event = await this.repository.findOne({
175
+ where: {
176
+ id: eventId,
177
+ deletedAt: IsNull()
178
+ }
179
+ });
180
+ if (!event) {
181
+ throw new NotFoundException('Event not found');
182
+ }
183
+ const existingParticipants = await participantRepo.find({
184
+ where: {
185
+ eventId,
186
+ userId: In(userIds),
187
+ deletedAt: IsNull()
188
+ }
189
+ });
190
+ const existingUserIds = new Set(existingParticipants.map((p)=>p.userId));
191
+ const newParticipants = userIds.filter((userId)=>!existingUserIds.has(userId)).map((userId)=>({
192
+ eventId,
193
+ userId,
194
+ isOrganizer: false
195
+ }));
196
+ if (newParticipants.length === 0) {
197
+ return existingParticipants;
198
+ }
199
+ const created = await participantRepo.save(newParticipants);
200
+ return [
201
+ ...existingParticipants,
202
+ ...created
203
+ ];
204
+ }
205
+ // Private business logic helpers
206
+ async addCreatorAndParticipants(eventId, participantIds, user) {
207
+ if (!user?.id) return;
208
+ const participantRepo = await this.ensureParticipantRepositoryInitialized();
209
+ const allUserIds = new Set([
210
+ user.id,
211
+ ...participantIds
212
+ ]);
213
+ const participants = Array.from(allUserIds).map((userId)=>({
214
+ eventId,
215
+ userId,
216
+ isOrganizer: userId === user.id
217
+ }));
218
+ await participantRepo.save(participants);
219
+ }
220
+ expandRecurringEvents(events, rangeStart, rangeEnd) {
221
+ const result = [];
222
+ const maxOccurrences = this.eventConfig.getMaxRecurrenceOccurrences();
223
+ for (const event of events){
224
+ if (event.recurrenceType === RecurrenceType.NONE) {
225
+ result.push(this.toCalendarEvent(event));
226
+ } else {
227
+ result.push(...this.generateOccurrences(event, rangeStart, rangeEnd, maxOccurrences));
228
+ }
229
+ }
230
+ return result.sort((a, b)=>new Date(a.startDateTime).getTime() - new Date(b.startDateTime).getTime());
231
+ }
232
+ generateOccurrences(event, rangeStart, rangeEnd, maxOccurrences) {
233
+ const occurrences = [];
234
+ const eventDuration = new Date(event.endDateTime).getTime() - new Date(event.startDateTime).getTime();
235
+ const recurrenceEnd = event.recurrenceEndDate ? new Date(event.recurrenceEndDate) : null;
236
+ const recurrenceType = event.recurrenceType;
237
+ const recurrenceDays = event.metadata?.recurrenceDays || [
238
+ new Date(event.startDateTime).getDay()
239
+ ];
240
+ let currentStart = new Date(event.startDateTime);
241
+ let count = 0;
242
+ if (recurrenceType === RecurrenceType.WEEKLY || recurrenceType === RecurrenceType.BIWEEKLY) {
243
+ let weekStart = new Date(event.startDateTime);
244
+ weekStart.setDate(weekStart.getDate() - weekStart.getDay());
245
+ while(count < maxOccurrences){
246
+ for (const dayOfWeek of recurrenceDays){
247
+ if (count >= maxOccurrences) break;
248
+ currentStart = new Date(weekStart);
249
+ currentStart.setDate(currentStart.getDate() + dayOfWeek);
250
+ currentStart.setHours(new Date(event.startDateTime).getHours());
251
+ currentStart.setMinutes(new Date(event.startDateTime).getMinutes());
252
+ if (currentStart < new Date(event.startDateTime)) continue;
253
+ const shouldBreak = this.shouldStopRecurrence(currentStart, recurrenceEnd, rangeEnd);
254
+ if (shouldBreak) {
255
+ count = maxOccurrences;
256
+ break;
257
+ }
258
+ const occurrence = this.tryCreateOccurrence(event, currentStart, eventDuration, rangeStart, rangeEnd, occurrences.length > 0);
259
+ if (occurrence) occurrences.push(occurrence);
260
+ count++;
261
+ }
262
+ const weeksToAdd = recurrenceType === RecurrenceType.BIWEEKLY ? 2 : 1;
263
+ weekStart.setDate(weekStart.getDate() + 7 * weeksToAdd);
264
+ }
265
+ } else {
266
+ const originalDayOfMonth = new Date(event.startDateTime).getDate();
267
+ while(count < maxOccurrences){
268
+ if (this.shouldStopRecurrence(currentStart, recurrenceEnd, rangeEnd)) break;
269
+ const occurrence = this.tryCreateOccurrence(event, currentStart, eventDuration, rangeStart, rangeEnd, count > 0);
270
+ if (occurrence) occurrences.push(occurrence);
271
+ currentStart = this.getNextOccurrenceDate(currentStart, recurrenceType, originalDayOfMonth);
272
+ count++;
273
+ }
274
+ }
275
+ return occurrences;
276
+ }
277
+ // Private utility helpers
278
+ async ensureParticipantRepositoryInitialized() {
279
+ if (!this.participantRepository) {
280
+ this.participantRepository = await this.dataSourceProvider.getRepository(EventParticipant);
281
+ }
282
+ return this.participantRepository;
283
+ }
284
+ async getParticipantEventIds(userId) {
285
+ const participantRepo = await this.ensureParticipantRepositoryInitialized();
286
+ const results = await participantRepo.createQueryBuilder('ep').select('ep.eventId').where('ep.userId = :userId', {
287
+ userId
288
+ }).andWhere('ep.deletedAt IS NULL').getRawMany();
289
+ return results.map((p)=>p.ep_event_id);
290
+ }
291
+ async loadParticipantsForEvents(events) {
292
+ const participantRepo = await this.ensureParticipantRepositoryInitialized();
293
+ const eventIds = events.map((e)=>e.id);
294
+ const participants = await participantRepo.find({
295
+ where: {
296
+ eventId: In(eventIds),
297
+ deletedAt: IsNull()
298
+ }
299
+ });
300
+ const participantsByEvent = new Map();
301
+ for (const p of participants){
302
+ const list = participantsByEvent.get(p.eventId) || [];
303
+ list.push(p);
304
+ participantsByEvent.set(p.eventId, list);
305
+ }
306
+ for (const event of events){
307
+ event.participants = participantsByEvent.get(event.id) || [];
308
+ }
309
+ }
310
+ applyEventCompanyFilter(query, user) {
311
+ applyCompanyFilter(query, {
312
+ isCompanyFeatureEnabled: this.eventConfig.isCompanyFeatureEnabled(),
313
+ entityAlias: 'event'
314
+ }, user);
315
+ }
316
+ shouldStopRecurrence(currentStart, recurrenceEnd, rangeEnd) {
317
+ return recurrenceEnd && currentStart > recurrenceEnd || currentStart > rangeEnd;
318
+ }
319
+ tryCreateOccurrence(event, currentStart, eventDuration, rangeStart, rangeEnd, isRecurrenceInstance) {
320
+ const currentEnd = new Date(currentStart.getTime() + eventDuration);
321
+ if (currentEnd >= rangeStart && currentStart <= rangeEnd) {
322
+ return {
323
+ ...this.toCalendarEvent(event),
324
+ id: `${event.id}_${currentStart.toISOString()}`,
325
+ originalEventId: event.id,
326
+ isRecurrenceInstance,
327
+ startDateTime: new Date(currentStart),
328
+ endDateTime: currentEnd
329
+ };
330
+ }
331
+ return null;
332
+ }
333
+ getNextOccurrenceDate(current, recurrenceType, originalDayOfMonth) {
334
+ const next = new Date(current);
335
+ switch(recurrenceType){
336
+ case RecurrenceType.DAILY:
337
+ next.setDate(next.getDate() + 1);
338
+ break;
339
+ case RecurrenceType.MONTHLY:
340
+ {
341
+ const targetDay = originalDayOfMonth ?? current.getDate();
342
+ next.setMonth(next.getMonth() + 1);
343
+ const lastDayOfMonth = new Date(next.getFullYear(), next.getMonth() + 1, 0).getDate();
344
+ next.setDate(Math.min(targetDay, lastDayOfMonth));
345
+ break;
346
+ }
347
+ default:
348
+ break;
349
+ }
350
+ return next;
351
+ }
352
+ // Private transformation helpers
353
+ toCalendarEvent(event) {
354
+ return {
355
+ id: event.id,
356
+ title: event.title,
357
+ description: event.description,
358
+ meetingLink: event.meetingLink,
359
+ startDateTime: event.startDateTime,
360
+ endDateTime: event.endDateTime,
361
+ isAllDay: event.isAllDay,
362
+ recurrenceType: event.recurrenceType,
363
+ recurrenceEndDate: event.recurrenceEndDate,
364
+ color: event.color,
365
+ isActive: event.isActive,
366
+ metadata: event.metadata,
367
+ createdAt: event.createdAt,
368
+ updatedAt: event.updatedAt,
369
+ deletedAt: event.deletedAt,
370
+ createdById: event.createdById,
371
+ updatedById: event.updatedById,
372
+ deletedById: event.deletedById,
373
+ participants: event.participants?.map((p)=>this.toParticipantInfo(p))
374
+ };
375
+ }
376
+ toParticipantInfo(p) {
377
+ return {
378
+ id: p.id,
379
+ eventId: p.eventId,
380
+ userId: p.userId,
381
+ status: p.status,
382
+ isOrganizer: p.isOrganizer,
383
+ metadata: p.metadata,
384
+ createdAt: p.createdAt,
385
+ updatedAt: p.updatedAt,
386
+ deletedAt: p.deletedAt,
387
+ createdById: p.createdById,
388
+ updatedById: p.updatedById,
389
+ deletedById: p.deletedById
390
+ };
391
+ }
392
+ constructor(cacheManager, utilsService, eventConfig, dataSourceProvider){
393
+ super('event', null, cacheManager, utilsService, EventService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "eventConfig", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "participantRepository", void 0), _define_property(this, "pendingParticipantsList", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.eventConfig = eventConfig, this.dataSourceProvider = dataSourceProvider, this.participantRepository = null, this.pendingParticipantsList = [];
394
+ }
395
+ }
396
+ EventService = _ts_decorate([
397
+ Injectable({
398
+ scope: Scope.REQUEST
399
+ }),
400
+ _ts_param(0, Inject('CACHE_INSTANCE')),
401
+ _ts_param(1, Inject(UtilsService)),
402
+ _ts_param(2, Inject(EventManagerConfigService)),
403
+ _ts_param(3, Inject(EventManagerDataSourceProvider)),
404
+ _ts_metadata("design:type", Function),
405
+ _ts_metadata("design:paramtypes", [
406
+ typeof HybridCache === "undefined" ? Object : HybridCache,
407
+ typeof UtilsService === "undefined" ? Object : UtilsService,
408
+ typeof EventManagerConfigService === "undefined" ? Object : EventManagerConfigService,
409
+ typeof EventManagerDataSourceProvider === "undefined" ? Object : EventManagerDataSourceProvider
410
+ ])
411
+ ], EventService);
@@ -0,0 +1,5 @@
1
+ export * from './event-manager-config.service';
2
+ export * from './event-manager-datasource.provider';
3
+ export * from './event-manager-helper.service';
4
+ export * from './event.service';
5
+ export * from './event-participant.service';
package/index.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ export * from './adapters';
2
+ export * from './controllers';
3
+ export * from './services';
4
+ export * from './entities';
5
+ export * from './modules';
6
+ export * from './config';
7
+ export * from './docs';
8
+ export * from './dtos';
9
+ export * from './interfaces';
@@ -0,0 +1,20 @@
1
+ import { IBootstrapAppConfig, IDataSourceServiceOptions, IDynamicModuleConfig, IModuleOptionsFactory } from '@flusys/nestjs-core';
2
+ import { ModuleMetadata, Type } from '@nestjs/common';
3
+ export interface IEventManagerModuleConfig extends IDataSourceServiceOptions {
4
+ defaultColor?: string;
5
+ maxRecurrenceOccurrences?: number;
6
+ }
7
+ export interface EventManagerModuleOptions extends IDynamicModuleConfig {
8
+ bootstrapAppConfig?: IBootstrapAppConfig;
9
+ config?: IEventManagerModuleConfig;
10
+ }
11
+ export interface EventManagerOptionsFactory extends IModuleOptionsFactory<IEventManagerModuleConfig> {
12
+ createEventManagerOptions(): Promise<IEventManagerModuleConfig> | IEventManagerModuleConfig;
13
+ }
14
+ export interface EventManagerModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'>, IDynamicModuleConfig {
15
+ bootstrapAppConfig: IBootstrapAppConfig;
16
+ useFactory?: (...args: any[]) => Promise<IEventManagerModuleConfig> | IEventManagerModuleConfig;
17
+ inject?: any[];
18
+ useClass?: Type<EventManagerOptionsFactory>;
19
+ useExisting?: Type<EventManagerOptionsFactory>;
20
+ }
@@ -0,0 +1,28 @@
1
+ import { ParticipantStatus, RecurrenceType } from '@flusys/nestjs-shared/enums';
2
+ import { IIdentity } from '@flusys/nestjs-shared/interfaces';
3
+ export interface IEvent extends IIdentity {
4
+ title: string;
5
+ description: string | null;
6
+ meetingLink: string | null;
7
+ startDateTime: Date;
8
+ endDateTime: Date;
9
+ isAllDay: boolean;
10
+ recurrenceType: RecurrenceType | string;
11
+ recurrenceEndDate: Date | null;
12
+ color: string;
13
+ isActive: boolean;
14
+ metadata: Record<string, unknown> | null;
15
+ companyId?: string | null;
16
+ }
17
+ export interface IEventParticipant extends IIdentity {
18
+ eventId: string;
19
+ userId: string;
20
+ status: ParticipantStatus | string;
21
+ isOrganizer: boolean;
22
+ metadata: Record<string, unknown> | null;
23
+ }
24
+ export interface ICalendarEvent extends IEvent {
25
+ originalEventId?: string;
26
+ isRecurrenceInstance?: boolean;
27
+ participants?: IEventParticipant[];
28
+ }
@@ -0,0 +1,2 @@
1
+ export * from './event.interface';
2
+ export * from './event-manager-module.interface';
@@ -0,0 +1,8 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import { EventManagerModuleAsyncOptions, EventManagerModuleOptions } from '../interfaces';
3
+ export declare class EventManagerModule {
4
+ static forRoot(options: EventManagerModuleOptions): DynamicModule;
5
+ static forRootAsync(options: EventManagerModuleAsyncOptions): DynamicModule;
6
+ private static buildProviders;
7
+ private static createAsyncProviders;
8
+ }
@@ -0,0 +1 @@
1
+ export * from './event-manager.module';
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@flusys/nestjs-event-manager",
3
+ "version": "3.0.1",
4
+ "description": "Event manager module with calendar support and recurrence",
5
+ "main": "cjs/index.js",
6
+ "module": "fesm/index.js",
7
+ "types": "index.d.ts",
8
+ "sideEffects": false,
9
+ "keywords": [
10
+ "nestjs",
11
+ "event-manager",
12
+ "calendar",
13
+ "recurrence",
14
+ "@flusys"
15
+ ],
16
+ "license": "UNLICENSED",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./index.d.ts",
20
+ "import": "./fesm/index.js",
21
+ "require": "./cjs/index.js"
22
+ },
23
+ "./config": {
24
+ "types": "./config/index.d.ts",
25
+ "import": "./fesm/config/index.js",
26
+ "require": "./cjs/config/index.js"
27
+ },
28
+ "./controllers": {
29
+ "types": "./controllers/index.d.ts",
30
+ "import": "./fesm/controllers/index.js",
31
+ "require": "./cjs/controllers/index.js"
32
+ },
33
+ "./docs": {
34
+ "types": "./docs/index.d.ts",
35
+ "import": "./fesm/docs/index.js",
36
+ "require": "./cjs/docs/index.js"
37
+ },
38
+ "./dtos": {
39
+ "types": "./dtos/index.d.ts",
40
+ "import": "./fesm/dtos/index.js",
41
+ "require": "./cjs/dtos/index.js"
42
+ },
43
+ "./entities": {
44
+ "types": "./entities/index.d.ts",
45
+ "import": "./fesm/entities/index.js",
46
+ "require": "./cjs/entities/index.js"
47
+ },
48
+ "./enums": {
49
+ "types": "./enums/index.d.ts",
50
+ "import": "./fesm/enums/index.js",
51
+ "require": "./cjs/enums/index.js"
52
+ },
53
+ "./interfaces": {
54
+ "types": "./interfaces/index.d.ts",
55
+ "import": "./fesm/interfaces/index.js",
56
+ "require": "./cjs/interfaces/index.js"
57
+ },
58
+ "./modules": {
59
+ "types": "./modules/index.d.ts",
60
+ "import": "./fesm/modules/index.js",
61
+ "require": "./cjs/modules/index.js"
62
+ },
63
+ "./services": {
64
+ "types": "./services/index.d.ts",
65
+ "import": "./fesm/services/index.js",
66
+ "require": "./cjs/services/index.js"
67
+ },
68
+ "./*": {
69
+ "types": "./*.d.ts",
70
+ "import": "./*.js",
71
+ "require": "./*.js"
72
+ }
73
+ },
74
+ "peerDependencies": {
75
+ "@nestjs/common": "^10.0.0 || ^11.0.0",
76
+ "@nestjs/config": "^3.0.0 || ^4.0.0",
77
+ "@nestjs/core": "^10.0.0 || ^11.0.0",
78
+ "@nestjs/platform-express": "^10.0.0 || ^11.0.0",
79
+ "@nestjs/swagger": "^7.0.0 || ^11.0.0",
80
+ "@nestjs/typeorm": "^10.0.0 || ^11.0.0",
81
+ "class-transformer": "^0.5.0",
82
+ "class-validator": "^0.14.0",
83
+ "typeorm": "^0.3.0"
84
+ },
85
+ "dependencies": {
86
+ "@flusys/nestjs-core": "3.0.1",
87
+ "@flusys/nestjs-shared": "3.0.1"
88
+ }
89
+ }
@@ -0,0 +1,13 @@
1
+ import { DatabaseMode } from '@flusys/nestjs-core';
2
+ import { IModuleConfigService } from '@flusys/nestjs-shared/interfaces';
3
+ import { EventManagerModuleOptions } from '../interfaces';
4
+ export declare class EventManagerConfigService implements IModuleConfigService {
5
+ private readonly options;
6
+ constructor(options: EventManagerModuleOptions);
7
+ isCompanyFeatureEnabled(): boolean;
8
+ getDatabaseMode(): DatabaseMode;
9
+ isMultiTenant(): boolean;
10
+ getDefaultColor(): string;
11
+ getMaxRecurrenceOccurrences(): number;
12
+ getOptions(): EventManagerModuleOptions;
13
+ }
@@ -0,0 +1,23 @@
1
+ import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
2
+ import { IDatabaseConfig, ITenantDatabaseConfig } from '@flusys/nestjs-core';
3
+ import { Logger } from '@nestjs/common';
4
+ import { Request } from 'express';
5
+ import { DataSource } from 'typeorm';
6
+ import { EventManagerConfigService } from './event-manager-config.service';
7
+ export declare class EventManagerDataSourceProvider extends MultiTenantDataSourceService {
8
+ private readonly configService;
9
+ protected readonly logger: Logger;
10
+ protected static readonly tenantConnections: Map<string, DataSource>;
11
+ protected static singleDataSource: DataSource | null;
12
+ protected static readonly tenantsRegistry: Map<string, ITenantDatabaseConfig>;
13
+ protected static initialized: boolean;
14
+ protected static readonly connectionLocks: Map<string, Promise<DataSource>>;
15
+ protected static singleConnectionLock: Promise<DataSource> | null;
16
+ constructor(configService: EventManagerConfigService, request?: Request);
17
+ private static buildParentOptions;
18
+ getEnableCompanyFeatureForTenant(tenant?: ITenantDatabaseConfig): boolean;
19
+ getEventManagerEntities(enableCompanyFeature?: boolean): Promise<any[]>;
20
+ protected createDataSourceFromConfig(config: IDatabaseConfig): Promise<DataSource>;
21
+ protected getSingleDataSource(): Promise<DataSource>;
22
+ protected getOrCreateTenantConnection(tenant: ITenantDatabaseConfig): Promise<DataSource>;
23
+ }
@@ -0,0 +1,27 @@
1
+ import { HybridCache } from '@flusys/nestjs-shared/classes';
2
+ import { UtilsService } from '@flusys/nestjs-shared/modules';
3
+ import { CreateEventOptions, EventResult, ParticipantStatus } from '@flusys/nestjs-shared';
4
+ import { EventManagerConfigService } from './event-manager-config.service';
5
+ import { EventManagerDataSourceProvider } from './event-manager-datasource.provider';
6
+ export declare class EventManagerHelperService {
7
+ private readonly cacheManager;
8
+ private readonly utilsService;
9
+ private readonly configService;
10
+ private readonly dataSourceProvider;
11
+ private readonly logger;
12
+ private readonly eventEntityName;
13
+ private dataSource;
14
+ constructor(cacheManager: HybridCache, utilsService: UtilsService, configService: EventManagerConfigService, dataSourceProvider: EventManagerDataSourceProvider);
15
+ private getDataSource;
16
+ private getEventRepository;
17
+ private getParticipantRepository;
18
+ createEvent(options: CreateEventOptions): Promise<EventResult>;
19
+ addParticipants(eventId: string, userIds: string[], _companyId?: string): Promise<void>;
20
+ removeParticipant(eventId: string, userId: string): Promise<void>;
21
+ updateParticipantStatus(participantId: string, status: ParticipantStatus): Promise<void>;
22
+ getEventsForUser(userId: string, startDate: Date, endDate: Date, companyId?: string): Promise<EventResult[]>;
23
+ getEventById(eventId: string): Promise<EventResult | null>;
24
+ private addEventParticipants;
25
+ private toEventResult;
26
+ private clearCache;
27
+ }