@memberjunction/ng-entity-permissions 4.0.0 → 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.
Files changed (2) hide show
  1. package/README.md +62 -445
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -1,27 +1,32 @@
1
1
  # @memberjunction/ng-entity-permissions
2
2
 
3
- The `@memberjunction/ng-entity-permissions` package provides Angular components for displaying and editing permissions for MemberJunction entities. It allows administrators to manage role-based access control (RBAC) for entities in a user-friendly grid interface.
3
+ Angular components for displaying and editing entity-level CRUD permissions in MemberJunction. Provides a grid-based interface for managing Read, Create, Update, and Delete permissions per role or per entity.
4
4
 
5
5
  ## Overview
6
6
 
7
- This package is part of the MemberJunction platform and provides essential UI components for managing entity-level permissions. It offers two primary components:
7
+ This package provides two main components for permission management: `EntityPermissionsGridComponent` displays a grid of permission checkboxes (filterable by entity or role), and `EntitySelectorWithGridComponent` combines an entity dropdown with the permissions grid for a complete permission management interface.
8
8
 
9
- 1. **EntityPermissionsGridComponent** - A grid that displays and allows editing of permissions for either a specific entity across all roles, or for a specific role across all entities
10
- 2. **EntityPermissionsSelectorWithGridComponent** - A composite component that combines an entity dropdown selector with the permissions grid
9
+ ```mermaid
10
+ graph TD
11
+ ESWG["EntitySelectorWithGridComponent"] --> DD["Entity Dropdown\n(Kendo DropDown)"]
12
+ ESWG --> EPG["EntityPermissionsGridComponent"]
13
+ EPG --> KG["Kendo Grid"]
14
+ EPG --> EP["EntityPermissionEntity[]"]
15
+
16
+ style ESWG fill:#7c5295,stroke:#563a6b,color:#fff
17
+ style DD fill:#2d8659,stroke:#1a5c3a,color:#fff
18
+ style EPG fill:#2d6a9f,stroke:#1a4971,color:#fff
19
+ style KG fill:#2d8659,stroke:#1a5c3a,color:#fff
20
+ style EP fill:#b8762f,stroke:#8a5722,color:#fff
21
+ ```
11
22
 
12
23
  ## Features
13
24
 
14
- - **Dual-mode operation**: View permissions by entity (all roles for one entity) or by role (all entities for one role)
15
- - **Grid-based interface** for intuitive permission management
16
- - **Batch editing** with transaction support for atomic updates
17
- - **Visual feedback** with dirty state indicators and row highlighting
18
- - **Quick actions**:
19
- - Flip all permissions of a specific type (Read/Create/Update/Delete)
20
- - Toggle all permissions in a row with intelligent logic
21
- - Revert individual row changes
22
- - **Real-time permission change events** with cancellation support
23
- - **Automatic permission record creation** for missing role/entity combinations
24
- - **Responsive loading states** with Kendo UI indicators
25
+ - **Dual mode**: View permissions by Entity (all roles for an entity) or by Role (all entities for a role)
26
+ - **Interactive grid**: Toggle CRUD permissions with checkboxes
27
+ - **Permission change events**: Emits `EntityPermissionChangedEvent` with cancel support
28
+ - **Entity selector**: Combined dropdown + grid for complete permission management
29
+ - **Auto-refresh**: Automatically reloads when entity or role changes
25
30
 
26
31
  ## Installation
27
32
 
@@ -29,459 +34,71 @@ This package is part of the MemberJunction platform and provides essential UI co
29
34
  npm install @memberjunction/ng-entity-permissions
30
35
  ```
31
36
 
32
- ## Requirements
33
-
34
- ### Peer Dependencies
35
- - Angular 21+
36
- - @angular/common
37
- - @angular/core
38
- - @angular/forms
39
- - @angular/router
37
+ ## Key Dependencies
40
38
 
41
- ### Runtime Dependencies
42
- - @memberjunction/core (v2.43.0+)
43
- - @memberjunction/core-entities (v2.43.0+)
44
- - @memberjunction/global (v2.43.0+)
45
- - @memberjunction/ng-container-directives (v2.43.0+)
46
- - @memberjunction/ng-shared (v2.43.0+)
47
- - @progress/kendo-angular-grid (v16.2.0)
48
- - @progress/kendo-angular-dropdowns (v16.2.0)
49
- - @progress/kendo-angular-buttons (v16.2.0)
50
- - @progress/kendo-angular-dialog (v16.2.0)
51
- - @progress/kendo-angular-indicators (v16.2.0)
39
+ | Dependency | Purpose |
40
+ |---|---|
41
+ | `@memberjunction/core` | Metadata, RunView |
42
+ | `@memberjunction/core-entities` | EntityPermissionEntity |
43
+ | `@progress/kendo-angular-grid` | Permissions grid |
44
+ | `@progress/kendo-angular-dropdowns` | Entity/role selector |
52
45
 
53
46
  ## Usage
54
47
 
55
- ### Module Setup
56
-
57
- First, import the `EntityPermissionsModule` in your Angular module:
58
-
59
- ```typescript
60
- import { EntityPermissionsModule } from '@memberjunction/ng-entity-permissions';
48
+ ### Entity Mode (All Roles for an Entity)
61
49
 
62
- @NgModule({
63
- imports: [
64
- CommonModule,
65
- FormsModule,
66
- // ... other imports
67
- EntityPermissionsModule
68
- ],
69
- })
70
- export class YourModule { }
50
+ ```html
51
+ <mj-entity-permissions-grid
52
+ [Mode]="'Entity'"
53
+ [EntityName]="'Contacts'"
54
+ (PermissionChanged)="onPermissionChanged($event)">
55
+ </mj-entity-permissions-grid>
71
56
  ```
72
57
 
73
- ### Basic Usage Examples
58
+ ### Role Mode (All Entities for a Role)
74
59
 
75
- #### Entity Permissions Grid - Entity Mode
76
-
77
- Display and manage permissions for a specific entity across all roles:
78
-
79
- ```typescript
80
- import { Component } from '@angular/core';
81
- import { EntityPermissionChangedEvent } from '@memberjunction/ng-entity-permissions';
82
-
83
- @Component({
84
- selector: 'app-entity-permissions',
85
- template: `
86
- <mj-entity-permissions-grid
87
- [EntityName]="'Customers'"
88
- [Mode]="'Entity'"
89
- (PermissionChanged)="onPermissionChanged($event)">
90
- </mj-entity-permissions-grid>
91
- `
92
- })
93
- export class EntityPermissionsComponent {
94
- onPermissionChanged(event: EntityPermissionChangedEvent) {
95
- console.log(`Permission ${event.PermissionTypeChanged} changed to ${event.Value} for role ${event.RoleID}`);
96
-
97
- // Optionally cancel the change based on business logic
98
- if (this.shouldPreventChange(event)) {
99
- event.Cancel = true;
100
- }
101
- }
102
-
103
- private shouldPreventChange(event: EntityPermissionChangedEvent): boolean {
104
- // Implement your business logic here
105
- return false;
106
- }
107
- }
60
+ ```html
61
+ <mj-entity-permissions-grid
62
+ [Mode]="'Role'"
63
+ [RoleName]="'Admin'"
64
+ (PermissionChanged)="onPermissionChanged($event)">
65
+ </mj-entity-permissions-grid>
108
66
  ```
109
67
 
110
- #### Entity Permissions Grid - Role Mode
111
-
112
- Display and manage permissions for a specific role across all entities:
68
+ ### Combined Selector + Grid
113
69
 
114
- ```typescript
115
- @Component({
116
- selector: 'app-role-permissions',
117
- template: `
118
- <mj-entity-permissions-grid
119
- [RoleName]="selectedRole"
120
- [Mode]="'Role'"
121
- (PermissionChanged)="onPermissionChanged($event)">
122
- </mj-entity-permissions-grid>
123
- `
124
- })
125
- export class RolePermissionsComponent {
126
- selectedRole = 'Administrator';
127
-
128
- onPermissionChanged(event: EntityPermissionChangedEvent) {
129
- // Handle permission changes for the role
130
- console.log(`Entity ${event.EntityName}: ${event.PermissionTypeChanged} = ${event.Value}`);
131
- }
132
- }
70
+ ```html
71
+ <mj-entity-selector-with-grid
72
+ (PermissionChanged)="onPermissionChanged($event)">
73
+ </mj-entity-selector-with-grid>
133
74
  ```
134
75
 
135
- #### Entity Selector with Permissions Grid
136
-
137
- Provide a dropdown to select entities dynamically:
138
-
139
- ```typescript
140
- @Component({
141
- selector: 'app-dynamic-permissions',
142
- template: `
143
- <div class="permissions-container">
144
- <h2>Entity Permissions Manager</h2>
145
- <mj-entity-permissions-selector-with-grid
146
- [EntityName]="initialEntity"
147
- (PermissionChanged)="handlePermissionChange($event)">
148
- </mj-entity-permissions-selector-with-grid>
149
- </div>
150
- `
151
- })
152
- export class DynamicPermissionsComponent {
153
- initialEntity = 'Customers'; // Optional initial selection
154
-
155
- handlePermissionChange(event: EntityPermissionChangedEvent) {
156
- // Process permission changes
157
- this.logPermissionChange(event);
158
- }
159
-
160
- private logPermissionChange(event: EntityPermissionChangedEvent) {
161
- console.log('Permission change:', {
162
- entity: event.EntityName,
163
- role: event.RoleID,
164
- type: event.PermissionTypeChanged,
165
- value: event.Value
166
- });
167
- }
168
- }
169
- ```
170
-
171
- ### Advanced Usage
172
-
173
- #### Programmatic Grid Control
174
-
175
- Access the grid component directly to perform operations:
176
-
177
- ```typescript
178
- import { ViewChild } from '@angular/core';
179
- import { EntityPermissionsGridComponent } from '@memberjunction/ng-entity-permissions';
180
-
181
- @Component({
182
- template: `
183
- <mj-entity-permissions-grid #permGrid
184
- [EntityName]="'Orders'"
185
- [Mode]="'Entity'">
186
- </mj-entity-permissions-grid>
187
-
188
- <button (click)="refreshPermissions()">Refresh</button>
189
- <button (click)="saveAllChanges()">Save All</button>
190
- <button (click)="flipReadPermissions()">Toggle All Read</button>
191
- `
192
- })
193
- export class AdvancedPermissionsComponent {
194
- @ViewChild('permGrid') permissionsGrid!: EntityPermissionsGridComponent;
195
-
196
- async refreshPermissions() {
197
- await this.permissionsGrid.Refresh();
198
- }
199
-
200
- async saveAllChanges() {
201
- if (this.permissionsGrid.NumDirtyPermissions > 0) {
202
- await this.permissionsGrid.savePermissions();
203
- console.log('All permissions saved successfully');
204
- }
205
- }
206
-
207
- flipReadPermissions() {
208
- this.permissionsGrid.flipAllPermissions('Read');
209
- }
210
- }
211
- ```
212
-
213
- #### Handling Transactions
214
-
215
- The component automatically handles batch updates using MemberJunction's transaction system:
216
-
217
- ```typescript
218
- // The grid component internally uses transaction groups for atomic updates
219
- // When savePermissions() is called, all dirty permissions are saved in a single transaction
220
- // This ensures data consistency and allows for rollback if any permission update fails
221
- ```
222
-
223
- ## API Reference
224
-
225
- ### EntityPermissionsGridComponent
226
-
227
- **Selector:** `mj-entity-permissions-grid`
228
-
229
- #### Properties
230
-
231
- ##### Inputs
232
-
233
- | Name | Type | Default | Description |
234
- |------|------|---------|-------------|
235
- | `Mode` | `'Entity' \| 'Role'` | `'Entity'` | Determines whether to display permissions for a specific entity across all roles or for a specific role across all entities |
236
- | `EntityName` | `string` | `undefined` | Required when Mode is 'Entity'. The name of the entity to show permissions for |
237
- | `RoleName` | `string` | `undefined` | Required when Mode is 'Role'. The name of the role to show permissions for |
238
- | `BottomMargin` | `number` | `0` | Bottom margin in pixels to apply to the grid container |
239
-
240
- ##### Outputs
241
-
242
- | Name | Type | Description |
243
- |------|------|-------------|
244
- | `PermissionChanged` | `EventEmitter<EntityPermissionChangedEvent>` | Emitted whenever a permission checkbox is toggled. The event can be cancelled by setting `event.Cancel = true` |
245
-
246
- ##### Public Properties
247
-
248
- | Name | Type | Description |
249
- |------|------|-------------|
250
- | `permissions` | `EntityPermissionEntity[]` | Array of permission records currently displayed in the grid |
251
- | `gridHeight` | `number` | Height of the grid in pixels (default: 750) |
252
- | `isLoading` | `boolean` | Loading state indicator |
253
- | `NumDirtyPermissions` | `number` | Count of permissions that have been modified but not saved |
254
-
255
- #### Methods
256
-
257
- | Method | Parameters | Return Type | Description |
258
- |--------|------------|-------------|-------------|
259
- | `Refresh()` | None | `Promise<void>` | Reloads all permissions data from the database. Automatically creates missing permission records for display |
260
- | `savePermissions()` | None | `Promise<void>` | Saves all modified permissions in a single transaction. Only saves permissions that are truly dirty |
261
- | `cancelEdit()` | None | `Promise<void>` | Reverts all unsaved changes to their original values |
262
- | `flipAllPermissions(type)` | `type: 'Read' \| 'Create' \| 'Update' \| 'Delete'` | `void` | Intelligently toggles all permissions of the specified type. If majority are ON, turns all OFF; otherwise turns all ON |
263
- | `flipRow(permission)` | `permission: EntityPermissionEntity` | `void` | Toggles all permissions in a row. If 2+ are ON, turns all OFF; otherwise turns all ON |
264
- | `revertRow(event, permission)` | `event: MouseEvent, permission: EntityPermissionEntity` | `void` | Reverts a single row to its original state |
265
-
266
- ### EntityPermissionsSelectorWithGridComponent
267
-
268
- **Selector:** `mj-entity-permissions-selector-with-grid`
269
-
270
- #### Properties
271
-
272
- ##### Inputs
273
-
274
- | Name | Type | Default | Description |
275
- |------|------|---------|-------------|
276
- | `EntityName` | `string` | `undefined` | Optional. Name of the initially selected entity |
277
- | `BottomMargin` | `number` | `0` | Bottom margin in pixels to apply to the component |
278
- | `CurrentEntity` | `EntityInfo \| undefined` | `undefined` | The currently selected entity object. Can be used for two-way binding |
279
-
280
- ##### Outputs
281
-
282
- | Name | Type | Description |
283
- |------|------|-------------|
284
- | `PermissionChanged` | `EventEmitter<EntityPermissionChangedEvent>` | Bubbles up permission change events from the embedded grid |
285
-
286
- ##### Public Properties
287
-
288
- | Name | Type | Description |
289
- |------|------|-------------|
290
- | `entityList` | `EntityInfo[]` | Alphabetically sorted list of all entities available for selection |
291
-
292
- ### EntityPermissionChangedEvent Interface
76
+ ### Permission Change Event
293
77
 
294
78
  ```typescript
295
- export type EntityPermissionChangedEvent = {
296
- EntityName: string; // Name of the entity whose permission changed
297
- RoleID: string; // ID of the role whose permission changed
298
- PermissionTypeChanged: 'Read' | 'Create' | 'Update' | 'Delete'; // The specific permission type that was modified
299
- Value: boolean; // The new value of the permission (true = granted, false = revoked)
300
- Cancel: boolean; // Set to true in event handler to cancel the change
79
+ onPermissionChanged(event: EntityPermissionChangedEvent) {
80
+ // event.EntityName, event.RoleID
81
+ // event.PermissionTypeChanged: 'Read' | 'Create' | 'Update' | 'Delete'
82
+ // event.Value: boolean
83
+ // Set event.Cancel = true to prevent the change
301
84
  }
302
85
  ```
303
86
 
304
- ### Internal Behavior Notes
305
-
306
- 1. **Automatic Record Creation**: When loading permissions, the component automatically creates unsaved EntityPermission records for any missing role/entity combinations. These appear in the grid but are only saved if the user enables at least one permission.
307
-
308
- 2. **Dirty State Logic**: A permission is considered "really dirty" if:
309
- - It's an existing saved record that has been modified, OR
310
- - It's a new record with at least one permission enabled
311
-
312
- 3. **Transaction Support**: All save operations use MemberJunction's TransactionGroup to ensure atomic updates across multiple permission records.
313
-
314
- 4. **Event Handling**: The grid uses a sophisticated event system where clicking on checkboxes or table cells properly toggles permissions while preventing event bubbling conflicts.
315
-
316
- ## Styling
317
-
318
- The components use standard HTML tables with custom CSS classes that can be overridden:
319
-
320
- ### CSS Classes
321
-
322
- | Class | Applied To | Description |
323
- |-------|------------|-------------|
324
- | `.grid` | `<table>` | Main permissions grid table |
325
- | `.permission-left-col` | First `<td>` in each row | Left column containing entity/role names and revert icon |
326
- | `.dirty-row` | `<tr>` | Applied to rows that have unsaved changes |
327
- | `.entity-selector` | `<kendo-dropdownlist>` | Styles the entity selection dropdown |
328
- | `.inner-grid` | `<mj-entity-permissions-grid>` | Applied to the grid within the selector component |
329
- | `.fa-arrow-rotate-left` | `<span>` icon | Revert icon shown in dirty rows |
330
-
331
- ### Customization Example
332
-
333
- ```css
334
- /* Custom styling for your application */
335
- ::ng-deep {
336
- .mj-entity-permissions-grid {
337
- .grid {
338
- border: 1px solid #ddd;
339
- width: 100%;
340
- }
341
-
342
- .dirty-row {
343
- background-color: #fff3cd;
344
-
345
- .permission-left-col {
346
- font-weight: bold;
347
-
348
- .fa-arrow-rotate-left {
349
- color: #856404;
350
- cursor: pointer;
351
- margin-left: 10px;
352
-
353
- &:hover {
354
- color: #533f03;
355
- }
356
- }
357
- }
358
- }
359
-
360
- th {
361
- cursor: pointer;
362
- user-select: none;
363
-
364
- &:hover {
365
- background-color: #f0f0f0;
366
- }
367
- }
368
- }
369
- }
370
- ```
371
-
372
- ## Integration with MemberJunction
373
-
374
- This package integrates seamlessly with the MemberJunction ecosystem:
87
+ ## Exported API
375
88
 
376
- ### Entity Permission System
377
- - Works with the `EntityPermissionEntity` from `@memberjunction/core-entities`
378
- - Respects the MemberJunction metadata system for entities and roles
379
- - Uses MemberJunction's transaction system for atomic updates
89
+ | Export | Type | Description |
90
+ |---|---|---|
91
+ | `EntityPermissionsGridComponent` | Component | CRUD permissions grid |
92
+ | `EntitySelectorWithGridComponent` | Component | Entity dropdown + permissions grid |
93
+ | `EntityPermissionChangedEvent` | Type | Permission change event shape |
94
+ | `EntityPermissionsModule` | NgModule | Module declaration |
380
95
 
381
- ### Metadata Integration
382
- - Automatically discovers all entities and roles from the Metadata provider
383
- - Validates entity and role names against the metadata
384
- - Creates properly typed entity objects using the metadata system
385
-
386
- ### RunView Integration
387
- - Uses `RunView` for efficient data loading with proper filtering
388
- - Leverages `ResultType: 'entity_object'` for automatic entity instantiation
389
- - Implements proper ordering for consistent display
390
-
391
- ## Build and Development
392
-
393
- This package uses the Angular compiler (`ngc`) for building:
96
+ ## Build
394
97
 
395
98
  ```bash
396
- # Build the package
397
- npm run build
398
-
399
- # The built files will be in the dist/ directory
400
- ```
401
-
402
- ### TypeScript Configuration
403
- - Targets ES2015 with ES2020 modules
404
- - Generates declaration files with source maps
405
- - Uses strict mode for type safety
406
- - Configured for Angular library compilation with strict templates
407
-
408
- ## Performance Considerations
409
-
410
- 1. **Efficient Loading**: The component loads all permissions in a single query and creates missing records in memory
411
- 2. **Batch Updates**: Uses transaction groups to minimize database round trips
412
- 3. **Smart Dirty Checking**: Only saves records that have actual changes
413
- 4. **Lazy Initialization**: Permission records for missing combinations are created on-demand
414
-
415
- ## Common Use Cases
416
-
417
- ### Admin Dashboard
418
- ```typescript
419
- // Create a comprehensive admin interface for managing all permissions
420
- @Component({
421
- template: `
422
- <mat-tab-group>
423
- <mat-tab label="By Entity">
424
- <mj-entity-permissions-selector-with-grid></mj-entity-permissions-selector-with-grid>
425
- </mat-tab>
426
- <mat-tab label="By Role">
427
- <select [(ngModel)]="selectedRole">
428
- <option *ngFor="let role of roles" [value]="role.Name">{{role.Name}}</option>
429
- </select>
430
- <mj-entity-permissions-grid
431
- *ngIf="selectedRole"
432
- [Mode]="'Role'"
433
- [RoleName]="selectedRole">
434
- </mj-entity-permissions-grid>
435
- </mat-tab>
436
- </mat-tab-group>
437
- `
438
- })
439
- export class PermissionsDashboardComponent {
440
- // Implementation
441
- }
442
- ```
443
-
444
- ### Role Creation Wizard
445
- ```typescript
446
- // Include permissions step in role creation
447
- @Component({
448
- template: `
449
- <div *ngIf="currentStep === 'permissions'">
450
- <h3>Configure Permissions for {{newRole.Name}}</h3>
451
- <mj-entity-permissions-grid
452
- [Mode]="'Role'"
453
- [RoleName]="newRole.Name"
454
- (PermissionChanged)="trackPermissionChanges($event)">
455
- </mj-entity-permissions-grid>
456
- </div>
457
- `
458
- })
459
- export class RoleWizardComponent {
460
- // Track changes and save with role
461
- }
99
+ cd packages/Angular/Explorer/entity-permissions && npm run build
462
100
  ```
463
101
 
464
- ## Troubleshooting
465
-
466
- ### Common Issues
467
-
468
- 1. **"EntityName is required when Mode is 'Entity'"**
469
- - Ensure you provide the EntityName input when using Entity mode
470
- - Check that the entity name matches exactly (case-sensitive)
471
-
472
- 2. **"Entity not found: [EntityName]"**
473
- - Verify the entity exists in MemberJunction metadata
474
- - Ensure metadata is properly loaded before component initialization
475
-
476
- 3. **Permissions not saving**
477
- - Check browser console for transaction errors
478
- - Verify user has permission to modify EntityPermission records
479
- - Ensure database constraints are not violated
480
-
481
- 4. **Grid not displaying**
482
- - Confirm all Kendo UI modules are properly imported
483
- - Check that EntityPermissionsModule is imported in your module
484
-
485
102
  ## License
486
103
 
487
- ISC License - see the root MemberJunction repository for details.
104
+ ISC
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-entity-permissions",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "MemberJunction: Angular components for displaying/editing permissions for an entity, and related components.",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -25,12 +25,12 @@
25
25
  "@angular/router": "21.1.3"
26
26
  },
27
27
  "dependencies": {
28
- "@memberjunction/core": "4.0.0",
29
- "@memberjunction/core-entities": "4.0.0",
30
- "@memberjunction/global": "4.0.0",
31
- "@memberjunction/ng-container-directives": "4.0.0",
32
- "@memberjunction/ng-shared": "4.0.0",
33
- "@memberjunction/ng-shared-generic": "4.0.0",
28
+ "@memberjunction/core": "4.1.0",
29
+ "@memberjunction/core-entities": "4.1.0",
30
+ "@memberjunction/global": "4.1.0",
31
+ "@memberjunction/ng-container-directives": "4.1.0",
32
+ "@memberjunction/ng-shared": "4.1.0",
33
+ "@memberjunction/ng-shared-generic": "4.1.0",
34
34
  "@progress/kendo-angular-buttons": "22.0.1",
35
35
  "@progress/kendo-angular-dialog": "22.0.1",
36
36
  "@progress/kendo-angular-dropdowns": "22.0.1",