@memberjunction/ng-explorer-settings 6.1.4 → 6.2.0-edge.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.
- package/dist/lib/account-info/account-info.component.d.ts +1 -1
- package/dist/lib/account-info/account-info.component.js +2 -2
- package/dist/lib/account-info/account-info.component.js.map +1 -1
- package/dist/lib/application-management/application-dialog/application-dialog.component.d.ts +93 -10
- package/dist/lib/application-management/application-dialog/application-dialog.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-dialog/application-dialog.component.js +232 -80
- package/dist/lib/application-management/application-dialog/application-dialog.component.js.map +1 -1
- package/dist/lib/application-management/application-management.component.d.ts +94 -11
- package/dist/lib/application-management/application-management.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-management.component.js +231 -67
- package/dist/lib/application-management/application-management.component.js.map +1 -1
- package/dist/lib/application-settings/application-settings.component.d.ts +4 -4
- package/dist/lib/application-settings/application-settings.component.js +10 -10
- package/dist/lib/application-settings/application-settings.component.js.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts +88 -10
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.js +219 -63
- package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -1
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.d.ts +44 -6
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js +117 -43
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js.map +1 -1
- package/dist/lib/notification-preferences/notification-preferences.component.d.ts +25 -3
- package/dist/lib/notification-preferences/notification-preferences.component.d.ts.map +1 -1
- package/dist/lib/notification-preferences/notification-preferences.component.js +62 -18
- package/dist/lib/notification-preferences/notification-preferences.component.js.map +1 -1
- package/dist/lib/role-management/role-dialog/role-dialog.component.d.ts +32 -4
- package/dist/lib/role-management/role-dialog/role-dialog.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-dialog/role-dialog.component.js +84 -30
- package/dist/lib/role-management/role-dialog/role-dialog.component.js.map +1 -1
- package/dist/lib/role-management/role-management-agent-context.d.ts +12 -4
- package/dist/lib/role-management/role-management-agent-context.d.ts.map +1 -1
- package/dist/lib/role-management/role-management-agent-context.js +26 -10
- package/dist/lib/role-management/role-management-agent-context.js.map +1 -1
- package/dist/lib/role-management/role-management.component.d.ts +95 -11
- package/dist/lib/role-management/role-management.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-management.component.js +259 -91
- package/dist/lib/role-management/role-management.component.js.map +1 -1
- package/dist/lib/settings/settings.component.d.ts +87 -11
- package/dist/lib/settings/settings.component.d.ts.map +1 -1
- package/dist/lib/settings/settings.component.js +211 -65
- package/dist/lib/settings/settings.component.js.map +1 -1
- package/dist/lib/shared/settings-card.component.d.ts +24 -4
- package/dist/lib/shared/settings-card.component.d.ts.map +1 -1
- package/dist/lib/shared/settings-card.component.js +46 -6
- package/dist/lib/shared/settings-card.component.js.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.d.ts +172 -18
- package/dist/lib/sql-logging/sql-logging.component.d.ts.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.js +320 -108
- package/dist/lib/sql-logging/sql-logging.component.js.map +1 -1
- package/dist/lib/user-app-config/user-app-config-content.component.js +3 -3
- package/dist/lib/user-app-config/user-app-config-content.component.js.map +1 -1
- package/dist/lib/user-app-config/user-app-config-reorder.d.ts +4 -0
- package/dist/lib/user-app-config/user-app-config-reorder.d.ts.map +1 -1
- package/dist/lib/user-app-config/user-app-config-reorder.js +11 -3
- package/dist/lib/user-app-config/user-app-config-reorder.js.map +1 -1
- package/dist/lib/user-management/transaction-group-refusals.d.ts +45 -0
- package/dist/lib/user-management/transaction-group-refusals.d.ts.map +1 -0
- package/dist/lib/user-management/transaction-group-refusals.js +49 -0
- package/dist/lib/user-management/transaction-group-refusals.js.map +1 -0
- package/dist/lib/user-management/user-dialog/user-dialog.component.d.ts +44 -6
- package/dist/lib/user-management/user-dialog/user-dialog.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-dialog/user-dialog.component.js +134 -45
- package/dist/lib/user-management/user-dialog/user-dialog.component.js.map +1 -1
- package/dist/lib/user-management/user-management-agent-context.d.ts +10 -0
- package/dist/lib/user-management/user-management-agent-context.d.ts.map +1 -1
- package/dist/lib/user-management/user-management-agent-context.js +26 -6
- package/dist/lib/user-management/user-management-agent-context.js.map +1 -1
- package/dist/lib/user-management/user-management.component.d.ts +174 -16
- package/dist/lib/user-management/user-management.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-management.component.js +501 -177
- package/dist/lib/user-management/user-management.component.js.map +1 -1
- package/dist/lib/user-profile-settings/user-profile-settings.component.d.ts +97 -15
- package/dist/lib/user-profile-settings/user-profile-settings.component.d.ts.map +1 -1
- package/dist/lib/user-profile-settings/user-profile-settings.component.js +280 -116
- package/dist/lib/user-profile-settings/user-profile-settings.component.js.map +1 -1
- package/package.json +20 -20
|
@@ -3,6 +3,7 @@ import { FormBuilder, Validators } from '@angular/forms';
|
|
|
3
3
|
import { RunView } from '@memberjunction/core';
|
|
4
4
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
5
5
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
6
|
+
import { ServerRefusalReasons } from '../transaction-group-refusals';
|
|
6
7
|
import * as i0 from "@angular/core";
|
|
7
8
|
import * as i1 from "@angular/forms";
|
|
8
9
|
import * as i2 from "@memberjunction/ng-ui-components";
|
|
@@ -235,21 +236,69 @@ function UserDialogComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
|
235
236
|
i0.ɵɵconditional(ctx_r1.isLoading ? 87 : 88);
|
|
236
237
|
} }
|
|
237
238
|
export class UserDialogComponent extends BaseAngularComponent {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
Data = null;
|
|
240
|
+
/** @deprecated Use {@link Data}. */
|
|
241
|
+
set data(value) {
|
|
242
|
+
this.Data = value;
|
|
243
|
+
}
|
|
244
|
+
/** @deprecated Use {@link Data}. */
|
|
245
|
+
get data() {
|
|
246
|
+
return this.Data;
|
|
247
|
+
}
|
|
248
|
+
Visible = false;
|
|
249
|
+
/** @deprecated Use {@link Visible}. */
|
|
250
|
+
set visible(value) {
|
|
251
|
+
this.Visible = value;
|
|
252
|
+
}
|
|
253
|
+
/** @deprecated Use {@link Visible}. */
|
|
254
|
+
get visible() {
|
|
255
|
+
return this.Visible;
|
|
256
|
+
}
|
|
257
|
+
Result = new EventEmitter();
|
|
258
|
+
/**
|
|
259
|
+
* @deprecated Use {@link Result}.
|
|
260
|
+
*
|
|
261
|
+
* The same emitter under the old binding name, so a template still binding
|
|
262
|
+
* (result) keeps working. Must stay AFTER Result: class fields
|
|
263
|
+
* initialise in order, and the other way round this captures undefined.
|
|
264
|
+
*/
|
|
265
|
+
result = this.Result;
|
|
241
266
|
fb = inject(FormBuilder);
|
|
242
267
|
cdr = inject(ChangeDetectorRef);
|
|
243
268
|
ngZone = inject(NgZone);
|
|
244
269
|
get metadata() { return this.ProviderToUse; }
|
|
245
|
-
|
|
270
|
+
UserForm;
|
|
271
|
+
/** @deprecated Use {@link UserForm}. */
|
|
272
|
+
get userForm() {
|
|
273
|
+
return this.UserForm;
|
|
274
|
+
}
|
|
275
|
+
/** @deprecated Use {@link UserForm}. */
|
|
276
|
+
set userForm(value) {
|
|
277
|
+
this.UserForm = value;
|
|
278
|
+
}
|
|
246
279
|
isLoading = false;
|
|
247
280
|
error = null;
|
|
248
|
-
|
|
249
|
-
|
|
281
|
+
SelectedRoleIds = new Set();
|
|
282
|
+
/** @deprecated Use {@link SelectedRoleIds}. */
|
|
283
|
+
get selectedRoleIds() {
|
|
284
|
+
return this.SelectedRoleIds;
|
|
285
|
+
}
|
|
286
|
+
/** @deprecated Use {@link SelectedRoleIds}. */
|
|
287
|
+
set selectedRoleIds(value) {
|
|
288
|
+
this.SelectedRoleIds = value;
|
|
289
|
+
}
|
|
290
|
+
ExistingUserRoles = [];
|
|
291
|
+
/** @deprecated Use {@link ExistingUserRoles}. */
|
|
292
|
+
get existingUserRoles() {
|
|
293
|
+
return this.ExistingUserRoles;
|
|
294
|
+
}
|
|
295
|
+
/** @deprecated Use {@link ExistingUserRoles}. */
|
|
296
|
+
set existingUserRoles(value) {
|
|
297
|
+
this.ExistingUserRoles = value;
|
|
298
|
+
}
|
|
250
299
|
constructor() {
|
|
251
300
|
super();
|
|
252
|
-
this.
|
|
301
|
+
this.UserForm = this.fb.group({
|
|
253
302
|
name: ['', [Validators.required, Validators.email]],
|
|
254
303
|
firstName: [''],
|
|
255
304
|
lastName: [''],
|
|
@@ -265,9 +314,9 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
265
314
|
ngOnChanges(changes) {
|
|
266
315
|
// Always clear state when data changes to prevent persistence bugs
|
|
267
316
|
if (changes['data']) {
|
|
268
|
-
this.
|
|
269
|
-
this.
|
|
270
|
-
if (this.
|
|
317
|
+
this.SelectedRoleIds.clear();
|
|
318
|
+
this.ExistingUserRoles = [];
|
|
319
|
+
if (this.Data?.user && this.IsEditMode) {
|
|
271
320
|
this.loadUserData();
|
|
272
321
|
}
|
|
273
322
|
else {
|
|
@@ -275,7 +324,7 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
275
324
|
}
|
|
276
325
|
}
|
|
277
326
|
// Reset form when dialog becomes visible and not in edit mode
|
|
278
|
-
if (changes['visible'] && this.
|
|
327
|
+
if (changes['visible'] && this.Visible && !this.IsEditMode) {
|
|
279
328
|
this.resetForm();
|
|
280
329
|
}
|
|
281
330
|
}
|
|
@@ -283,7 +332,7 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
283
332
|
// Cleanup if needed
|
|
284
333
|
}
|
|
285
334
|
resetForm() {
|
|
286
|
-
this.
|
|
335
|
+
this.UserForm.reset({
|
|
287
336
|
name: '',
|
|
288
337
|
firstName: '',
|
|
289
338
|
lastName: '',
|
|
@@ -292,25 +341,33 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
292
341
|
type: 'User',
|
|
293
342
|
isActive: true
|
|
294
343
|
});
|
|
295
|
-
this.
|
|
344
|
+
this.SelectedRoleIds.clear();
|
|
296
345
|
this.error = null;
|
|
297
346
|
}
|
|
298
347
|
onEscapeKey(event) {
|
|
299
|
-
if (this.
|
|
348
|
+
if (this.Visible) {
|
|
300
349
|
this.onCancel();
|
|
301
350
|
}
|
|
302
351
|
}
|
|
352
|
+
get WindowTitle() {
|
|
353
|
+
return this.IsEditMode ? 'Edit User' : 'Create New User';
|
|
354
|
+
}
|
|
355
|
+
/** @deprecated Use {@link WindowTitle}. */
|
|
303
356
|
get windowTitle() {
|
|
304
|
-
return this.
|
|
357
|
+
return this.WindowTitle;
|
|
358
|
+
}
|
|
359
|
+
get IsEditMode() {
|
|
360
|
+
return this.Data?.mode === 'edit';
|
|
305
361
|
}
|
|
362
|
+
/** @deprecated Use {@link IsEditMode}. */
|
|
306
363
|
get isEditMode() {
|
|
307
|
-
return this.
|
|
364
|
+
return this.IsEditMode;
|
|
308
365
|
}
|
|
309
366
|
async loadUserData() {
|
|
310
|
-
if (!this.
|
|
367
|
+
if (!this.Data?.user)
|
|
311
368
|
return;
|
|
312
|
-
const user = this.
|
|
313
|
-
this.
|
|
369
|
+
const user = this.Data.user;
|
|
370
|
+
this.UserForm.patchValue({
|
|
314
371
|
name: user.Name,
|
|
315
372
|
firstName: user.FirstName,
|
|
316
373
|
lastName: user.LastName,
|
|
@@ -331,10 +388,10 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
331
388
|
ResultType: 'entity_object'
|
|
332
389
|
});
|
|
333
390
|
if (result.Success && result.Results) {
|
|
334
|
-
this.
|
|
391
|
+
this.ExistingUserRoles = result.Results;
|
|
335
392
|
// Pre-select existing roles
|
|
336
|
-
for (const userRole of this.
|
|
337
|
-
this.
|
|
393
|
+
for (const userRole of this.ExistingUserRoles) {
|
|
394
|
+
this.SelectedRoleIds.add(userRole.RoleID);
|
|
338
395
|
}
|
|
339
396
|
}
|
|
340
397
|
}
|
|
@@ -342,36 +399,44 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
342
399
|
console.warn('Failed to load existing user roles:', error);
|
|
343
400
|
}
|
|
344
401
|
}
|
|
345
|
-
|
|
402
|
+
OnRoleToggle(roleId, event) {
|
|
346
403
|
event.stopPropagation();
|
|
347
404
|
const checkbox = event.target;
|
|
348
405
|
if (checkbox.checked) {
|
|
349
|
-
this.
|
|
406
|
+
this.SelectedRoleIds.add(roleId);
|
|
350
407
|
}
|
|
351
408
|
else {
|
|
352
|
-
this.
|
|
409
|
+
this.SelectedRoleIds.delete(roleId);
|
|
353
410
|
}
|
|
354
411
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
412
|
+
/** @deprecated Use {@link OnRoleToggle}. */
|
|
413
|
+
onRoleToggle(roleId, event) {
|
|
414
|
+
return this.OnRoleToggle(roleId, event);
|
|
415
|
+
}
|
|
416
|
+
ToggleRole(roleId) {
|
|
417
|
+
if (this.SelectedRoleIds.has(roleId)) {
|
|
418
|
+
this.SelectedRoleIds.delete(roleId);
|
|
358
419
|
}
|
|
359
420
|
else {
|
|
360
|
-
this.
|
|
421
|
+
this.SelectedRoleIds.add(roleId);
|
|
361
422
|
}
|
|
362
423
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
424
|
+
/** @deprecated Use {@link ToggleRole}. */
|
|
425
|
+
toggleRole(roleId) {
|
|
426
|
+
return this.ToggleRole(roleId);
|
|
427
|
+
}
|
|
428
|
+
async OnSubmit() {
|
|
429
|
+
if (this.UserForm.invalid) {
|
|
430
|
+
this.markFormGroupTouched(this.UserForm);
|
|
366
431
|
return;
|
|
367
432
|
}
|
|
368
433
|
this.isLoading = true;
|
|
369
434
|
this.error = null;
|
|
370
435
|
try {
|
|
371
436
|
let user;
|
|
372
|
-
if (this.
|
|
437
|
+
if (this.IsEditMode && this.Data?.user) {
|
|
373
438
|
// Edit existing user
|
|
374
|
-
user = this.
|
|
439
|
+
user = this.Data.user;
|
|
375
440
|
}
|
|
376
441
|
else {
|
|
377
442
|
// Create new user
|
|
@@ -379,7 +444,7 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
379
444
|
user.NewRecord();
|
|
380
445
|
}
|
|
381
446
|
// Update user properties
|
|
382
|
-
const formValue = this.
|
|
447
|
+
const formValue = this.UserForm.value;
|
|
383
448
|
user.Name = formValue.name;
|
|
384
449
|
user.FirstName = formValue.firstName;
|
|
385
450
|
user.LastName = formValue.lastName;
|
|
@@ -394,7 +459,7 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
394
459
|
}
|
|
395
460
|
// Handle role assignments
|
|
396
461
|
await this.updateUserRoles(user.ID);
|
|
397
|
-
this.
|
|
462
|
+
this.Result.emit({ action: 'save', user });
|
|
398
463
|
}
|
|
399
464
|
catch (error) {
|
|
400
465
|
console.error('Error saving user:', error);
|
|
@@ -410,13 +475,17 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
410
475
|
});
|
|
411
476
|
}
|
|
412
477
|
}
|
|
478
|
+
/** @deprecated Use {@link OnSubmit}. */
|
|
479
|
+
async onSubmit() {
|
|
480
|
+
return this.OnSubmit();
|
|
481
|
+
}
|
|
413
482
|
async updateUserRoles(userId) {
|
|
414
483
|
try {
|
|
415
484
|
// Get current role IDs from existing UserRole entities
|
|
416
|
-
const existingRoleIds = new Set(this.
|
|
485
|
+
const existingRoleIds = new Set(this.ExistingUserRoles.map(ur => ur.RoleID));
|
|
417
486
|
// Determine roles to add and remove
|
|
418
|
-
const rolesToAdd = Array.from(this.
|
|
419
|
-
const rolesToRemove = this.
|
|
487
|
+
const rolesToAdd = Array.from(this.SelectedRoleIds).filter(roleId => !existingRoleIds.has(roleId));
|
|
488
|
+
const rolesToRemove = this.ExistingUserRoles.filter(userRole => !this.SelectedRoleIds.has(userRole.RoleID));
|
|
420
489
|
if (rolesToAdd.length === 0 && rolesToRemove.length === 0) {
|
|
421
490
|
return;
|
|
422
491
|
}
|
|
@@ -429,11 +498,17 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
429
498
|
// same reason as `UserManagementComponent.executeBulkRoleAssign`, where the transaction-group
|
|
430
499
|
// semantics — and why the server-side #4282 guard is NOT what this catches — are set out in full.
|
|
431
500
|
const refusals = [];
|
|
501
|
+
// Enrolled rows are kept so the SERVER's reason can be read back off them below; without
|
|
502
|
+
// that, the reason #4309 puts on LatestResult has nobody left to read it.
|
|
503
|
+
const enrolled = [];
|
|
432
504
|
for (const userRole of rolesToRemove) {
|
|
433
505
|
userRole.TransactionGroup = tg;
|
|
434
506
|
if (!await userRole.Delete()) {
|
|
435
507
|
refusals.push(`Remove ${this.describeRole(userRole.RoleID)}: ${userRole.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
436
508
|
}
|
|
509
|
+
else {
|
|
510
|
+
enrolled.push({ Label: `Remove ${this.describeRole(userRole.RoleID)}`, Entity: userRole });
|
|
511
|
+
}
|
|
437
512
|
}
|
|
438
513
|
for (const roleId of rolesToAdd) {
|
|
439
514
|
const userRole = await this.metadata.GetEntityObject('MJ: User Roles');
|
|
@@ -444,6 +519,9 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
444
519
|
if (!await userRole.Save()) {
|
|
445
520
|
refusals.push(`Add ${this.describeRole(roleId)}: ${userRole.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
446
521
|
}
|
|
522
|
+
else {
|
|
523
|
+
enrolled.push({ Label: `Add ${this.describeRole(roleId)}`, Entity: userRole });
|
|
524
|
+
}
|
|
447
525
|
}
|
|
448
526
|
if (refusals.length > 0) {
|
|
449
527
|
// Nothing was written: the refused rows never enrolled, and the rest are still only queued
|
|
@@ -451,7 +529,12 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
451
529
|
throw new Error(`Failed to update user roles — no role changes were made.\n${refusals.join('\n')}`);
|
|
452
530
|
}
|
|
453
531
|
if (!await tg.Submit()) {
|
|
454
|
-
|
|
532
|
+
// Every row enrolled, so this is a SERVER-side refusal (or a rollback). Since #4309 the
|
|
533
|
+
// server says which row and why, and that reason is now on each entity's LatestResult.
|
|
534
|
+
const reasons = ServerRefusalReasons(enrolled);
|
|
535
|
+
throw new Error(reasons.length > 0
|
|
536
|
+
? `Failed to update user roles — all changes have been rolled back.\n${reasons.join('\n')}`
|
|
537
|
+
: 'Failed to update user roles — all changes have been rolled back');
|
|
455
538
|
}
|
|
456
539
|
}
|
|
457
540
|
catch (error) {
|
|
@@ -461,10 +544,10 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
461
544
|
}
|
|
462
545
|
/** Names a role for a refusal message; falls back to the ID when the catalog has no match. */
|
|
463
546
|
describeRole(roleId) {
|
|
464
|
-
return this.
|
|
547
|
+
return this.Data?.availableRoles.find(r => UUIDsEqual(r.ID, roleId))?.Name ?? roleId;
|
|
465
548
|
}
|
|
466
549
|
onCancel() {
|
|
467
|
-
this.
|
|
550
|
+
this.Result.emit({ action: 'cancel' });
|
|
468
551
|
}
|
|
469
552
|
markFormGroupTouched(formGroup) {
|
|
470
553
|
Object.keys(formGroup.controls).forEach(key => {
|
|
@@ -475,7 +558,7 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
475
558
|
static ɵfac = function UserDialogComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || UserDialogComponent)(); };
|
|
476
559
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UserDialogComponent, selectors: [["mj-user-dialog"]], hostBindings: function UserDialogComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
477
560
|
i0.ɵɵlistener("keydown.escape", function UserDialogComponent_keydown_escape_HostBindingHandler($event) { return ctx.onEscapeKey($event); }, i0.ɵɵresolveDocument);
|
|
478
|
-
} }, inputs: { data: "data", visible: "visible" }, outputs: { result: "result" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 1, vars: 1, consts: [[1, "modal-backdrop"], [1, "modal-backdrop", 3, "click"], [1, "modal-dialog", 3, "click"], [1, "modal-header"], [1, "dialog-header"], [1, "dialog-title"], [1, "fa-solid", "fa-user"], [1, "dialog-subtitle"], ["type", "button", "aria-label", "Close dialog", 1, "modal-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "modal-body"], [3, "ngSubmit", "formGroup"], [1, "content-section"], [1, "form-section"], [1, "section-header"], [1, "section-title"], [1, "fa-solid", "fa-id-card"], [1, "section-description"], [1, "form-grid"], [1, "form-field"], ["for", "name", 1, "field-label", "required"], ["id", "name", "type", "email", "formControlName", "name", "placeholder", "john@company.com", 1, "field-input"], [1, "field-error"], [1, "form-row"], ["for", "firstName", 1, "field-label"], ["id", "firstName", "type", "text", "formControlName", "firstName", "placeholder", "John", 1, "field-input"], ["for", "lastName", 1, "field-label"], ["id", "lastName", "type", "text", "formControlName", "lastName", "placeholder", "Doe", 1, "field-input"], ["for", "email", 1, "field-label", "required"], ["id", "email", "type", "email", "formControlName", "email", "placeholder", "john@company.com", 1, "field-input"], ["for", "title", 1, "field-label"], ["id", "title", "type", "text", "formControlName", "title", "placeholder", "Software Engineer", 1, "field-input"], [1, "fa-solid", "fa-cog"], ["for", "type", 1, "field-label"], ["id", "type", "formControlName", "type", 1, "field-select"], ["value", "User"], ["value", "Owner"], [1, "checkbox-field"], ["id", "isActive", "type", "checkbox", "formControlName", "isActive", 1, "checkbox-input"], ["for", "isActive", 1, "checkbox-label"], [1, "checkbox-indicator"], [2, "font-size", "12px", "color", "var(--md-on-surface-variant)", "margin-top", "2px"], [1, "fa-solid", "fa-user-tag"], [1, "roles-grid"], [1, "role-card", 3, "selected"], [1, "modal-footer"], ["type", "submit", 1, "btn", "btn-primary", 3, "disabled"], ["type", "button", 1, "btn", "btn-secondary", 3, "click"], ["Variant", "error", 3, "Message"], [1, "fa-solid", "fa-exclamation-circle"], [1, "role-card", 3, "click"], [1, "role-content"], ["type", "checkbox", 1, "checkbox-input", 3, "change", "id", "checked"], [1, "checkbox-label", 3, "for"], [1, "role-info"], [1, "role-name"], [1, "role-description"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-save"]], template: function UserDialogComponent_Template(rf, ctx) { if (rf & 1) {
|
|
561
|
+
} }, inputs: { Data: "Data", data: "data", Visible: "Visible", visible: "visible" }, outputs: { Result: "Result", result: "result" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 1, vars: 1, consts: [[1, "modal-backdrop"], [1, "modal-backdrop", 3, "click"], [1, "modal-dialog", 3, "click"], [1, "modal-header"], [1, "dialog-header"], [1, "dialog-title"], [1, "fa-solid", "fa-user"], [1, "dialog-subtitle"], ["type", "button", "aria-label", "Close dialog", 1, "modal-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "modal-body"], [3, "ngSubmit", "formGroup"], [1, "content-section"], [1, "form-section"], [1, "section-header"], [1, "section-title"], [1, "fa-solid", "fa-id-card"], [1, "section-description"], [1, "form-grid"], [1, "form-field"], ["for", "name", 1, "field-label", "required"], ["id", "name", "type", "email", "formControlName", "name", "placeholder", "john@company.com", 1, "field-input"], [1, "field-error"], [1, "form-row"], ["for", "firstName", 1, "field-label"], ["id", "firstName", "type", "text", "formControlName", "firstName", "placeholder", "John", 1, "field-input"], ["for", "lastName", 1, "field-label"], ["id", "lastName", "type", "text", "formControlName", "lastName", "placeholder", "Doe", 1, "field-input"], ["for", "email", 1, "field-label", "required"], ["id", "email", "type", "email", "formControlName", "email", "placeholder", "john@company.com", 1, "field-input"], ["for", "title", 1, "field-label"], ["id", "title", "type", "text", "formControlName", "title", "placeholder", "Software Engineer", 1, "field-input"], [1, "fa-solid", "fa-cog"], ["for", "type", 1, "field-label"], ["id", "type", "formControlName", "type", 1, "field-select"], ["value", "User"], ["value", "Owner"], [1, "checkbox-field"], ["id", "isActive", "type", "checkbox", "formControlName", "isActive", 1, "checkbox-input"], ["for", "isActive", 1, "checkbox-label"], [1, "checkbox-indicator"], [2, "font-size", "12px", "color", "var(--md-on-surface-variant)", "margin-top", "2px"], [1, "fa-solid", "fa-user-tag"], [1, "roles-grid"], [1, "role-card", 3, "selected"], [1, "modal-footer"], ["type", "submit", 1, "btn", "btn-primary", 3, "disabled"], ["type", "button", 1, "btn", "btn-secondary", 3, "click"], ["Variant", "error", 3, "Message"], [1, "fa-solid", "fa-exclamation-circle"], [1, "role-card", 3, "click"], [1, "role-content"], ["type", "checkbox", 1, "checkbox-input", 3, "change", "id", "checked"], [1, "checkbox-label", 3, "for"], [1, "role-info"], [1, "role-name"], [1, "role-description"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-save"]], template: function UserDialogComponent_Template(rf, ctx) { if (rf & 1) {
|
|
479
562
|
i0.ɵɵconditionalCreate(0, UserDialogComponent_Conditional_0_Template, 92, 12, "div", 0);
|
|
480
563
|
} if (rf & 2) {
|
|
481
564
|
i0.ɵɵconditional(ctx.visible ? 0 : -1);
|
|
@@ -484,15 +567,21 @@ export class UserDialogComponent extends BaseAngularComponent {
|
|
|
484
567
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UserDialogComponent, [{
|
|
485
568
|
type: Component,
|
|
486
569
|
args: [{ standalone: false, selector: 'mj-user-dialog', encapsulation: ViewEncapsulation.None, template: "<!-- MD3 Modal Dialog -->\n@if (visible) {\n <div class=\"modal-backdrop\" (click)=\"onCancel()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header with MJ Blue -->\n <div class=\"modal-header\">\n <div class=\"dialog-header\">\n <h2 class=\"dialog-title\">\n <i class=\"fa-solid fa-user\"></i>\n {{ isEditMode ? 'Edit User' : 'Create New User' }}\n </h2>\n <p class=\"dialog-subtitle\">\n {{ isEditMode ? 'Update user information and role assignments' : 'Add a new user to the system' }}\n </p>\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"onCancel()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Modal Body -->\n <div class=\"modal-body\">\n <form [formGroup]=\"userForm\" (ngSubmit)=\"onSubmit()\">\n @if (error) {\n <div class=\"content-section\">\n <mj-alert Variant=\"error\" [Message]=\"error\" />\n </div>\n }\n <!-- Basic Information Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-id-card\"></i>\n Basic Information\n </h3>\n <p class=\"section-description\">Enter the user's personal and contact details</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"name\">Username/Email</label>\n <input\n id=\"name\"\n type=\"email\"\n class=\"field-input\"\n formControlName=\"name\"\n placeholder=\"john@company.com\"\n [class.error]=\"userForm.get('name')?.invalid && userForm.get('name')?.touched\"\n />\n @if (userForm.get('name')?.invalid && userForm.get('name')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (userForm.get('name')?.errors?.['required']) {\n Username/Email is required\n }\n @if (userForm.get('name')?.errors?.['email']) {\n Please enter a valid email address\n }\n </div>\n }\n </div>\n <div class=\"form-row\">\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"firstName\">First Name</label>\n <input\n id=\"firstName\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"firstName\"\n placeholder=\"John\"\n />\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"lastName\">Last Name</label>\n <input\n id=\"lastName\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"lastName\"\n placeholder=\"Doe\"\n />\n </div>\n </div>\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"email\">Email Address</label>\n <input\n id=\"email\"\n type=\"email\"\n class=\"field-input\"\n formControlName=\"email\"\n placeholder=\"john@company.com\"\n [class.error]=\"userForm.get('email')?.invalid && userForm.get('email')?.touched\"\n />\n @if (userForm.get('email')?.invalid && userForm.get('email')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (userForm.get('email')?.errors?.['required']) {\n Email address is required\n }\n @if (userForm.get('email')?.errors?.['email']) {\n Please enter a valid email address\n }\n </div>\n }\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"title\">Job Title</label>\n <input\n id=\"title\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"title\"\n placeholder=\"Software Engineer\"\n />\n </div>\n </div>\n </div>\n </div>\n <!-- User Settings Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-cog\"></i>\n User Settings\n </h3>\n <p class=\"section-description\">Configure user type and account status</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"type\">User Type</label>\n <select id=\"type\" class=\"field-select\" formControlName=\"type\">\n <option value=\"User\">Standard User</option>\n <option value=\"Owner\">System Owner</option>\n </select>\n </div>\n <div class=\"form-field\">\n <div class=\"checkbox-field\">\n <input\n id=\"isActive\"\n type=\"checkbox\"\n class=\"checkbox-input\"\n formControlName=\"isActive\"\n />\n <label class=\"checkbox-label\" for=\"isActive\">\n <div class=\"checkbox-indicator\"></div>\n <div>\n <strong>Active Account</strong>\n <div style=\"font-size: 12px; color: var(--md-on-surface-variant); margin-top: 2px;\">\n User can log in and access the system\n </div>\n </div>\n </label>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <!-- Role Assignment Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-user-tag\"></i>\n Role Assignment\n </h3>\n <p class=\"section-description\">Select the roles to assign to this user</p>\n </div>\n <div class=\"roles-grid\">\n @for (role of data?.availableRoles; track role.ID) {\n <div class=\"role-card\"\n [class.selected]=\"selectedRoleIds.has(role.ID)\"\n (click)=\"toggleRole(role.ID)\">\n <div class=\"role-content\">\n <div class=\"checkbox-field\">\n <input\n type=\"checkbox\"\n class=\"checkbox-input\"\n [id]=\"'role-' + role.ID\"\n [checked]=\"selectedRoleIds.has(role.ID)\"\n (change)=\"onRoleToggle(role.ID, $event)\"\n />\n <label class=\"checkbox-label\" [for]=\"'role-' + role.ID\">\n <div class=\"checkbox-indicator\"></div>\n </label>\n </div>\n <div class=\"role-info\">\n <h4 class=\"role-name\">{{ role.Name }}</h4>\n @if (role.Description) {\n <p class=\"role-description\">{{ role.Description }}</p>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Modal Footer -->\n <div class=\"modal-footer\">\n <button\n type=\"submit\"\n class=\"btn btn-primary\"\n [disabled]=\"userForm.invalid || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n {{ isEditMode ? 'Update User' : 'Create User' }}\n }\n </button>\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n}\n", styles: ["/* ============================================\n User Dialog Component\n Uses MJ Design Tokens (--mj-*)\n ============================================ */\n\n/* ============================================\n Modal Backdrop & Container\n ============================================ */\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1050;\n padding: 1rem;\n animation: fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.modal-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-width: 900px;\n max-height: calc(100vh - 2rem);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: scale(0.95) translateY(20px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n/* ============================================\n Modal Header (MJ Blue)\n ============================================ */\n\n.modal-header {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n padding: 24px 32px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n flex-shrink: 0;\n}\n\n.dialog-header {\n flex: 1;\n min-width: 0;\n}\n\n.dialog-title {\n margin: 0;\n font-size: 1.5rem;\n font-weight: 600;\n line-height: 1.3;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n color: var(--mj-brand-on-primary);\n}\n\n.dialog-title i {\n font-size: 1.25rem;\n opacity: 0.95;\n}\n\n.dialog-subtitle {\n margin: 0.5rem 0 0 0;\n font-size: 0.875rem;\n opacity: 0.9;\n font-weight: 400;\n line-height: 1.4;\n}\n\n.modal-close {\n background: transparent;\n border: none;\n color: var(--mj-brand-on-primary);\n cursor: pointer;\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 40px;\n height: 40px;\n flex-shrink: 0;\n}\n\n.modal-close:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.modal-close:active {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(0.95);\n}\n\n.modal-close i {\n font-size: 1.25rem;\n}\n\n/* ============================================\n Modal Body\n ============================================ */\n\n.modal-body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 0;\n background: var(--mj-bg-page);\n}\n\n.modal-body::-webkit-scrollbar {\n width: 8px;\n}\n\n.modal-body::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-sunken);\n}\n\n.modal-body::-webkit-scrollbar-thumb {\n background: var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n}\n\n.modal-body::-webkit-scrollbar-thumb:hover {\n background: var(--mj-color-neutral-400);\n}\n\n/* ============================================\n Form Structure\n ============================================ */\n\n.content-section {\n padding: 1.5rem 2rem;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.content-section:last-child {\n border-bottom: none;\n}\n\n.form-section {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.section-header {\n margin-bottom: 1rem;\n}\n\n.section-title {\n margin: 0 0 0.5rem 0;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.75rem;\n}\n\n.section-title i {\n font-size: 1.25rem;\n color: var(--mj-brand-primary);\n}\n\n.section-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* ============================================\n Form Grid Layout\n ============================================ */\n\n.form-grid {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.form-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1.5rem;\n}\n\n@media (max-width: 640px) {\n .form-row {\n grid-template-columns: 1fr;\n gap: 1.5rem;\n }\n}\n\n/* ============================================\n Form Fields\n ============================================ */\n\n.form-field {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.field-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.field-label.required::after {\n content: '*';\n color: var(--mj-status-error);\n margin-left: 0.25rem;\n}\n\n.field-input,\n.field-select {\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 0.875rem 1rem;\n font-size: 1rem;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: var(--mj-bg-page);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n width: 100%;\n box-sizing: border-box;\n}\n\n.field-input:hover:not(:disabled),\n.field-select:hover:not(:disabled) {\n border-color: var(--mj-color-neutral-400);\n}\n\n.field-input:focus,\n.field-select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-focus-ring);\n}\n\n.field-input:disabled,\n.field-select:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.field-input.error,\n.field-select.error {\n border-color: var(--mj-status-error);\n}\n\n.field-input.error:focus,\n.field-select.error:focus {\n --mj-ring-color: var(--mj-status-error);\n box-shadow: var(--mj-focus-ring);\n}\n\n.field-input::placeholder,\n.field-select::placeholder {\n color: var(--mj-text-secondary);\n}\n\n/* Select Dropdown Styling */\n.field-select {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2379747E' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e\");\n background-position: right 16px center;\n background-repeat: no-repeat;\n background-size: 16px;\n padding-right: calc(16px + 32px);\n appearance: none;\n cursor: pointer;\n}\n\n.field-error {\n font-size: 0.75rem;\n color: var(--mj-status-error);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n}\n\n.field-error i {\n font-size: 0.875rem;\n}\n\n/* ============================================\n Checkboxes\n ============================================ */\n\n.checkbox-field {\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n cursor: pointer;\n margin-bottom: 0;\n}\n\n.checkbox-input {\n position: absolute;\n opacity: 0;\n cursor: pointer;\n}\n\n.checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n cursor: pointer;\n font-size: 0.875rem;\n color: var(--mj-text-primary);\n line-height: 1.4;\n margin: 0;\n flex: 1;\n}\n\n.checkbox-indicator {\n width: 20px;\n height: 20px;\n border: 2px solid var(--mj-color-neutral-400);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-page);\n flex-shrink: 0;\n position: relative;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n margin-top: 2px;\n}\n\n.checkbox-indicator::after {\n content: '';\n position: absolute;\n left: 6px;\n top: 2px;\n width: 4px;\n height: 8px;\n border: solid var(--mj-brand-on-primary);\n border-width: 0 2px 2px 0;\n opacity: 0;\n transform: rotate(45deg) scale(0.8);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.checkbox-input:checked + .checkbox-label .checkbox-indicator {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.checkbox-input:checked + .checkbox-label .checkbox-indicator::after {\n opacity: 1;\n transform: rotate(45deg) scale(1);\n}\n\n.checkbox-input:focus + .checkbox-label .checkbox-indicator {\n box-shadow: var(--mj-focus-ring);\n}\n\n.checkbox-label strong {\n display: block;\n font-weight: 600;\n margin-bottom: 0.125rem;\n}\n\n/* ============================================\n Role Cards\n ============================================ */\n\n.roles-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 1rem;\n}\n\n@media (max-width: 640px) {\n .roles-grid {\n grid-template-columns: 1fr;\n }\n}\n\n.role-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n padding: 1.25rem;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n flex-direction: column;\n}\n\n.role-card:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-md);\n transform: translateY(-2px);\n}\n\n.role-card.selected {\n background: var(--mj-brand-accent-subtle);\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-md);\n}\n\n.role-content {\n display: flex;\n align-items: flex-start;\n gap: 1rem;\n}\n\n.role-info {\n flex: 1;\n min-width: 0;\n}\n\n.role-name {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n line-height: 1.3;\n}\n\n.role-description {\n font-size: 0.75rem;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n margin: 0;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* ============================================\n Modal Footer (Buttons)\n ============================================ */\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 1rem;\n padding: 1.5rem 2rem;\n background: var(--mj-bg-surface-sunken);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n/* ============================================\n Buttons\n ============================================ */\n\n.btn {\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 500;\n border-radius: var(--mj-radius-full);\n border: none;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n font-family: inherit;\n text-transform: none;\n letter-spacing: 0.01em;\n}\n\n.btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.btn:active:not(:disabled) {\n transform: scale(0.98);\n}\n\n.btn i {\n font-size: 1rem;\n}\n\n.btn i.fa-spinner {\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Primary Button (MJ Blue - Dark Button) */\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-light);\n box-shadow: var(--mj-shadow-md);\n}\n\n.btn-primary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n box-shadow: none;\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Secondary Button (Light Button) */\n.btn-secondary {\n background: var(--mj-bg-page);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-color-neutral-400);\n box-shadow: none;\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.btn-secondary:disabled {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* ============================================\n Responsive Design\n ============================================ */\n\n@media (max-width: 768px) {\n .modal-backdrop {\n padding: 0;\n }\n\n .modal-dialog {\n max-width: 100%;\n max-height: 100vh;\n border-radius: 0;\n animation: slideInMobile 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n @keyframes slideInMobile {\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .modal-header {\n padding: 20px 16px;\n border-radius: 0;\n }\n\n .dialog-title {\n font-size: 1.25rem;\n }\n\n .dialog-subtitle {\n font-size: 0.8125rem;\n }\n\n .content-section {\n padding: 1.25rem 1rem;\n }\n\n .section-title {\n font-size: 1.125rem;\n }\n\n .modal-footer {\n padding: 1rem;\n flex-direction: column;\n }\n\n .btn {\n width: 100%;\n justify-content: center;\n }\n\n .roles-grid {\n grid-template-columns: 1fr;\n }\n}\n\n@media (max-width: 480px) {\n .modal-header {\n padding: 16px 12px;\n }\n\n .dialog-title {\n font-size: 1.125rem;\n gap: 0.5rem;\n }\n\n .dialog-title i {\n font-size: 1rem;\n }\n\n .dialog-subtitle {\n font-size: 0.75rem;\n margin-top: 0.25rem;\n }\n\n .content-section {\n padding: 1rem 0.75rem;\n }\n\n .section-title {\n font-size: 1rem;\n gap: 0.5rem;\n }\n\n .field-input,\n .field-select {\n padding: 0.75rem;\n font-size: 0.875rem;\n }\n\n .modal-footer {\n padding: 0.75rem;\n }\n\n .btn {\n padding: 0.625rem 1.25rem;\n font-size: 0.8125rem;\n }\n}\n\n/* ============================================\n Accessibility\n ============================================ */\n\n/* Focus Visible Styles */\n*:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n/* Reduced Motion Support */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* High Contrast Mode Support */\n@media (prefers-contrast: high) {\n .modal-dialog {\n border: 2px solid var(--mj-border-default);\n }\n\n .btn {\n border: 2px solid currentColor;\n }\n\n .field-input,\n .field-select {\n border-width: 3px;\n }\n\n .role-card {\n border-width: 2px;\n }\n}\n\n/* ============================================\n Print Styles\n ============================================ */\n\n@media print {\n .modal-backdrop {\n display: none;\n }\n}\n"] }]
|
|
487
|
-
}], () => [], {
|
|
570
|
+
}], () => [], { Data: [{
|
|
571
|
+
type: Input
|
|
572
|
+
}], data: [{
|
|
573
|
+
type: Input
|
|
574
|
+
}], Visible: [{
|
|
488
575
|
type: Input
|
|
489
576
|
}], visible: [{
|
|
490
577
|
type: Input
|
|
578
|
+
}], Result: [{
|
|
579
|
+
type: Output
|
|
491
580
|
}], result: [{
|
|
492
581
|
type: Output
|
|
493
582
|
}], onEscapeKey: [{
|
|
494
583
|
type: HostListener,
|
|
495
584
|
args: ['document:keydown.escape', ['$event']]
|
|
496
585
|
}] }); })();
|
|
497
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserDialogComponent, { className: "UserDialogComponent", filePath: "src/lib/user-management/user-dialog/user-dialog.component.ts", lineNumber:
|
|
586
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserDialogComponent, { className: "UserDialogComponent", filePath: "src/lib/user-management/user-dialog/user-dialog.component.ts", lineNumber: 28 }); })();
|
|
498
587
|
//# sourceMappingURL=user-dialog.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/user-management/user-dialog/user-dialog.component.ts","../../../../src/lib/user-management/user-dialog/user-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, SimpleChanges, inject, HostListener, ViewEncapsulation, ChangeDetectorRef, NgZone } from '@angular/core';\n\nimport { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';\nimport { Metadata, RunView } from '@memberjunction/core';\nimport { MJUserEntity, MJRoleEntity, MJUserRoleEntity } from '@memberjunction/core-entities';\n\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nexport interface UserDialogData {\n user?: MJUserEntity;\n mode: 'create' | 'edit';\n availableRoles: MJRoleEntity[];\n}\n\nexport interface UserDialogResult {\n action: 'save' | 'cancel';\n user?: MJUserEntity;\n}\n\n@Component({\n standalone: false,\n selector: 'mj-user-dialog',\n encapsulation: ViewEncapsulation.None,\n templateUrl: './user-dialog.component.html',\n styleUrls: ['./user-dialog.component.css']\n})\nexport class UserDialogComponent extends BaseAngularComponent implements OnInit, OnDestroy, OnChanges {\n @Input() data: UserDialogData | null = null;\n @Input() visible = false;\n @Output() result = new EventEmitter<UserDialogResult>();\n\n private fb = inject(FormBuilder);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n private get metadata() { return this.ProviderToUse; }\n public userForm: FormGroup;\n public isLoading = false;\n public error: string | null = null;\n public selectedRoleIds = new Set<string>();\n public existingUserRoles: MJUserRoleEntity[] = [];\n\n constructor() {\n super();\n this.userForm = this.fb.group({\n name: ['', [Validators.required, Validators.email]],\n firstName: [''],\n lastName: [''],\n email: ['', [Validators.required, Validators.email]],\n title: [''],\n type: ['User', Validators.required],\n isActive: [true]\n });\n }\n\n ngOnInit(): void {\n // Initial setup\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n // Always clear state when data changes to prevent persistence bugs\n if (changes['data']) {\n this.selectedRoleIds.clear();\n this.existingUserRoles = [];\n \n if (this.data?.user && this.isEditMode) {\n this.loadUserData();\n } else {\n this.resetForm();\n }\n }\n \n // Reset form when dialog becomes visible and not in edit mode\n if (changes['visible'] && this.visible && !this.isEditMode) {\n this.resetForm();\n }\n }\n\n ngOnDestroy(): void {\n // Cleanup if needed\n }\n\n private resetForm(): void {\n this.userForm.reset({\n name: '',\n firstName: '',\n lastName: '',\n email: '',\n title: '',\n type: 'User',\n isActive: true\n });\n this.selectedRoleIds.clear();\n this.error = null;\n }\n\n @HostListener('document:keydown.escape', ['$event'])\n onEscapeKey(event: Event): void {\n if (this.visible) {\n this.onCancel();\n }\n }\n\n public get windowTitle(): string {\n return this.isEditMode ? 'Edit User' : 'Create New User';\n }\n\n public get isEditMode(): boolean {\n return this.data?.mode === 'edit';\n }\n\n private async loadUserData(): Promise<void> {\n if (!this.data?.user) return;\n\n const user = this.data.user;\n this.userForm.patchValue({\n name: user.Name,\n firstName: user.FirstName,\n lastName: user.LastName,\n email: user.Email,\n title: user.Title,\n type: user.Type,\n isActive: user.IsActive\n });\n\n // Load existing user roles\n await this.loadExistingUserRoles(user.ID);\n }\n\n private async loadExistingUserRoles(userId: string): Promise<void> {\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<MJUserRoleEntity>({\n EntityName: 'MJ: User Roles',\n ExtraFilter: `UserID='${userId}'`,\n ResultType: 'entity_object'\n });\n\n if (result.Success && result.Results) {\n this.existingUserRoles = result.Results;\n // Pre-select existing roles\n for (const userRole of this.existingUserRoles) {\n this.selectedRoleIds.add(userRole.RoleID);\n }\n }\n } catch (error) {\n console.warn('Failed to load existing user roles:', error);\n }\n }\n\n public onRoleToggle(roleId: string, event: Event): void {\n event.stopPropagation();\n const checkbox = event.target as HTMLInputElement;\n if (checkbox.checked) {\n this.selectedRoleIds.add(roleId);\n } else {\n this.selectedRoleIds.delete(roleId);\n }\n }\n\n public toggleRole(roleId: string): void {\n if (this.selectedRoleIds.has(roleId)) {\n this.selectedRoleIds.delete(roleId);\n } else {\n this.selectedRoleIds.add(roleId);\n }\n }\n\n public async onSubmit(): Promise<void> {\n if (this.userForm.invalid) {\n this.markFormGroupTouched(this.userForm);\n return;\n }\n\n this.isLoading = true;\n this.error = null;\n\n try {\n let user: MJUserEntity;\n\n if (this.isEditMode && this.data?.user) {\n // Edit existing user\n user = this.data.user;\n } else {\n // Create new user\n user = await this.metadata.GetEntityObject<MJUserEntity>('MJ: Users');\n user.NewRecord();\n }\n\n // Update user properties\n const formValue = this.userForm.value;\n user.Name = formValue.name;\n user.FirstName = formValue.firstName;\n user.LastName = formValue.lastName;\n user.Email = formValue.email;\n user.Title = formValue.title;\n user.Type = formValue.type;\n user.IsActive = formValue.isActive;\n\n // Save user\n const saveResult = await user.Save();\n if (!saveResult) {\n throw new Error(user.LatestResult?.Message || 'Failed to save user');\n }\n\n // Handle role assignments\n await this.updateUserRoles(user.ID);\n\n this.result.emit({ action: 'save', user });\n\n } catch (error: unknown) {\n console.error('Error saving user:', error);\n this.ngZone.run(() => {\n this.error = error instanceof Error ? error.message : 'An unexpected error occurred';\n this.cdr.markForCheck();\n });\n } finally {\n this.ngZone.run(() => {\n this.isLoading = false;\n this.cdr.markForCheck();\n });\n }\n }\n\n private async updateUserRoles(userId: string): Promise<void> {\n try {\n // Get current role IDs from existing UserRole entities\n const existingRoleIds = new Set(this.existingUserRoles.map(ur => ur.RoleID));\n\n // Determine roles to add and remove\n const rolesToAdd = Array.from(this.selectedRoleIds).filter(roleId => !existingRoleIds.has(roleId));\n const rolesToRemove = this.existingUserRoles.filter(userRole => !this.selectedRoleIds.has(userRole.RoleID));\n\n if (rolesToAdd.length === 0 && rolesToRemove.length === 0) {\n return;\n }\n\n // Batch all role deletes and adds into one transactional GraphQL call\n const tg = await this.metadata.CreateTransactionGroup();\n\n // Each Delete()/Save() only ENROLS the row in the group; the write is deferred to Submit().\n // A false return therefore means the row was refused CLIENT-side and never enrolled — so\n // submitting anyway writes a PARTIAL change, or an empty one whose Submit() returns true for\n // having nothing to do, and either way the dialog closes reporting success. Same rule and\n // same reason as `UserManagementComponent.executeBulkRoleAssign`, where the transaction-group\n // semantics — and why the server-side #4282 guard is NOT what this catches — are set out in full.\n const refusals: string[] = [];\n\n for (const userRole of rolesToRemove) {\n userRole.TransactionGroup = tg;\n if (!await userRole.Delete()) {\n refusals.push(`Remove ${this.describeRole(userRole.RoleID)}: ${userRole.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n }\n\n for (const roleId of rolesToAdd) {\n const userRole = await this.metadata.GetEntityObject<MJUserRoleEntity>('MJ: User Roles');\n userRole.NewRecord();\n userRole.UserID = userId;\n userRole.RoleID = roleId;\n userRole.TransactionGroup = tg;\n if (!await userRole.Save()) {\n refusals.push(`Add ${this.describeRole(roleId)}: ${userRole.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n }\n\n if (refusals.length > 0) {\n // Nothing was written: the refused rows never enrolled, and the rest are still only queued\n // because Submit() is not reached.\n throw new Error(`Failed to update user roles — no role changes were made.\\n${refusals.join('\\n')}`);\n }\n\n if (!await tg.Submit()) {\n throw new Error('Failed to update user roles — all changes have been rolled back');\n }\n } catch (error) {\n console.error('Error updating user roles:', error);\n throw error;\n }\n }\n\n /** Names a role for a refusal message; falls back to the ID when the catalog has no match. */\n private describeRole(roleId: string): string {\n return this.data?.availableRoles.find(r => UUIDsEqual(r.ID, roleId))?.Name ?? roleId;\n }\n\n public onCancel(): void {\n this.result.emit({ action: 'cancel' });\n }\n\n private markFormGroupTouched(formGroup: FormGroup): void {\n Object.keys(formGroup.controls).forEach(key => {\n const control = formGroup.get(key);\n control?.markAsTouched();\n });\n }\n}","<!-- MD3 Modal Dialog -->\n@if (visible) {\n <div class=\"modal-backdrop\" (click)=\"onCancel()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header with MJ Blue -->\n <div class=\"modal-header\">\n <div class=\"dialog-header\">\n <h2 class=\"dialog-title\">\n <i class=\"fa-solid fa-user\"></i>\n {{ isEditMode ? 'Edit User' : 'Create New User' }}\n </h2>\n <p class=\"dialog-subtitle\">\n {{ isEditMode ? 'Update user information and role assignments' : 'Add a new user to the system' }}\n </p>\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"onCancel()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Modal Body -->\n <div class=\"modal-body\">\n <form [formGroup]=\"userForm\" (ngSubmit)=\"onSubmit()\">\n @if (error) {\n <div class=\"content-section\">\n <mj-alert Variant=\"error\" [Message]=\"error\" />\n </div>\n }\n <!-- Basic Information Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-id-card\"></i>\n Basic Information\n </h3>\n <p class=\"section-description\">Enter the user's personal and contact details</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"name\">Username/Email</label>\n <input\n id=\"name\"\n type=\"email\"\n class=\"field-input\"\n formControlName=\"name\"\n placeholder=\"john@company.com\"\n [class.error]=\"userForm.get('name')?.invalid && userForm.get('name')?.touched\"\n />\n @if (userForm.get('name')?.invalid && userForm.get('name')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (userForm.get('name')?.errors?.['required']) {\n Username/Email is required\n }\n @if (userForm.get('name')?.errors?.['email']) {\n Please enter a valid email address\n }\n </div>\n }\n </div>\n <div class=\"form-row\">\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"firstName\">First Name</label>\n <input\n id=\"firstName\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"firstName\"\n placeholder=\"John\"\n />\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"lastName\">Last Name</label>\n <input\n id=\"lastName\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"lastName\"\n placeholder=\"Doe\"\n />\n </div>\n </div>\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"email\">Email Address</label>\n <input\n id=\"email\"\n type=\"email\"\n class=\"field-input\"\n formControlName=\"email\"\n placeholder=\"john@company.com\"\n [class.error]=\"userForm.get('email')?.invalid && userForm.get('email')?.touched\"\n />\n @if (userForm.get('email')?.invalid && userForm.get('email')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (userForm.get('email')?.errors?.['required']) {\n Email address is required\n }\n @if (userForm.get('email')?.errors?.['email']) {\n Please enter a valid email address\n }\n </div>\n }\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"title\">Job Title</label>\n <input\n id=\"title\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"title\"\n placeholder=\"Software Engineer\"\n />\n </div>\n </div>\n </div>\n </div>\n <!-- User Settings Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-cog\"></i>\n User Settings\n </h3>\n <p class=\"section-description\">Configure user type and account status</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"type\">User Type</label>\n <select id=\"type\" class=\"field-select\" formControlName=\"type\">\n <option value=\"User\">Standard User</option>\n <option value=\"Owner\">System Owner</option>\n </select>\n </div>\n <div class=\"form-field\">\n <div class=\"checkbox-field\">\n <input\n id=\"isActive\"\n type=\"checkbox\"\n class=\"checkbox-input\"\n formControlName=\"isActive\"\n />\n <label class=\"checkbox-label\" for=\"isActive\">\n <div class=\"checkbox-indicator\"></div>\n <div>\n <strong>Active Account</strong>\n <div style=\"font-size: 12px; color: var(--md-on-surface-variant); margin-top: 2px;\">\n User can log in and access the system\n </div>\n </div>\n </label>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <!-- Role Assignment Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-user-tag\"></i>\n Role Assignment\n </h3>\n <p class=\"section-description\">Select the roles to assign to this user</p>\n </div>\n <div class=\"roles-grid\">\n @for (role of data?.availableRoles; track role.ID) {\n <div class=\"role-card\"\n [class.selected]=\"selectedRoleIds.has(role.ID)\"\n (click)=\"toggleRole(role.ID)\">\n <div class=\"role-content\">\n <div class=\"checkbox-field\">\n <input\n type=\"checkbox\"\n class=\"checkbox-input\"\n [id]=\"'role-' + role.ID\"\n [checked]=\"selectedRoleIds.has(role.ID)\"\n (change)=\"onRoleToggle(role.ID, $event)\"\n />\n <label class=\"checkbox-label\" [for]=\"'role-' + role.ID\">\n <div class=\"checkbox-indicator\"></div>\n </label>\n </div>\n <div class=\"role-info\">\n <h4 class=\"role-name\">{{ role.Name }}</h4>\n @if (role.Description) {\n <p class=\"role-description\">{{ role.Description }}</p>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Modal Footer -->\n <div class=\"modal-footer\">\n <button\n type=\"submit\"\n class=\"btn btn-primary\"\n [disabled]=\"userForm.invalid || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n {{ isEditMode ? 'Update User' : 'Create User' }}\n }\n </button>\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"user-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/user-management/user-dialog/user-dialog.component.ts","../../../../src/lib/user-management/user-dialog/user-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, SimpleChanges, inject, HostListener, ViewEncapsulation, ChangeDetectorRef, NgZone } from '@angular/core';\n\nimport { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';\nimport { Metadata, RunView } from '@memberjunction/core';\nimport { MJUserEntity, MJRoleEntity, MJUserRoleEntity } from '@memberjunction/core-entities';\n\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { EnrolledRow, ServerRefusalReasons } from '../transaction-group-refusals';\nexport interface UserDialogData {\n user?: MJUserEntity;\n mode: 'create' | 'edit';\n availableRoles: MJRoleEntity[];\n}\n\nexport interface UserDialogResult {\n action: 'save' | 'cancel';\n user?: MJUserEntity;\n}\n\n@Component({\n standalone: false,\n selector: 'mj-user-dialog',\n encapsulation: ViewEncapsulation.None,\n templateUrl: './user-dialog.component.html',\n styleUrls: ['./user-dialog.component.css']\n})\nexport class UserDialogComponent extends BaseAngularComponent implements OnInit, OnDestroy, OnChanges {\n @Input() Data: UserDialogData | null = null;\n\n /** @deprecated Use {@link Data}. */\n @Input() set data(value: UserDialogData | null) {\n this.Data = value;\n }\n /** @deprecated Use {@link Data}. */\n get data(): UserDialogData | null {\n return this.Data;\n }\n @Input() Visible = false;\n\n /** @deprecated Use {@link Visible}. */\n @Input() set visible(value: UserDialogComponent['Visible']) {\n this.Visible = value;\n }\n /** @deprecated Use {@link Visible}. */\n get visible(): UserDialogComponent['Visible'] {\n return this.Visible;\n }\n @Output() Result = new EventEmitter<UserDialogResult>();\n\n /**\n * @deprecated Use {@link Result}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (result) keeps working. Must stay AFTER Result: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() result = this.Result;\n\n private fb = inject(FormBuilder);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n private get metadata() { return this.ProviderToUse; }\n public UserForm: FormGroup;\n\n /** @deprecated Use {@link UserForm}. */\n public get userForm(): FormGroup {\n return this.UserForm;\n }\n /** @deprecated Use {@link UserForm}. */\n public set userForm(value: FormGroup) {\n this.UserForm = value;\n }\n public isLoading = false;\n public error: string | null = null;\n public SelectedRoleIds = new Set<string>();\n\n /** @deprecated Use {@link SelectedRoleIds}. */\n public get selectedRoleIds() {\n return this.SelectedRoleIds;\n }\n /** @deprecated Use {@link SelectedRoleIds}. */\n public set selectedRoleIds(value) {\n this.SelectedRoleIds = value;\n }\n public ExistingUserRoles: MJUserRoleEntity[] = [];\n\n /** @deprecated Use {@link ExistingUserRoles}. */\n public get existingUserRoles(): MJUserRoleEntity[] {\n return this.ExistingUserRoles;\n }\n /** @deprecated Use {@link ExistingUserRoles}. */\n public set existingUserRoles(value: MJUserRoleEntity[]) {\n this.ExistingUserRoles = value;\n }\n\n constructor() {\n super();\n this.UserForm = this.fb.group({\n name: ['', [Validators.required, Validators.email]],\n firstName: [''],\n lastName: [''],\n email: ['', [Validators.required, Validators.email]],\n title: [''],\n type: ['User', Validators.required],\n isActive: [true]\n });\n }\n\n ngOnInit(): void {\n // Initial setup\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n // Always clear state when data changes to prevent persistence bugs\n if (changes['data']) {\n this.SelectedRoleIds.clear();\n this.ExistingUserRoles = [];\n \n if (this.Data?.user && this.IsEditMode) {\n this.loadUserData();\n } else {\n this.resetForm();\n }\n }\n \n // Reset form when dialog becomes visible and not in edit mode\n if (changes['visible'] && this.Visible && !this.IsEditMode) {\n this.resetForm();\n }\n }\n\n ngOnDestroy(): void {\n // Cleanup if needed\n }\n\n private resetForm(): void {\n this.UserForm.reset({\n name: '',\n firstName: '',\n lastName: '',\n email: '',\n title: '',\n type: 'User',\n isActive: true\n });\n this.SelectedRoleIds.clear();\n this.error = null;\n }\n\n @HostListener('document:keydown.escape', ['$event'])\n onEscapeKey(event: Event): void {\n if (this.Visible) {\n this.onCancel();\n }\n }\n\n public get WindowTitle(): string {\n return this.IsEditMode ? 'Edit User' : 'Create New User';\n }\n\n /** @deprecated Use {@link WindowTitle}. */\n public get windowTitle(): string {\n return this.WindowTitle;\n }\n\n public get IsEditMode(): boolean {\n return this.Data?.mode === 'edit';\n }\n\n /** @deprecated Use {@link IsEditMode}. */\n public get isEditMode(): boolean {\n return this.IsEditMode;\n }\n\n private async loadUserData(): Promise<void> {\n if (!this.Data?.user) return;\n\n const user = this.Data.user;\n this.UserForm.patchValue({\n name: user.Name,\n firstName: user.FirstName,\n lastName: user.LastName,\n email: user.Email,\n title: user.Title,\n type: user.Type,\n isActive: user.IsActive\n });\n\n // Load existing user roles\n await this.loadExistingUserRoles(user.ID);\n }\n\n private async loadExistingUserRoles(userId: string): Promise<void> {\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<MJUserRoleEntity>({\n EntityName: 'MJ: User Roles',\n ExtraFilter: `UserID='${userId}'`,\n ResultType: 'entity_object'\n });\n\n if (result.Success && result.Results) {\n this.ExistingUserRoles = result.Results;\n // Pre-select existing roles\n for (const userRole of this.ExistingUserRoles) {\n this.SelectedRoleIds.add(userRole.RoleID);\n }\n }\n } catch (error) {\n console.warn('Failed to load existing user roles:', error);\n }\n }\n\n public OnRoleToggle(roleId: string, event: Event): void {\n event.stopPropagation();\n const checkbox = event.target as HTMLInputElement;\n if (checkbox.checked) {\n this.SelectedRoleIds.add(roleId);\n } else {\n this.SelectedRoleIds.delete(roleId);\n }\n }\n\n /** @deprecated Use {@link OnRoleToggle}. */\n public onRoleToggle(roleId: string, event: Event): void {\n return this.OnRoleToggle(roleId, event);\n }\n\n public ToggleRole(roleId: string): void {\n if (this.SelectedRoleIds.has(roleId)) {\n this.SelectedRoleIds.delete(roleId);\n } else {\n this.SelectedRoleIds.add(roleId);\n }\n }\n\n /** @deprecated Use {@link ToggleRole}. */\n public toggleRole(roleId: string): void {\n return this.ToggleRole(roleId);\n }\n\n public async OnSubmit(): Promise<void> {\n if (this.UserForm.invalid) {\n this.markFormGroupTouched(this.UserForm);\n return;\n }\n\n this.isLoading = true;\n this.error = null;\n\n try {\n let user: MJUserEntity;\n\n if (this.IsEditMode && this.Data?.user) {\n // Edit existing user\n user = this.Data.user;\n } else {\n // Create new user\n user = await this.metadata.GetEntityObject<MJUserEntity>('MJ: Users');\n user.NewRecord();\n }\n\n // Update user properties\n const formValue = this.UserForm.value;\n user.Name = formValue.name;\n user.FirstName = formValue.firstName;\n user.LastName = formValue.lastName;\n user.Email = formValue.email;\n user.Title = formValue.title;\n user.Type = formValue.type;\n user.IsActive = formValue.isActive;\n\n // Save user\n const saveResult = await user.Save();\n if (!saveResult) {\n throw new Error(user.LatestResult?.Message || 'Failed to save user');\n }\n\n // Handle role assignments\n await this.updateUserRoles(user.ID);\n\n this.Result.emit({ action: 'save', user });\n\n } catch (error: unknown) {\n console.error('Error saving user:', error);\n this.ngZone.run(() => {\n this.error = error instanceof Error ? error.message : 'An unexpected error occurred';\n this.cdr.markForCheck();\n });\n } finally {\n this.ngZone.run(() => {\n this.isLoading = false;\n this.cdr.markForCheck();\n });\n }\n }\n\n /** @deprecated Use {@link OnSubmit}. */\n public async onSubmit(): Promise<void> {\n return this.OnSubmit();\n }\n\n private async updateUserRoles(userId: string): Promise<void> {\n try {\n // Get current role IDs from existing UserRole entities\n const existingRoleIds = new Set(this.ExistingUserRoles.map(ur => ur.RoleID));\n\n // Determine roles to add and remove\n const rolesToAdd = Array.from(this.SelectedRoleIds).filter(roleId => !existingRoleIds.has(roleId));\n const rolesToRemove = this.ExistingUserRoles.filter(userRole => !this.SelectedRoleIds.has(userRole.RoleID));\n\n if (rolesToAdd.length === 0 && rolesToRemove.length === 0) {\n return;\n }\n\n // Batch all role deletes and adds into one transactional GraphQL call\n const tg = await this.metadata.CreateTransactionGroup();\n\n // Each Delete()/Save() only ENROLS the row in the group; the write is deferred to Submit().\n // A false return therefore means the row was refused CLIENT-side and never enrolled — so\n // submitting anyway writes a PARTIAL change, or an empty one whose Submit() returns true for\n // having nothing to do, and either way the dialog closes reporting success. Same rule and\n // same reason as `UserManagementComponent.executeBulkRoleAssign`, where the transaction-group\n // semantics — and why the server-side #4282 guard is NOT what this catches — are set out in full.\n const refusals: string[] = [];\n // Enrolled rows are kept so the SERVER's reason can be read back off them below; without\n // that, the reason #4309 puts on LatestResult has nobody left to read it.\n const enrolled: EnrolledRow[] = [];\n\n for (const userRole of rolesToRemove) {\n userRole.TransactionGroup = tg;\n if (!await userRole.Delete()) {\n refusals.push(`Remove ${this.describeRole(userRole.RoleID)}: ${userRole.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n else {\n enrolled.push({ Label: `Remove ${this.describeRole(userRole.RoleID)}`, Entity: userRole });\n }\n }\n\n for (const roleId of rolesToAdd) {\n const userRole = await this.metadata.GetEntityObject<MJUserRoleEntity>('MJ: User Roles');\n userRole.NewRecord();\n userRole.UserID = userId;\n userRole.RoleID = roleId;\n userRole.TransactionGroup = tg;\n if (!await userRole.Save()) {\n refusals.push(`Add ${this.describeRole(roleId)}: ${userRole.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n else {\n enrolled.push({ Label: `Add ${this.describeRole(roleId)}`, Entity: userRole });\n }\n }\n\n if (refusals.length > 0) {\n // Nothing was written: the refused rows never enrolled, and the rest are still only queued\n // because Submit() is not reached.\n throw new Error(`Failed to update user roles — no role changes were made.\\n${refusals.join('\\n')}`);\n }\n\n if (!await tg.Submit()) {\n // Every row enrolled, so this is a SERVER-side refusal (or a rollback). Since #4309 the\n // server says which row and why, and that reason is now on each entity's LatestResult.\n const reasons = ServerRefusalReasons(enrolled);\n throw new Error(reasons.length > 0\n ? `Failed to update user roles — all changes have been rolled back.\\n${reasons.join('\\n')}`\n : 'Failed to update user roles — all changes have been rolled back');\n }\n } catch (error) {\n console.error('Error updating user roles:', error);\n throw error;\n }\n }\n\n /** Names a role for a refusal message; falls back to the ID when the catalog has no match. */\n private describeRole(roleId: string): string {\n return this.Data?.availableRoles.find(r => UUIDsEqual(r.ID, roleId))?.Name ?? roleId;\n }\n\n public onCancel(): void {\n this.Result.emit({ action: 'cancel' });\n }\n\n private markFormGroupTouched(formGroup: FormGroup): void {\n Object.keys(formGroup.controls).forEach(key => {\n const control = formGroup.get(key);\n control?.markAsTouched();\n });\n }\n}","<!-- MD3 Modal Dialog -->\n@if (visible) {\n <div class=\"modal-backdrop\" (click)=\"onCancel()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header with MJ Blue -->\n <div class=\"modal-header\">\n <div class=\"dialog-header\">\n <h2 class=\"dialog-title\">\n <i class=\"fa-solid fa-user\"></i>\n {{ isEditMode ? 'Edit User' : 'Create New User' }}\n </h2>\n <p class=\"dialog-subtitle\">\n {{ isEditMode ? 'Update user information and role assignments' : 'Add a new user to the system' }}\n </p>\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"onCancel()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Modal Body -->\n <div class=\"modal-body\">\n <form [formGroup]=\"userForm\" (ngSubmit)=\"onSubmit()\">\n @if (error) {\n <div class=\"content-section\">\n <mj-alert Variant=\"error\" [Message]=\"error\" />\n </div>\n }\n <!-- Basic Information Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-id-card\"></i>\n Basic Information\n </h3>\n <p class=\"section-description\">Enter the user's personal and contact details</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"name\">Username/Email</label>\n <input\n id=\"name\"\n type=\"email\"\n class=\"field-input\"\n formControlName=\"name\"\n placeholder=\"john@company.com\"\n [class.error]=\"userForm.get('name')?.invalid && userForm.get('name')?.touched\"\n />\n @if (userForm.get('name')?.invalid && userForm.get('name')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (userForm.get('name')?.errors?.['required']) {\n Username/Email is required\n }\n @if (userForm.get('name')?.errors?.['email']) {\n Please enter a valid email address\n }\n </div>\n }\n </div>\n <div class=\"form-row\">\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"firstName\">First Name</label>\n <input\n id=\"firstName\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"firstName\"\n placeholder=\"John\"\n />\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"lastName\">Last Name</label>\n <input\n id=\"lastName\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"lastName\"\n placeholder=\"Doe\"\n />\n </div>\n </div>\n <div class=\"form-field\">\n <label class=\"field-label required\" for=\"email\">Email Address</label>\n <input\n id=\"email\"\n type=\"email\"\n class=\"field-input\"\n formControlName=\"email\"\n placeholder=\"john@company.com\"\n [class.error]=\"userForm.get('email')?.invalid && userForm.get('email')?.touched\"\n />\n @if (userForm.get('email')?.invalid && userForm.get('email')?.touched) {\n <div class=\"field-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n @if (userForm.get('email')?.errors?.['required']) {\n Email address is required\n }\n @if (userForm.get('email')?.errors?.['email']) {\n Please enter a valid email address\n }\n </div>\n }\n </div>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"title\">Job Title</label>\n <input\n id=\"title\"\n type=\"text\"\n class=\"field-input\"\n formControlName=\"title\"\n placeholder=\"Software Engineer\"\n />\n </div>\n </div>\n </div>\n </div>\n <!-- User Settings Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-cog\"></i>\n User Settings\n </h3>\n <p class=\"section-description\">Configure user type and account status</p>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"type\">User Type</label>\n <select id=\"type\" class=\"field-select\" formControlName=\"type\">\n <option value=\"User\">Standard User</option>\n <option value=\"Owner\">System Owner</option>\n </select>\n </div>\n <div class=\"form-field\">\n <div class=\"checkbox-field\">\n <input\n id=\"isActive\"\n type=\"checkbox\"\n class=\"checkbox-input\"\n formControlName=\"isActive\"\n />\n <label class=\"checkbox-label\" for=\"isActive\">\n <div class=\"checkbox-indicator\"></div>\n <div>\n <strong>Active Account</strong>\n <div style=\"font-size: 12px; color: var(--md-on-surface-variant); margin-top: 2px;\">\n User can log in and access the system\n </div>\n </div>\n </label>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <!-- Role Assignment Section -->\n <div class=\"content-section\">\n <div class=\"form-section\">\n <div class=\"section-header\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-user-tag\"></i>\n Role Assignment\n </h3>\n <p class=\"section-description\">Select the roles to assign to this user</p>\n </div>\n <div class=\"roles-grid\">\n @for (role of data?.availableRoles; track role.ID) {\n <div class=\"role-card\"\n [class.selected]=\"selectedRoleIds.has(role.ID)\"\n (click)=\"toggleRole(role.ID)\">\n <div class=\"role-content\">\n <div class=\"checkbox-field\">\n <input\n type=\"checkbox\"\n class=\"checkbox-input\"\n [id]=\"'role-' + role.ID\"\n [checked]=\"selectedRoleIds.has(role.ID)\"\n (change)=\"onRoleToggle(role.ID, $event)\"\n />\n <label class=\"checkbox-label\" [for]=\"'role-' + role.ID\">\n <div class=\"checkbox-indicator\"></div>\n </label>\n </div>\n <div class=\"role-info\">\n <h4 class=\"role-name\">{{ role.Name }}</h4>\n @if (role.Description) {\n <p class=\"role-description\">{{ role.Description }}</p>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- Modal Footer -->\n <div class=\"modal-footer\">\n <button\n type=\"submit\"\n class=\"btn btn-primary\"\n [disabled]=\"userForm.invalid || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n {{ isEditMode ? 'Update User' : 'Create User' }}\n }\n </button>\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n}\n"]}
|