@gloww/gloww 20.0.0-beta.42 → 20.0.0-beta.45

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.
@@ -4,7 +4,7 @@ import { Subject, firstValueFrom, BehaviorSubject, of, combineLatest, forkJoin,
4
4
  import * as i1 from '@angular/common/http';
5
5
  import { HttpClient, HttpParams, HttpEventType, HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi, HttpResponse } from '@angular/common/http';
6
6
  import * as i3 from '@angular/common';
7
- import { Location, PlatformLocation, AsyncPipe, NgClass, isPlatformBrowser, NgStyle, CommonModule, DatePipe } from '@angular/common';
7
+ import { Location, PlatformLocation, AsyncPipe, NgClass, isPlatformBrowser, NgStyle, CommonModule } from '@angular/common';
8
8
  import * as i1$2 from '@angular/material/dialog';
9
9
  import { MAT_DIALOG_DATA, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogConfig, MatDialogModule } from '@angular/material/dialog';
10
10
  import { CdkScrollable } from '@angular/cdk/scrolling';
@@ -16,18 +16,19 @@ import * as i1$1 from '@angular/platform-browser';
16
16
  import * as i5 from '@kolkov/angular-editor';
17
17
  import { AngularEditorModule } from '@kolkov/angular-editor';
18
18
  import { switchMap, map, tap, filter, catchError, take, mergeMap, retryWhen, scan, delay, first, distinctUntilChanged, debounceTime, finalize } from 'rxjs/operators';
19
- import * as i9 from '@angular/material/sort';
19
+ import * as i9$1 from '@angular/material/sort';
20
20
  import { MatSort, MatSortModule } from '@angular/material/sort';
21
- import * as i7 from '@angular/material/paginator';
21
+ import * as i7$1 from '@angular/material/paginator';
22
22
  import { MatPaginator, MatPaginatorIntl, MatPaginatorModule } from '@angular/material/paginator';
23
23
  import * as i10 from '@angular/material/table';
24
24
  import { MatColumnDef, MatTableDataSource, MatTable, MatHeaderCellDef, MatHeaderCell, MatCellDef, MatCell, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow, MatTableModule } from '@angular/material/table';
25
- import * as i8 from '@angular/material/progress-spinner';
25
+ import * as i8$1 from '@angular/material/progress-spinner';
26
26
  import { MatProgressSpinner, MatProgressSpinnerModule } from '@angular/material/progress-spinner';
27
- import * as i5$1 from '@angular/material/card';
27
+ import * as i5$2 from '@angular/material/card';
28
28
  import { MatCard, MatCardContent, MatCardActions, MatCardHeader, MatCardTitle, MatCardModule } from '@angular/material/card';
29
- import * as i6 from '@angular/material/icon';
29
+ import * as i7 from '@angular/material/icon';
30
30
  import { MatIcon, MatIconModule } from '@angular/material/icon';
31
+ import * as i6 from '@angular/material/input';
31
32
  import { MatError, MatFormField, MatInput, MatLabel, MatInputModule, MatSuffix } from '@angular/material/input';
32
33
  import * as i4 from '@ctrl/ngx-codemirror';
33
34
  import { CodemirrorModule, CodemirrorComponent } from '@ctrl/ngx-codemirror';
@@ -39,6 +40,7 @@ import { NgxMatDatetimePickerModule, NgxMatTimepickerComponent, NgxMatNativeDate
39
40
  import moment from 'moment';
40
41
  import * as i1$4 from '@ngx-translate/core';
41
42
  import { TranslateDefaultParser } from '@ngx-translate/core';
43
+ import * as i9 from '@angular/material/select';
42
44
  import { MatSelect, MatOption, MatSelectModule } from '@angular/material/select';
43
45
  import { trigger, state, style, transition, animate } from '@angular/animations';
44
46
  import { MatListItem, MatListItemIcon, MatListItemTitle } from '@angular/material/list';
@@ -55,6 +57,10 @@ import JSZip from 'jszip';
55
57
  import { MatAutocompleteTrigger, MatAutocomplete, MatAutocompleteModule } from '@angular/material/autocomplete';
56
58
  import { MAT_DATE_LOCALE } from '@angular/material/core';
57
59
  import { AngularResizeEventModule } from 'angular-resize-event';
60
+ import * as i5$1 from '@angular/material/checkbox';
61
+ import { MatCheckboxModule } from '@angular/material/checkbox';
62
+ import * as i8 from '@angular/material/radio';
63
+ import { MatRadioModule } from '@angular/material/radio';
58
64
 
59
65
  const API_SERVER_URL = new InjectionToken('ApiServerUrl.config');
60
66
  const GLOWW_APPLI = new InjectionToken('GlowwAppli.config');
@@ -123,6 +129,11 @@ class GlowwService {
123
129
  async noRestSecurity() {
124
130
  return await firstValueFrom(this.http.get(`/admin/ORMS/${this.InternalBasePath.replace(/_mgmt/, "")}/noRestSecurity`));
125
131
  }
132
+ commitUserTask(userTaskId, args) {
133
+ return this.http.post(`/FlowMgmt/UserTask/Commit/${userTaskId}`, args ?? {}, {
134
+ responseType: 'text'
135
+ });
136
+ }
126
137
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: GlowwService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
127
138
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: GlowwService, providedIn: 'root' }); }
128
139
  }
@@ -4171,13 +4182,153 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4171
4182
  type: Injectable
4172
4183
  }], ctorParameters: () => [{ type: GlowwI18nService }] });
4173
4184
 
4185
+ class AssignedUserTaskResponseDialogComponent {
4186
+ constructor(formBuilder, dialogRef, data, sanitizer) {
4187
+ this.formBuilder = formBuilder;
4188
+ this.dialogRef = dialogRef;
4189
+ this.data = data;
4190
+ this.sanitizer = sanitizer;
4191
+ this.externalWindowRef = null;
4192
+ this.choiceFieldNames = [];
4193
+ this.form = this.formBuilder.group({});
4194
+ }
4195
+ ngOnInit() {
4196
+ this.initializeForm();
4197
+ this.initializeExternalContent();
4198
+ }
4199
+ onCancel() {
4200
+ this.externalWindowRef?.close();
4201
+ this.dialogRef.close();
4202
+ }
4203
+ onSubmit() {
4204
+ if (this.form.invalid) {
4205
+ this.form.markAllAsTouched();
4206
+ return;
4207
+ }
4208
+ this.dialogRef.close({ payload: this.buildPayload() });
4209
+ }
4210
+ reopenExternalWindow() {
4211
+ if (!this.data.externalUrl) {
4212
+ return;
4213
+ }
4214
+ this.externalWindowRef = window.open(this.data.externalUrl, '_blank', 'noopener,noreferrer');
4215
+ }
4216
+ control(name) {
4217
+ return this.form.get(name);
4218
+ }
4219
+ choiceControlName(choice) {
4220
+ return this.choiceFieldName(choice);
4221
+ }
4222
+ initializeForm() {
4223
+ switch (this.data.mode) {
4224
+ case 'single-choice':
4225
+ this.form.addControl('choice', new UntypedFormControl(null, Validators.required));
4226
+ break;
4227
+ case 'multi-choice':
4228
+ (this.data.choices ?? []).forEach(choice => {
4229
+ const fieldName = this.choiceFieldName(choice);
4230
+ this.choiceFieldNames.push(fieldName);
4231
+ this.form.addControl(fieldName, new UntypedFormControl(false));
4232
+ });
4233
+ break;
4234
+ case 'adhoc':
4235
+ case 'external':
4236
+ (this.data.fields ?? []).forEach(field => {
4237
+ this.form.addControl(field.name, new UntypedFormControl(this.resolveDefaultValue(field), field.required ? Validators.required : null));
4238
+ });
4239
+ break;
4240
+ }
4241
+ }
4242
+ initializeExternalContent() {
4243
+ if (this.data.mode !== 'external' || !this.data.externalUrl) {
4244
+ return;
4245
+ }
4246
+ if (this.data.externalOpenMode === 'window') {
4247
+ this.reopenExternalWindow();
4248
+ return;
4249
+ }
4250
+ this.externalSafeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.data.externalUrl);
4251
+ }
4252
+ resolveDefaultValue(field) {
4253
+ if (field.defaultValue !== undefined) {
4254
+ return field.defaultValue;
4255
+ }
4256
+ const currentValue = this.data.currentVariables?.[field.name];
4257
+ if (currentValue !== undefined) {
4258
+ return currentValue;
4259
+ }
4260
+ return field.type === 'boolean' ? false : null;
4261
+ }
4262
+ buildPayload() {
4263
+ const payload = {};
4264
+ switch (this.data.mode) {
4265
+ case 'single-choice': {
4266
+ const selectedValue = this.form.getRawValue().choice;
4267
+ const selectedChoice = (this.data.choices ?? []).find(choice => choice.value === selectedValue);
4268
+ payload.response = selectedValue;
4269
+ payload.choice = selectedValue;
4270
+ payload.responseLabel = selectedChoice?.label ?? selectedValue;
4271
+ if (selectedChoice?.fieldName) {
4272
+ payload[selectedChoice.fieldName] = true;
4273
+ }
4274
+ break;
4275
+ }
4276
+ case 'multi-choice': {
4277
+ const selectedChoices = (this.data.choices ?? []).filter(choice => {
4278
+ const fieldName = this.choiceFieldName(choice);
4279
+ return !!this.form.getRawValue()[fieldName];
4280
+ });
4281
+ payload.responses = selectedChoices.map(choice => choice.value);
4282
+ payload.choices = [...payload.responses];
4283
+ payload.responseLabels = selectedChoices.map(choice => choice.label);
4284
+ selectedChoices.forEach(choice => {
4285
+ payload[choice.fieldName ?? choice.value] = true;
4286
+ });
4287
+ break;
4288
+ }
4289
+ case 'adhoc':
4290
+ case 'external':
4291
+ Object.assign(payload, this.form.getRawValue());
4292
+ break;
4293
+ }
4294
+ return payload;
4295
+ }
4296
+ choiceFieldName(choice) {
4297
+ return choice.fieldName ?? `choice_${choice.value.replace(/[^a-zA-Z0-9_]/g, '_')}`;
4298
+ }
4299
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AssignedUserTaskResponseDialogComponent, deps: [{ token: i1$3.UntypedFormBuilder }, { token: i1$2.MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
4300
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: AssignedUserTaskResponseDialogComponent, isStandalone: true, selector: "glw-assigned-user-task-response-dialog", ngImport: i0, template: "<h2 mat-dialog-title>{{ data.title }}</h2>\n\n<mat-dialog-content cdkScrollable>\n @if (data.message) {\n <div class=\"assigned-user-task-response-message\">{{ data.message }}</div>\n }\n\n <form [formGroup]=\"form\" class=\"assigned-user-task-response-form\">\n @if (data.mode === 'single-choice') {\n <mat-radio-group formControlName=\"choice\" class=\"assigned-user-task-response-choices\">\n @for (choice of data.choices ?? []; track choice.value) {\n <mat-radio-button [value]=\"choice.value\">{{ choice.label }}</mat-radio-button>\n }\n </mat-radio-group>\n }\n\n @if (data.mode === 'multi-choice') {\n <div class=\"assigned-user-task-response-choices\">\n @for (choice of data.choices ?? []; track choice.value) {\n <mat-checkbox [formControlName]=\"choiceControlName(choice)\">\n {{ choice.label }}\n </mat-checkbox>\n }\n </div>\n }\n\n @if (data.mode === 'adhoc' || data.mode === 'external') {\n <div class=\"assigned-user-task-response-fields\">\n @for (field of data.fields ?? []; track field.name) {\n <div class=\"assigned-user-task-response-field\">\n @if (field.type === 'boolean') {\n <mat-checkbox [formControlName]=\"field.name\">{{ field.label }}</mat-checkbox>\n }\n\n @if (field.type === 'text' || field.type === 'number' || field.type === 'textarea') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ field.label }}</mat-label>\n @if (field.type === 'textarea') {\n <textarea\n matInput\n [formControlName]=\"field.name\"\n [rows]=\"field.rows ?? 4\"\n [placeholder]=\"field.placeholder ?? ''\"></textarea>\n } @else {\n <input\n matInput\n [type]=\"field.type === 'number' ? 'number' : 'text'\"\n [formControlName]=\"field.name\"\n [placeholder]=\"field.placeholder ?? ''\" />\n }\n </mat-form-field>\n }\n\n @if (field.type === 'select') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ field.label }}</mat-label>\n <mat-select [formControlName]=\"field.name\">\n @for (option of field.options ?? []; track option.value) {\n <mat-option [value]=\"option.value\">{{ option.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @if (field.type === 'date' || field.type === 'datetime') {\n <label class=\"assigned-user-task-response-datetime-label\">{{ field.label }}</label>\n <gloww-datetime\n [mode]=\"field.type === 'date' ? 'date' : 'datetime'\"\n [showSeconds]=\"field.type === 'datetime'\"\n [formControlName]=\"field.name\"></gloww-datetime>\n }\n </div>\n }\n </div>\n }\n\n @if (data.mode === 'external' && data.externalOpenMode !== 'window' && externalSafeUrl) {\n <div class=\"assigned-user-task-response-iframe-wrapper\">\n <iframe class=\"assigned-user-task-response-iframe\" [src]=\"externalSafeUrl\" title=\"User task external response\"></iframe>\n </div>\n }\n\n @if (data.mode === 'external' && data.externalOpenMode === 'window') {\n <div class=\"assigned-user-task-response-window-message\">\n {{ data.externalWindowMessage }}\n </div>\n <button mat-stroked-button type=\"button\" (click)=\"reopenExternalWindow()\">\n <mat-icon>open_in_new</mat-icon>\n {{ data.reopenExternalLabel }}\n </button>\n }\n </form>\n</mat-dialog-content>\n\n<mat-dialog-actions align=\"end\">\n <button mat-button type=\"button\" (click)=\"onCancel()\">{{ data.cancelLabel }}</button>\n <button mat-flat-button color=\"primary\" type=\"button\" (click)=\"onSubmit()\">\n {{ data.mode === 'external' ? data.completeExternalLabel : data.submitLabel }}\n </button>\n</mat-dialog-actions>\n", styles: [":host{display:block}.assigned-user-task-response-message{margin-bottom:16px;white-space:pre-wrap}.assigned-user-task-response-form{display:flex;flex-direction:column;gap:16px;min-width:min(640px,100vw - 64px)}.assigned-user-task-response-choices,.assigned-user-task-response-fields{display:flex;flex-direction:column;gap:12px}.assigned-user-task-response-field{display:flex;flex-direction:column;gap:8px}.assigned-user-task-response-datetime-label{font-weight:600}.assigned-user-task-response-iframe-wrapper{border:1px solid rgba(0,0,0,.12);border-radius:8px;overflow:hidden;min-height:60vh}.assigned-user-task-response-iframe{border:0;display:block;height:60vh;width:100%}.assigned-user-task-response-window-message{color:#000000b3}mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i6.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i6.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i8.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i8.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i9.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: DatetimeComponent, selector: "gloww-datetime", inputs: ["value", "display", "placeHolder", "mode", "showSeconds"] }] }); }
4301
+ }
4302
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AssignedUserTaskResponseDialogComponent, decorators: [{
4303
+ type: Component,
4304
+ args: [{ selector: 'glw-assigned-user-task-response-dialog', standalone: true, imports: [
4305
+ CommonModule,
4306
+ ReactiveFormsModule,
4307
+ CdkScrollable,
4308
+ MatDialogTitle,
4309
+ MatDialogContent,
4310
+ MatDialogActions,
4311
+ MatButtonModule,
4312
+ MatCheckboxModule,
4313
+ MatFormFieldModule,
4314
+ MatIconModule,
4315
+ MatInputModule,
4316
+ MatRadioModule,
4317
+ MatSelectModule,
4318
+ DatetimeComponent
4319
+ ], template: "<h2 mat-dialog-title>{{ data.title }}</h2>\n\n<mat-dialog-content cdkScrollable>\n @if (data.message) {\n <div class=\"assigned-user-task-response-message\">{{ data.message }}</div>\n }\n\n <form [formGroup]=\"form\" class=\"assigned-user-task-response-form\">\n @if (data.mode === 'single-choice') {\n <mat-radio-group formControlName=\"choice\" class=\"assigned-user-task-response-choices\">\n @for (choice of data.choices ?? []; track choice.value) {\n <mat-radio-button [value]=\"choice.value\">{{ choice.label }}</mat-radio-button>\n }\n </mat-radio-group>\n }\n\n @if (data.mode === 'multi-choice') {\n <div class=\"assigned-user-task-response-choices\">\n @for (choice of data.choices ?? []; track choice.value) {\n <mat-checkbox [formControlName]=\"choiceControlName(choice)\">\n {{ choice.label }}\n </mat-checkbox>\n }\n </div>\n }\n\n @if (data.mode === 'adhoc' || data.mode === 'external') {\n <div class=\"assigned-user-task-response-fields\">\n @for (field of data.fields ?? []; track field.name) {\n <div class=\"assigned-user-task-response-field\">\n @if (field.type === 'boolean') {\n <mat-checkbox [formControlName]=\"field.name\">{{ field.label }}</mat-checkbox>\n }\n\n @if (field.type === 'text' || field.type === 'number' || field.type === 'textarea') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ field.label }}</mat-label>\n @if (field.type === 'textarea') {\n <textarea\n matInput\n [formControlName]=\"field.name\"\n [rows]=\"field.rows ?? 4\"\n [placeholder]=\"field.placeholder ?? ''\"></textarea>\n } @else {\n <input\n matInput\n [type]=\"field.type === 'number' ? 'number' : 'text'\"\n [formControlName]=\"field.name\"\n [placeholder]=\"field.placeholder ?? ''\" />\n }\n </mat-form-field>\n }\n\n @if (field.type === 'select') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ field.label }}</mat-label>\n <mat-select [formControlName]=\"field.name\">\n @for (option of field.options ?? []; track option.value) {\n <mat-option [value]=\"option.value\">{{ option.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @if (field.type === 'date' || field.type === 'datetime') {\n <label class=\"assigned-user-task-response-datetime-label\">{{ field.label }}</label>\n <gloww-datetime\n [mode]=\"field.type === 'date' ? 'date' : 'datetime'\"\n [showSeconds]=\"field.type === 'datetime'\"\n [formControlName]=\"field.name\"></gloww-datetime>\n }\n </div>\n }\n </div>\n }\n\n @if (data.mode === 'external' && data.externalOpenMode !== 'window' && externalSafeUrl) {\n <div class=\"assigned-user-task-response-iframe-wrapper\">\n <iframe class=\"assigned-user-task-response-iframe\" [src]=\"externalSafeUrl\" title=\"User task external response\"></iframe>\n </div>\n }\n\n @if (data.mode === 'external' && data.externalOpenMode === 'window') {\n <div class=\"assigned-user-task-response-window-message\">\n {{ data.externalWindowMessage }}\n </div>\n <button mat-stroked-button type=\"button\" (click)=\"reopenExternalWindow()\">\n <mat-icon>open_in_new</mat-icon>\n {{ data.reopenExternalLabel }}\n </button>\n }\n </form>\n</mat-dialog-content>\n\n<mat-dialog-actions align=\"end\">\n <button mat-button type=\"button\" (click)=\"onCancel()\">{{ data.cancelLabel }}</button>\n <button mat-flat-button color=\"primary\" type=\"button\" (click)=\"onSubmit()\">\n {{ data.mode === 'external' ? data.completeExternalLabel : data.submitLabel }}\n </button>\n</mat-dialog-actions>\n", styles: [":host{display:block}.assigned-user-task-response-message{margin-bottom:16px;white-space:pre-wrap}.assigned-user-task-response-form{display:flex;flex-direction:column;gap:16px;min-width:min(640px,100vw - 64px)}.assigned-user-task-response-choices,.assigned-user-task-response-fields{display:flex;flex-direction:column;gap:12px}.assigned-user-task-response-field{display:flex;flex-direction:column;gap:8px}.assigned-user-task-response-datetime-label{font-weight:600}.assigned-user-task-response-iframe-wrapper{border:1px solid rgba(0,0,0,.12);border-radius:8px;overflow:hidden;min-height:60vh}.assigned-user-task-response-iframe{border:0;display:block;height:60vh;width:100%}.assigned-user-task-response-window-message{color:#000000b3}mat-form-field{width:100%}\n"] }]
4320
+ }], ctorParameters: () => [{ type: i1$3.UntypedFormBuilder }, { type: i1$2.MatDialogRef }, { type: undefined, decorators: [{
4321
+ type: Inject,
4322
+ args: [MAT_DIALOG_DATA]
4323
+ }] }, { type: i1$1.DomSanitizer }] });
4324
+
4174
4325
  class AssignedUserTasksComponent {
4175
- constructor(glowwService, authenticationService, i18n, router) {
4326
+ constructor(glowwService, authenticationService, i18n, dialog) {
4176
4327
  this.glowwService = glowwService;
4177
4328
  this.authenticationService = authenticationService;
4178
4329
  this.i18n = i18n;
4179
- this.router = router;
4180
- this.displayedColumns = ['action', 'task', 'definition', 'execution', 'status', 'message', 'assignment'];
4330
+ this.dialog = dialog;
4331
+ this.displayedColumns = ['action', 'task', 'definition', 'status', 'message', 'assignment'];
4181
4332
  this.dataSource = new MatTableDataSource([]);
4182
4333
  this.loading = false;
4183
4334
  this.errorMessage = '';
@@ -4203,28 +4354,49 @@ class AssignedUserTasksComponent {
4203
4354
  }), finalize(() => {
4204
4355
  this.loading = false;
4205
4356
  })).subscribe((items) => {
4206
- this.dataSource.data = items ?? [];
4357
+ this.dataSource.data = (items ?? []).filter(item => this.isPendingTask(item));
4207
4358
  if (this.paginator) {
4208
4359
  this.dataSource.paginator = this.paginator;
4209
4360
  }
4210
4361
  });
4211
4362
  }
4212
- openExecution(taskAssignment) {
4213
- const executionId = taskAssignment.IsCandidate?.HasAsExecutionStep?.ExecutionID;
4214
- if (!executionId) {
4363
+ async respondToTask(taskAssignment) {
4364
+ const userTaskId = taskAssignment.UserTaskId ?? taskAssignment.IsCandidate?.USERTASKID;
4365
+ if (!userTaskId) {
4215
4366
  return;
4216
4367
  }
4217
- this.router.navigate(['/execution', executionId]);
4218
- }
4219
- openExecutionSteps(taskAssignment) {
4220
- const executionId = taskAssignment.IsCandidate?.HasAsExecutionStep?.ExecutionID;
4221
- if (!executionId) {
4222
- return;
4368
+ this.respondingTaskId = userTaskId;
4369
+ try {
4370
+ const task = await firstValueFrom(this.glowwService.getBPMN2_UserTask(userTaskId));
4371
+ const dialogData = this.buildDialogData(task);
4372
+ const dialogRef = this.dialog.open(AssignedUserTaskResponseDialogComponent, {
4373
+ width: 'min(960px, calc(100vw - 24px))',
4374
+ maxWidth: 'calc(100vw - 24px)',
4375
+ maxHeight: 'calc(100vh - 24px)',
4376
+ disableClose: true,
4377
+ autoFocus: false,
4378
+ data: dialogData
4379
+ });
4380
+ const result = await firstValueFrom(dialogRef.afterClosed());
4381
+ if (!result?.payload) {
4382
+ return;
4383
+ }
4384
+ const currentUser = this.authenticationService.currentUserValue;
4385
+ const payload = {
4386
+ ...this.parseVariables(task.VARIABLES),
4387
+ ...result.payload,
4388
+ __Username: currentUser?.username ?? '',
4389
+ __UserTaskId: userTaskId
4390
+ };
4391
+ await firstValueFrom(this.glowwService.commitUserTask(userTaskId, payload));
4392
+ this.loadAssignments();
4393
+ }
4394
+ catch (error) {
4395
+ this.errorMessage = error?.error?.message || error?.message || `${error}`;
4396
+ }
4397
+ finally {
4398
+ this.respondingTaskId = undefined;
4223
4399
  }
4224
- this.router.navigate(['/executionsteps', executionId]);
4225
- }
4226
- hasExecution(taskAssignment) {
4227
- return !!taskAssignment.IsCandidate?.HasAsExecutionStep?.ExecutionID;
4228
4400
  }
4229
4401
  getTaskLabel(taskAssignment) {
4230
4402
  return taskAssignment.IsCandidate?.Title
@@ -4240,16 +4412,6 @@ class AssignedUserTasksComponent {
4240
4412
  ? `${execution.DefinitionID} (${execution.ProcessID})`
4241
4413
  : execution.DefinitionID;
4242
4414
  }
4243
- getExecutionLabel(taskAssignment) {
4244
- const step = taskAssignment.IsCandidate?.HasAsExecutionStep;
4245
- if (!step?.ExecutionID) {
4246
- return '';
4247
- }
4248
- const processStepId = step.ProcessStepID ?? taskAssignment.IsCandidate?.ProcessStepID;
4249
- return processStepId
4250
- ? `${step.ExecutionID} / ${processStepId}`
4251
- : `${step.ExecutionID}`;
4252
- }
4253
4415
  getStatusLabel(taskAssignment) {
4254
4416
  return taskAssignment.IsCandidate?.Statut
4255
4417
  || taskAssignment.STATUT
@@ -4261,6 +4423,10 @@ class AssignedUserTasksComponent {
4261
4423
  : this.t('GLOWW.CANDIDATE', 'Candidate');
4262
4424
  return `${taskAssignment.Domain ? `${taskAssignment.Domain}\\` : ''}${taskAssignment.Username ?? ''} (${type})`;
4263
4425
  }
4426
+ canRespond(taskAssignment) {
4427
+ return !!(taskAssignment.UserTaskId ?? taskAssignment.IsCandidate?.USERTASKID)
4428
+ && this.isPendingTask(taskAssignment);
4429
+ }
4264
4430
  buildQuery() {
4265
4431
  const currentUser = this.authenticationService.currentUserValue;
4266
4432
  const query = {};
@@ -4270,16 +4436,19 @@ class AssignedUserTasksComponent {
4270
4436
  if (currentUser?.domain) {
4271
4437
  query.Domain = currentUser.domain;
4272
4438
  }
4439
+ query.STATUT = 'WAITING';
4273
4440
  return query;
4274
4441
  }
4442
+ isPendingTask(taskAssignment) {
4443
+ const status = this.getStatusLabel(taskAssignment).toUpperCase();
4444
+ return status === '' || status === 'RUNNING';
4445
+ }
4275
4446
  getSortValue(item, property) {
4276
4447
  switch (property) {
4277
4448
  case 'task':
4278
4449
  return this.getTaskLabel(item).toLowerCase();
4279
4450
  case 'definition':
4280
4451
  return this.getDefinitionLabel(item).toLowerCase();
4281
- case 'execution':
4282
- return item.IsCandidate?.HasAsExecutionStep?.ExecutionID ?? 0;
4283
4452
  case 'status':
4284
4453
  return this.getStatusLabel(item).toLowerCase();
4285
4454
  case 'message':
@@ -4290,26 +4459,232 @@ class AssignedUserTasksComponent {
4290
4459
  return item.AssignmentID ?? 0;
4291
4460
  }
4292
4461
  }
4293
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AssignedUserTasksComponent, deps: [{ token: 'glowwService' }, { token: AuthenticationService }, { token: GlowwI18nService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
4294
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: AssignedUserTasksComponent, isStandalone: true, selector: "glw-assigned-user-tasks", viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true, static: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], ngImport: i0, template: "<mat-card class=\"assigned-user-tasks-card\" appearance=\"outlined\">\n <mat-card-header class=\"assigned-user-tasks-header\">\n <mat-card-title>{{ t('GLOWW.MY_ASSIGNED_TASKS', 'My assigned tasks') }}</mat-card-title>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"loadAssignments()\">\n <mat-icon>refresh</mat-icon>\n {{ t('COMMON.REFRESH', 'Refresh') }}\n </button>\n </mat-card-header>\n\n <mat-card-content>\n @if (errorMessage) {\n <div class=\"assigned-user-tasks-error\">\n {{ t('GLOWW.UNABLE_TO_QUERY', 'Unable to query') }}: {{ errorMessage }}\n </div>\n }\n\n @if (loading) {\n <div class=\"assigned-user-tasks-loading\">\n <mat-spinner diameter=\"36\"></mat-spinner>\n </div>\n }\n\n @if (!loading) {\n <div class=\"assigned-user-tasks-table-wrapper\">\n <table mat-table [dataSource]=\"dataSource\" matSort>\n <ng-container matColumnDef=\"action\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let obj\">\n <button mat-icon-button type=\"button\" (click)=\"openExecution(obj)\" [disabled]=\"!hasExecution(obj)\" [attr.aria-label]=\"t('LISTS.DISPLAY', 'Display')\">\n <i class=\"fal fa-eye\"></i>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"openExecutionSteps(obj)\" [disabled]=\"!hasExecution(obj)\" [attr.aria-label]=\"t('LISTS.STEPS', 'Steps')\">\n <i class=\"fal fa-list-check\"></i>\n </button>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"task\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"task\">{{ t('LISTS.TASK', 'Task') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getTaskLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"definition\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"definition\">{{ t('LISTS.DEFINITION_ID', 'Definition ID') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getDefinitionLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"execution\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"execution\">{{ t('LISTS.EXECUTION', 'Execution') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getExecutionLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"status\">{{ t('LISTS.STATUS', 'Status') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getStatusLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"message\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"message\">{{ t('LISTS.MESSAGE', 'Message') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ obj.IsCandidate?.MESSAGE }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"assignment\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"assignment\">{{ t('LISTS.ASSIGNED_TO', 'Assigned to') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getAssignmentLabel(obj) }}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns\"></tr>\n </table>\n </div>\n\n @if (!dataSource.data.length) {\n <div class=\"assigned-user-tasks-empty\">\n {{ t('GLOWW.NO_ASSIGNED_TASKS', 'No task is currently assigned to you.') }}\n </div>\n }\n }\n </mat-card-content>\n\n <mat-card-actions>\n <mat-paginator [pageSize]=\"10\" [pageSizeOptions]=\"[10, 20, 50, 100]\" [showFirstLastButtons]=\"true\"></mat-paginator>\n </mat-card-actions>\n</mat-card>\n", styles: [":host{display:block}.assigned-user-tasks-card{margin:16px}.assigned-user-tasks-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-bottom:12px}.assigned-user-tasks-loading,.assigned-user-tasks-empty,.assigned-user-tasks-error{padding:16px 0}.assigned-user-tasks-table-wrapper{overflow:auto}table{width:100%}th:first-child,td:first-child{width:88px;white-space:nowrap}button[mat-icon-button]{margin-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i5$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i5$1.MatCardActions, selector: "mat-card-actions", inputs: ["align"], exportAs: ["matCardActions"] }, { kind: "directive", type: i5$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i5$1.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i5$1.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i7.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i8.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i9.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i9.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i10.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i10.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i10.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i10.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i10.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i10.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i10.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i10.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i10.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i10.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }] }); }
4462
+ buildDialogData(task) {
4463
+ const jsInfo = this.parseMaybeJson(task.JSINFO);
4464
+ const formInfo = this.parseMaybeJson(task.Form);
4465
+ const currentVariables = this.parseVariables(task.VARIABLES);
4466
+ const fields = this.normalizeFields(jsInfo, formInfo, currentVariables);
4467
+ const externalConfig = this.resolveExternalConfig(task, jsInfo, formInfo);
4468
+ if (externalConfig) {
4469
+ return {
4470
+ mode: 'external',
4471
+ title: task.DialogTitle || task.Title || this.t('GLOWW.RESPOND_TO_TASK', 'Respond to task'),
4472
+ message: task.MESSAGE,
4473
+ fields,
4474
+ currentVariables,
4475
+ externalUrl: externalConfig.url,
4476
+ externalOpenMode: externalConfig.openMode,
4477
+ submitLabel: this.t('COMMON.SAVE', 'Save'),
4478
+ cancelLabel: this.t('COMMON.CANCEL', 'Cancel'),
4479
+ reopenExternalLabel: this.t('GLOWW.REOPEN_EXTERNAL_FORM', 'Reopen external form'),
4480
+ completeExternalLabel: this.t('GLOWW.COMPLETE_TASK', 'Complete task'),
4481
+ externalWindowMessage: this.t('GLOWW.COMPLETE_EXTERNAL_FORM_THEN_VALIDATE', 'Complete the external form, then validate the task here.')
4482
+ };
4483
+ }
4484
+ if ((task.FormType || '1') === '2') {
4485
+ return {
4486
+ mode: 'multi-choice',
4487
+ title: task.DialogTitle || task.Title || this.t('GLOWW.RESPOND_TO_TASK', 'Respond to task'),
4488
+ message: task.MESSAGE,
4489
+ choices: this.parseChoices(task.CHOICES),
4490
+ submitLabel: this.t('GLOWW.SUBMIT_RESPONSE', 'Submit response'),
4491
+ cancelLabel: this.t('COMMON.CANCEL', 'Cancel'),
4492
+ reopenExternalLabel: '',
4493
+ completeExternalLabel: '',
4494
+ externalWindowMessage: ''
4495
+ };
4496
+ }
4497
+ if ((task.FormType || '1') === '1' && (task.CHOICES || '').trim()) {
4498
+ return {
4499
+ mode: 'single-choice',
4500
+ title: task.DialogTitle || task.Title || this.t('GLOWW.RESPOND_TO_TASK', 'Respond to task'),
4501
+ message: task.MESSAGE,
4502
+ choices: this.parseChoices(task.CHOICES),
4503
+ submitLabel: this.t('GLOWW.SUBMIT_RESPONSE', 'Submit response'),
4504
+ cancelLabel: this.t('COMMON.CANCEL', 'Cancel'),
4505
+ reopenExternalLabel: '',
4506
+ completeExternalLabel: '',
4507
+ externalWindowMessage: ''
4508
+ };
4509
+ }
4510
+ return {
4511
+ mode: 'adhoc',
4512
+ title: task.DialogTitle || task.Title || this.t('GLOWW.RESPOND_TO_TASK', 'Respond to task'),
4513
+ message: task.MESSAGE,
4514
+ fields: fields.length > 0 ? fields : [{
4515
+ name: 'response',
4516
+ label: this.t('GLOWW.RESPONSE', 'Response'),
4517
+ type: 'textarea',
4518
+ rows: 6
4519
+ }],
4520
+ currentVariables,
4521
+ submitLabel: this.t('GLOWW.SUBMIT_RESPONSE', 'Submit response'),
4522
+ cancelLabel: this.t('COMMON.CANCEL', 'Cancel'),
4523
+ reopenExternalLabel: '',
4524
+ completeExternalLabel: '',
4525
+ externalWindowMessage: ''
4526
+ };
4527
+ }
4528
+ parseChoices(rawChoices) {
4529
+ if (!rawChoices?.trim()) {
4530
+ return [];
4531
+ }
4532
+ const parsedJson = this.parseMaybeJson(rawChoices);
4533
+ if (Array.isArray(parsedJson)) {
4534
+ return parsedJson
4535
+ .map(choice => this.normalizeChoice(choice))
4536
+ .filter((choice) => !!choice);
4537
+ }
4538
+ return rawChoices
4539
+ .split(/\r?\n/)
4540
+ .map(line => line.trim())
4541
+ .filter(line => !!line)
4542
+ .map(line => this.normalizeChoice(line))
4543
+ .filter((choice) => !!choice);
4544
+ }
4545
+ normalizeChoice(choice) {
4546
+ if (!choice && choice !== 0) {
4547
+ return null;
4548
+ }
4549
+ if (typeof choice === 'object') {
4550
+ const value = `${choice.value ?? choice.key ?? choice.id ?? choice.name ?? choice.label ?? choice}`;
4551
+ const label = `${choice.label ?? choice.name ?? value}`;
4552
+ return {
4553
+ value,
4554
+ label,
4555
+ fieldName: choice.fieldName ?? choice.name
4556
+ };
4557
+ }
4558
+ const text = `${choice}`.trim();
4559
+ const match = text.match(/^([^|=;:]+)\s*[|=;:]\s*(.+)$/);
4560
+ if (!match) {
4561
+ return { value: text, label: text };
4562
+ }
4563
+ const value = match[1].trim();
4564
+ const label = match[2].trim();
4565
+ return {
4566
+ value,
4567
+ label,
4568
+ fieldName: value.replace(/[^a-zA-Z0-9_]/g, '_')
4569
+ };
4570
+ }
4571
+ parseMaybeJson(rawValue) {
4572
+ if (!rawValue?.trim()) {
4573
+ return null;
4574
+ }
4575
+ try {
4576
+ return JSON.parse(rawValue);
4577
+ }
4578
+ catch {
4579
+ return null;
4580
+ }
4581
+ }
4582
+ parseVariables(rawValue) {
4583
+ const parsed = this.parseMaybeJson(rawValue);
4584
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
4585
+ ? parsed
4586
+ : {};
4587
+ }
4588
+ normalizeFields(jsInfo, formInfo, currentVariables) {
4589
+ const rawFields = Array.isArray(jsInfo)
4590
+ ? jsInfo
4591
+ : Array.isArray(jsInfo?.fields)
4592
+ ? jsInfo.fields
4593
+ : Array.isArray(formInfo)
4594
+ ? formInfo
4595
+ : Array.isArray(formInfo?.fields)
4596
+ ? formInfo.fields
4597
+ : [];
4598
+ return rawFields
4599
+ .map((field) => this.normalizeField(field, currentVariables))
4600
+ .filter((field) => !!field);
4601
+ }
4602
+ normalizeField(field, currentVariables) {
4603
+ if (!field) {
4604
+ return null;
4605
+ }
4606
+ if (typeof field === 'string') {
4607
+ return {
4608
+ name: field,
4609
+ label: field,
4610
+ type: 'text',
4611
+ defaultValue: currentVariables[field]
4612
+ };
4613
+ }
4614
+ const typeMap = {
4615
+ string: 'text',
4616
+ text: 'text',
4617
+ textarea: 'textarea',
4618
+ memo: 'textarea',
4619
+ number: 'number',
4620
+ integer: 'number',
4621
+ bool: 'boolean',
4622
+ boolean: 'boolean',
4623
+ select: 'select',
4624
+ choice: 'select',
4625
+ date: 'date',
4626
+ datetime: 'datetime'
4627
+ };
4628
+ const name = field.name ?? field.key ?? field.id;
4629
+ if (!name) {
4630
+ return null;
4631
+ }
4632
+ return {
4633
+ name,
4634
+ label: field.label ?? field.title ?? name,
4635
+ type: typeMap[(field.type ?? 'text').toString().toLowerCase()] ?? 'text',
4636
+ required: !!field.required,
4637
+ rows: field.rows,
4638
+ placeholder: field.placeholder,
4639
+ defaultValue: currentVariables[name] ?? field.defaultValue ?? field.value,
4640
+ options: Array.isArray(field.options)
4641
+ ? field.options.map((option) => ({
4642
+ value: option?.value ?? option?.key ?? option,
4643
+ label: option?.label ?? option?.name ?? option?.value ?? option
4644
+ }))
4645
+ : undefined
4646
+ };
4647
+ }
4648
+ resolveExternalConfig(task, jsInfo, formInfo) {
4649
+ const configCandidate = [jsInfo, formInfo].find(candidate => candidate && typeof candidate === 'object' && !Array.isArray(candidate));
4650
+ const directUrl = `${configCandidate?.url ?? configCandidate?.href ?? configCandidate?.externalUrl ?? task.Form ?? task.JSINFO ?? ''}`.trim();
4651
+ if (!this.looksLikeUrl(directUrl)) {
4652
+ return null;
4653
+ }
4654
+ const openModeValue = `${configCandidate?.openMode ?? configCandidate?.openIn ?? configCandidate?.target ?? ''}`.toLowerCase();
4655
+ const openMode = configCandidate?.newWindow === true
4656
+ || openModeValue === 'window'
4657
+ || openModeValue === 'new-window'
4658
+ || openModeValue === '_blank'
4659
+ ? 'window'
4660
+ : 'iframe';
4661
+ return {
4662
+ url: directUrl,
4663
+ openMode
4664
+ };
4665
+ }
4666
+ looksLikeUrl(value) {
4667
+ return !!value && /^(https?:\/\/|\/)/i.test(value.trim());
4668
+ }
4669
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AssignedUserTasksComponent, deps: [{ token: 'glowwService' }, { token: AuthenticationService }, { token: GlowwI18nService }, { token: i1$2.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
4670
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: AssignedUserTasksComponent, isStandalone: true, selector: "glw-assigned-user-tasks", viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true, static: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], ngImport: i0, template: "<mat-card class=\"assigned-user-tasks-card\" appearance=\"outlined\">\n <mat-card-header class=\"assigned-user-tasks-header\">\n <mat-card-title>{{ t('GLOWW.MY_ASSIGNED_TASKS', 'My assigned tasks') }}</mat-card-title>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"loadAssignments()\">\n <mat-icon>refresh</mat-icon>\n {{ t('COMMON.REFRESH', 'Refresh') }}\n </button>\n </mat-card-header>\n\n <mat-card-content>\n @if (errorMessage) {\n <div class=\"assigned-user-tasks-error\">\n {{ t('GLOWW.UNABLE_TO_QUERY', 'Unable to query') }}: {{ errorMessage }}\n </div>\n }\n\n @if (loading) {\n <div class=\"assigned-user-tasks-loading\">\n <mat-spinner diameter=\"36\"></mat-spinner>\n </div>\n }\n\n @if (!loading) {\n <div class=\"assigned-user-tasks-table-wrapper\">\n <table mat-table [dataSource]=\"dataSource\" matSort>\n <ng-container matColumnDef=\"action\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let obj\">\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n (click)=\"respondToTask(obj)\"\n [disabled]=\"!canRespond(obj) || respondingTaskId === (obj.UserTaskId ?? obj.IsCandidate?.USERTASKID)\">\n <i class=\"fal fa-reply\"></i>\n {{ t('GLOWW.RESPOND', 'Respond') }}\n </button>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"task\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"task\">{{ t('LISTS.TASK', 'Task') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getTaskLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"definition\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"definition\">{{ t('LISTS.DEFINITION_ID', 'Definition ID') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getDefinitionLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"status\">{{ t('LISTS.STATUS', 'Status') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getStatusLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"message\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"message\">{{ t('LISTS.MESSAGE', 'Message') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ obj.IsCandidate?.MESSAGE }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"assignment\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"assignment\">{{ t('LISTS.ASSIGNED_TO', 'Assigned to') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getAssignmentLabel(obj) }}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns\"></tr>\n </table>\n </div>\n\n @if (!dataSource.data.length) {\n <div class=\"assigned-user-tasks-empty\">\n {{ t('GLOWW.NO_ASSIGNED_TASKS', 'No task is currently assigned to you.') }}\n </div>\n }\n }\n </mat-card-content>\n\n <mat-card-actions>\n <mat-paginator [pageSize]=\"10\" [pageSizeOptions]=\"[10, 20, 50, 100]\" [showFirstLastButtons]=\"true\"></mat-paginator>\n </mat-card-actions>\n</mat-card>\n", styles: [":host{display:block}.assigned-user-tasks-card{margin:16px}.assigned-user-tasks-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-bottom:12px}.assigned-user-tasks-loading,.assigned-user-tasks-empty,.assigned-user-tasks-error{padding:16px 0}.assigned-user-tasks-table-wrapper{overflow:auto}table{width:100%}th:first-child,td:first-child{width:140px;white-space:nowrap}button[mat-stroked-button]{min-width:110px}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i5$2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i5$2.MatCardActions, selector: "mat-card-actions", inputs: ["align"], exportAs: ["matCardActions"] }, { kind: "directive", type: i5$2.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i5$2.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i5$2.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i8$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i9$1.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i9$1.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i10.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i10.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i10.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i10.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i10.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i10.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i10.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i10.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i10.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i10.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }] }); }
4295
4671
  }
4296
4672
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AssignedUserTasksComponent, decorators: [{
4297
4673
  type: Component,
4298
4674
  args: [{ selector: 'glw-assigned-user-tasks', imports: [
4299
- RouterLink,
4300
- DatePipe,
4301
4675
  MatButtonModule,
4302
4676
  MatCardModule,
4303
4677
  MatIconModule,
4678
+ MatDialogModule,
4304
4679
  MatPaginatorModule,
4305
4680
  MatProgressSpinnerModule,
4306
4681
  MatSortModule,
4307
4682
  MatTableModule
4308
- ], template: "<mat-card class=\"assigned-user-tasks-card\" appearance=\"outlined\">\n <mat-card-header class=\"assigned-user-tasks-header\">\n <mat-card-title>{{ t('GLOWW.MY_ASSIGNED_TASKS', 'My assigned tasks') }}</mat-card-title>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"loadAssignments()\">\n <mat-icon>refresh</mat-icon>\n {{ t('COMMON.REFRESH', 'Refresh') }}\n </button>\n </mat-card-header>\n\n <mat-card-content>\n @if (errorMessage) {\n <div class=\"assigned-user-tasks-error\">\n {{ t('GLOWW.UNABLE_TO_QUERY', 'Unable to query') }}: {{ errorMessage }}\n </div>\n }\n\n @if (loading) {\n <div class=\"assigned-user-tasks-loading\">\n <mat-spinner diameter=\"36\"></mat-spinner>\n </div>\n }\n\n @if (!loading) {\n <div class=\"assigned-user-tasks-table-wrapper\">\n <table mat-table [dataSource]=\"dataSource\" matSort>\n <ng-container matColumnDef=\"action\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let obj\">\n <button mat-icon-button type=\"button\" (click)=\"openExecution(obj)\" [disabled]=\"!hasExecution(obj)\" [attr.aria-label]=\"t('LISTS.DISPLAY', 'Display')\">\n <i class=\"fal fa-eye\"></i>\n </button>\n <button mat-icon-button type=\"button\" (click)=\"openExecutionSteps(obj)\" [disabled]=\"!hasExecution(obj)\" [attr.aria-label]=\"t('LISTS.STEPS', 'Steps')\">\n <i class=\"fal fa-list-check\"></i>\n </button>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"task\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"task\">{{ t('LISTS.TASK', 'Task') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getTaskLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"definition\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"definition\">{{ t('LISTS.DEFINITION_ID', 'Definition ID') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getDefinitionLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"execution\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"execution\">{{ t('LISTS.EXECUTION', 'Execution') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getExecutionLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"status\">{{ t('LISTS.STATUS', 'Status') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getStatusLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"message\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"message\">{{ t('LISTS.MESSAGE', 'Message') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ obj.IsCandidate?.MESSAGE }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"assignment\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"assignment\">{{ t('LISTS.ASSIGNED_TO', 'Assigned to') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getAssignmentLabel(obj) }}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns\"></tr>\n </table>\n </div>\n\n @if (!dataSource.data.length) {\n <div class=\"assigned-user-tasks-empty\">\n {{ t('GLOWW.NO_ASSIGNED_TASKS', 'No task is currently assigned to you.') }}\n </div>\n }\n }\n </mat-card-content>\n\n <mat-card-actions>\n <mat-paginator [pageSize]=\"10\" [pageSizeOptions]=\"[10, 20, 50, 100]\" [showFirstLastButtons]=\"true\"></mat-paginator>\n </mat-card-actions>\n</mat-card>\n", styles: [":host{display:block}.assigned-user-tasks-card{margin:16px}.assigned-user-tasks-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-bottom:12px}.assigned-user-tasks-loading,.assigned-user-tasks-empty,.assigned-user-tasks-error{padding:16px 0}.assigned-user-tasks-table-wrapper{overflow:auto}table{width:100%}th:first-child,td:first-child{width:88px;white-space:nowrap}button[mat-icon-button]{margin-right:4px}\n"] }]
4683
+ ], template: "<mat-card class=\"assigned-user-tasks-card\" appearance=\"outlined\">\n <mat-card-header class=\"assigned-user-tasks-header\">\n <mat-card-title>{{ t('GLOWW.MY_ASSIGNED_TASKS', 'My assigned tasks') }}</mat-card-title>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"loadAssignments()\">\n <mat-icon>refresh</mat-icon>\n {{ t('COMMON.REFRESH', 'Refresh') }}\n </button>\n </mat-card-header>\n\n <mat-card-content>\n @if (errorMessage) {\n <div class=\"assigned-user-tasks-error\">\n {{ t('GLOWW.UNABLE_TO_QUERY', 'Unable to query') }}: {{ errorMessage }}\n </div>\n }\n\n @if (loading) {\n <div class=\"assigned-user-tasks-loading\">\n <mat-spinner diameter=\"36\"></mat-spinner>\n </div>\n }\n\n @if (!loading) {\n <div class=\"assigned-user-tasks-table-wrapper\">\n <table mat-table [dataSource]=\"dataSource\" matSort>\n <ng-container matColumnDef=\"action\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let obj\">\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n (click)=\"respondToTask(obj)\"\n [disabled]=\"!canRespond(obj) || respondingTaskId === (obj.UserTaskId ?? obj.IsCandidate?.USERTASKID)\">\n <i class=\"fal fa-reply\"></i>\n {{ t('GLOWW.RESPOND', 'Respond') }}\n </button>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"task\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"task\">{{ t('LISTS.TASK', 'Task') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getTaskLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"definition\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"definition\">{{ t('LISTS.DEFINITION_ID', 'Definition ID') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getDefinitionLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"status\">{{ t('LISTS.STATUS', 'Status') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getStatusLabel(obj) }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"message\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"message\">{{ t('LISTS.MESSAGE', 'Message') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ obj.IsCandidate?.MESSAGE }}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"assignment\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header=\"assignment\">{{ t('LISTS.ASSIGNED_TO', 'Assigned to') }}</th>\n <td mat-cell *matCellDef=\"let obj\">{{ getAssignmentLabel(obj) }}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns\"></tr>\n </table>\n </div>\n\n @if (!dataSource.data.length) {\n <div class=\"assigned-user-tasks-empty\">\n {{ t('GLOWW.NO_ASSIGNED_TASKS', 'No task is currently assigned to you.') }}\n </div>\n }\n }\n </mat-card-content>\n\n <mat-card-actions>\n <mat-paginator [pageSize]=\"10\" [pageSizeOptions]=\"[10, 20, 50, 100]\" [showFirstLastButtons]=\"true\"></mat-paginator>\n </mat-card-actions>\n</mat-card>\n", styles: [":host{display:block}.assigned-user-tasks-card{margin:16px}.assigned-user-tasks-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-bottom:12px}.assigned-user-tasks-loading,.assigned-user-tasks-empty,.assigned-user-tasks-error{padding:16px 0}.assigned-user-tasks-table-wrapper{overflow:auto}table{width:100%}th:first-child,td:first-child{width:140px;white-space:nowrap}button[mat-stroked-button]{min-width:110px}\n"] }]
4309
4684
  }], ctorParameters: () => [{ type: undefined, decorators: [{
4310
4685
  type: Inject,
4311
4686
  args: ['glowwService']
4312
- }] }, { type: AuthenticationService }, { type: GlowwI18nService }, { type: i2.Router }], propDecorators: { sort: [{
4687
+ }] }, { type: AuthenticationService }, { type: GlowwI18nService }, { type: i1$2.MatDialog }], propDecorators: { sort: [{
4313
4688
  type: ViewChild,
4314
4689
  args: [MatSort, { static: true }]
4315
4690
  }], paginator: [{