@flusys/nestjs-event-manager 4.0.2 → 4.1.0
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/README.md
CHANGED
|
@@ -1,152 +1,80 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @flusys/nestjs-event-manager
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
> **Version:** 4.0.2
|
|
5
|
-
> **Type:** Calendar event management with recurrence support and multi-tenant capabilities
|
|
3
|
+
> Calendar event management for NestJS — recurrence patterns (DAILY, WEEKLY, BIWEEKLY, MONTHLY), participant tracking with status, company scoping, and a cross-module adapter for dependency-free integration.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/nestjs-event-manager)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nestjs.com/)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
|
|
11
|
+
---
|
|
8
12
|
|
|
9
13
|
## Table of Contents
|
|
10
14
|
|
|
11
15
|
- [Overview](#overview)
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Compatibility](#compatibility)
|
|
12
18
|
- [Installation](#installation)
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
19
|
+
- [Quick Start](#quick-start)
|
|
20
|
+
- [Module Registration](#module-registration)
|
|
21
|
+
- [forRoot (Sync)](#forroot-sync)
|
|
22
|
+
- [forRootAsync (Factory)](#forrootasync-factory)
|
|
23
|
+
- [Configuration Reference](#configuration-reference)
|
|
24
|
+
- [API Endpoints](#api-endpoints)
|
|
16
25
|
- [Entities](#entities)
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
24
|
-
- [Swagger Configuration](#swagger-configuration)
|
|
25
|
-
- [Event Manager Adapter](#event-manager-adapter)
|
|
26
|
-
- [Best Practices](#best-practices)
|
|
27
|
-
- [API Reference](#api-reference)
|
|
26
|
+
- [Recurrence Patterns](#recurrence-patterns)
|
|
27
|
+
- [Participant Status](#participant-status)
|
|
28
|
+
- [Cross-Module Adapter (EVENT_MANAGER_ADAPTER)](#cross-module-adapter-event_manager_adapter)
|
|
29
|
+
- [Exported Services](#exported-services)
|
|
30
|
+
- [Programmatic Usage](#programmatic-usage)
|
|
31
|
+
- [Troubleshooting](#troubleshooting)
|
|
32
|
+
- [License](#license)
|
|
28
33
|
|
|
29
34
|
---
|
|
30
35
|
|
|
31
36
|
## Overview
|
|
32
37
|
|
|
33
|
-
`@flusys/nestjs-event-manager` provides a
|
|
34
|
-
|
|
35
|
-
- **Event CRUD** - Create, read, update, delete events with rich metadata
|
|
36
|
-
- **Recurrence Support** - Daily, weekly, biweekly, and monthly patterns
|
|
37
|
-
- **Participant Management** - Track event participants with status
|
|
38
|
-
- **Calendar Queries** - Date range queries with recurrence expansion
|
|
39
|
-
- **Adapter Pattern** - Cross-module event creation without direct dependencies
|
|
40
|
-
- **Company Scoping** - Per-company event isolation
|
|
41
|
-
- **Multi-Tenant Support** - Separate databases per tenant
|
|
42
|
-
|
|
43
|
-
### Package Hierarchy
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
@flusys/nestjs-core <- Foundation
|
|
47
|
-
|
|
|
48
|
-
@flusys/nestjs-shared <- Shared utilities
|
|
49
|
-
|
|
|
50
|
-
@flusys/nestjs-event-manager <- Event management (THIS PACKAGE)
|
|
51
|
-
```
|
|
38
|
+
`@flusys/nestjs-event-manager` provides a structured calendar event system with rich recurrence support and per-user participant tracking. The module exposes an `EVENT_MANAGER_ADAPTER` injection token so other modules (Notification, Email) can create events without taking a direct dependency on this package.
|
|
52
39
|
|
|
53
40
|
---
|
|
54
41
|
|
|
55
|
-
##
|
|
42
|
+
## Features
|
|
56
43
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
44
|
+
- **Calendar events** — Title, description, meeting link, time range, color, all-day flag
|
|
45
|
+
- **Recurrence engine** — NONE, DAILY, WEEKLY, BIWEEKLY, MONTHLY patterns with configurable end dates and day-of-week sets
|
|
46
|
+
- **Participant tracking** — Per-user status: PENDING, ACCEPTED, DECLINED, TENTATIVE
|
|
47
|
+
- **Organizer designation** — Mark specific participants as event organizer
|
|
48
|
+
- **Company scoping** — Optional `companyId`/`branchId` on events
|
|
49
|
+
- **Cross-module adapter** — `EVENT_MANAGER_ADAPTER` token for creating events without circular imports
|
|
50
|
+
- **REQUEST-scoped services** — Dynamic entity resolution per HTTP request
|
|
51
|
+
- **POST-only RPC** — All endpoints use `POST`
|
|
60
52
|
|
|
61
53
|
---
|
|
62
54
|
|
|
63
|
-
##
|
|
55
|
+
## Compatibility
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// Permissions (from @flusys/nestjs-shared)
|
|
73
|
-
export const EVENT_PERMISSIONS = {
|
|
74
|
-
CREATE: 'event.create',
|
|
75
|
-
READ: 'event.read',
|
|
76
|
-
UPDATE: 'event.update',
|
|
77
|
-
DELETE: 'event.delete',
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const EVENT_PARTICIPANT_PERMISSIONS = {
|
|
81
|
-
CREATE: 'event_participant.create',
|
|
82
|
-
READ: 'event_participant.read',
|
|
83
|
-
UPDATE: 'event_participant.update',
|
|
84
|
-
DELETE: 'event_participant.delete',
|
|
85
|
-
};
|
|
86
|
-
```
|
|
57
|
+
| Package | Version |
|
|
58
|
+
|---------|---------|
|
|
59
|
+
| `@flusys/nestjs-core` | `^4.0.0` |
|
|
60
|
+
| `@flusys/nestjs-shared` | `^4.0.0` |
|
|
61
|
+
| `@nestjs/core` | `^11.0.0` |
|
|
62
|
+
| `typeorm` | `^0.3.0` |
|
|
63
|
+
| Node.js | `>= 18.x` |
|
|
87
64
|
|
|
88
65
|
---
|
|
89
66
|
|
|
90
|
-
##
|
|
67
|
+
## Installation
|
|
91
68
|
|
|
92
|
-
```
|
|
93
|
-
nestjs-event-manager/
|
|
94
|
-
├── src/
|
|
95
|
-
│ ├── modules/
|
|
96
|
-
│ │ └── event-manager.module.ts # Main module with provider registration
|
|
97
|
-
│ │
|
|
98
|
-
│ ├── adapters/
|
|
99
|
-
│ │ ├── event-manager.adapter.ts # Adapter for cross-module usage
|
|
100
|
-
│ │ └── index.ts
|
|
101
|
-
│ │
|
|
102
|
-
│ ├── config/
|
|
103
|
-
│ │ ├── event-manager.constants.ts # Module constants
|
|
104
|
-
│ │ └── index.ts
|
|
105
|
-
│ │
|
|
106
|
-
│ ├── services/
|
|
107
|
-
│ │ ├── event.service.ts # Event CRUD + calendar queries
|
|
108
|
-
│ │ ├── event-participant.service.ts # Participant CRUD
|
|
109
|
-
│ │ ├── event-manager-config.service.ts # Module configuration
|
|
110
|
-
│ │ ├── event-manager-datasource.provider.ts # Dynamic entity loading
|
|
111
|
-
│ │ ├── event-manager-helper.service.ts # Helper for adapter (singleton)
|
|
112
|
-
│ │ └── index.ts
|
|
113
|
-
│ │
|
|
114
|
-
│ ├── controllers/
|
|
115
|
-
│ │ ├── event.controller.ts # /event-manager/event/*
|
|
116
|
-
│ │ ├── event-participant.controller.ts # /event-manager/participant/*
|
|
117
|
-
│ │ └── index.ts
|
|
118
|
-
│ │
|
|
119
|
-
│ ├── entities/
|
|
120
|
-
│ │ ├── event.entity.ts # Event base
|
|
121
|
-
│ │ ├── event-with-company.entity.ts # Event with company
|
|
122
|
-
│ │ ├── event-participant.entity.ts # Participant entity
|
|
123
|
-
│ │ └── index.ts
|
|
124
|
-
│ │
|
|
125
|
-
│ ├── dtos/
|
|
126
|
-
│ │ ├── event.dto.ts # Event DTOs
|
|
127
|
-
│ │ ├── event-participant.dto.ts # Participant DTOs
|
|
128
|
-
│ │ ├── calendar-query.dto.ts # Calendar query DTO
|
|
129
|
-
│ │ └── index.ts
|
|
130
|
-
│ │
|
|
131
|
-
│ ├── interfaces/
|
|
132
|
-
│ │ ├── event.interface.ts # IEvent, IEventParticipant, ICalendarEvent
|
|
133
|
-
│ │ ├── event-manager-module.interface.ts # Module options
|
|
134
|
-
│ │ └── index.ts
|
|
135
|
-
│ │
|
|
136
|
-
│ ├── enums/
|
|
137
|
-
│ │ └── index.ts # Re-exports from @flusys/nestjs-shared
|
|
138
|
-
│ │
|
|
139
|
-
│ ├── docs/
|
|
140
|
-
│ │ └── index.ts # Swagger config, permissions
|
|
141
|
-
│ │
|
|
142
|
-
│ └── index.ts # Main export file
|
|
69
|
+
```bash
|
|
70
|
+
npm install @flusys/nestjs-event-manager @flusys/nestjs-shared @flusys/nestjs-core
|
|
143
71
|
```
|
|
144
72
|
|
|
145
73
|
---
|
|
146
74
|
|
|
147
|
-
##
|
|
75
|
+
## Quick Start
|
|
148
76
|
|
|
149
|
-
###
|
|
77
|
+
### Without Company Feature
|
|
150
78
|
|
|
151
79
|
```typescript
|
|
152
80
|
import { Module } from '@nestjs/common';
|
|
@@ -155,25 +83,9 @@ import { EventManagerModule } from '@flusys/nestjs-event-manager';
|
|
|
155
83
|
@Module({
|
|
156
84
|
imports: [
|
|
157
85
|
EventManagerModule.forRoot({
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
databaseMode: 'single',
|
|
162
|
-
enableCompanyFeature: false,
|
|
163
|
-
permissionMode: 'RBAC',
|
|
164
|
-
},
|
|
165
|
-
config: {
|
|
166
|
-
defaultDatabaseConfig: {
|
|
167
|
-
type: 'postgres',
|
|
168
|
-
host: 'localhost',
|
|
169
|
-
port: 5432,
|
|
170
|
-
username: 'postgres',
|
|
171
|
-
password: 'password',
|
|
172
|
-
database: 'myapp',
|
|
173
|
-
},
|
|
174
|
-
defaultColor: '#3B82F6',
|
|
175
|
-
maxRecurrenceOccurrences: 365,
|
|
176
|
-
},
|
|
86
|
+
dataSource: appDataSource, // TypeORM DataSource instance
|
|
87
|
+
enableCompanyFeature: false,
|
|
88
|
+
cacheInstance: hybridCache, // HybridCache instance
|
|
177
89
|
}),
|
|
178
90
|
],
|
|
179
91
|
})
|
|
@@ -184,1145 +96,340 @@ export class AppModule {}
|
|
|
184
96
|
|
|
185
97
|
```typescript
|
|
186
98
|
EventManagerModule.forRoot({
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
enableCompanyFeature: true,
|
|
192
|
-
permissionMode: 'FULL',
|
|
193
|
-
},
|
|
194
|
-
config: {
|
|
195
|
-
defaultDatabaseConfig: { /* ... */ },
|
|
196
|
-
defaultColor: '#3B82F6',
|
|
197
|
-
maxRecurrenceOccurrences: 365,
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Async Configuration
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
EventManagerModule.forRootAsync({
|
|
206
|
-
global: true,
|
|
207
|
-
includeController: true,
|
|
208
|
-
bootstrapAppConfig: {
|
|
209
|
-
databaseMode: 'single',
|
|
210
|
-
enableCompanyFeature: true,
|
|
211
|
-
permissionMode: 'FULL',
|
|
212
|
-
},
|
|
213
|
-
imports: [ConfigModule],
|
|
214
|
-
useFactory: async (configService: ConfigService) => ({
|
|
215
|
-
defaultDatabaseConfig: configService.getDatabaseConfig(),
|
|
216
|
-
defaultColor: configService.get('EVENT_DEFAULT_COLOR', '#3B82F6'),
|
|
217
|
-
maxRecurrenceOccurrences: configService.get('MAX_RECURRENCE', 365),
|
|
218
|
-
}),
|
|
219
|
-
inject: [ConfigService],
|
|
220
|
-
});
|
|
99
|
+
dataSource: appDataSource,
|
|
100
|
+
enableCompanyFeature: true, // Adds companyId/branchId to Event entity
|
|
101
|
+
cacheInstance: hybridCache,
|
|
102
|
+
})
|
|
221
103
|
```
|
|
222
104
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
/** Default event color (default: '#3B82F6') */
|
|
228
|
-
defaultColor?: string;
|
|
229
|
-
/** Maximum recurrence occurrences to generate (default: 365) */
|
|
230
|
-
maxRecurrenceOccurrences?: number;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
interface EventManagerModuleOptions extends IDynamicModuleConfig {
|
|
234
|
-
bootstrapAppConfig?: IBootstrapAppConfig;
|
|
235
|
-
config?: IEventManagerModuleConfig;
|
|
236
|
-
}
|
|
237
|
-
```
|
|
105
|
+
After registration:
|
|
106
|
+
- `EventController` at `/event-manager/event/*`
|
|
107
|
+
- `EventParticipantController` at `/event-manager/participant/*`
|
|
108
|
+
- `EVENT_MANAGER_ADAPTER` token is available for injection
|
|
238
109
|
|
|
239
110
|
---
|
|
240
111
|
|
|
241
|
-
##
|
|
242
|
-
|
|
243
|
-
### Enums (from @flusys/nestjs-shared)
|
|
244
|
-
|
|
245
|
-
The enums are defined in `@flusys/nestjs-shared/enums` and re-exported from this package for convenience:
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
// Import from shared (canonical source)
|
|
249
|
-
import { RecurrenceType, ParticipantStatus } from '@flusys/nestjs-shared/enums';
|
|
250
|
-
|
|
251
|
-
// Or import from event-manager (re-export)
|
|
252
|
-
import { RecurrenceType, ParticipantStatus } from '@flusys/nestjs-event-manager/enums';
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
**RecurrenceType:**
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
export enum RecurrenceType {
|
|
259
|
-
NONE = 'none',
|
|
260
|
-
DAILY = 'daily',
|
|
261
|
-
WEEKLY = 'weekly',
|
|
262
|
-
BIWEEKLY = 'biweekly',
|
|
263
|
-
MONTHLY = 'monthly',
|
|
264
|
-
}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
**ParticipantStatus:**
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
export enum ParticipantStatus {
|
|
271
|
-
PENDING = 'pending',
|
|
272
|
-
ACCEPTED = 'accepted',
|
|
273
|
-
DECLINED = 'declined',
|
|
274
|
-
TENTATIVE = 'tentative',
|
|
275
|
-
}
|
|
276
|
-
```
|
|
112
|
+
## Module Registration
|
|
277
113
|
|
|
278
|
-
###
|
|
114
|
+
### forRoot (Sync)
|
|
279
115
|
|
|
280
116
|
```typescript
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
// Company-specific entities
|
|
285
|
-
export const EventManagerCompanyEntities = [EventWithCompany, EventParticipant];
|
|
117
|
+
EventManagerModule.forRoot({
|
|
118
|
+
/** TypeORM DataSource connected to the application database */
|
|
119
|
+
dataSource: DataSource;
|
|
286
120
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return enableCompanyFeature ? EventManagerCompanyEntities : EventManagerCoreEntities;
|
|
290
|
-
}
|
|
121
|
+
/** When true, Event entity includes companyId/branchId columns */
|
|
122
|
+
enableCompanyFeature: boolean;
|
|
291
123
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
124
|
+
/** Cache instance (HybridCache from nestjs-shared) */
|
|
125
|
+
cacheInstance: HybridCache;
|
|
126
|
+
})
|
|
295
127
|
```
|
|
296
128
|
|
|
297
|
-
###
|
|
129
|
+
### forRootAsync (Factory)
|
|
298
130
|
|
|
299
131
|
```typescript
|
|
300
|
-
|
|
301
|
-
@Index(['startDateTime', 'endDateTime'])
|
|
302
|
-
@Index(['isActive'])
|
|
303
|
-
export class Event extends Identity {
|
|
304
|
-
@Column({ type: 'varchar', length: 255 })
|
|
305
|
-
title!: string;
|
|
306
|
-
|
|
307
|
-
@Column({ type: 'text', nullable: true })
|
|
308
|
-
description!: string | null;
|
|
309
|
-
|
|
310
|
-
@Column({ type: 'varchar', length: 500, nullable: true })
|
|
311
|
-
meetingLink!: string | null;
|
|
312
|
-
|
|
313
|
-
@Column({ type: 'timestamp' })
|
|
314
|
-
@Index()
|
|
315
|
-
startDateTime!: Date;
|
|
316
|
-
|
|
317
|
-
@Column({ type: 'timestamp' })
|
|
318
|
-
endDateTime!: Date;
|
|
319
|
-
|
|
320
|
-
@Column({ type: 'boolean', default: false })
|
|
321
|
-
isAllDay!: boolean;
|
|
322
|
-
|
|
323
|
-
@Column({ type: 'varchar', length: 20, default: 'none' })
|
|
324
|
-
@Index()
|
|
325
|
-
recurrenceType!: string;
|
|
132
|
+
import { EnvConfigService } from '@flusys/nestjs-core';
|
|
326
133
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
@Column({ type: 'simple-json', nullable: true })
|
|
337
|
-
metadata!: Record<string, unknown> | null;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// With company feature
|
|
341
|
-
@Entity({ name: 'event' })
|
|
342
|
-
@Index(['companyId', 'isActive'])
|
|
343
|
-
export class EventWithCompany extends Event {
|
|
344
|
-
@Column({ type: 'uuid', nullable: true })
|
|
345
|
-
companyId!: string | null;
|
|
346
|
-
}
|
|
134
|
+
EventManagerModule.forRootAsync({
|
|
135
|
+
imports: [ConfigModule],
|
|
136
|
+
inject: [EnvConfigService],
|
|
137
|
+
useFactory: (envConfig: EnvConfigService) => ({
|
|
138
|
+
dataSource: envConfig.getDataSource(),
|
|
139
|
+
enableCompanyFeature: envConfig.isCompanyFeatureEnabled(),
|
|
140
|
+
cacheInstance: envConfig.getCacheInstance(),
|
|
141
|
+
}),
|
|
142
|
+
})
|
|
347
143
|
```
|
|
348
144
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
@Entity({ name: 'event_participant' })
|
|
353
|
-
@Index(['eventId'])
|
|
354
|
-
@Index(['userId'])
|
|
355
|
-
@Index(['eventId', 'userId'], { unique: true })
|
|
356
|
-
export class EventParticipant extends Identity {
|
|
357
|
-
@Column({ type: 'uuid' })
|
|
358
|
-
eventId!: string;
|
|
359
|
-
|
|
360
|
-
@Column({ type: 'uuid' })
|
|
361
|
-
userId!: string;
|
|
362
|
-
|
|
363
|
-
@Column({ type: 'varchar', length: 50, default: 'pending' })
|
|
364
|
-
status!: string;
|
|
365
|
-
|
|
366
|
-
@Column({ type: 'boolean', default: false })
|
|
367
|
-
isOrganizer!: boolean;
|
|
145
|
+
**Registered providers:**
|
|
368
146
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
147
|
+
| Provider | Token | Scope |
|
|
148
|
+
|----------|-------|-------|
|
|
149
|
+
| Module options | `EVENT_MANAGER_MODULE_OPTIONS` | Singleton |
|
|
150
|
+
| `EventManagerConfigService` | class | Singleton |
|
|
151
|
+
| `EventManagerDataSourceProvider` | class | Singleton |
|
|
152
|
+
| `EventService` | class | REQUEST |
|
|
153
|
+
| `EventParticipantService` | class | REQUEST |
|
|
154
|
+
| `EventManagerHelperService` | class | Singleton |
|
|
155
|
+
| `EventManagerAdapter` | `EVENT_MANAGER_ADAPTER` | Singleton |
|
|
373
156
|
|
|
374
|
-
**
|
|
157
|
+
**Exported:** `EventManagerHelperService`, `EVENT_MANAGER_ADAPTER`, `EventManagerConfigService`
|
|
375
158
|
|
|
376
159
|
---
|
|
377
160
|
|
|
378
|
-
##
|
|
379
|
-
|
|
380
|
-
### EventService
|
|
381
|
-
|
|
382
|
-
Handles event CRUD and calendar queries.
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
@Injectable({ scope: Scope.REQUEST })
|
|
386
|
-
export class EventService extends RequestScopedApiService<
|
|
387
|
-
CreateEventDto,
|
|
388
|
-
UpdateEventDto,
|
|
389
|
-
IEvent,
|
|
390
|
-
EventBase,
|
|
391
|
-
Repository<EventBase>
|
|
392
|
-
> {
|
|
393
|
-
// Standard CRUD methods inherited
|
|
394
|
-
|
|
395
|
-
/** Get events for calendar date range with recurrence expansion */
|
|
396
|
-
async getEventsForCalendarRange(
|
|
397
|
-
dto: CalendarQueryDto,
|
|
398
|
-
user?: ILoggedUserInfo
|
|
399
|
-
): Promise<ICalendarEvent[]>;
|
|
400
|
-
|
|
401
|
-
/** Get event with participant list */
|
|
402
|
-
async getEventWithParticipants(
|
|
403
|
-
eventId: string,
|
|
404
|
-
user?: ILoggedUserInfo
|
|
405
|
-
): Promise<EventBase | null>;
|
|
406
|
-
|
|
407
|
-
/** Add participants to event */
|
|
408
|
-
async addParticipants(
|
|
409
|
-
dto: BulkAddParticipantsDto,
|
|
410
|
-
user?: ILoggedUserInfo
|
|
411
|
-
): Promise<EventParticipantBase[]>;
|
|
412
|
-
}
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
**Key Features:**
|
|
416
|
-
- Dynamic entity resolution (Event vs EventWithCompany)
|
|
417
|
-
- Automatic participant creation on event insert
|
|
418
|
-
- Recurrence expansion for calendar queries
|
|
419
|
-
- Company filtering when enabled
|
|
420
|
-
|
|
421
|
-
### EventParticipantService
|
|
422
|
-
|
|
423
|
-
Handles participant CRUD.
|
|
424
|
-
|
|
425
|
-
```typescript
|
|
426
|
-
@Injectable({ scope: Scope.REQUEST })
|
|
427
|
-
export class EventParticipantService extends RequestScopedApiService<
|
|
428
|
-
CreateEventParticipantDto,
|
|
429
|
-
UpdateEventParticipantDto,
|
|
430
|
-
IEventParticipant,
|
|
431
|
-
EventParticipantBase,
|
|
432
|
-
Repository<EventParticipantBase>
|
|
433
|
-
> {
|
|
434
|
-
// Standard CRUD methods inherited
|
|
435
|
-
|
|
436
|
-
/** Update participant status */
|
|
437
|
-
async updateStatus(
|
|
438
|
-
dto: UpdateParticipantStatusDto,
|
|
439
|
-
user?: ILoggedUserInfo
|
|
440
|
-
): Promise<EventParticipantBase>;
|
|
441
|
-
|
|
442
|
-
/** Get participants for event */
|
|
443
|
-
async getParticipantsByEvent(eventId: string): Promise<EventParticipantBase[]>;
|
|
444
|
-
|
|
445
|
-
/** Get user's participations */
|
|
446
|
-
async getParticipantsByUser(userId: string): Promise<EventParticipantBase[]>;
|
|
447
|
-
|
|
448
|
-
/** Remove participant from event */
|
|
449
|
-
async removeParticipant(
|
|
450
|
-
eventId: string,
|
|
451
|
-
userId: string,
|
|
452
|
-
user?: ILoggedUserInfo
|
|
453
|
-
): Promise<void>;
|
|
454
|
-
}
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
### EventManagerConfigService
|
|
458
|
-
|
|
459
|
-
Module configuration service.
|
|
161
|
+
## Configuration Reference
|
|
460
162
|
|
|
461
163
|
```typescript
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
isCompanyFeatureEnabled(): boolean;
|
|
466
|
-
|
|
467
|
-
/** Get database mode */
|
|
468
|
-
getDatabaseMode(): 'single' | 'multi-tenant';
|
|
164
|
+
interface IEventManagerModuleOptions {
|
|
165
|
+
/** Connected TypeORM DataSource */
|
|
166
|
+
dataSource: DataSource;
|
|
469
167
|
|
|
470
|
-
/**
|
|
471
|
-
|
|
168
|
+
/** When true, uses company-aware Event entity variant */
|
|
169
|
+
enableCompanyFeature: boolean;
|
|
472
170
|
|
|
473
|
-
/**
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
/** Get max recurrence occurrences */
|
|
477
|
-
getMaxRecurrenceOccurrences(): number;
|
|
171
|
+
/** HybridCache instance for caching */
|
|
172
|
+
cacheInstance: HybridCache;
|
|
478
173
|
}
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
### EventManagerDataSourceProvider
|
|
482
|
-
|
|
483
|
-
Dynamic entity loading with isolated cache.
|
|
484
174
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
protected static override readonly tenantConnections = new Map<string, DataSource>();
|
|
490
|
-
protected static override singleDataSource: DataSource | null = null;
|
|
491
|
-
|
|
492
|
-
/** Get entities based on company feature */
|
|
493
|
-
async getEventManagerEntities(enableCompanyFeature?: boolean): Promise<any[]>;
|
|
494
|
-
|
|
495
|
-
/** Get company feature for current tenant */
|
|
496
|
-
getEnableCompanyFeatureForCurrentTenant(): boolean;
|
|
175
|
+
interface IEventManagerModuleAsyncOptions {
|
|
176
|
+
imports?: ModuleMetadata['imports'];
|
|
177
|
+
inject?: any[];
|
|
178
|
+
useFactory: (...args: any[]) => IEventManagerModuleOptions | Promise<IEventManagerModuleOptions>;
|
|
497
179
|
}
|
|
498
180
|
```
|
|
499
181
|
|
|
500
182
|
---
|
|
501
183
|
|
|
502
|
-
##
|
|
503
|
-
|
|
504
|
-
### EventController
|
|
505
|
-
|
|
506
|
-
Base path: `/event-manager/event`
|
|
507
|
-
|
|
508
|
-
```typescript
|
|
509
|
-
@Controller('event-manager/event')
|
|
510
|
-
@ApiTags('Event Manager - Events')
|
|
511
|
-
@ApiBearerAuth()
|
|
512
|
-
export class EventController extends createApiController<
|
|
513
|
-
CreateEventDto,
|
|
514
|
-
UpdateEventDto,
|
|
515
|
-
EventResponseDto,
|
|
516
|
-
IEvent,
|
|
517
|
-
EventService
|
|
518
|
-
>() {
|
|
519
|
-
// Inherited CRUD endpoints with permissions
|
|
520
|
-
|
|
521
|
-
@Post('calendar')
|
|
522
|
-
@UseGuards(JwtAuthGuard)
|
|
523
|
-
@RequirePermission(EVENT_PERMISSIONS.READ)
|
|
524
|
-
async getCalendarEvents(@Body() dto: CalendarQueryDto, @CurrentUser() user): Promise<ListResponseDto>;
|
|
525
|
-
|
|
526
|
-
@Post('get-with-participants')
|
|
527
|
-
@UseGuards(JwtAuthGuard)
|
|
528
|
-
@RequirePermission(EVENT_PERMISSIONS.READ)
|
|
529
|
-
async getWithParticipants(@Body() dto: { id: string }, @CurrentUser() user): Promise<SingleResponseDto>;
|
|
530
|
-
|
|
531
|
-
@Post('add-participants')
|
|
532
|
-
@UseGuards(JwtAuthGuard)
|
|
533
|
-
@RequirePermission(EVENT_PERMISSIONS.UPDATE)
|
|
534
|
-
async addParticipants(@Body() dto: BulkAddParticipantsDto, @CurrentUser() user): Promise<SingleResponseDto>;
|
|
535
|
-
}
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
### EventParticipantController
|
|
539
|
-
|
|
540
|
-
Base path: `/event-manager/participant`
|
|
184
|
+
## API Endpoints
|
|
541
185
|
|
|
542
|
-
|
|
543
|
-
@Controller('event-manager/participant')
|
|
544
|
-
@ApiTags('Event Manager - Participants')
|
|
545
|
-
@ApiBearerAuth()
|
|
546
|
-
export class EventParticipantController extends createApiController<...>() {
|
|
547
|
-
// Inherited CRUD endpoints with permissions
|
|
548
|
-
|
|
549
|
-
@Post('update-status')
|
|
550
|
-
@UseGuards(JwtAuthGuard)
|
|
551
|
-
@RequirePermission(EVENT_PARTICIPANT_PERMISSIONS.UPDATE)
|
|
552
|
-
async updateStatus(@Body() dto: UpdateParticipantStatusDto, @CurrentUser() user): Promise<SingleResponseDto>;
|
|
553
|
-
|
|
554
|
-
@Post('get-by-event')
|
|
555
|
-
@UseGuards(JwtAuthGuard)
|
|
556
|
-
@RequirePermission(EVENT_PARTICIPANT_PERMISSIONS.READ)
|
|
557
|
-
async getByEvent(@Body() dto: { eventId: string }): Promise<ListResponseDto>;
|
|
558
|
-
|
|
559
|
-
@Post('get-by-user')
|
|
560
|
-
@UseGuards(JwtAuthGuard)
|
|
561
|
-
@RequirePermission(EVENT_PARTICIPANT_PERMISSIONS.READ)
|
|
562
|
-
async getByUser(@Body() dto: { userId: string }): Promise<ListResponseDto>;
|
|
563
|
-
|
|
564
|
-
@Post('remove')
|
|
565
|
-
@UseGuards(JwtAuthGuard)
|
|
566
|
-
@RequirePermission(EVENT_PARTICIPANT_PERMISSIONS.DELETE)
|
|
567
|
-
async remove(@Body() dto: { eventId: string; userId: string }, @CurrentUser() user): Promise<MessageResponseDto>;
|
|
568
|
-
}
|
|
569
|
-
```
|
|
186
|
+
All endpoints use **POST** and require JWT authentication.
|
|
570
187
|
|
|
571
|
-
|
|
188
|
+
### Events — `POST /event-manager/event/*`
|
|
572
189
|
|
|
573
|
-
|
|
190
|
+
| Endpoint | Permission | Description |
|
|
191
|
+
|----------|-----------|-------------|
|
|
192
|
+
| `POST /event-manager/event/insert` | `event.create` | Create a new calendar event |
|
|
193
|
+
| `POST /event-manager/event/get-all` | `event.read` | List events with pagination and date filters |
|
|
194
|
+
| `POST /event-manager/event/get/:id` | `event.read` | Get event by ID |
|
|
195
|
+
| `POST /event-manager/event/update` | `event.update` | Update event details |
|
|
196
|
+
| `POST /event-manager/event/delete` | `event.delete` | Delete event (and all participants) |
|
|
197
|
+
| `POST /event-manager/event/get-my-events` | JWT | Get events where current user is a participant |
|
|
198
|
+
| `POST /event-manager/event/get-by-date-range` | `event.read` | Get events within a date range |
|
|
574
199
|
|
|
575
|
-
|
|
200
|
+
**Create event request:**
|
|
576
201
|
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
@IsNotEmpty()
|
|
598
|
-
@IsDateString()
|
|
599
|
-
endDateTime!: Date;
|
|
600
|
-
|
|
601
|
-
@IsOptional()
|
|
602
|
-
@IsBoolean()
|
|
603
|
-
isAllDay?: boolean;
|
|
604
|
-
|
|
605
|
-
@IsOptional()
|
|
606
|
-
@IsEnum(RecurrenceType)
|
|
607
|
-
recurrenceType?: RecurrenceType;
|
|
608
|
-
|
|
609
|
-
@IsOptional()
|
|
610
|
-
@IsDateString()
|
|
611
|
-
recurrenceEndDate?: Date;
|
|
612
|
-
|
|
613
|
-
@IsOptional()
|
|
614
|
-
@IsString()
|
|
615
|
-
@MaxLength(7)
|
|
616
|
-
color?: string;
|
|
617
|
-
|
|
618
|
-
@IsOptional()
|
|
619
|
-
@IsBoolean()
|
|
620
|
-
isActive?: boolean;
|
|
621
|
-
|
|
622
|
-
@IsOptional()
|
|
623
|
-
metadata?: Record<string, unknown>;
|
|
624
|
-
|
|
625
|
-
@IsOptional()
|
|
626
|
-
@IsUUID('4', { each: true })
|
|
627
|
-
participantIds?: string[];
|
|
202
|
+
```json
|
|
203
|
+
POST /event-manager/event/insert
|
|
204
|
+
{
|
|
205
|
+
"title": "Team Standup",
|
|
206
|
+
"description": "Daily standup meeting",
|
|
207
|
+
"startDate": "2026-03-10T09:00:00Z",
|
|
208
|
+
"endDate": "2026-03-10T09:30:00Z",
|
|
209
|
+
"isAllDay": false,
|
|
210
|
+
"color": "#3B82F6",
|
|
211
|
+
"meetingLink": "https://meet.example.com/standup",
|
|
212
|
+
"recurrence": {
|
|
213
|
+
"type": "DAILY",
|
|
214
|
+
"endDate": "2026-12-31",
|
|
215
|
+
"daysOfWeek": [1, 2, 3, 4, 5]
|
|
216
|
+
},
|
|
217
|
+
"participants": [
|
|
218
|
+
{ "userId": "uuid-1", "isOrganizer": true },
|
|
219
|
+
{ "userId": "uuid-2", "isOrganizer": false }
|
|
220
|
+
]
|
|
628
221
|
}
|
|
629
222
|
```
|
|
630
223
|
|
|
631
|
-
###
|
|
224
|
+
### Participants — `POST /event-manager/participant/*`
|
|
632
225
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
```
|
|
226
|
+
| Endpoint | Permission | Description |
|
|
227
|
+
|----------|-----------|-------------|
|
|
228
|
+
| `POST /event-manager/participant/insert` | `event.update` | Add participant to event |
|
|
229
|
+
| `POST /event-manager/participant/get-all` | `event.read` | List participants for an event |
|
|
230
|
+
| `POST /event-manager/participant/update-status` | JWT | Update own participation status |
|
|
231
|
+
| `POST /event-manager/participant/delete` | `event.update` | Remove participant from event |
|
|
640
232
|
|
|
641
|
-
|
|
233
|
+
**Update status request:**
|
|
642
234
|
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
@IsNotEmpty()
|
|
650
|
-
@IsDateString()
|
|
651
|
-
endDate!: Date;
|
|
652
|
-
|
|
653
|
-
@IsOptional()
|
|
654
|
-
@IsBoolean()
|
|
655
|
-
includeParticipants?: boolean;
|
|
656
|
-
|
|
657
|
-
@IsOptional()
|
|
658
|
-
@IsUUID()
|
|
659
|
-
userId?: string;
|
|
660
|
-
|
|
661
|
-
@IsOptional()
|
|
662
|
-
@IsBoolean()
|
|
663
|
-
activeOnly?: boolean;
|
|
235
|
+
```json
|
|
236
|
+
POST /event-manager/participant/update-status
|
|
237
|
+
{
|
|
238
|
+
"eventId": "uuid",
|
|
239
|
+
"status": "ACCEPTED"
|
|
664
240
|
}
|
|
665
241
|
```
|
|
666
242
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
```typescript
|
|
670
|
-
export class CreateEventParticipantDto {
|
|
671
|
-
@IsNotEmpty()
|
|
672
|
-
@IsUUID()
|
|
673
|
-
eventId!: string;
|
|
243
|
+
---
|
|
674
244
|
|
|
675
|
-
|
|
676
|
-
@IsUUID()
|
|
677
|
-
userId!: string;
|
|
245
|
+
## Entities
|
|
678
246
|
|
|
679
|
-
|
|
680
|
-
@IsEnum(ParticipantStatus)
|
|
681
|
-
status?: ParticipantStatus;
|
|
247
|
+
### Core Entities
|
|
682
248
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
249
|
+
| Entity | Table | Description |
|
|
250
|
+
|--------|-------|-------------|
|
|
251
|
+
| `Event` | `event_manager_event` | Calendar event with recurrence config |
|
|
252
|
+
| `EventParticipant` | `event_manager_participant` | User participation with status |
|
|
686
253
|
|
|
687
|
-
|
|
688
|
-
metadata?: Record<string, unknown>;
|
|
689
|
-
}
|
|
690
|
-
```
|
|
254
|
+
### Company Feature Entities (`enableCompanyFeature: true`)
|
|
691
255
|
|
|
692
|
-
|
|
256
|
+
| Entity | Table | Description |
|
|
257
|
+
|--------|-------|-------------|
|
|
258
|
+
| `EventWithCompany` | `event_manager_event` | Same as Event + `companyId` and `branchId` |
|
|
693
259
|
|
|
694
260
|
```typescript
|
|
695
|
-
|
|
696
|
-
@IsUUID()
|
|
697
|
-
@IsNotEmpty()
|
|
698
|
-
id!: string;
|
|
699
|
-
|
|
700
|
-
@IsNotEmpty()
|
|
701
|
-
@IsEnum(ParticipantStatus)
|
|
702
|
-
status!: ParticipantStatus;
|
|
703
|
-
}
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### BulkAddParticipantsDto
|
|
261
|
+
import { EventManagerModule } from '@flusys/nestjs-event-manager';
|
|
707
262
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
@IsNotEmpty()
|
|
715
|
-
@IsUUID('4', { each: true })
|
|
716
|
-
userIds!: string[];
|
|
717
|
-
}
|
|
263
|
+
TypeOrmModule.forRoot({
|
|
264
|
+
entities: [
|
|
265
|
+
...EventManagerModule.getEntities({ enableCompanyFeature: true }),
|
|
266
|
+
],
|
|
267
|
+
})
|
|
718
268
|
```
|
|
719
269
|
|
|
720
270
|
---
|
|
721
271
|
|
|
722
|
-
## Recurrence
|
|
272
|
+
## Recurrence Patterns
|
|
723
273
|
|
|
724
|
-
|
|
274
|
+
| Type | Description |
|
|
275
|
+
|------|-------------|
|
|
276
|
+
| `NONE` | Single occurrence, no recurrence |
|
|
277
|
+
| `DAILY` | Repeats every day (or specific days via `daysOfWeek`) |
|
|
278
|
+
| `WEEKLY` | Repeats every week on the same day |
|
|
279
|
+
| `BIWEEKLY` | Repeats every two weeks |
|
|
280
|
+
| `MONTHLY` | Repeats on the same day of month |
|
|
725
281
|
|
|
726
|
-
|
|
727
|
-
2. **Metadata:** Weekly recurrence uses `metadata.recurrenceDays` array (0=Sunday, 6=Saturday)
|
|
728
|
-
3. **Expansion:** Calendar queries expand recurring events into individual instances
|
|
729
|
-
4. **Instance Markers:** Expanded instances have `isRecurrenceInstance: true` and `originalEventId`
|
|
730
|
-
|
|
731
|
-
### Recurrence Expansion Logic
|
|
282
|
+
**Recurrence configuration:**
|
|
732
283
|
|
|
733
284
|
```typescript
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const occurrences: ICalendarEvent[] = [];
|
|
740
|
-
const recurrenceEnd = event.recurrenceEndDate || rangeEnd;
|
|
741
|
-
const maxOccurrences = this.eventConfig.getMaxRecurrenceOccurrences();
|
|
742
|
-
|
|
743
|
-
let currentDate = new Date(event.startDateTime);
|
|
744
|
-
|
|
745
|
-
while (currentDate <= recurrenceEnd && occurrences.length < maxOccurrences) {
|
|
746
|
-
if (currentDate >= rangeStart && currentDate <= rangeEnd) {
|
|
747
|
-
occurrences.push(this.createOccurrence(event, currentDate));
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// Advance based on recurrence type
|
|
751
|
-
switch (event.recurrenceType) {
|
|
752
|
-
case RecurrenceType.DAILY:
|
|
753
|
-
currentDate.setDate(currentDate.getDate() + 1);
|
|
754
|
-
break;
|
|
755
|
-
case RecurrenceType.WEEKLY:
|
|
756
|
-
currentDate = this.getNextWeeklyOccurrence(event, currentDate);
|
|
757
|
-
break;
|
|
758
|
-
case RecurrenceType.BIWEEKLY:
|
|
759
|
-
currentDate.setDate(currentDate.getDate() + 14);
|
|
760
|
-
break;
|
|
761
|
-
case RecurrenceType.MONTHLY:
|
|
762
|
-
currentDate.setMonth(currentDate.getMonth() + 1);
|
|
763
|
-
break;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
return occurrences;
|
|
285
|
+
interface IRecurrenceConfig {
|
|
286
|
+
type: 'NONE' | 'DAILY' | 'WEEKLY' | 'BIWEEKLY' | 'MONTHLY';
|
|
287
|
+
endDate?: string; // ISO date — when recurrence stops
|
|
288
|
+
daysOfWeek?: number[]; // 0=Sunday, 1=Monday, ... 6=Saturday (for DAILY/WEEKLY)
|
|
289
|
+
interval?: number; // Custom interval (e.g., every 3 weeks)
|
|
768
290
|
}
|
|
769
291
|
```
|
|
770
292
|
|
|
771
|
-
### Weekly Recurrence with Specific Days
|
|
772
|
-
|
|
773
|
-
```typescript
|
|
774
|
-
// Event with recurrence on Mon, Wed, Fri
|
|
775
|
-
const event = await eventService.insert({
|
|
776
|
-
title: 'Team Standup',
|
|
777
|
-
startDateTime: new Date('2026-03-02T09:00:00'),
|
|
778
|
-
endDateTime: new Date('2026-03-02T09:30:00'),
|
|
779
|
-
recurrenceType: RecurrenceType.WEEKLY,
|
|
780
|
-
recurrenceEndDate: new Date('2026-06-30'),
|
|
781
|
-
metadata: {
|
|
782
|
-
recurrenceDays: [1, 3, 5], // Monday, Wednesday, Friday
|
|
783
|
-
},
|
|
784
|
-
}, user);
|
|
785
|
-
```
|
|
786
|
-
|
|
787
293
|
---
|
|
788
294
|
|
|
789
|
-
##
|
|
790
|
-
|
|
791
|
-
All event manager endpoints are prefixed with `/event-manager`.
|
|
792
|
-
|
|
793
|
-
### Event Endpoints
|
|
794
|
-
|
|
795
|
-
| Endpoint | Method | Description | Permission |
|
|
796
|
-
|----------|--------|-------------|------------|
|
|
797
|
-
| `/event-manager/event/insert` | POST | Create event | `event.create` |
|
|
798
|
-
| `/event-manager/event/insert-many` | POST | Bulk create | `event.create` |
|
|
799
|
-
| `/event-manager/event/get/:id` | POST | Get by ID | `event.read` |
|
|
800
|
-
| `/event-manager/event/get-all` | POST | List with pagination | `event.read` |
|
|
801
|
-
| `/event-manager/event/update` | POST | Update event | `event.update` |
|
|
802
|
-
| `/event-manager/event/update-many` | POST | Bulk update | `event.update` |
|
|
803
|
-
| `/event-manager/event/delete` | POST | Soft delete | `event.delete` |
|
|
804
|
-
| `/event-manager/event/calendar` | POST | Calendar query with expansion | `event.read` |
|
|
805
|
-
| `/event-manager/event/get-with-participants` | POST | Get with participant list | `event.read` |
|
|
806
|
-
| `/event-manager/event/add-participants` | POST | Add participants | `event.update` |
|
|
807
|
-
|
|
808
|
-
### Participant Endpoints
|
|
809
|
-
|
|
810
|
-
| Endpoint | Method | Description | Permission |
|
|
811
|
-
|----------|--------|-------------|------------|
|
|
812
|
-
| `/event-manager/participant/insert` | POST | Create participant | `event_participant.create` |
|
|
813
|
-
| `/event-manager/participant/insert-many` | POST | Bulk create | `event_participant.create` |
|
|
814
|
-
| `/event-manager/participant/get/:id` | POST | Get by ID | `event_participant.read` |
|
|
815
|
-
| `/event-manager/participant/get-all` | POST | List with pagination | `event_participant.read` |
|
|
816
|
-
| `/event-manager/participant/update` | POST | Update participant | `event_participant.update` |
|
|
817
|
-
| `/event-manager/participant/update-many` | POST | Bulk update | `event_participant.update` |
|
|
818
|
-
| `/event-manager/participant/delete` | POST | Soft delete | `event_participant.delete` |
|
|
819
|
-
| `/event-manager/participant/update-status` | POST | Update status | `event_participant.update` |
|
|
820
|
-
| `/event-manager/participant/get-by-event` | POST | Get by event | `event_participant.read` |
|
|
821
|
-
| `/event-manager/participant/get-by-user` | POST | Get by user | `event_participant.read` |
|
|
822
|
-
| `/event-manager/participant/remove` | POST | Remove from event | `event_participant.delete` |
|
|
823
|
-
|
|
824
|
-
### Request/Response Examples
|
|
825
|
-
|
|
826
|
-
**Create Event:**
|
|
295
|
+
## Participant Status
|
|
827
296
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
"description": "Weekly sync",
|
|
835
|
-
"startDateTime": "2026-03-01T10:00:00Z",
|
|
836
|
-
"endDateTime": "2026-03-01T11:00:00Z",
|
|
837
|
-
"recurrenceType": "weekly",
|
|
838
|
-
"recurrenceEndDate": "2026-06-01T00:00:00Z",
|
|
839
|
-
"metadata": { "recurrenceDays": [1, 3, 5] },
|
|
840
|
-
"participantIds": ["user-id-1", "user-id-2"],
|
|
841
|
-
"color": "#3B82F6"
|
|
842
|
-
}'
|
|
843
|
-
|
|
844
|
-
# Response:
|
|
845
|
-
{
|
|
846
|
-
"success": true,
|
|
847
|
-
"message": "Event created successfully",
|
|
848
|
-
"data": {
|
|
849
|
-
"id": "event-uuid",
|
|
850
|
-
"title": "Team Meeting",
|
|
851
|
-
"startDateTime": "2026-03-01T10:00:00.000Z",
|
|
852
|
-
"recurrenceType": "weekly",
|
|
853
|
-
...
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
```
|
|
857
|
-
|
|
858
|
-
**Calendar Query:**
|
|
297
|
+
| Status | Description |
|
|
298
|
+
|--------|-------------|
|
|
299
|
+
| `PENDING` | Invited but not responded |
|
|
300
|
+
| `ACCEPTED` | Confirmed attendance |
|
|
301
|
+
| `DECLINED` | Declined invitation |
|
|
302
|
+
| `TENTATIVE` | Tentatively attending |
|
|
859
303
|
|
|
860
|
-
|
|
861
|
-
curl -X POST http://localhost:2002/event-manager/event/calendar \
|
|
862
|
-
-H "Authorization: Bearer <token>" \
|
|
863
|
-
-H "Content-Type: application/json" \
|
|
864
|
-
-d '{
|
|
865
|
-
"startDate": "2026-03-01",
|
|
866
|
-
"endDate": "2026-03-31",
|
|
867
|
-
"includeParticipants": true,
|
|
868
|
-
"activeOnly": true
|
|
869
|
-
}'
|
|
870
|
-
|
|
871
|
-
# Response includes expanded recurring events
|
|
872
|
-
{
|
|
873
|
-
"success": true,
|
|
874
|
-
"data": [
|
|
875
|
-
{
|
|
876
|
-
"id": "event-uuid",
|
|
877
|
-
"title": "Team Meeting",
|
|
878
|
-
"startDateTime": "2026-03-03T10:00:00.000Z",
|
|
879
|
-
"isRecurrenceInstance": true,
|
|
880
|
-
"originalEventId": "event-uuid",
|
|
881
|
-
"participants": [...]
|
|
882
|
-
},
|
|
883
|
-
{
|
|
884
|
-
"id": "event-uuid",
|
|
885
|
-
"title": "Team Meeting",
|
|
886
|
-
"startDateTime": "2026-03-05T10:00:00.000Z",
|
|
887
|
-
"isRecurrenceInstance": true,
|
|
888
|
-
"originalEventId": "event-uuid",
|
|
889
|
-
"participants": [...]
|
|
890
|
-
},
|
|
891
|
-
...
|
|
892
|
-
],
|
|
893
|
-
"meta": { "total": 15, "page": 1, "pageSize": 100, "count": 15 }
|
|
894
|
-
}
|
|
895
|
-
```
|
|
304
|
+
Participants update their own status via `POST /event-manager/participant/update-status`.
|
|
896
305
|
|
|
897
306
|
---
|
|
898
307
|
|
|
899
|
-
##
|
|
308
|
+
## Cross-Module Adapter (EVENT_MANAGER_ADAPTER)
|
|
900
309
|
|
|
901
|
-
|
|
310
|
+
The adapter allows other modules to create and manage events without importing `@flusys/nestjs-event-manager` directly. This prevents circular dependencies.
|
|
902
311
|
|
|
903
|
-
The
|
|
312
|
+
**The interface is defined in `@flusys/nestjs-shared`:**
|
|
904
313
|
|
|
905
314
|
```typescript
|
|
906
|
-
|
|
907
|
-
export class EventManagerDataSourceProvider extends MultiTenantDataSourceService {
|
|
908
|
-
// Isolated static cache per module
|
|
909
|
-
protected static override readonly tenantConnections = new Map<string, DataSource>();
|
|
910
|
-
protected static override singleDataSource: DataSource | null = null;
|
|
911
|
-
protected static override tenantsRegistry: ITenant[] = [];
|
|
912
|
-
protected static override initialized = false;
|
|
913
|
-
protected static override connectionLocks = new Map<string, Promise<DataSource>>();
|
|
914
|
-
protected static override singleConnectionLock: Promise<DataSource> | null = null;
|
|
915
|
-
|
|
916
|
-
constructor(
|
|
917
|
-
@Inject(EventManagerConfigService) private readonly configService: EventManagerConfigService,
|
|
918
|
-
@Optional() @Inject(REQUEST) protected override readonly request?: Request,
|
|
919
|
-
) {
|
|
920
|
-
super(configService, request);
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
async getEventManagerEntities(enableCompanyFeature?: boolean): Promise<any[]> {
|
|
924
|
-
const featureEnabled = enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
|
|
925
|
-
return getEventManagerEntitiesByConfig(featureEnabled);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
// Override methods to use EventManagerDataSourceProvider's static cache
|
|
929
|
-
protected override async getSingleDataSource(): Promise<DataSource> {
|
|
930
|
-
// Uses EventManagerDataSourceProvider.singleDataSource
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
protected override async getOrCreateTenantConnection(tenant: ITenant): Promise<DataSource> {
|
|
934
|
-
// Uses EventManagerDataSourceProvider.tenantConnections
|
|
935
|
-
}
|
|
936
|
-
}
|
|
315
|
+
import { IEventManagerAdapter, EVENT_MANAGER_ADAPTER } from '@flusys/nestjs-shared/interfaces';
|
|
937
316
|
```
|
|
938
317
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
## Multi-Tenant Support
|
|
942
|
-
|
|
943
|
-
### Company Filtering
|
|
944
|
-
|
|
945
|
-
When company feature is enabled:
|
|
318
|
+
**Using the adapter in another module:**
|
|
946
319
|
|
|
947
320
|
```typescript
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
applyCompanyFilter(query, {
|
|
952
|
-
isCompanyFeatureEnabled: this.eventConfig.isCompanyFeatureEnabled(),
|
|
953
|
-
entityAlias: 'event',
|
|
954
|
-
}, user);
|
|
955
|
-
|
|
956
|
-
return result;
|
|
957
|
-
}
|
|
958
|
-
```
|
|
321
|
+
import { IEventManagerAdapter } from '@flusys/nestjs-shared/interfaces';
|
|
322
|
+
import { EVENT_MANAGER_ADAPTER } from '@flusys/nestjs-shared/interfaces';
|
|
959
323
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
324
|
+
@Injectable()
|
|
325
|
+
export class MeetingService {
|
|
326
|
+
constructor(
|
|
327
|
+
@Optional() @Inject(EVENT_MANAGER_ADAPTER)
|
|
328
|
+
private readonly eventAdapter?: IEventManagerAdapter,
|
|
329
|
+
) {}
|
|
965
330
|
|
|
966
|
-
|
|
967
|
-
|
|
331
|
+
async scheduleMeeting(data: { title: string; userId: string; date: Date }): Promise<void> {
|
|
332
|
+
await this.eventAdapter?.createEvent({
|
|
333
|
+
title: data.title,
|
|
334
|
+
startDate: data.date,
|
|
335
|
+
participants: [{ userId: data.userId, isOrganizer: true }],
|
|
336
|
+
});
|
|
968
337
|
}
|
|
969
|
-
|
|
970
|
-
return entity;
|
|
971
338
|
}
|
|
972
339
|
```
|
|
973
340
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
## Swagger Configuration
|
|
341
|
+
Using `@Optional()` means your module works even when `EventManagerModule` is not registered.
|
|
977
342
|
|
|
978
|
-
|
|
343
|
+
**IEventManagerAdapter interface:**
|
|
979
344
|
|
|
980
345
|
```typescript
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
// Returns:
|
|
990
|
-
// {
|
|
991
|
-
// title: 'Event Manager API',
|
|
992
|
-
// description: '... dynamic description based on config ...',
|
|
993
|
-
// version: '1.0',
|
|
994
|
-
// path: 'api/docs/event-manager',
|
|
995
|
-
// bearerAuth: true,
|
|
996
|
-
// }
|
|
346
|
+
interface IEventManagerAdapter {
|
|
347
|
+
createEvent(data: ICreateEventData): Promise<{ id: string }>;
|
|
348
|
+
updateEvent(id: string, data: Partial<ICreateEventData>): Promise<void>;
|
|
349
|
+
deleteEvent(id: string): Promise<void>;
|
|
350
|
+
addParticipant(eventId: string, userId: string): Promise<void>;
|
|
351
|
+
removeParticipant(eventId: string, userId: string): Promise<void>;
|
|
352
|
+
updateParticipantStatus(eventId: string, userId: string, status: ParticipantStatus): Promise<void>;
|
|
353
|
+
}
|
|
997
354
|
```
|
|
998
355
|
|
|
999
356
|
---
|
|
1000
357
|
|
|
1001
|
-
##
|
|
358
|
+
## Exported Services
|
|
1002
359
|
|
|
1003
|
-
|
|
360
|
+
| Service | Description |
|
|
361
|
+
|---------|-------------|
|
|
362
|
+
| `EventManagerHelperService` | Singleton service for programmatic event management from other modules |
|
|
363
|
+
| `EventService` | Full event CRUD (REQUEST-scoped) |
|
|
364
|
+
| `EventParticipantService` | Participant management (REQUEST-scoped) |
|
|
365
|
+
| `EventManagerConfigService` | Exposes runtime config |
|
|
366
|
+
| `EVENT_MANAGER_ADAPTER` | Cross-module adapter token |
|
|
1004
367
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
```typescript
|
|
1008
|
-
import { ParticipantStatus, RecurrenceType } from '@flusys/nestjs-shared/enums';
|
|
1009
|
-
|
|
1010
|
-
export interface IEventManagerAdapter {
|
|
1011
|
-
createEvent(options: CreateEventOptions): Promise<EventResult>;
|
|
1012
|
-
addParticipants(eventId: string, userIds: string[], companyId?: string): Promise<void>;
|
|
1013
|
-
removeParticipant(eventId: string, userId: string): Promise<void>;
|
|
1014
|
-
updateParticipantStatus(participantId: string, status: ParticipantStatus): Promise<void>;
|
|
1015
|
-
getEventsForUser?(userId: string, startDate: Date, endDate: Date, companyId?: string): Promise<EventResult[]>;
|
|
1016
|
-
getEventById?(eventId: string): Promise<EventResult | null>;
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
export interface CreateEventOptions {
|
|
1020
|
-
title: string;
|
|
1021
|
-
description?: string;
|
|
1022
|
-
meetingLink?: string;
|
|
1023
|
-
startDateTime: Date;
|
|
1024
|
-
endDateTime: Date;
|
|
1025
|
-
isAllDay?: boolean;
|
|
1026
|
-
recurrenceType?: RecurrenceType;
|
|
1027
|
-
recurrenceEndDate?: Date;
|
|
1028
|
-
color?: string;
|
|
1029
|
-
metadata?: Record<string, unknown>;
|
|
1030
|
-
participantIds?: string[];
|
|
1031
|
-
organizerId?: string;
|
|
1032
|
-
companyId?: string;
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
export interface EventResult {
|
|
1036
|
-
id: string;
|
|
1037
|
-
title: string;
|
|
1038
|
-
description?: string | null;
|
|
1039
|
-
meetingLink?: string | null;
|
|
1040
|
-
startDateTime: Date;
|
|
1041
|
-
endDateTime: Date;
|
|
1042
|
-
isAllDay: boolean;
|
|
1043
|
-
recurrenceType: string;
|
|
1044
|
-
recurrenceEndDate?: Date | null;
|
|
1045
|
-
color: string;
|
|
1046
|
-
isActive: boolean;
|
|
1047
|
-
metadata?: Record<string, unknown> | null;
|
|
1048
|
-
companyId?: string | null;
|
|
1049
|
-
createdAt: Date;
|
|
1050
|
-
updatedAt: Date;
|
|
1051
|
-
}
|
|
368
|
+
---
|
|
1052
369
|
|
|
1053
|
-
|
|
1054
|
-
```
|
|
370
|
+
## Programmatic Usage
|
|
1055
371
|
|
|
1056
|
-
|
|
372
|
+
Use `EventManagerHelperService` (singleton) for programmatic access from within the same application:
|
|
1057
373
|
|
|
1058
374
|
```typescript
|
|
1059
|
-
import {
|
|
1060
|
-
import {
|
|
1061
|
-
EVENT_MANAGER_ADAPTER,
|
|
1062
|
-
IEventManagerAdapter,
|
|
1063
|
-
RecurrenceType,
|
|
1064
|
-
} from '@flusys/nestjs-shared';
|
|
375
|
+
import { EventManagerHelperService } from '@flusys/nestjs-event-manager';
|
|
1065
376
|
|
|
1066
377
|
@Injectable()
|
|
1067
|
-
export class
|
|
378
|
+
export class OnboardingService {
|
|
1068
379
|
constructor(
|
|
1069
|
-
@
|
|
1070
|
-
private readonly
|
|
380
|
+
@Inject(EventManagerHelperService)
|
|
381
|
+
private readonly eventHelper: EventManagerHelperService,
|
|
1071
382
|
) {}
|
|
1072
383
|
|
|
1073
|
-
async
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
return;
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
const event = await this.eventAdapter.createEvent({
|
|
1080
|
-
title: 'Team Meeting',
|
|
1081
|
-
description: 'Weekly sync meeting',
|
|
1082
|
-
startDateTime: new Date(),
|
|
1083
|
-
endDateTime: new Date(Date.now() + 3600000), // 1 hour later
|
|
1084
|
-
recurrenceType: RecurrenceType.WEEKLY,
|
|
1085
|
-
participantIds: [userId],
|
|
1086
|
-
organizerId: userId,
|
|
1087
|
-
companyId,
|
|
1088
|
-
});
|
|
1089
|
-
|
|
1090
|
-
return event;
|
|
1091
|
-
}
|
|
384
|
+
async scheduleOnboardingCall(userId: string): Promise<void> {
|
|
385
|
+
const nextWeek = new Date();
|
|
386
|
+
nextWeek.setDate(nextWeek.getDate() + 7);
|
|
1092
387
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
388
|
+
await this.eventHelper.createEvent({
|
|
389
|
+
title: 'Onboarding Call',
|
|
390
|
+
startDate: nextWeek,
|
|
391
|
+
endDate: new Date(nextWeek.getTime() + 30 * 60 * 1000), // +30 min
|
|
392
|
+
participants: [{ userId, isOrganizer: false }],
|
|
393
|
+
});
|
|
1097
394
|
}
|
|
1098
395
|
}
|
|
1099
396
|
```
|
|
1100
397
|
|
|
1101
|
-
### Module Registration Order
|
|
1102
|
-
|
|
1103
|
-
**Important:** The `EventManagerModule` must be imported BEFORE any module that injects the adapter:
|
|
1104
|
-
|
|
1105
|
-
```typescript
|
|
1106
|
-
@Module({
|
|
1107
|
-
imports: [
|
|
1108
|
-
// EventManagerModule FIRST
|
|
1109
|
-
EventManagerModule.forRoot({
|
|
1110
|
-
global: true,
|
|
1111
|
-
// ...config
|
|
1112
|
-
}),
|
|
1113
|
-
|
|
1114
|
-
// Modules that use the adapter AFTER
|
|
1115
|
-
MyFeatureModule,
|
|
1116
|
-
],
|
|
1117
|
-
})
|
|
1118
|
-
export class AppModule {}
|
|
1119
|
-
```
|
|
1120
|
-
|
|
1121
|
-
### Adapter Methods
|
|
1122
|
-
|
|
1123
|
-
| Method | Description |
|
|
1124
|
-
|--------|-------------|
|
|
1125
|
-
| `createEvent(options)` | Create a new event with optional participants |
|
|
1126
|
-
| `addParticipants(eventId, userIds)` | Add users as participants to an event |
|
|
1127
|
-
| `removeParticipant(eventId, userId)` | Remove a user from an event |
|
|
1128
|
-
| `updateParticipantStatus(participantId, status)` | Update RSVP status |
|
|
1129
|
-
| `getEventsForUser(userId, startDate, endDate)` | Get user's events in date range |
|
|
1130
|
-
| `getEventById(eventId)` | Get a single event by ID |
|
|
1131
|
-
|
|
1132
|
-
### Helper Service
|
|
1133
|
-
|
|
1134
|
-
The adapter delegates to `EventManagerHelperService`, a singleton service that handles the actual business logic:
|
|
1135
|
-
|
|
1136
|
-
```typescript
|
|
1137
|
-
@Injectable()
|
|
1138
|
-
export class EventManagerHelperService {
|
|
1139
|
-
async createEvent(options: CreateEventOptions): Promise<EventResult>;
|
|
1140
|
-
async addParticipants(eventId: string, userIds: string[], companyId?: string): Promise<void>;
|
|
1141
|
-
async removeParticipant(eventId: string, userId: string): Promise<void>;
|
|
1142
|
-
async updateParticipantStatus(participantId: string, status: ParticipantStatus): Promise<void>;
|
|
1143
|
-
async getEventsForUser(userId: string, startDate: Date, endDate: Date, companyId?: string): Promise<EventResult[]>;
|
|
1144
|
-
async getEventById(eventId: string): Promise<EventResult | null>;
|
|
1145
|
-
}
|
|
1146
|
-
```
|
|
1147
|
-
|
|
1148
398
|
---
|
|
1149
399
|
|
|
1150
|
-
##
|
|
400
|
+
## Troubleshooting
|
|
1151
401
|
|
|
1152
|
-
|
|
402
|
+
**`EVENT_MANAGER_ADAPTER` is undefined**
|
|
1153
403
|
|
|
1154
|
-
|
|
1155
|
-
// Daily standup
|
|
1156
|
-
await eventService.insert({
|
|
1157
|
-
title: 'Daily Standup',
|
|
1158
|
-
recurrenceType: RecurrenceType.DAILY,
|
|
1159
|
-
recurrenceEndDate: new Date('2026-12-31'),
|
|
1160
|
-
}, user);
|
|
1161
|
-
|
|
1162
|
-
// Weekly on specific days
|
|
1163
|
-
await eventService.insert({
|
|
1164
|
-
title: 'Team Sync',
|
|
1165
|
-
recurrenceType: RecurrenceType.WEEKLY,
|
|
1166
|
-
metadata: { recurrenceDays: [1, 4] }, // Monday, Thursday
|
|
1167
|
-
}, user);
|
|
1168
|
-
|
|
1169
|
-
// Monthly meeting
|
|
1170
|
-
await eventService.insert({
|
|
1171
|
-
title: 'Monthly Review',
|
|
1172
|
-
recurrenceType: RecurrenceType.MONTHLY,
|
|
1173
|
-
}, user);
|
|
1174
|
-
```
|
|
404
|
+
`EventManagerModule` was not registered in the application module. Either register it or use `@Optional()` in the consuming service.
|
|
1175
405
|
|
|
1176
|
-
|
|
406
|
+
---
|
|
1177
407
|
|
|
1178
|
-
|
|
1179
|
-
// Only fetch what you need
|
|
1180
|
-
const events = await eventService.getEventsForCalendarRange({
|
|
1181
|
-
startDate: monthStart,
|
|
1182
|
-
endDate: monthEnd,
|
|
1183
|
-
includeParticipants: false, // Only include if needed
|
|
1184
|
-
activeOnly: true,
|
|
1185
|
-
}, user);
|
|
1186
|
-
```
|
|
408
|
+
**Events not filtered by company**
|
|
1187
409
|
|
|
1188
|
-
|
|
410
|
+
Ensure `enableCompanyFeature: true` is set and the JWT contains `companyId`. Without the company ID in the token, company-scoped queries return all events.
|
|
1189
411
|
|
|
1190
|
-
|
|
1191
|
-
// Update participant response
|
|
1192
|
-
await participantService.updateStatus({
|
|
1193
|
-
id: participantId,
|
|
1194
|
-
status: ParticipantStatus.ACCEPTED,
|
|
1195
|
-
}, user);
|
|
1196
|
-
```
|
|
412
|
+
---
|
|
1197
413
|
|
|
1198
|
-
|
|
414
|
+
**Recurrence events not expanding**
|
|
1199
415
|
|
|
1200
|
-
|
|
1201
|
-
// Use the default color from config
|
|
1202
|
-
const defaultColor = eventConfig.getDefaultColor();
|
|
1203
|
-
|
|
1204
|
-
// Or use predefined colors
|
|
1205
|
-
const EVENT_COLORS = [
|
|
1206
|
-
'#3B82F6', // Blue
|
|
1207
|
-
'#EF4444', // Red
|
|
1208
|
-
'#10B981', // Green
|
|
1209
|
-
'#F59E0B', // Amber
|
|
1210
|
-
'#8B5CF6', // Purple
|
|
1211
|
-
];
|
|
1212
|
-
```
|
|
416
|
+
`nestjs-event-manager` stores the recurrence configuration as a JSON column — it does not auto-expand recurring events into separate database rows. Your frontend or a separate expansion service is responsible for generating occurrences from the recurrence config.
|
|
1213
417
|
|
|
1214
418
|
---
|
|
1215
419
|
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
### Main Exports
|
|
420
|
+
**`No metadata for entity`**
|
|
1219
421
|
|
|
422
|
+
Register entities in TypeORM:
|
|
1220
423
|
```typescript
|
|
1221
|
-
|
|
1222
|
-
import { EventManagerModule } from '@flusys/nestjs-event-manager';
|
|
1223
|
-
|
|
1224
|
-
// Adapter (for cross-module usage)
|
|
1225
|
-
import { EventManagerAdapter } from '@flusys/nestjs-event-manager/adapters';
|
|
1226
|
-
|
|
1227
|
-
// Adapter Interface & Token (from shared)
|
|
1228
|
-
import {
|
|
1229
|
-
EVENT_MANAGER_ADAPTER,
|
|
1230
|
-
IEventManagerAdapter,
|
|
1231
|
-
CreateEventOptions,
|
|
1232
|
-
EventResult,
|
|
1233
|
-
} from '@flusys/nestjs-shared/interfaces';
|
|
1234
|
-
|
|
1235
|
-
// Services
|
|
1236
|
-
import {
|
|
1237
|
-
EventService,
|
|
1238
|
-
EventParticipantService,
|
|
1239
|
-
EventManagerConfigService,
|
|
1240
|
-
EventManagerDataSourceProvider,
|
|
1241
|
-
EventManagerHelperService,
|
|
1242
|
-
} from '@flusys/nestjs-event-manager/services';
|
|
1243
|
-
|
|
1244
|
-
// Entities
|
|
1245
|
-
import {
|
|
1246
|
-
Event,
|
|
1247
|
-
EventBase,
|
|
1248
|
-
EventWithCompany,
|
|
1249
|
-
EventParticipant,
|
|
1250
|
-
EventParticipantBase,
|
|
1251
|
-
EventManagerCoreEntities,
|
|
1252
|
-
EventManagerCompanyEntities,
|
|
1253
|
-
getEventManagerEntitiesByConfig,
|
|
1254
|
-
} from '@flusys/nestjs-event-manager/entities';
|
|
1255
|
-
|
|
1256
|
-
// DTOs
|
|
1257
|
-
import {
|
|
1258
|
-
CreateEventDto,
|
|
1259
|
-
UpdateEventDto,
|
|
1260
|
-
EventResponseDto,
|
|
1261
|
-
CalendarEventResponseDto,
|
|
1262
|
-
CalendarQueryDto,
|
|
1263
|
-
CreateEventParticipantDto,
|
|
1264
|
-
UpdateEventParticipantDto,
|
|
1265
|
-
EventParticipantResponseDto,
|
|
1266
|
-
UpdateParticipantStatusDto,
|
|
1267
|
-
BulkAddParticipantsDto,
|
|
1268
|
-
} from '@flusys/nestjs-event-manager/dtos';
|
|
1269
|
-
|
|
1270
|
-
// Interfaces
|
|
1271
|
-
import {
|
|
1272
|
-
IEvent,
|
|
1273
|
-
IEventParticipant,
|
|
1274
|
-
ICalendarEvent,
|
|
1275
|
-
IEventManagerModuleConfig,
|
|
1276
|
-
EventManagerModuleOptions,
|
|
1277
|
-
EventManagerModuleAsyncOptions,
|
|
1278
|
-
EventManagerOptionsFactory,
|
|
1279
|
-
} from '@flusys/nestjs-event-manager/interfaces';
|
|
1280
|
-
|
|
1281
|
-
// Enums (canonical source)
|
|
1282
|
-
import {
|
|
1283
|
-
RecurrenceType,
|
|
1284
|
-
ParticipantStatus,
|
|
1285
|
-
} from '@flusys/nestjs-shared/enums';
|
|
1286
|
-
|
|
1287
|
-
// Or re-export from event-manager
|
|
1288
|
-
import {
|
|
1289
|
-
RecurrenceType,
|
|
1290
|
-
ParticipantStatus,
|
|
1291
|
-
} from '@flusys/nestjs-event-manager/enums';
|
|
1292
|
-
|
|
1293
|
-
// Controllers
|
|
1294
|
-
import {
|
|
1295
|
-
EventController,
|
|
1296
|
-
EventParticipantController,
|
|
1297
|
-
} from '@flusys/nestjs-event-manager/controllers';
|
|
1298
|
-
|
|
1299
|
-
// Config & Docs
|
|
1300
|
-
import { EVENT_MANAGER_MODULE_OPTIONS } from '@flusys/nestjs-event-manager/config';
|
|
1301
|
-
import {
|
|
1302
|
-
EVENT_PERMISSIONS,
|
|
1303
|
-
EVENT_PARTICIPANT_PERMISSIONS,
|
|
1304
|
-
eventManagerSwaggerConfig,
|
|
1305
|
-
} from '@flusys/nestjs-event-manager/docs';
|
|
424
|
+
entities: [...EventManagerModule.getEntities({ enableCompanyFeature: true })]
|
|
1306
425
|
```
|
|
1307
426
|
|
|
1308
427
|
---
|
|
1309
428
|
|
|
1310
|
-
##
|
|
1311
|
-
|
|
1312
|
-
The `@flusys/nestjs-event-manager` package provides:
|
|
429
|
+
## License
|
|
1313
430
|
|
|
1314
|
-
|
|
1315
|
-
- **Recurrence Support** - Daily, weekly, biweekly, monthly patterns
|
|
1316
|
-
- **Calendar Queries** - Date range queries with automatic recurrence expansion
|
|
1317
|
-
- **Participant Management** - Track participants with status (pending, accepted, declined, tentative)
|
|
1318
|
-
- **Adapter Pattern** - Cross-module event creation via `IEventManagerAdapter`
|
|
1319
|
-
- **Company Scoping** - Per-company event isolation
|
|
1320
|
-
- **Multi-Tenant** - Separate databases per tenant
|
|
1321
|
-
- **POST-only RPC** - All endpoints use POST method
|
|
1322
|
-
- **Permission Guards** - All endpoints protected by permissions
|
|
431
|
+
MIT © FLUSYS
|
|
1323
432
|
|
|
1324
433
|
---
|
|
1325
434
|
|
|
1326
|
-
**
|
|
1327
|
-
**Version:** 3.0.0
|
|
1328
|
-
**NestJS Version:** 11
|
|
435
|
+
> Part of the **FLUSYS** framework — a full-stack monorepo powering Angular 21 + NestJS 11 applications.
|