ng-configcat-publicapi-ui 2.3.2 → 2.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/bundles/ng-configcat-publicapi-ui.umd.js +1029 -870
  2. package/bundles/ng-configcat-publicapi-ui.umd.js.map +1 -1
  3. package/esm2015/lib/_services/form-helper.js +3 -3
  4. package/esm2015/lib/_services/public-api.service.js +9 -11
  5. package/esm2015/lib/_services/setting-list-form-helper.js +25 -18
  6. package/esm2015/lib/authorization/authorization.component.js +23 -18
  7. package/esm2015/lib/base/base.component.js +18 -16
  8. package/esm2015/lib/configuration.js +7 -4
  9. package/esm2015/lib/dialogs/concurrent-dialog/concurrent-dialog.component.js +15 -12
  10. package/esm2015/lib/dialogs/expand-dialog/expand-dialog.component.js +31 -20
  11. package/esm2015/lib/dialogs/reason-dialog/reason-dialog.component.js +19 -14
  12. package/esm2015/lib/dialogs/save-snackbar/save-snackbar.component.js +12 -9
  13. package/esm2015/lib/ng-configcat-publicapi-ui.module.js +116 -57
  14. package/esm2015/lib/selects/config-select/config-select.component.js +36 -23
  15. package/esm2015/lib/selects/environment-select/environment-select.component.js +23 -16
  16. package/esm2015/lib/selects/product-select/product-select.component.js +21 -15
  17. package/esm2015/lib/selects/setting-select/setting-select.component.js +23 -16
  18. package/esm2015/lib/setting-item/setting-item.component.js +46 -31
  19. package/esm2015/lib/setting-list/setting/rollout-percentage-items/rollout-percentage-items.component.js +32 -14
  20. package/esm2015/lib/setting-list/setting/rollout-rules/rollout-rules.component.js +53 -26
  21. package/esm2015/lib/setting-list/setting/setting.component.js +45 -26
  22. package/esm2015/lib/setting-list/setting/value/value.component.js +24 -16
  23. package/esm2015/lib/setting-list/setting-list.component.js +43 -27
  24. package/esm2015/ng-configcat-publicapi-ui.js +1 -15
  25. package/fesm2015/ng-configcat-publicapi-ui.js +986 -828
  26. package/fesm2015/ng-configcat-publicapi-ui.js.map +1 -1
  27. package/lib/_services/form-helper.d.ts +1 -1
  28. package/lib/_services/public-api.service.d.ts +10 -7
  29. package/lib/_services/setting-list-form-helper.d.ts +5 -2
  30. package/lib/authorization/authorization.component.d.ts +4 -1
  31. package/lib/base/base.component.d.ts +6 -3
  32. package/lib/configuration.d.ts +3 -0
  33. package/lib/dialogs/concurrent-dialog/concurrent-dialog.component.d.ts +3 -0
  34. package/lib/dialogs/expand-dialog/expand-dialog.component.d.ts +7 -3
  35. package/lib/dialogs/reason-dialog/reason-dialog.component.d.ts +3 -0
  36. package/lib/dialogs/save-snackbar/save-snackbar.component.d.ts +3 -0
  37. package/lib/ng-configcat-publicapi-ui.module.d.ts +38 -0
  38. package/lib/selects/config-select/config-select.component.d.ts +3 -0
  39. package/lib/selects/environment-select/environment-select.component.d.ts +3 -0
  40. package/lib/selects/product-select/product-select.component.d.ts +3 -0
  41. package/lib/selects/setting-select/setting-select.component.d.ts +3 -0
  42. package/lib/setting-item/setting-item.component.d.ts +3 -0
  43. package/lib/setting-list/setting/rollout-percentage-items/rollout-percentage-items.component.d.ts +7 -2
  44. package/lib/setting-list/setting/rollout-rules/rollout-rules.component.d.ts +10 -4
  45. package/lib/setting-list/setting/setting.component.d.ts +3 -0
  46. package/lib/setting-list/setting/value/value.component.d.ts +3 -0
  47. package/lib/setting-list/setting-list.component.d.ts +5 -1
  48. package/ng-configcat-publicapi-ui.d.ts +1 -14
  49. package/package.json +8 -10
  50. package/ng-configcat-publicapi-ui.metadata.json +0 -1
@@ -1,28 +1,41 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Optional, Component, Input, EventEmitter, Output, forwardRef, Inject, ChangeDetectorRef, NgModule } from '@angular/core';
3
- import * as i1 from '@angular/common/http';
4
- import { HttpClient, HttpClientModule } from '@angular/common/http';
2
+ import { Injectable, Optional, Component, Input, Inject, EventEmitter, Output, forwardRef, NgModule } from '@angular/core';
5
3
  import { MeService, Configuration as Configuration$1, ProductsService, ConfigsService, EnvironmentsService, FeatureFlagsSettingsService, FeatureFlagSettingValuesService, IntegrationLinksService, SettingType, RolloutRuleComparator, ApiModule } from 'ng-configcat-publicapi';
6
- import { throwError } from 'rxjs';
4
+ import * as i1 from '@angular/common/http';
5
+ import { throwError, of } from 'rxjs';
7
6
  import { map, catchError } from 'rxjs/operators';
8
- import * as i1$1 from '@angular/forms';
9
- import { NG_VALUE_ACCESSOR, Validators, FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms';
10
- import { CommonModule } from '@angular/common';
7
+ import * as i2 from '@angular/forms';
8
+ import { Validators, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms';
9
+ import * as i1$1 from '@angular/material/dialog';
10
+ import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
11
+ import * as i3 from '@angular/material/form-field';
11
12
  import { MatFormFieldModule } from '@angular/material/form-field';
12
- import { MatOptionModule } from '@angular/material/core';
13
- import { MatSelectModule } from '@angular/material/select';
13
+ import * as i4 from '@angular/material/button';
14
14
  import { MatButtonModule } from '@angular/material/button';
15
+ import * as i5 from '@angular/material/input';
15
16
  import { MatInputModule } from '@angular/material/input';
16
- import { MatDialogRef, MAT_DIALOG_DATA, MatDialog, MatDialogModule } from '@angular/material/dialog';
17
- import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
18
- import * as semver from 'semver';
19
- import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
20
- import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
17
+ import * as i5$1 from '@angular/common';
18
+ import { CommonModule } from '@angular/common';
19
+ import * as i3$1 from '@angular/router';
20
+ import { RouterModule } from '@angular/router';
21
+ import * as i2$2 from '@angular/material/snack-bar';
22
+ import { MatSnackBarModule } from '@angular/material/snack-bar';
23
+ import * as i2$1 from '@angular/material/icon';
21
24
  import { MatIconModule } from '@angular/material/icon';
22
- import { MatTooltipModule } from '@angular/material/tooltip';
25
+ import * as i8 from '@angular/cdk/drag-drop';
26
+ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
27
+ import { valid } from 'semver';
28
+ import * as i3$2 from '@angular/material/chips';
23
29
  import { MatChipsModule } from '@angular/material/chips';
30
+ import * as i4$1 from '@angular/material/autocomplete';
24
31
  import { MatAutocompleteModule } from '@angular/material/autocomplete';
25
- import { RouterModule } from '@angular/router';
32
+ import * as i4$2 from '@angular/material/core';
33
+ import { MatOptionModule } from '@angular/material/core';
34
+ import * as i5$2 from '@angular/material/tooltip';
35
+ import { MatTooltipModule } from '@angular/material/tooltip';
36
+ import * as i3$3 from '@angular/material/select';
37
+ import { MatSelectModule } from '@angular/material/select';
38
+ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
26
39
  import { MatMenuModule } from '@angular/material/menu';
27
40
 
28
41
  class Configuration {
@@ -30,9 +43,11 @@ class Configuration {
30
43
  this.basePath = 'https://api.configcat.com';
31
44
  }
32
45
  }
33
- Configuration.decorators = [
34
- { type: Injectable }
35
- ];
46
+ Configuration.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: Configuration, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
47
+ Configuration.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: Configuration });
48
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: Configuration, decorators: [{
49
+ type: Injectable
50
+ }] });
36
51
 
37
52
  class PublicApiService {
38
53
  constructor(httpClient, configuration) {
@@ -89,14 +104,14 @@ class PublicApiService {
89
104
  }));
90
105
  }
91
106
  }
92
- PublicApiService.ɵprov = i0.ɵɵdefineInjectable({ factory: function PublicApiService_Factory() { return new PublicApiService(i0.ɵɵinject(i1.HttpClient), i0.ɵɵinject(Configuration, 8)); }, token: PublicApiService, providedIn: "root" });
93
- PublicApiService.decorators = [
94
- { type: Injectable, args: [{ providedIn: 'root' },] }
95
- ];
96
- PublicApiService.ctorParameters = () => [
97
- { type: HttpClient },
98
- { type: Configuration, decorators: [{ type: Optional }] }
99
- ];
107
+ PublicApiService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: PublicApiService, deps: [{ token: i1.HttpClient }, { token: Configuration, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
108
+ PublicApiService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: PublicApiService, providedIn: 'root' });
109
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: PublicApiService, decorators: [{
110
+ type: Injectable,
111
+ args: [{ providedIn: 'root' }]
112
+ }], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: Configuration, decorators: [{
113
+ type: Optional
114
+ }] }]; } });
100
115
 
101
116
  class BaseComponent {
102
117
  constructor(publicApiService) {
@@ -121,336 +136,68 @@ class BaseComponent {
121
136
  return this.publicApiService.createSettingValuesService(this.basicAuthUsername, this.basicAuthPassword, this.basePath);
122
137
  }
123
138
  }
124
- BaseComponent.decorators = [
125
- { type: Component, args: [{
126
- selector: 'base',
127
- template: '<div></div>'
128
- },] }
129
- ];
130
- BaseComponent.ctorParameters = () => [
131
- { type: PublicApiService }
132
- ];
133
- BaseComponent.propDecorators = {
134
- basePath: [{ type: Input }],
135
- basicAuthUsername: [{ type: Input }],
136
- basicAuthPassword: [{ type: Input }]
137
- };
138
-
139
- class SettingItemComponent extends BaseComponent {
140
- constructor(publicApiService) {
141
- super(publicApiService);
142
- this.canEditSetting = true;
143
- this.canDeleteSetting = true;
144
- this.deleteSettingText = 'Delete setting';
145
- this.showEnvironmentName = true;
146
- this.editSettingRequested = new EventEmitter();
147
- this.deleteSettingRequested = new EventEmitter();
148
- this.loadSucceeded = new EventEmitter();
149
- this.loadFailed = new EventEmitter();
150
- this.saveSucceeded = new EventEmitter();
151
- this.saveFailed = new EventEmitter();
152
- this.formValuesChanged = new EventEmitter();
153
- this.loadSettingValues = () => {
154
- return this.createSettingValuesService().getSettingValue(this.environmentId, this.settingId)
155
- .pipe(map(settingValue => {
156
- this.settingValue = settingValue;
157
- const settingValues = {
158
- settingValues: [settingValue],
159
- readOnly: settingValue.readOnly,
160
- config: settingValue.config,
161
- environment: settingValue.environment
162
- };
163
- this.loadSucceeded.emit(settingValues);
164
- return settingValues;
165
- }), catchError(error => {
166
- this.loadFailed.emit(error);
167
- return throwError(error);
168
- }));
169
- };
170
- this.handleSave = (model) => {
171
- if (model.settingValues.length === 0) {
172
- return;
173
- }
174
- const updateModel = model.settingValues[0];
175
- return this.createSettingValuesService().replaceSettingValue(this.environmentId, this.settingId, updateModel, model.reason)
176
- .pipe(map(settingValue => {
177
- this.settingValue = settingValue;
178
- const settingValues = {
179
- settingValues: [settingValue],
180
- readOnly: settingValue.readOnly,
181
- config: settingValue.config,
182
- environment: settingValue.environment
183
- };
184
- this.saveSucceeded.emit(settingValues);
185
- return settingValues;
186
- }), catchError(error => {
187
- this.saveFailed.emit(error);
188
- return throwError(error);
189
- }));
190
- };
191
- }
192
- ngOnInit() {
193
- }
194
- onEditSettingRequested(setting) {
195
- this.editSettingRequested.emit({
196
- setting: setting.setting,
197
- environment: this.settingValue.environment,
198
- config: this.settingValue.config
199
- });
200
- }
201
- onDeleteSettingRequested(setting) {
202
- this.deleteSettingRequested.emit({
203
- setting: setting.setting,
204
- environment: this.settingValue.environment,
205
- config: this.settingValue.config
206
- });
207
- }
208
- onFormValuesChanged() {
209
- this.formValuesChanged.emit();
210
- }
211
- }
212
- SettingItemComponent.decorators = [
213
- { type: Component, args: [{
214
- selector: 'configcat-setting-item',
215
- template: "<configcat-setting-list [canEditSetting]=\"canEditSetting\" [canDeleteSetting]=\"canDeleteSetting\"\n [onSave]=\"handleSave\" [loadSettingValues]=\"loadSettingValues\"\n (editSettingRequested)=\"onEditSettingRequested($event)\" (deleteSettingRequested)=\"onDeleteSettingRequested($event)\"\n [deleteSettingText]=\"deleteSettingText\"\n [showEnvironmentName]=\"showEnvironmentName\" (formValuesChanged)=\"onFormValuesChanged()\">\n</configcat-setting-list>",
216
- styles: [".container{font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;background-color:#f3f3f3;border:1px solid rgba(0,0,0,.12);border-radius:4px;margin:1em 0}.container .setting-full{position:relative;display:flex;flex-wrap:nowrap}.container .setting-full .setting-details{display:flex;flex-direction:column;width:400px;padding:1em 0 1em 1em}.container .setting-full .setting-details>*{margin:.5em}.container .setting-full .setting-details .setting-name{flex-direction:row}.container .setting-full .setting-details .setting-name .name{font-weight:bold;word-wrap:break-word;max-width:100%}.container .setting-full .setting-details .setting-key{word-wrap:break-word}.container .setting-full .setting-details .description{font-size:.8em}.container .setting-full .setting-details code{background-color:#d3d3d3;padding:3px 6px}.container .setting-full .rules-list{width:100%;padding:1em}.container .setting-full .targeting-button{margin-left:1rem;margin-bottom:1em}.all-other{min-height:40px;justify-content:flex-start;border:1px solid #d5d5d5;border-radius:4px;background-color:#fff;margin:.5em 1em;padding:4px 0 4px .5em}.all-other .chip{background-color:#67b1fc3d}.all-other .served-value{margin-left:auto;min-width:100px}.end{margin-left:auto}.flex{display:flex;flex-wrap:wrap;align-items:center}.show-hide-toggle{margin-left:1em;cursor:pointer}@media (max-width: 1200px){.setting-full .setting-details{width:300px}}@media (max-width: 700px){.setting-full{flex-wrap:wrap}.setting-full .setting-details{padding:.2em 0 .2em .2em}.setting-full .all-other{margin:.5em .3em}}\n"]
217
- },] }
218
- ];
219
- SettingItemComponent.ctorParameters = () => [
220
- { type: PublicApiService }
221
- ];
222
- SettingItemComponent.propDecorators = {
223
- productId: [{ type: Input }],
224
- environmentId: [{ type: Input }],
225
- configId: [{ type: Input }],
226
- settingId: [{ type: Input }],
227
- canEditSetting: [{ type: Input }],
228
- canDeleteSetting: [{ type: Input }],
229
- deleteSettingText: [{ type: Input }],
230
- showEnvironmentName: [{ type: Input }],
231
- editSettingRequested: [{ type: Output }],
232
- deleteSettingRequested: [{ type: Output }],
233
- loadSucceeded: [{ type: Output }],
234
- loadFailed: [{ type: Output }],
235
- saveSucceeded: [{ type: Output }],
236
- saveFailed: [{ type: Output }],
237
- formValuesChanged: [{ type: Output }]
238
- };
239
-
240
- class SettingSelectComponent extends BaseComponent {
241
- constructor(publicApiService) {
242
- super(publicApiService);
243
- }
244
- ngOnInit() {
245
- this.settings = [];
246
- this.reloadSettings();
247
- }
248
- ngOnChanges() {
249
- this.reloadSettings();
250
- }
251
- reloadSettings() {
252
- if (this.configId) {
253
- this.createSettingsService().getSettings(this.configId).subscribe(settings => {
254
- this.settings = settings;
255
- if (this.settings.length > 0) {
256
- this.valueFormControl.setValue(this.settings[0].settingId);
257
- }
258
- });
259
- }
260
- }
261
- }
262
- SettingSelectComponent.decorators = [
263
- { type: Component, args: [{
264
- selector: 'configcat-setting-select',
265
- template: "<mat-form-field class=\"form-field\">\n <mat-select placeholder=\"Feature flag or setting\" [formControl]=\"valueFormControl\" *ngIf=\"settings\" name=\"value\">\n <mat-option *ngFor=\"let setting of settings\" [value]=\"setting.settingId\">{{setting.name}}\n </mat-option>\n </mat-select>\n</mat-form-field>",
266
- styles: [".form-field{width:100%}\n"]
267
- },] }
268
- ];
269
- SettingSelectComponent.ctorParameters = () => [
270
- { type: PublicApiService }
271
- ];
272
- SettingSelectComponent.propDecorators = {
273
- valueFormControl: [{ type: Input }],
274
- configId: [{ type: Input }]
275
- };
139
+ BaseComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: BaseComponent, deps: [{ token: PublicApiService }], target: i0.ɵɵFactoryTarget.Component });
140
+ BaseComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: BaseComponent, selector: "base", inputs: { basePath: "basePath", basicAuthUsername: "basicAuthUsername", basicAuthPassword: "basicAuthPassword" }, ngImport: i0, template: '<div></div>', isInline: true });
141
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: BaseComponent, decorators: [{
142
+ type: Component,
143
+ args: [{
144
+ selector: 'base',
145
+ template: '<div></div>'
146
+ }]
147
+ }], ctorParameters: function () { return [{ type: PublicApiService }]; }, propDecorators: { basePath: [{
148
+ type: Input
149
+ }], basicAuthUsername: [{
150
+ type: Input
151
+ }], basicAuthPassword: [{
152
+ type: Input
153
+ }] } });
276
154
 
277
- class ProductSelectComponent extends BaseComponent {
278
- constructor(publicApiService) {
279
- super(publicApiService);
155
+ class ReasonDialogComponent {
156
+ constructor(dialogRef, formBuilder) {
157
+ this.dialogRef = dialogRef;
158
+ this.formBuilder = formBuilder;
280
159
  }
281
160
  ngOnInit() {
282
- this.products = [];
283
- this.createProductsService().getProducts().subscribe(products => {
284
- this.products = products;
285
- if (this.products.length > 0) {
286
- this.valueFormControl.setValue(this.products[0].productId, {
287
- emitEvent: true,
288
- emitModelToViewChange: true, emitViewToModelChange: true
289
- });
290
- }
161
+ this.formGroup = this.formBuilder.group({
162
+ reason: ['', [Validators.required, Validators.maxLength(1000)]]
291
163
  });
292
164
  }
293
- }
294
- ProductSelectComponent.decorators = [
295
- { type: Component, args: [{
296
- selector: 'configcat-product-select',
297
- template: "<mat-form-field class=\"form-field\">\n <mat-select placeholder=\"Product\" [formControl]=\"valueFormControl\" name=\"productValue\">\n <mat-option *ngFor=\"let product of products\" [value]=\"product.productId\">{{product.name}}\n </mat-option>\n </mat-select>\n</mat-form-field>",
298
- styles: [".form-field{width:100%}\n"]
299
- },] }
300
- ];
301
- ProductSelectComponent.ctorParameters = () => [
302
- { type: PublicApiService }
303
- ];
304
- ProductSelectComponent.propDecorators = {
305
- valueFormControl: [{ type: Input }]
306
- };
307
-
308
- class ConfigSelectComponent extends BaseComponent {
309
- constructor(publicApiService) {
310
- super(publicApiService);
311
- }
312
- ngOnInit() {
313
- this.configs = [];
314
- this.reloadConfigs();
315
- }
316
- ngOnChanges() {
317
- this.reloadConfigs();
318
- }
319
- reloadConfigs() {
320
- if (this.productId) {
321
- this.createConfigsService().getConfigs(this.productId).subscribe(configs => {
322
- this.configs = configs;
323
- if (this.configs.length > 0) {
324
- this.valueFormControl.setValue(this.configs[0].configId);
325
- }
326
- });
327
- }
328
- }
329
- }
330
- ConfigSelectComponent.decorators = [
331
- { type: Component, args: [{
332
- selector: 'configcat-config-select',
333
- template: "<mat-form-field class=\"form-field\">\n <mat-select placeholder=\"Config\" [formControl]=\"valueFormControl\" *ngIf=\"configs\" name=\"value\">\n <mat-option *ngFor=\"let config of configs\" [value]=\"config.configId\">{{config.name}}</mat-option>\n </mat-select>\n</mat-form-field>",
334
- providers: [
335
- {
336
- provide: NG_VALUE_ACCESSOR,
337
- useExisting: forwardRef(() => ConfigSelectComponent),
338
- multi: true
339
- }
340
- ],
341
- styles: [".form-field{width:100%}\n"]
342
- },] }
343
- ];
344
- ConfigSelectComponent.ctorParameters = () => [
345
- { type: PublicApiService }
346
- ];
347
- ConfigSelectComponent.propDecorators = {
348
- valueFormControl: [{ type: Input }],
349
- productId: [{ type: Input }]
350
- };
351
-
352
- class EnvironmentSelectComponent extends BaseComponent {
353
- constructor(publicApiService) {
354
- super(publicApiService);
355
- }
356
- ngOnInit() {
357
- this.environments = [];
358
- this.reloadEnvironments();
359
- }
360
- ngOnChanges() {
361
- this.reloadEnvironments();
362
- }
363
- reloadEnvironments() {
364
- if (this.productId) {
365
- this.createEnvironmentsService().getEnvironments(this.productId).subscribe(environments => {
366
- this.environments = environments;
367
- if (this.environments.length > 0) {
368
- this.valueFormControl.setValue(this.environments[0].environmentId);
369
- }
370
- });
371
- }
372
- }
373
- }
374
- EnvironmentSelectComponent.decorators = [
375
- { type: Component, args: [{
376
- selector: 'configcat-environment-select',
377
- template: "<mat-form-field class=\"form-field\">\n <mat-select placeholder=\"Environment\" [formControl]=\"valueFormControl\" *ngIf=\"environments\" name=\"value\">\n <mat-option *ngFor=\"let environment of environments\" [value]=\"environment.environmentId\">{{environment.name}}\n </mat-option>\n </mat-select>\n</mat-form-field>",
378
- styles: [".form-field{width:100%}\n"]
379
- },] }
380
- ];
381
- EnvironmentSelectComponent.ctorParameters = () => [
382
- { type: PublicApiService }
383
- ];
384
- EnvironmentSelectComponent.propDecorators = {
385
- valueFormControl: [{ type: Input }],
386
- productId: [{ type: Input }]
387
- };
388
-
389
- class FormHelper {
390
- static getErrorMessage(control, hint = '') {
391
- if (!control || control.valid) {
165
+ handleSaveButton() {
166
+ if (!this.formGroup.valid) {
392
167
  return;
393
168
  }
394
- if (control.hasError('required')) {
395
- return `${hint} The field is required.`;
396
- }
397
- if (control.hasError('pattern')) {
398
- return 'Not a valid e-mail address.';
399
- }
400
- if (control.hasError('minlength')) {
401
- return 'The field must be at least ' + control.errors.minlength.requiredLength + ' characters long';
402
- }
403
- if (control.hasError('maxlength')) {
404
- return 'The field must be at max ' + control.errors.maxlength.requiredLength + ' characters long';
405
- }
406
- return;
169
+ this.dialogRef.close({ button: 'save', reason: this.formGroup.controls.reason.value });
170
+ }
171
+ cancel() {
172
+ this.dialogRef.close({ button: 'cancel' });
407
173
  }
408
174
  }
175
+ ReasonDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ReasonDialogComponent, deps: [{ token: i1$1.MatDialogRef }, { token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Component });
176
+ ReasonDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: ReasonDialogComponent, selector: "configcat-reason-dialog", ngImport: i0, template: "<h3 mat-dialog-title>Add notes</h3>\n<p>This will appear in the Why column of the Audit Log.</p>\n<form [formGroup]=\"formGroup\" (ngSubmit)=\"handleSaveButton()\">\n <div mat-dialog-content class=\"container\">\n <mat-form-field appearance=\"outline\">\n <mat-label></mat-label>\n <textarea matInput formControlName=\"reason\" name=\"reason\" placeholder=\"I'm changing settings because...\"\n rows=\"3\"></textarea>\n <mat-error *ngIf=\"formGroup.controls.reason.invalid\">The field is required.</mat-error>\n </mat-form-field>\n </div>\n <br>\n <div mat-dialog-actions>\n <button type=\"submit\" style=\"margin-right: 10px;\" mat-raised-button color=\"primary\">Add notes & Save</button>\n <button type=\"button\" mat-raised-button (click)=\"cancel()\">Cancel</button>\n </div>\n</form>", styles: [".container>*{width:100%}\n"], components: [{ type: i3.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }, { type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }], directives: [{ type: i1$1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i1$1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { type: i3.MatLabel, selector: "mat-label" }, { type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["id", "disabled", "required", "type", "value", "readonly", "placeholder", "errorStateMatcher", "aria-describedby"], exportAs: ["matInput"] }, { type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlName, selector: "[formControlName]", inputs: ["disabled", "formControlName", "ngModel"], outputs: ["ngModelChange"] }, { type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.MatError, selector: "mat-error", inputs: ["id"] }, { type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]" }] });
177
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ReasonDialogComponent, decorators: [{
178
+ type: Component,
179
+ args: [{
180
+ selector: 'configcat-reason-dialog',
181
+ templateUrl: './reason-dialog.component.html',
182
+ styleUrls: ['./reason-dialog.component.scss']
183
+ }]
184
+ }], ctorParameters: function () { return [{ type: i1$1.MatDialogRef }, { type: i2.FormBuilder }]; } });
409
185
 
410
- class AuthorizationComponent {
411
- constructor(formBuilder, publicApiService) {
412
- this.formBuilder = formBuilder;
413
- this.publicApiService = publicApiService;
414
- this.basePath = null;
415
- this.authorized = new EventEmitter();
416
- this.FormHelper = FormHelper;
417
- }
186
+ class SaveSnackbarComponent {
187
+ constructor() { }
418
188
  ngOnInit() {
419
- this.formGroup = this.formBuilder.group({
420
- basicAuthUsername: ['', Validators.required],
421
- basicAuthPassword: ['', Validators.required],
422
- });
423
- }
424
- onSubmit() {
425
- if (!this.formGroup.valid) {
426
- return;
427
- }
428
- const meService = this.publicApiService.createMeService(this.formGroup.controls.basicAuthUsername.value, this.formGroup.controls.basicAuthPassword.value, this.basePath);
429
- meService.getMe().subscribe(me => {
430
- this.authorized.emit({
431
- basicAuthUsername: this.formGroup.controls.basicAuthUsername.value,
432
- basicAuthPassword: this.formGroup.controls.basicAuthPassword.value
433
- });
434
- }, error => {
435
- this.formGroup.setErrors({ invalidAuthorization: 'Invalid Basic auth user name or password' });
436
- });
437
189
  }
438
190
  }
439
- AuthorizationComponent.decorators = [
440
- { type: Component, args: [{
441
- selector: 'configcat-authorization',
442
- template: "<form [formGroup]=\"formGroup\" (ngSubmit)=\"onSubmit()\">\n <div class=\"container\">\n <div class=\"form\">\n <p>\n <a href=\"https://app.configcat.com/my-account/public-api-credentials\" target=\"_blank\">\n Get your Basic Auth user name and password</a> to access ConfigCat Public API.\n <br /><br />\n <b>Note!</b> Your ConfigCat account's email address and password will not work here.\n </p>\n\n <mat-form-field class=\"form-field\">\n <mat-label>Basic auth user name</mat-label>\n <input matInput placeholder=\"Basic auth user name\" formControlName=\"basicAuthUsername\">\n <mat-error *ngIf=\"formGroup.controls.basicAuthUsername.invalid\">\n {{FormHelper.getErrorMessage(formGroup.controls.basicAuthUsername)}}\n </mat-error>\n </mat-form-field>\n <mat-form-field class=\"form-field\">\n <mat-label>Basic auth password</mat-label>\n <input matInput placeholder=\"Basic auth password\" formControlName=\"basicAuthPassword\" type=\"password\"\n autocomplete=\"current-password\">\n <mat-error *ngIf=\"formGroup.controls.basicAuthPassword.invalid\">\n {{FormHelper.getErrorMessage(formGroup.controls.basicAuthPassword)}}\n </mat-error>\n </mat-form-field>\n <div>\n <button mat-raised-button color=\"primary\" type=\"submit\" class=\"submit\">Authorize</button>\n <mat-error *ngIf=\"formGroup.invalid && formGroup.errors && formGroup.errors.invalidAuthorization\">\n {{formGroup.errors.invalidAuthorization}}</mat-error>\n </div>\n </div>\n </div>\n</form>",
443
- styles: [".container{display:flex;margin:8px}.form{width:100%}.form-field{width:100%;margin-bottom:8px}.submit{margin-top:8px;margin-bottom:8px}\n"]
444
- },] }
445
- ];
446
- AuthorizationComponent.ctorParameters = () => [
447
- { type: FormBuilder },
448
- { type: PublicApiService }
449
- ];
450
- AuthorizationComponent.propDecorators = {
451
- basePath: [{ type: Input }],
452
- authorized: [{ type: Output }]
453
- };
191
+ SaveSnackbarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SaveSnackbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
192
+ SaveSnackbarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: SaveSnackbarComponent, selector: "configcat-save-snackbar", ngImport: i0, template: "<p class=\"header\">Settings saved successfully.</p>", styles: ["p{text-align:center;font-weight:bold}.header{font-size:1.2em}\n"] });
193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SaveSnackbarComponent, decorators: [{
194
+ type: Component,
195
+ args: [{
196
+ selector: 'configcat-save-snackbar',
197
+ templateUrl: './save-snackbar.component.html',
198
+ styleUrls: ['./save-snackbar.component.scss']
199
+ }]
200
+ }], ctorParameters: function () { return []; } });
454
201
 
455
202
  class ConcurrentDialogComponent {
456
203
  constructor(dialogRef) {
@@ -465,200 +212,252 @@ class ConcurrentDialogComponent {
465
212
  this.dialogRef.close({ button: 'discard' });
466
213
  }
467
214
  }
468
- ConcurrentDialogComponent.decorators = [
469
- { type: Component, args: [{
470
- selector: 'configcat-concurrent-dialog',
471
- template: "<h3 mat-dialog-title>Concurrent update detected</h3>\n<p>\n Somebody updated the feature flags & settings while you were editing them.\n</p>\n<ul>\n <li>\n To discard your local changes and reload the updated values, click <strong>Discard my changes & reload</strong>\n </li>\n <li>\n To save you local values and overwrite the remote changes, click <strong>Save anyway</strong>\n </li>\n</ul>\n<p>For more info about the changes check the <a [routerLink]=\"['/auditlog']\" target=\"_blank\" rel=\"noopener noreferrer\" >audit logs</a> in a new tab.</p>\n<div mat-dialog-actions>\n <button type=\"button\" style=\"margin-right: 10px;\" mat-raised-button color=\"primary\" (click)=\"discard()\">Discard\n local changes & reload</button>\n <button type=\"button\" mat-raised-button (click)=\"save()\">Save anyway</button>\n</div>",
472
- styles: [""]
473
- },] }
474
- ];
475
- ConcurrentDialogComponent.ctorParameters = () => [
476
- { type: MatDialogRef }
477
- ];
215
+ ConcurrentDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ConcurrentDialogComponent, deps: [{ token: i1$1.MatDialogRef }], target: i0.ɵɵFactoryTarget.Component });
216
+ ConcurrentDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: ConcurrentDialogComponent, selector: "configcat-concurrent-dialog", ngImport: i0, template: "<h3 mat-dialog-title>Concurrent update detected</h3>\n<p>\n Somebody updated the feature flags & settings while you were editing them.\n</p>\n<ul>\n <li>\n To discard your local changes and reload the updated values, click <strong>Discard my changes & reload</strong>\n </li>\n <li>\n To save you local values and overwrite the remote changes, click <strong>Save anyway</strong>\n </li>\n</ul>\n<p>For more info about the changes check the <a [routerLink]=\"['/auditlog']\" target=\"_blank\" rel=\"noopener noreferrer\" >audit logs</a> in a new tab.</p>\n<div mat-dialog-actions>\n <button type=\"button\" style=\"margin-right: 10px;\" mat-raised-button color=\"primary\" (click)=\"discard()\">Discard\n local changes & reload</button>\n <button type=\"button\" mat-raised-button (click)=\"save()\">Save anyway</button>\n</div>", styles: [""], components: [{ type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }], directives: [{ type: i1$1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { type: i3$1.RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: ["routerLink", "target", "queryParams", "fragment", "queryParamsHandling", "preserveFragment", "skipLocationChange", "replaceUrl", "state", "relativeTo"] }, { type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]" }] });
217
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ConcurrentDialogComponent, decorators: [{
218
+ type: Component,
219
+ args: [{
220
+ selector: 'configcat-concurrent-dialog',
221
+ templateUrl: './concurrent-dialog.component.html',
222
+ styleUrls: ['./concurrent-dialog.component.scss']
223
+ }]
224
+ }], ctorParameters: function () { return [{ type: i1$1.MatDialogRef }]; } });
478
225
 
479
- class ExpandDialogComponent {
480
- constructor(dialogRef, formBuilder, data) {
481
- this.dialogRef = dialogRef;
226
+ class SettingListFormHelper {
227
+ constructor(formBuilder) {
482
228
  this.formBuilder = formBuilder;
483
- this.data = data;
484
- }
485
- ngOnInit() {
486
- if (this.data.multiline) {
487
- this.buildFormGroup();
488
- }
489
- else {
490
- this.formControlSubscription = this.data.control.valueChanges.subscribe(value => {
491
- this.data.control.setValue(value, { onlySelf: true, emitEvent: false, emitModelToViewChange: true });
492
- this.data.control.markAsDirty();
493
- }, error => { }, () => { });
494
- }
495
- }
496
- values() {
497
- return this.formGroup.get('values');
498
229
  }
499
- buildFormGroup() {
500
- const value = this.data.control.value ? this.data.control.value : '';
501
- const list = value.split(',').map(v => v.trim()).filter(v => !!v);
502
- this.formGroup = this.formBuilder.group({
503
- values: this.formBuilder.array(list.map(v => this.formBuilder.control(v)))
504
- });
505
- this.formGroupSubscription = this.formGroup.valueChanges.subscribe((formGroupValue) => {
506
- this.data.control.setValue(formGroupValue.values
507
- .filter(v => !!v)
508
- .join(','), { onlySelf: true, emitEvent: false, emitModelToViewChange: true });
509
- this.data.control.updateValueAndValidity();
510
- this.data.control.markAsDirty();
511
- });
230
+ createSettingValuesFormGroup(settingValuesModel) {
231
+ const formGroup = this.formBuilder.group({
232
+ settingValues: this.formBuilder.array(settingValuesModel.settingValues.map(sv => {
233
+ return this.createSettingValueFormGroup(sv, settingValuesModel.readOnly);
234
+ })),
235
+ // Decorator
236
+ environment: this.formBuilder.group({
237
+ environmentId: [{ value: settingValuesModel.environment.environmentId, disabled: true }],
238
+ name: [{ value: settingValuesModel.environment.name, disabled: true }],
239
+ }),
240
+ comparisonAttributes: [[]],
241
+ allRolloutRulesCount: [0]
242
+ });
243
+ const rolloutRules = this.getAllRolloutRules(formGroup);
244
+ const comparisonAttributes = this.getComparisonAttributes(rolloutRules);
245
+ formGroup.patchValue({ allRolloutRulesCount: rolloutRules.length });
246
+ formGroup.patchValue({ comparisonAttributes: comparisonAttributes });
247
+ return formGroup;
512
248
  }
513
- add() {
514
- this.formGroup.controls.values.push(this.formBuilder.control(''));
249
+ createSettingValueFormGroup(settingValueModel, readOnly) {
250
+ if (!settingValueModel.setting) {
251
+ return this.formBuilder.group({});
252
+ }
253
+ return this.formBuilder.group({
254
+ settingId: [settingValueModel.setting.settingId],
255
+ value: [
256
+ { value: settingValueModel.value, disabled: readOnly },
257
+ this.getValidatorsForSettingType(settingValueModel.setting.settingType)
258
+ ],
259
+ rolloutPercentageItems: settingValueModel.rolloutPercentageItems
260
+ ? this.formBuilder.array(settingValueModel.rolloutPercentageItems.map(p => {
261
+ // @ts-ignore()
262
+ return this.createRolloutPercentageItemFormGroup(p, readOnly, settingValueModel.setting.settingType);
263
+ }))
264
+ : this.formBuilder.array([]), rolloutRules: settingValueModel.rolloutRules
265
+ ? this.formBuilder.array(settingValueModel.rolloutRules.map(r => {
266
+ // @ts-ignore()
267
+ return this.createRolloutRuleFormGroup(r, readOnly, settingValueModel.setting.settingType);
268
+ }))
269
+ : this.formBuilder.array([]),
270
+ updatedAt: [{ value: settingValueModel.updatedAt, disabled: true }],
271
+ lastUpdaterUserEmail: [{ value: settingValueModel.lastUpdaterUserEmail, disabled: true }],
272
+ lastUpdaterUserFullName: [{ value: settingValueModel.lastUpdaterUserFullName, disabled: true }],
273
+ rolloutPercentageItemsSum: [{ value: 100, disabled: false }, [Validators.min(100), Validators.max(100)]],
274
+ // Decoration
275
+ setting: this.formBuilder.group({
276
+ settingId: [{ value: settingValueModel.setting.settingId, disabled: true }],
277
+ key: [{ value: settingValueModel.setting.key, disabled: true }],
278
+ name: [{ value: settingValueModel.setting.name, disabled: true }],
279
+ hint: [{ value: settingValueModel.setting.hint, disabled: true }],
280
+ settingType: [{ value: settingValueModel.setting.settingType, disabled: true }],
281
+ creatorEmail: [{ value: settingValueModel.setting.creatorEmail, disabled: true }],
282
+ creatorFullName: [{ value: settingValueModel.setting.creatorFullName, disabled: true }],
283
+ createdAt: [{ value: settingValueModel.setting.createdAt, disabled: true }]
284
+ }),
285
+ integrationLinks: settingValueModel.integrationLinks ? this.formBuilder.array(settingValueModel.integrationLinks.map(il => {
286
+ return this.formBuilder.group({
287
+ key: [{ value: il.key, disabled: true }],
288
+ description: [{ value: il.description, disabled: true }],
289
+ integrationLinkType: [{ value: il.integrationLinkType, disabled: true }],
290
+ url: [{ value: il.url, disabled: true }],
291
+ });
292
+ })) : this.formBuilder.array([])
293
+ });
515
294
  }
516
- remove(index) {
517
- this.formGroup.controls.values.removeAt(index);
295
+ createRolloutPercentageItemFormGroup(p, readOnly, settingType) {
296
+ return this.formBuilder.group({
297
+ percentage: [{ value: p.percentage, disabled: readOnly }, [Validators.required, Validators.min(0), Validators.max(100)]],
298
+ value: [
299
+ { value: p.value, disabled: readOnly },
300
+ this.getValidatorsForSettingType(settingType)
301
+ ],
302
+ valueDisabled: [
303
+ { value: p.value, disabled: true }, // For percentage item value
304
+ ]
305
+ });
518
306
  }
519
- ngOnDestroy() {
520
- if (this.formControlSubscription) {
521
- this.formControlSubscription.unsubscribe();
522
- this.formControlSubscription = null;
523
- }
524
- if (this.formGroupSubscription) {
525
- this.formGroupSubscription.unsubscribe();
526
- this.formGroupSubscription = null;
307
+ createRolloutRuleFormGroup(r, readOnly, settingType) {
308
+ return this.formBuilder.group({
309
+ comparisonAttribute: [{ value: r.comparisonAttribute, disabled: readOnly }, [Validators.required]],
310
+ comparator: [{ value: r.comparator, disabled: readOnly }, [Validators.required]],
311
+ comparisonValue: [{ value: r.comparisonValue, disabled: readOnly }, [Validators.required]],
312
+ value: [
313
+ { value: r.value, disabled: readOnly },
314
+ this.getValidatorsForSettingType(settingType)
315
+ ],
316
+ });
317
+ }
318
+ getValidatorsForSettingType(settingType) {
319
+ switch (settingType) {
320
+ case SettingType.Int:
321
+ return [Validators.required,
322
+ Validators.min(-2147483648), Validators.max(2147483647), Validators.pattern('^[-+]?[0-9]\\d*$')];
323
+ case SettingType.Double:
324
+ return [Validators.required, Validators.min(-1.79769313486232E+308), Validators.max(1.79769313486232E+308)];
325
+ case SettingType.String:
326
+ case SettingType.Boolean:
327
+ return [Validators.required];
328
+ default:
329
+ return [];
527
330
  }
528
331
  }
529
- close() {
530
- this.dialogRef.close();
332
+ getAllRolloutRules(formGroup) {
333
+ const settingValues = formGroup.get('settingValues');
334
+ const rolloutRules = settingValues.controls.map(cv => cv.get('rolloutRules').value);
335
+ return rolloutRules.length === 0 ? rolloutRules : rolloutRules.reduce((a, b) => a.concat(b));
336
+ }
337
+ getComparisonAttributes(rolloutRules) {
338
+ if (rolloutRules.length > 0) {
339
+ const currentComparisonAttributes = rolloutRules.map(r => r.comparisonAttribute);
340
+ const defaultComparisonAttributes = ['Identifier', 'Email', 'Country'];
341
+ const comparisonAttributes = currentComparisonAttributes.filter(c => defaultComparisonAttributes.indexOf(c) === -1);
342
+ return Array.from(new Set(comparisonAttributes));
343
+ }
344
+ else {
345
+ return [];
346
+ }
531
347
  }
532
348
  }
533
- ExpandDialogComponent.decorators = [
534
- { type: Component, args: [{
535
- selector: 'configcat-expand-dialog',
536
- template: "<div mat-dialog-content class=\"container\">\n\n <mat-form-field appearance=\"outline\" *ngIf=\"!data.multiline\">\n <mat-label></mat-label>\n <textarea matInput [formControl]=\"data.control\" rows=\"15\"></textarea>\n </mat-form-field>\n <div [formGroup]=\"formGroup\" *ngIf=\"data.multiline\">\n <div *ngFor=\"let valueControl of values().controls; let i = index;\" class=\"value-field\">\n <mat-form-field appearance=\"outline\">\n <input matInput [formControl]=\"valueControl\" rows=\"15\" />\n </mat-form-field>\n <button (click)=\"remove(i)\" mat-icon-button type=\"button\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n </div>\n <button (click)=\"add()\" mat-icon-button type=\"button\">\n <mat-icon>add</mat-icon>\n </button>\n </div>\n</div>\n<div mat-dialog-actions>\n <button type=\"button\" mat-raised-button color=\"primary\" (click)=\"close()\">Done</button>\n</div>",
537
- styles: [".container>*{width:100%}.container>* .value-field{display:flex;font-size:12px;flex-wrap:nowrap}.container>* .value-field mat-form-field{flex-grow:1}\n"]
538
- },] }
539
- ];
540
- ExpandDialogComponent.ctorParameters = () => [
541
- { type: MatDialogRef },
542
- { type: FormBuilder },
543
- { type: undefined, decorators: [{ type: Inject, args: [MAT_DIALOG_DATA,] }] }
544
- ];
349
+ SettingListFormHelper.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingListFormHelper, deps: [{ token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Injectable });
350
+ SettingListFormHelper.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingListFormHelper, providedIn: 'root' });
351
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingListFormHelper, decorators: [{
352
+ type: Injectable,
353
+ args: [{ providedIn: 'root' }]
354
+ }], ctorParameters: function () { return [{ type: i2.FormBuilder }]; } });
545
355
 
546
- class ReasonDialogComponent {
547
- constructor(dialogRef, formBuilder) {
356
+ class ExpandDialogComponent {
357
+ constructor(dialogRef, formBuilder, data) {
548
358
  this.dialogRef = dialogRef;
549
359
  this.formBuilder = formBuilder;
360
+ this.data = data;
550
361
  }
551
362
  ngOnInit() {
552
- this.formGroup = this.formBuilder.group({
553
- reason: ['', [Validators.required, Validators.maxLength(1000)]]
554
- });
555
- }
556
- handleSaveButton() {
557
- if (!this.formGroup.valid) {
558
- return;
363
+ if (this.data.multiline) {
364
+ this.buildFormGroup();
365
+ }
366
+ else {
367
+ this.formControlSubscription = this.data.control.valueChanges.subscribe((value) => {
368
+ this.data.control.setValue(value, { onlySelf: true, emitEvent: false, emitModelToViewChange: true });
369
+ this.data.control.markAsDirty();
370
+ }, () => { }, () => { });
559
371
  }
560
- this.dialogRef.close({ button: 'save', reason: this.formGroup.controls.reason.value });
561
- }
562
- cancel() {
563
- this.dialogRef.close({ button: 'cancel' });
564
- }
565
- }
566
- ReasonDialogComponent.decorators = [
567
- { type: Component, args: [{
568
- selector: 'configcat-reason-dialog',
569
- template: "<h3 mat-dialog-title>Add notes</h3>\n<p>This will appear in the Why column of the Audit Log.</p>\n<form [formGroup]=\"formGroup\" (ngSubmit)=\"handleSaveButton()\">\n <div mat-dialog-content class=\"container\">\n <mat-form-field appearance=\"outline\">\n <mat-label></mat-label>\n <textarea matInput formControlName=\"reason\" name=\"reason\" placeholder=\"I'm changing settings because...\"\n rows=\"3\"></textarea>\n <mat-error *ngIf=\"formGroup.controls.reason.invalid\">The field is required.</mat-error>\n </mat-form-field>\n </div>\n <br>\n <div mat-dialog-actions>\n <button type=\"submit\" style=\"margin-right: 10px;\" mat-raised-button color=\"primary\">Add notes & Save</button>\n <button type=\"button\" mat-raised-button (click)=\"cancel()\">Cancel</button>\n </div>\n</form>",
570
- styles: [".container>*{width:100%}\n"]
571
- },] }
572
- ];
573
- ReasonDialogComponent.ctorParameters = () => [
574
- { type: MatDialogRef },
575
- { type: FormBuilder }
576
- ];
577
-
578
- class SaveSnackbarComponent {
579
- constructor() { }
580
- ngOnInit() {
581
- }
582
- }
583
- SaveSnackbarComponent.decorators = [
584
- { type: Component, args: [{
585
- selector: 'configcat-save-snackbar',
586
- template: "<p class=\"header\">Settings saved successfully.</p>",
587
- styles: ["p{text-align:center;font-weight:bold}.header{font-size:1.2em}\n"]
588
- },] }
589
- ];
590
- SaveSnackbarComponent.ctorParameters = () => [];
591
-
592
- class RolloutPercentageItemsComponent {
593
- constructor() {
594
- this.SettingType = SettingType;
595
372
  }
596
- setting() {
597
- return this.formGroup.get('setting').value;
373
+ values() {
374
+ return this.formGroup.get('values');
598
375
  }
599
- rolloutRules() {
600
- return this.formGroup.get('rolloutRules');
376
+ getValueControls() {
377
+ return this.values().controls.map(c => c);
601
378
  }
602
- rolloutPercentageItems() {
603
- return this.formGroup.get('rolloutPercentageItems');
379
+ buildFormGroup() {
380
+ const value = this.data.control.value ? this.data.control.value : '';
381
+ const list = value.split(',').map((v) => v.trim()).filter((v) => !!v);
382
+ this.formGroup = this.formBuilder.group({
383
+ values: this.formBuilder.array(list.map((v) => this.formBuilder.control(v)))
384
+ });
385
+ this.formGroupSubscription = this.formGroup.valueChanges.subscribe((formGroupValue) => {
386
+ this.data.control.setValue(formGroupValue.values
387
+ .filter((v) => !!v)
388
+ .join(','), { onlySelf: true, emitEvent: false, emitModelToViewChange: true });
389
+ this.data.control.updateValueAndValidity();
390
+ this.data.control.markAsDirty();
391
+ });
604
392
  }
605
- rolloutPercentageItemsSum() {
606
- return this.formGroup.get('rolloutPercentageItemsSum');
393
+ add() {
394
+ this.formGroup.controls.values.push(this.formBuilder.control(''));
607
395
  }
608
- value(rolloutPercentageItem) {
609
- return this.setting().settingType === SettingType.Boolean ?
610
- rolloutPercentageItem.get('valueDisabled') :
611
- rolloutPercentageItem.get('value');
396
+ remove(index) {
397
+ this.formGroup.controls.values.removeAt(index);
612
398
  }
613
- percentageChanged(index) {
614
- if (this.rolloutPercentageItems().controls[index].value.percentage < 0) {
615
- this.rolloutPercentageItems().controls[index].controls.percentage.setValue(0);
616
- }
617
- if (this.rolloutPercentageItems().controls[index].value.percentage > 100) {
618
- this.rolloutPercentageItems().controls[index].controls.percentage.setValue(100);
399
+ ngOnDestroy() {
400
+ if (this.formControlSubscription) {
401
+ this.formControlSubscription.unsubscribe();
402
+ this.formControlSubscription = null;
619
403
  }
620
- if (this.rolloutPercentageItems().controls.length === 2) {
621
- this.rolloutPercentageItems().controls[1 - index].controls.percentage
622
- .setValue(100 - this.rolloutPercentageItems().controls[index].value.percentage);
404
+ if (this.formGroupSubscription) {
405
+ this.formGroupSubscription.unsubscribe();
406
+ this.formGroupSubscription = null;
623
407
  }
624
- this.updateSum();
625
408
  }
626
- updateSum() {
627
- if (this.rolloutPercentageItems().controls.length === 0) {
628
- this.rolloutPercentageItemsSum().setValue(100);
629
- }
630
- else {
631
- this.rolloutPercentageItemsSum().setValue(this.rolloutPercentageItems().controls.map(v => v.value.percentage)
632
- .reduce((a, b) => a + b));
633
- }
409
+ close() {
410
+ this.dialogRef.close();
634
411
  }
635
- removeRolloutPercentageItem(index) {
636
- this.rolloutPercentageItems().removeAt(index);
637
- this.rolloutPercentageItems().markAsDirty();
638
- this.updateSum();
412
+ }
413
+ ExpandDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ExpandDialogComponent, deps: [{ token: i1$1.MatDialogRef }, { token: i2.FormBuilder }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
414
+ ExpandDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: ExpandDialogComponent, selector: "configcat-expand-dialog", ngImport: i0, template: "<div mat-dialog-content class=\"container\">\n\n <mat-form-field appearance=\"outline\" *ngIf=\"!data.multiline\">\n <mat-label></mat-label>\n <textarea matInput [formControl]=\"data.control\" rows=\"15\"></textarea>\n </mat-form-field>\n <div [formGroup]=\"formGroup\" *ngIf=\"data.multiline\">\n <div *ngFor=\"let valueControl of getValueControls(); let i = index;\"\n class=\"value-field\">\n <mat-form-field appearance=\"outline\">\n <input matInput [formControl]=\"valueControl\" rows=\"15\" />\n </mat-form-field>\n <button (click)=\"remove(i)\" mat-icon-button type=\"button\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n </div>\n <button (click)=\"add()\" mat-icon-button type=\"button\">\n <mat-icon>add</mat-icon>\n </button>\n </div>\n</div>\n<div mat-dialog-actions>\n <button type=\"button\" mat-raised-button color=\"primary\" (click)=\"close()\">Done</button>\n</div>", styles: [".container>*{width:100%}.container>* .value-field{display:flex;font-size:12px;flex-wrap:nowrap}.container>* .value-field mat-form-field{flex-grow:1}\n"], components: [{ type: i3.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }, { type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i1$1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.MatLabel, selector: "mat-label" }, { type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["id", "disabled", "required", "type", "value", "readonly", "placeholder", "errorStateMatcher", "aria-describedby"], exportAs: ["matInput"] }, { type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i5$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]" }] });
415
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ExpandDialogComponent, decorators: [{
416
+ type: Component,
417
+ args: [{
418
+ selector: 'configcat-expand-dialog',
419
+ templateUrl: './expand-dialog.component.html',
420
+ styleUrls: ['./expand-dialog.component.scss']
421
+ }]
422
+ }], ctorParameters: function () { return [{ type: i1$1.MatDialogRef }, { type: i2.FormBuilder }, { type: undefined, decorators: [{
423
+ type: Inject,
424
+ args: [MAT_DIALOG_DATA]
425
+ }] }]; } });
426
+
427
+ class ValueComponent {
428
+ constructor(dialog) {
429
+ this.dialog = dialog;
430
+ this.SettingType = SettingType;
639
431
  }
640
- removeAll() {
641
- this.rolloutPercentageItems().clear();
642
- this.rolloutPercentageItems().markAsDirty();
643
- this.updateSum();
432
+ popOut() {
433
+ this.dialog.open(ExpandDialogComponent, {
434
+ width: '640px',
435
+ data: { control: this.valueFormControl }
436
+ });
644
437
  }
645
438
  }
646
- RolloutPercentageItemsComponent.decorators = [
647
- { type: Component, args: [{
648
- selector: 'configcat-rollout-percentage-items',
649
- template: "<div class=\"percentage-wrapper\" [formGroup]=\"formGroup\">\n\n <div class=\"if\" *ngIf=\"rolloutRules().controls.length > 0\">\n <mat-chip-list>\n <mat-chip class=\"chip\">ELSE IF</mat-chip>\n </mat-chip-list>\n </div>\n <div class=\"grow\">\n <div class=\"percentage-row flex\"\n *ngFor=\"let rolloutPercentageItem of rolloutPercentageItems().controls; let i = index\">\n <div class=\"flex\">\n <input type=\"number\" [formControl]=\"rolloutPercentageItem.controls.percentage\"\n (input)=\"percentageChanged(i)\">%\n <span class=\"users\">of <a href=\"https://configcat.com/docs/advanced/user-object\"\n target=\"_blank\" rel=\"noopener noreferrer\">users</a>\n </span>\n </div>\n\n <div class=\"delete-option\" matTooltip=\"Remove this % option\"\n *ngIf=\"!readOnly && rolloutPercentageItems().controls.length > 2\"\n (click)=\"removeRolloutPercentageItem(i)\">\n <mat-icon>cancel</mat-icon>\n </div>\n\n <span data-tooltip=\"The sum of percentages must be equal to 100.\"\n *ngIf=\"(i === rolloutPercentageItems().controls.length -1) && (rolloutPercentageItemsSum().invalid)\">\n <mat-icon color=\"warn\">\n error_outline\n </mat-icon>\n </span>\n\n <div class=\"value flex\">\n <span class=\"value-name\" *ngIf=\"setting().settingType !== SettingType.Boolean\">Served value:</span>\n <configcat-value class=\"served-value\" [valueFormControl]=\"value(rolloutPercentageItem)\" [setting]=\"setting()\">\n </configcat-value>\n </div>\n <div class=\"delete\" matTooltip=\"Remove % options\" (click)=\"removeAll()\" *ngIf=\"!readOnly\">\n <mat-icon>cancel</mat-icon>\n </div>\n </div>\n </div>\n</div>",
650
- styles: [".percentage-wrapper{border:1px solid #d5d5d5;background-color:#fff;border-radius:4px;margin:.5em 1em;position:relative;padding:.5em 0;display:flex}.percentage-wrapper .delete{position:absolute;color:#d6a9a9;cursor:pointer;top:-8px;right:-12px}.percentage-wrapper .delete:hover{color:#ed5251}.percentage-wrapper .delete mat-icon{font-size:20px}.percentage-wrapper .delete-option{color:#d6a9a9;cursor:pointer;margin-left:1rem;margin-top:4px}.percentage-wrapper .delete-option:hover{color:#ed5251}.percentage-wrapper .delete-option mat-icon{font-size:20px}.if{display:flex;align-items:center}.if>*{margin:0 .5em}.if .spacing{margin-right:44px}.if .chip{background-color:#67b1fc3d}.grow{flex-grow:1}.percentage-row{min-height:40px;margin-left:.5em}.flex{display:flex;flex-wrap:wrap;align-items:center}.value{margin-left:auto;min-width:100px}.value .value-name{display:none}input{background-color:#fff;border:1px solid #e5e5e5;min-height:28px;width:40px;margin-right:5px;font-size:15px;padding:0 5px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300}.users{margin-left:20px}@media (max-width: 700px){.percentage-wrapper{margin:.5em .3em}}\n"]
651
- },] }
652
- ];
653
- RolloutPercentageItemsComponent.ctorParameters = () => [];
654
- RolloutPercentageItemsComponent.propDecorators = {
655
- formGroup: [{ type: Input }],
656
- readOnly: [{ type: Input }]
657
- };
439
+ ValueComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ValueComponent, deps: [{ token: i1$1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
440
+ ValueComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: ValueComponent, selector: "configcat-value", inputs: { valueFormControl: "valueFormControl", setting: "setting" }, ngImport: i0, template: "<div class=\"flex\">\n <div class=\"container\" [ngSwitch]=\"setting.settingType\">\n <div *ngSwitchCase=\"SettingType.String\" class=\"with-validation\">\n <span *ngIf=\"valueFormControl.invalid\" class=\"error\">*</span>\n <input [ngClass]=\"{'invalid': valueFormControl.invalid}\" type=\"text\"\n [formControl]=\"valueFormControl\" placeholder=\"my value\" name=\"value\">\n <div class=\"expand\" (click)=\"popOut()\" matTooltip=\"Pop-out Editor\">\n <mat-icon>height</mat-icon>\n </div>\n </div>\n <div *ngSwitchCase=\"SettingType.Int\" class=\"with-validation\">\n <span *ngIf=\"valueFormControl.invalid\" class=\"error\">*</span>\n <input [ngClass]=\"{'invalid': valueFormControl.invalid}\" type=\"number\" [formControl]=\"valueFormControl\"\n placeholder=\"42\" name=\"value\" />\n </div>\n <div *ngSwitchCase=\"SettingType.Double\" class=\"with-validation\">\n <span *ngIf=\"valueFormControl.invalid\" class=\"error\">*</span>\n <input [ngClass]=\"{'invalid': valueFormControl.invalid}\" type=\"number\" [formControl]=\"valueFormControl\"\n placeholder=\"3.14\" name=\"value\">\n </div>\n <div *ngSwitchCase=\"SettingType.Boolean\" class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" [formControl]=\"valueFormControl\" name=\"value\" />\n <span class=\"slider round\"></span>\n </label>\n </div>\n </div>\n <div *ngIf=\"setting.settingType === SettingType.Boolean\" class=\"spacer\"></div>\n\n</div>\n", styles: [".switch{position:relative;width:66px;height:30px;margin-left:4px;margin-top:0;margin-bottom:0}.switch input{display:none}.slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ff5252;transition:.4s;width:66px;height:30px}.slider:before{position:absolute;content:\"\";height:22px;width:22px;left:4px;bottom:4px;background-color:#fff;transition:.4s}input:checked+.slider{background-color:#9acd32}input:focus+.slider{box-shadow:0 0 1px #9acd32}input:checked+.slider:before{transform:translate(36px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}.slider:after{content:\"Off\";color:#fff;display:block;position:absolute;transform:translate(-14%,-50%);top:50%;left:50%;font-size:10px;font-family:Verdana,sans-serif}input:checked+.slider:after{content:\"On\";transform:translate(-91%,-50%)}\n", ".flex{display:flex;margin-right:20px;align-items:center}.flex .spacer{width:15px}.flex .container .with-validation{display:flex;flex-wrap:nowrap}.flex .container .toggle{width:66px;height:30px}.flex .container input{background-color:#fff;border:1px solid #e5e5e5;min-height:28px;font-size:15px;padding:0 30px 0 10px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300;min-width:170px}.flex .container .invalid{border:1px solid #ff5252}.flex .container .error{color:#ff5252;margin-right:5px}@media (max-width: 1200px){.flex .container input{min-width:auto}}.flex .variationId{margin-left:5px;cursor:pointer;color:#888;display:flex;align-items:center}.flex .expand{position:relative;top:2px;height:24px;cursor:pointer;margin-left:-28px;z-index:1;color:#b3b3b3;background-color:#fff}.flex .expand mat-icon{transform:rotate(45deg)}\n"], components: [{ type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i5$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { type: i5$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i5$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i5$2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }] });
441
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ValueComponent, decorators: [{
442
+ type: Component,
443
+ args: [{
444
+ selector: 'configcat-value',
445
+ templateUrl: './value.component.html',
446
+ styleUrls: [
447
+ './slider.scss',
448
+ './value.component.scss'
449
+ ]
450
+ }]
451
+ }], ctorParameters: function () { return [{ type: i1$1.MatDialog }]; }, propDecorators: { valueFormControl: [{
452
+ type: Input
453
+ }], setting: [{
454
+ type: Input
455
+ }] } });
658
456
 
659
457
  class RolloutRulesComponent {
660
458
  constructor(dialog) {
661
459
  this.dialog = dialog;
460
+ this.readOnly = false;
662
461
  this.SettingType = SettingType;
663
462
  this.defaultComparisonAttributes = ['Identifier', 'Email', 'Country'];
664
463
  this.rolloutRuleComparators = [
@@ -683,16 +482,19 @@ class RolloutRulesComponent {
683
482
  ];
684
483
  }
685
484
  onlyFirstTooltip() {
686
- return this.formGroup.root.get('allRolloutRulesCount').value > 20 || this.rolloutRules().controls.length > 10;
485
+ var _a;
486
+ return ((_a = this.formGroup.root.get('allRolloutRulesCount')) === null || _a === void 0 ? void 0 : _a.value) > 20 || this.rolloutRules().controls.length > 10;
687
487
  }
688
488
  setting() {
689
- return this.formGroup.get('setting').value;
489
+ var _a;
490
+ return (_a = this.formGroup.get('setting')) === null || _a === void 0 ? void 0 : _a.value;
690
491
  }
691
492
  rolloutRules() {
692
493
  return this.formGroup.get('rolloutRules');
693
494
  }
694
495
  comparisonAttributes() {
695
- return this.formGroup.root.get('comparisonAttributes').value;
496
+ var _a;
497
+ return (_a = this.formGroup.root.get('comparisonAttributes')) === null || _a === void 0 ? void 0 : _a.value;
696
498
  }
697
499
  removeRule(index) {
698
500
  this.rolloutRules().removeAt(index);
@@ -704,9 +506,9 @@ class RolloutRulesComponent {
704
506
  this.formGroup.markAsDirty();
705
507
  }
706
508
  attributeChanged(event, comparisonAttribute) {
707
- if (this.defaultComparisonAttributes.indexOf(event) !== -1) {
708
- if (comparisonAttribute.value !== event) {
709
- comparisonAttribute.setValue(event);
509
+ if (this.defaultComparisonAttributes.indexOf(event.value) !== -1) {
510
+ if (comparisonAttribute.value !== event.value) {
511
+ comparisonAttribute.setValue(event.value);
710
512
  this.formGroup.markAsDirty();
711
513
  }
712
514
  }
@@ -854,7 +656,7 @@ class RolloutRulesComponent {
854
656
  }
855
657
  }
856
658
  validateSemVer(c) {
857
- if (semver.valid(c.value) === null) {
659
+ if (valid(c.value) === null) {
858
660
  return {
859
661
  validateSemVer: {
860
662
  valid: false
@@ -871,8 +673,8 @@ class RolloutRulesComponent {
871
673
  }
872
674
  };
873
675
  }
874
- const semvers = c.value.split(',').map(s => s.trim());
875
- if (semvers.find(s => s && s !== '' && semver.valid(s) === null)) {
676
+ const semvers = c.value.split(',').map((s) => s.trim());
677
+ if (semvers.find((s) => s && s !== '' && valid(s) === null)) {
876
678
  return {
877
679
  validateSemVerList: {
878
680
  valid: false
@@ -893,177 +695,238 @@ class RolloutRulesComponent {
893
695
  trackByIndex(index) {
894
696
  return index;
895
697
  }
698
+ getRolloutRuleValueFormControl(rolloutRule) {
699
+ return rolloutRule.controls.value;
700
+ }
701
+ getRolloutRuleComparisonValueFormControl(rolloutRule) {
702
+ return rolloutRule.controls.comparisonValue;
703
+ }
704
+ getRolloutRuleComparisonAttributeFormControl(rolloutRule) {
705
+ return rolloutRule.controls.comparisonAttribute;
706
+ }
707
+ getRolloutRuleComparatorFormControl(rolloutRule) {
708
+ return rolloutRule.controls.comparator;
709
+ }
896
710
  }
897
- RolloutRulesComponent.decorators = [
898
- { type: Component, args: [{
899
- selector: 'configcat-rollout-rules',
900
- template: "<div [formGroup]=\"formGroup\">\n <div cdkDropList (cdkDropListDropped)=\"onDrop($event)\" [cdkDropListDisabled]=\"readOnly\"\n [cdkDropListData]=\"rolloutRules().controls\">\n <div *ngFor=\"let rolloutRule of rolloutRules().controls; trackBy: trackByIndex; let i = index\" cdkDrag>\n <div class=\"targeting-row\">\n <div class=\"fields flex\">\n <div class=\"drag-handle\" cdkDragHandle data-tooltip=\"Drag here to reorder\" *ngIf=\"!readOnly\">\n <mat-icon>drag_indicator</mat-icon>\n </div>\n <div class=\"if\">\n <mat-chip-list [class.spacing]=\"i === 0\">\n <mat-chip class=\"chip\">{{ i === 0 ? 'IF' : 'ELSE IF' }}</mat-chip>\n </mat-chip-list>\n <a href=\"https://configcat.com/docs/advanced/user-object\" target=\"_blank\" rel=\"noopener noreferrer\">user's</a>\n </div>\n\n <select class=\"custom-select attribute-select\" matTooltip=\"Comparison attribute\"\n matTooltipPosition=\"above\" [matTooltipDisabled]=\"onlyFirstTooltip() && i !== 0\"\n (change)=\"attributeChanged($event.target.value, rolloutRule.controls.comparisonAttribute)\">\n <option *ngFor=\"let comparisonAttribute of defaultComparisonAttributes\"\n [selected]=\"comparisonAttribute === rolloutRule.controls.comparisonAttribute.value\">\n {{comparisonAttribute}}</option>\n <option\n [selected]=\"defaultComparisonAttributes.indexOf(rolloutRule.controls.comparisonAttribute.value) === -1\">\n Custom\n </option>\n </select>\n\n\n <span class=\"attribute-span\" data-tooltip=\"Custom user attribute\"\n [hidden]=\"defaultComparisonAttributes.indexOf(rolloutRule.controls.comparisonAttribute.value) !== -1\">\n <span class=\"attribute-error\" *ngIf=\"rolloutRule.controls.comparisonAttribute.invalid\">*</span>\n <input [ngClass]=\"{'invalid': rolloutRule.controls.comparisonAttribute.invalid}\" type=\"text\"\n matInput [matAutocomplete]=\"auto\" [formControl]=\"rolloutRule.controls.comparisonAttribute\"\n class=\"attribute\" placeholder=\"e.g. Eye color\">\n </span>\n\n <mat-autocomplete #auto=\"matAutocomplete\">\n <mat-option *ngFor=\"let option of comparisonAttributes()\" [value]=\"option\">\n {{option}}\n </mat-option>\n </mat-autocomplete>\n\n <div class=\"select-wrapper\">\n <select class=\"custom-select\" [formControl]=\"rolloutRule.controls.comparator\"\n matTooltip=\"Comparator\" matTooltipPosition=\"above\"\n [matTooltipDisabled]=\"onlyFirstTooltip() && i !== 0\"\n (change)=\"comparatorChanged(rolloutRule)\">\n <optgroup label=\"SENSITIVE TEXT\">\n <option *ngFor=\"let comparator of rolloutRuleComparators.slice(16, 18)\"\n [ngValue]=\"comparator.value\">\n {{comparator.label}}\n </option>\n </optgroup>\n <optgroup label=\"TEXT\">\n <option *ngFor=\"let comparator of rolloutRuleComparators.slice(0, 4)\"\n [ngValue]=\"comparator.value\">\n {{comparator.label}}\n </option>\n </optgroup>\n <optgroup label=\"SEMANTIC VERSION\">\n <option *ngFor=\"let comparator of rolloutRuleComparators.slice(4, 10)\"\n [ngValue]=\"comparator.value\">\n {{comparator.label}}\n </option>\n </optgroup>\n <optgroup label=\"NUMBER\">\n <option *ngFor=\"let comparator of rolloutRuleComparators.slice(10, 16)\"\n [ngValue]=\"comparator.value\">\n {{comparator.label}}\n </option>\n </optgroup>\n </select>\n <div [class]=\"getInfoClass(rolloutRule)\" [matTooltip]=\"getInfoTooltip(rolloutRule)\"\n [matTooltipDisabled]=\"onlyFirstTooltip() && i !== 0\">\n <a href=\"https://configcat.com/docs/advanced/targeting/#comparator\" target=\"_blank\" rel=\"noopener noreferrer\">\n <mat-icon>info</mat-icon>\n </a>\n </div>\n </div>\n\n <div class=\"comparison-value\" data-tooltip=\"Comparison value\">\n <span class=\"error\" *ngIf=\"rolloutRule.controls.comparisonValue.invalid\">*</span>\n\n <input [ngClass]=\"{'invalid': rolloutRule.controls.comparisonValue.invalid}\"\n [formControl]=\"rolloutRule.controls.comparisonValue\"\n [type]=\"getComparisonValueType(rolloutRule)\" [placeholder]=\"getPlaceHolder(rolloutRule)\">\n <div class=\"expand\" matTooltip=\"Pop-out Editor\" (click)=\"popOut(rolloutRule)\" *ngIf=\"getComparisonValueType(rolloutRule)==='text'\">\n <mat-icon>height</mat-icon>\n </div>\n </div>\n\n <div class=\"delete\" matTooltip=\"Remove targeting rule\"\n [matTooltipDisabled]=\"onlyFirstTooltip() && i !== 0\" *ngIf=\"!readOnly\" (click)=\"removeRule(i)\">\n <mat-icon>cancel</mat-icon>\n </div>\n </div>\n <div class=\"value flex\">\n <div class=\"serve\" *ngIf=\"setting().settingType !== SettingType.Boolean\">\n <mat-chip-list>\n <mat-chip class=\"chip\">Serve:</mat-chip>\n </mat-chip-list>\n </div>\n\n <configcat-value class=\"served-value\" [valueFormControl]=\"rolloutRule.controls.value\"\n [setting]=\"setting()\">\n </configcat-value>\n </div>\n </div>\n </div>\n </div>\n</div>",
901
- styles: [".cdk-drop-list-dragging .cdk-drag{transition:transform .25s cubic-bezier(0,0,.2,1)}.cdk-drag-animating{transition:transform .3s cubic-bezier(0,0,.2,1)}.targeting-row{border:1px solid #d5d5d5;border-radius:4px;background-color:#fff;margin:.5em 1em;display:flex;position:relative;padding:.5em 0}.targeting-row .select-wrapper{position:relative}.targeting-row .info{position:absolute;cursor:pointer;top:-8px;left:166px}.targeting-row .info a{color:#7fb3fd}.targeting-row .info a:hover{color:#0068f0}.targeting-row .info mat-icon{font-size:18px}.targeting-row .warn{position:absolute;cursor:pointer;top:-8px;left:128px}.targeting-row .warn a{color:#fdc67f}.targeting-row .warn a:hover{color:#ff9200}.targeting-row .warn mat-icon{font-size:18px}.chip{background-color:#67b1fc3d}.fields{justify-content:left;max-width:1060px;flex-grow:100}.fields .if{display:flex;align-items:center}.fields .if>*{margin:0 .5em}.fields .if .spacing{margin-right:46px}.fields .attribute-span{margin-left:.5em}.fields .attribute{width:170px;background-color:#fff;border:1px solid #e5e5e5;min-height:28px;min-width:170px;font-size:15px;padding:0 10px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300}.fields .invalid{border:1px solid #ff5252}.fields .attribute-error{color:#ff5252;margin-right:5px}.fields .delete{position:absolute;color:#d6a9a9;cursor:pointer;top:-8px;right:-12px;z-index:999}.fields .delete:hover{color:#ed5251}.fields .delete mat-icon{font-size:20px}.fields .drag-handle{position:absolute;cursor:-webkit-grab;cursor:-moz-grab;left:-20px;top:14px}.fields .drag-handle mat-icon{font-size:20px}.fields .custom-select{margin-left:.5em;width:170px;background-color:#fff;border:1px solid #e5e5e5;min-height:30px;min-width:170px;font-size:.8em;padding:0 5px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300}.fields .attribute-select{min-width:100px;width:100px;margin-left:0}.fields .comparison-value{flex-grow:100;min-width:200px;display:flex;align-items:center;margin-left:.5em}.fields .comparison-value .error{color:#ff5252;margin:0 5px}.fields .comparison-value input{background-color:#fff;border:1px solid #e5e5e5;min-height:28px;min-width:170px;flex-grow:100;font-size:15px;padding:0 30px 0 10px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300}.fields .comparison-value .invalid{border:1px solid #ff5252}.value{margin-left:auto;flex-wrap:nowrap}.value .serve{margin:0 5px}.warn{padding-left:3em;font-size:.8em}.flex{display:flex;flex-wrap:wrap;align-items:center}.expand{position:absolute;right:2px;font-size:8px;cursor:pointer;margin-left:-28px;z-index:1;color:#b3b3b3;background-color:#fff}.expand mat-icon{transform:rotate(45deg)}@media (max-width: 1200px){.targeting-row{flex-wrap:wrap}.targeting-row .fields{max-width:500px}}@media (max-width: 700px){.targeting-row{flex-direction:column;margin:.5em .3em}.targeting-row .help{left:161px}.targeting-row .fields{flex-direction:column;align-items:stretch}.targeting-row .fields .custom-select{margin:0}.targeting-row .fields .comparison-value{margin:0}}\n"]
902
- },] }
903
- ];
904
- RolloutRulesComponent.ctorParameters = () => [
905
- { type: MatDialog }
906
- ];
907
- RolloutRulesComponent.propDecorators = {
908
- formGroup: [{ type: Input }],
909
- readOnly: [{ type: Input }]
910
- };
711
+ RolloutRulesComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: RolloutRulesComponent, deps: [{ token: i1$1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
712
+ RolloutRulesComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: RolloutRulesComponent, selector: "configcat-rollout-rules", inputs: { formGroup: "formGroup", readOnly: "readOnly" }, ngImport: i0, template: "<div [formGroup]=\"formGroup\">\n <div cdkDropList (cdkDropListDropped)=\"onDrop($event)\" [cdkDropListDisabled]=\"readOnly\"\n [cdkDropListData]=\"rolloutRules().controls\">\n <div *ngFor=\"let rolloutRule of rolloutRules().controls; trackBy: trackByIndex; let i = index\" cdkDrag>\n <div class=\"targeting-row\">\n <div class=\"fields flex\">\n <div class=\"drag-handle\" cdkDragHandle data-tooltip=\"Drag here to reorder\" *ngIf=\"!readOnly\">\n <mat-icon>drag_indicator</mat-icon>\n </div>\n <div class=\"if\">\n <mat-chip-list [class.spacing]=\"i === 0\">\n <mat-chip class=\"chip\">{{ i === 0 ? 'IF' : 'ELSE IF' }}</mat-chip>\n </mat-chip-list>\n <a href=\"https://configcat.com/docs/advanced/user-object\" target=\"_blank\"\n rel=\"noopener noreferrer\">user's</a>\n </div>\n\n <select class=\"custom-select attribute-select\" matTooltip=\"Comparison attribute\"\n matTooltipPosition=\"above\" [matTooltipDisabled]=\"onlyFirstTooltip() && i !== 0\"\n (change)=\"attributeChanged($event.target, getRolloutRuleComparisonAttributeFormControl(rolloutRule))\">\n <option *ngFor=\"let comparisonAttribute of defaultComparisonAttributes\"\n [selected]=\"comparisonAttribute === getRolloutRuleComparisonAttributeFormControl(rolloutRule).value\">\n {{comparisonAttribute}}</option>\n <option\n [selected]=\"defaultComparisonAttributes.indexOf(getRolloutRuleComparisonAttributeFormControl(rolloutRule).value) === -1\">\n Custom\n </option>\n </select>\n\n\n <span class=\"attribute-span\" data-tooltip=\"Custom user attribute\"\n [hidden]=\"defaultComparisonAttributes.indexOf(getRolloutRuleComparisonAttributeFormControl(rolloutRule).value) !== -1\">\n <span class=\"attribute-error\"\n *ngIf=\"getRolloutRuleComparisonAttributeFormControl(rolloutRule).invalid\">*</span>\n <input\n [ngClass]=\"{'invalid': getRolloutRuleComparisonAttributeFormControl(rolloutRule).invalid}\"\n type=\"text\" matInput [matAutocomplete]=\"auto\"\n [formControl]=\"getRolloutRuleComparisonAttributeFormControl(rolloutRule)\" class=\"attribute\"\n placeholder=\"e.g. Eye color\">\n </span>\n\n <mat-autocomplete #auto=\"matAutocomplete\">\n <mat-option *ngFor=\"let option of comparisonAttributes()\" [value]=\"option\">\n {{option}}\n </mat-option>\n </mat-autocomplete>\n\n <div class=\"select-wrapper\">\n <select class=\"custom-select\" [formControl]=\"getRolloutRuleComparatorFormControl(rolloutRule)\"\n matTooltip=\"Comparator\" matTooltipPosition=\"above\"\n [matTooltipDisabled]=\"onlyFirstTooltip() && i !== 0\"\n (change)=\"comparatorChanged(rolloutRule)\">\n <optgroup label=\"SENSITIVE TEXT\">\n <option *ngFor=\"let comparator of rolloutRuleComparators.slice(16, 18)\"\n [ngValue]=\"comparator.value\">\n {{comparator.label}}\n </option>\n </optgroup>\n <optgroup label=\"TEXT\">\n <option *ngFor=\"let comparator of rolloutRuleComparators.slice(0, 4)\"\n [ngValue]=\"comparator.value\">\n {{comparator.label}}\n </option>\n </optgroup>\n <optgroup label=\"SEMANTIC VERSION\">\n <option *ngFor=\"let comparator of rolloutRuleComparators.slice(4, 10)\"\n [ngValue]=\"comparator.value\">\n {{comparator.label}}\n </option>\n </optgroup>\n <optgroup label=\"NUMBER\">\n <option *ngFor=\"let comparator of rolloutRuleComparators.slice(10, 16)\"\n [ngValue]=\"comparator.value\">\n {{comparator.label}}\n </option>\n </optgroup>\n </select>\n <div [class]=\"getInfoClass(rolloutRule)\" [matTooltip]=\"getInfoTooltip(rolloutRule)\"\n [matTooltipDisabled]=\"onlyFirstTooltip() && i !== 0\">\n <a href=\"https://configcat.com/docs/advanced/targeting/#comparator\" target=\"_blank\"\n rel=\"noopener noreferrer\">\n <mat-icon>info</mat-icon>\n </a>\n </div>\n </div>\n\n <div class=\"comparison-value\" data-tooltip=\"Comparison value\">\n <span class=\"error\"\n *ngIf=\"getRolloutRuleComparisonValueFormControl(rolloutRule).invalid\">*</span>\n\n <input [ngClass]=\"{'invalid': getRolloutRuleComparisonValueFormControl(rolloutRule).invalid}\"\n [formControl]=\"getRolloutRuleComparisonValueFormControl(rolloutRule)\"\n [type]=\"getComparisonValueType(rolloutRule)\" [placeholder]=\"getPlaceHolder(rolloutRule)\">\n <div class=\"expand\" matTooltip=\"Pop-out Editor\" (click)=\"popOut(rolloutRule)\"\n *ngIf=\"getComparisonValueType(rolloutRule)==='text'\">\n <mat-icon>height</mat-icon>\n </div>\n </div>\n\n <div class=\"delete\" matTooltip=\"Remove targeting rule\"\n [matTooltipDisabled]=\"onlyFirstTooltip() && i !== 0\" *ngIf=\"!readOnly\" (click)=\"removeRule(i)\">\n <mat-icon>cancel</mat-icon>\n </div>\n </div>\n <div class=\"value flex\">\n <div class=\"serve\" *ngIf=\"setting().settingType !== SettingType.Boolean\">\n <mat-chip-list>\n <mat-chip class=\"chip\">Serve:</mat-chip>\n </mat-chip-list>\n </div>\n\n <configcat-value class=\"served-value\"\n [valueFormControl]=\"getRolloutRuleValueFormControl(rolloutRule)\" [setting]=\"setting()\">\n </configcat-value>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: [".cdk-drop-list-dragging .cdk-drag{transition:transform .25s cubic-bezier(0,0,.2,1)}.cdk-drag-animating{transition:transform .3s cubic-bezier(0,0,.2,1)}.targeting-row{border:1px solid #d5d5d5;border-radius:4px;background-color:#fff;margin:.5em 1em;display:flex;position:relative;padding:.5em 0}.targeting-row .select-wrapper{position:relative}.targeting-row .info{position:absolute;cursor:pointer;top:-8px;left:166px}.targeting-row .info a{color:#7fb3fd}.targeting-row .info a:hover{color:#0068f0}.targeting-row .info mat-icon{font-size:18px}.targeting-row .warn{position:absolute;cursor:pointer;top:-8px;left:128px}.targeting-row .warn a{color:#fdc67f}.targeting-row .warn a:hover{color:#ff9200}.targeting-row .warn mat-icon{font-size:18px}.chip{background-color:#67b1fc3d}.fields{justify-content:left;max-width:1060px;flex-grow:100}.fields .if{display:flex;align-items:center}.fields .if>*{margin:0 .5em}.fields .if .spacing{margin-right:46px}.fields .attribute-span{margin-left:.5em}.fields .attribute{width:170px;background-color:#fff;border:1px solid #e5e5e5;min-height:28px;min-width:170px;font-size:15px;padding:0 10px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300}.fields .invalid{border:1px solid #ff5252}.fields .attribute-error{color:#ff5252;margin-right:5px}.fields .delete{position:absolute;color:#d6a9a9;cursor:pointer;top:-8px;right:-12px;z-index:999}.fields .delete:hover{color:#ed5251}.fields .delete mat-icon{font-size:20px}.fields .drag-handle{position:absolute;cursor:-moz-grab;left:-20px;top:14px}.fields .drag-handle mat-icon{font-size:20px}.fields .custom-select{margin-left:.5em;width:170px;background-color:#fff;border:1px solid #e5e5e5;min-height:30px;min-width:170px;font-size:.8em;padding:0 5px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300}.fields .attribute-select{min-width:100px;width:100px;margin-left:0}.fields .comparison-value{flex-grow:100;min-width:200px;display:flex;align-items:center;margin-left:.5em}.fields .comparison-value .error{color:#ff5252;margin:0 5px}.fields .comparison-value input{background-color:#fff;border:1px solid #e5e5e5;min-height:28px;min-width:170px;flex-grow:100;font-size:15px;padding:0 30px 0 10px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300}.fields .comparison-value .invalid{border:1px solid #ff5252}.value{margin-left:auto;flex-wrap:nowrap}.value .serve{margin:0 5px}.warn{padding-left:3em;font-size:.8em}.flex{display:flex;flex-wrap:wrap;align-items:center}.expand{position:absolute;right:2px;font-size:8px;cursor:pointer;margin-left:-28px;z-index:1;color:#b3b3b3;background-color:#fff}.expand mat-icon{transform:rotate(45deg)}@media (max-width: 1200px){.targeting-row{flex-wrap:wrap}.targeting-row .fields{max-width:500px}}@media (max-width: 700px){.targeting-row{flex-direction:column;margin:.5em .3em}.targeting-row .help{left:161px}.targeting-row .fields{flex-direction:column;align-items:stretch}.targeting-row .fields .custom-select{margin:0}.targeting-row .fields .comparison-value{margin:0}}\n"], components: [{ type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: i3$2.MatChipList, selector: "mat-chip-list", inputs: ["aria-orientation", "multiple", "compareWith", "value", "required", "placeholder", "disabled", "selectable", "tabIndex", "errorStateMatcher"], outputs: ["change", "valueChange"], exportAs: ["matChipList"] }, { type: i4$1.MatAutocomplete, selector: "mat-autocomplete", inputs: ["disableRipple"], exportAs: ["matAutocomplete"] }, { type: i4$2.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { type: ValueComponent, selector: "configcat-value", inputs: ["valueFormControl", "setting"] }], directives: [{ type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i8.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "id", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListAutoScrollDisabled", "cdkDropListOrientation", "cdkDropListLockAxis", "cdkDropListData", "cdkDropListAutoScrollStep"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { type: i5$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i8.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragDisabled", "cdkDragStartDelay", "cdkDragLockAxis", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragBoundary", "cdkDragRootElement", "cdkDragPreviewContainer", "cdkDragData", "cdkDragFreeDragPosition"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i8.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { type: i3$2.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["color", "disableRipple", "tabIndex", "selected", "value", "selectable", "disabled", "removable"], outputs: ["selectionChange", "destroyed", "removed"], exportAs: ["matChip"] }, { type: i5$2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["id", "disabled", "required", "type", "value", "readonly", "placeholder", "errorStateMatcher", "aria-describedby"], exportAs: ["matInput"] }, { type: i4$1.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", exportAs: ["matAutocompleteTrigger"] }, { type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i5$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }] });
713
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: RolloutRulesComponent, decorators: [{
714
+ type: Component,
715
+ args: [{
716
+ selector: 'configcat-rollout-rules',
717
+ templateUrl: './rollout-rules.component.html',
718
+ styleUrls: ['./rollout-rules.component.scss']
719
+ }]
720
+ }], ctorParameters: function () { return [{ type: i1$1.MatDialog }]; }, propDecorators: { formGroup: [{
721
+ type: Input
722
+ }], readOnly: [{
723
+ type: Input
724
+ }] } });
911
725
 
912
- class ValueComponent {
913
- constructor(dialog) {
914
- this.dialog = dialog;
726
+ class RolloutPercentageItemsComponent {
727
+ constructor() {
728
+ this.readOnly = false;
915
729
  this.SettingType = SettingType;
916
730
  }
917
- popOut() {
918
- this.dialog.open(ExpandDialogComponent, {
919
- width: '640px',
920
- data: { control: this.valueFormControl }
921
- });
731
+ setting() {
732
+ var _a;
733
+ return (_a = this.formGroup.get('setting')) === null || _a === void 0 ? void 0 : _a.value;
922
734
  }
923
- }
924
- ValueComponent.decorators = [
925
- { type: Component, args: [{
926
- selector: 'configcat-value',
927
- template: "<div class=\"flex\">\n <div class=\"container\" [ngSwitch]=\"setting.settingType\">\n <div *ngSwitchCase=\"SettingType.String\" class=\"with-validation\">\n <span *ngIf=\"valueFormControl.invalid\" class=\"error\">*</span>\n <input [ngClass]=\"{'invalid': valueFormControl.invalid}\" type=\"text\"\n [formControl]=\"valueFormControl\" placeholder=\"my value\" name=\"value\">\n <div class=\"expand\" (click)=\"popOut()\" matTooltip=\"Pop-out Editor\">\n <mat-icon>height</mat-icon>\n </div>\n </div>\n <div *ngSwitchCase=\"SettingType.Int\" class=\"with-validation\">\n <span *ngIf=\"valueFormControl.invalid\" class=\"error\">*</span>\n <input [ngClass]=\"{'invalid': valueFormControl.invalid}\" type=\"number\" [formControl]=\"valueFormControl\"\n placeholder=\"42\" name=\"value\" />\n </div>\n <div *ngSwitchCase=\"SettingType.Double\" class=\"with-validation\">\n <span *ngIf=\"valueFormControl.invalid\" class=\"error\">*</span>\n <input [ngClass]=\"{'invalid': valueFormControl.invalid}\" type=\"number\" [formControl]=\"valueFormControl\"\n placeholder=\"3.14\" name=\"value\">\n </div>\n <div *ngSwitchCase=\"SettingType.Boolean\" class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" [formControl]=\"valueFormControl\" name=\"value\" />\n <span class=\"slider round\"></span>\n </label>\n </div>\n </div>\n <div *ngIf=\"setting.settingType === SettingType.Boolean\" class=\"spacer\"></div>\n\n</div>\n",
928
- styles: [".switch{position:relative;width:66px;height:30px;margin-left:4px;margin-top:0;margin-bottom:0}.switch input{display:none}.slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ff5252;transition:.4s;width:66px;height:30px}.slider:before{position:absolute;content:\"\";height:22px;width:22px;left:4px;bottom:4px;background-color:#fff;transition:.4s}input:checked+.slider{background-color:#9acd32}input:focus+.slider{box-shadow:0 0 1px #9acd32}input:checked+.slider:before{transform:translate(36px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}.slider:after{content:\"Off\";color:#fff;display:block;position:absolute;transform:translate(-14%,-50%);top:50%;left:50%;font-size:10px;font-family:Verdana,sans-serif}input:checked+.slider:after{content:\"On\";transform:translate(-91%,-50%)}\n", ".flex{display:flex;margin-right:20px;align-items:center}.flex .spacer{width:15px}.flex .container .with-validation{display:flex;flex-wrap:nowrap}.flex .container .toggle{width:66px;height:30px}.flex .container input{background-color:#fff;border:1px solid #e5e5e5;min-height:28px;font-size:15px;padding:0 30px 0 10px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300;min-width:170px}.flex .container .invalid{border:1px solid #ff5252}.flex .container .error{color:#ff5252;margin-right:5px}@media (max-width: 1200px){.flex .container input{min-width:auto}}.flex .variationId{margin-left:5px;cursor:pointer;color:#888;display:flex;align-items:center}.flex .expand{position:relative;top:2px;height:24px;cursor:pointer;margin-left:-28px;z-index:1;color:#b3b3b3;background-color:#fff}.flex .expand mat-icon{transform:rotate(45deg)}\n"]
929
- },] }
930
- ];
931
- ValueComponent.ctorParameters = () => [
932
- { type: MatDialog }
933
- ];
934
- ValueComponent.propDecorators = {
935
- valueFormControl: [{ type: Input }],
936
- setting: [{ type: Input }]
937
- };
938
-
939
- class SettingListFormHelper {
940
- constructor(formBuilder) {
941
- this.formBuilder = formBuilder;
735
+ rolloutRules() {
736
+ return this.formGroup.get('rolloutRules');
942
737
  }
943
- createSettingValuesFormGroup(settingValuesModel) {
944
- const formGroup = this.formBuilder.group({
945
- settingValues: this.formBuilder.array(settingValuesModel.settingValues.map(sv => {
946
- return this.createSettingValueFormGroup(sv, settingValuesModel.readOnly);
947
- })),
948
- // Decorator
949
- environment: this.formBuilder.group({
950
- environmentId: [{ value: settingValuesModel.environment.environmentId, disabled: true }],
951
- name: [{ value: settingValuesModel.environment.name, disabled: true }],
952
- }),
953
- comparisonAttributes: [[]],
954
- allRolloutRulesCount: [0]
955
- });
956
- const rolloutRules = this.getAllRolloutRules(formGroup);
957
- const comparisonAttributes = this.getComparisonAttributes(rolloutRules);
958
- formGroup.patchValue({ allRolloutRulesCount: rolloutRules.length });
959
- formGroup.patchValue({ comparisonAttributes: comparisonAttributes });
960
- return formGroup;
738
+ rolloutPercentageItems() {
739
+ return this.formGroup.get('rolloutPercentageItems');
961
740
  }
962
- createSettingValueFormGroup(settingValueModel, readOnly) {
963
- return this.formBuilder.group({
964
- settingId: [settingValueModel.setting.settingId],
965
- value: [
966
- { value: settingValueModel.value, disabled: readOnly },
967
- this.getValidatorsForSettingType(settingValueModel.setting.settingType)
968
- ],
969
- rolloutPercentageItems: this.formBuilder.array(settingValueModel.rolloutPercentageItems.map(p => {
970
- return this.createRolloutPercentageItemFormGroup(p, readOnly, settingValueModel.setting.settingType);
971
- })),
972
- rolloutRules: this.formBuilder.array(settingValueModel.rolloutRules.map(r => {
973
- return this.createRolloutRuleFormGroup(r, readOnly, settingValueModel.setting.settingType);
974
- })),
975
- updatedAt: [{ value: settingValueModel.updatedAt, disabled: true }],
976
- lastUpdaterUserEmail: [{ value: settingValueModel.lastUpdaterUserEmail, disabled: true }],
977
- lastUpdaterUserFullName: [{ value: settingValueModel.lastUpdaterUserFullName, disabled: true }],
978
- rolloutPercentageItemsSum: [{ value: 100, disabled: false }, [Validators.min(100), Validators.max(100)]],
979
- // Decoration
980
- setting: this.formBuilder.group({
981
- settingId: [{ value: settingValueModel.setting.settingId, disabled: true }],
982
- key: [{ value: settingValueModel.setting.key, disabled: true }],
983
- name: [{ value: settingValueModel.setting.name, disabled: true }],
984
- hint: [{ value: settingValueModel.setting.hint, disabled: true }],
985
- settingType: [{ value: settingValueModel.setting.settingType, disabled: true }],
986
- creatorEmail: [{ value: settingValueModel.setting.creatorEmail, disabled: true }],
987
- creatorFullName: [{ value: settingValueModel.setting.creatorFullName, disabled: true }],
988
- createdAt: [{ value: settingValueModel.setting.createdAt, disabled: true }]
989
- }),
990
- integrationLinks: this.formBuilder.array(settingValueModel.integrationLinks.map(il => {
991
- return this.formBuilder.group({
992
- key: [{ value: il.key, disabled: true }],
993
- description: [{ value: il.description, disabled: true }],
994
- integrationLinkType: [{ value: il.integrationLinkType, disabled: true }],
995
- url: [{ value: il.url, disabled: true }],
996
- });
997
- }))
998
- });
741
+ rolloutPercentageItemsSum() {
742
+ return this.formGroup.get('rolloutPercentageItemsSum');
999
743
  }
1000
- createRolloutPercentageItemFormGroup(p, readOnly, settingType) {
1001
- return this.formBuilder.group({
1002
- percentage: [{ value: p.percentage, disabled: readOnly }, [Validators.required, Validators.min(0), Validators.max(100)]],
1003
- value: [
1004
- { value: p.value, disabled: readOnly },
1005
- [].concat(...[Validators.required, this.getValidatorsForSettingType(settingType)])
1006
- ],
1007
- valueDisabled: [
1008
- { value: p.value, disabled: true }, // For percentage item value
1009
- ]
1010
- });
744
+ value(rolloutPercentageItem) {
745
+ return this.setting().settingType === SettingType.Boolean ?
746
+ rolloutPercentageItem.get('valueDisabled') :
747
+ rolloutPercentageItem.get('value');
1011
748
  }
1012
- createRolloutRuleFormGroup(r, readOnly, settingType) {
1013
- return this.formBuilder.group({
1014
- comparisonAttribute: [{ value: r.comparisonAttribute, disabled: readOnly }, [Validators.required]],
1015
- comparator: [{ value: r.comparator, disabled: readOnly }, [Validators.required]],
1016
- comparisonValue: [{ value: r.comparisonValue, disabled: readOnly }, [Validators.required]],
1017
- value: [
1018
- { value: r.value, disabled: readOnly },
1019
- this.getValidatorsForSettingType(settingType)
1020
- ],
1021
- });
749
+ percentageChanged(index) {
750
+ if (this.rolloutPercentageItems().controls[index].value.percentage < 0) {
751
+ this.rolloutPercentageItems().controls[index].controls.percentage.setValue(0);
752
+ }
753
+ if (this.rolloutPercentageItems().controls[index].value.percentage > 100) {
754
+ this.rolloutPercentageItems().controls[index].controls.percentage.setValue(100);
755
+ }
756
+ if (this.rolloutPercentageItems().controls.length === 2) {
757
+ this.rolloutPercentageItems().controls[1 - index].controls.percentage
758
+ .setValue(100 - this.rolloutPercentageItems().controls[index].value.percentage);
759
+ }
760
+ this.updateSum();
1022
761
  }
1023
- getValidatorsForSettingType(settingType) {
1024
- switch (settingType) {
1025
- case SettingType.Int:
1026
- return [Validators.required,
1027
- Validators.min(-2147483648), Validators.max(2147483647), Validators.pattern('^[-+]?[0-9]\\d*$')];
1028
- case SettingType.Double:
1029
- return [Validators.required, Validators.min(-1.79769313486232E+308), Validators.max(1.79769313486232E+308)];
1030
- case SettingType.String:
1031
- case SettingType.Boolean:
1032
- return [Validators.required];
1033
- default:
1034
- return [];
762
+ updateSum() {
763
+ if (this.rolloutPercentageItems().controls.length === 0) {
764
+ this.rolloutPercentageItemsSum().setValue(100);
765
+ }
766
+ else {
767
+ this.rolloutPercentageItemsSum().setValue(this.rolloutPercentageItems().controls.map(v => v.value.percentage)
768
+ .reduce((a, b) => a + b));
1035
769
  }
1036
770
  }
1037
- getAllRolloutRules(formGroup) {
1038
- const settingValues = formGroup.get('settingValues');
1039
- const rolloutRules = settingValues.controls.map(cv => cv.get('rolloutRules').value);
1040
- return rolloutRules.length === 0 ? rolloutRules : rolloutRules.reduce((a, b) => a.concat(b));
771
+ removeRolloutPercentageItem(index) {
772
+ this.rolloutPercentageItems().removeAt(index);
773
+ this.rolloutPercentageItems().markAsDirty();
774
+ this.updateSum();
1041
775
  }
1042
- getComparisonAttributes(rolloutRules) {
1043
- if (rolloutRules.length > 0) {
1044
- const currentComparisonAttributes = rolloutRules.map(r => r.comparisonAttribute);
1045
- const defaultComparisonAttributes = ['Identifier', 'Email', 'Country'];
1046
- const comparisonAttributes = currentComparisonAttributes.filter(c => defaultComparisonAttributes.indexOf(c) === -1);
1047
- return Array.from(new Set(comparisonAttributes));
776
+ removeAll() {
777
+ this.rolloutPercentageItems().clear();
778
+ this.rolloutPercentageItems().markAsDirty();
779
+ this.updateSum();
780
+ }
781
+ getRolloutPercentageItemFormGroups() {
782
+ var _a, _b;
783
+ return (_b = (_a = this.rolloutPercentageItems()) === null || _a === void 0 ? void 0 : _a.controls) === null || _b === void 0 ? void 0 : _b.map(c => c);
784
+ }
785
+ getPercentageFormControl(rolloutPercentageItem) {
786
+ return rolloutPercentageItem.controls.percentage;
787
+ }
788
+ }
789
+ RolloutPercentageItemsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: RolloutPercentageItemsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
790
+ RolloutPercentageItemsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: RolloutPercentageItemsComponent, selector: "configcat-rollout-percentage-items", inputs: { formGroup: "formGroup", readOnly: "readOnly" }, ngImport: i0, template: "<div class=\"percentage-wrapper\" [formGroup]=\"formGroup\">\n\n <div class=\"if\" *ngIf=\"rolloutRules().controls.length > 0\">\n <mat-chip-list>\n <mat-chip class=\"chip\">ELSE IF</mat-chip>\n </mat-chip-list>\n </div>\n <div class=\"grow\">\n <div class=\"percentage-row flex\"\n *ngFor=\"let rolloutPercentageItem of getRolloutPercentageItemFormGroups(); let i = index\">\n <div class=\"flex\">\n <input type=\"number\" [formControl]=\"getPercentageFormControl(rolloutPercentageItem)\"\n (input)=\"percentageChanged(i)\">%\n <span class=\"users\">of <a href=\"https://configcat.com/docs/advanced/user-object\" target=\"_blank\"\n rel=\"noopener noreferrer\">users</a>\n </span>\n </div>\n\n <div class=\"delete-option\" matTooltip=\"Remove this % option\"\n *ngIf=\"!readOnly && rolloutPercentageItems().controls.length > 2\"\n (click)=\"removeRolloutPercentageItem(i)\">\n <mat-icon>cancel</mat-icon>\n </div>\n\n <span data-tooltip=\"The sum of percentages must be equal to 100.\"\n *ngIf=\"(i === rolloutPercentageItems().controls.length -1) && (rolloutPercentageItemsSum().invalid)\">\n <mat-icon color=\"warn\">\n error_outline\n </mat-icon>\n </span>\n\n <div class=\"value flex\">\n <span class=\"value-name\" *ngIf=\"setting().settingType !== SettingType.Boolean\">Served value:</span>\n <configcat-value class=\"served-value\" [valueFormControl]=\"value(rolloutPercentageItem)\"\n [setting]=\"setting()\">\n </configcat-value>\n </div>\n <div class=\"delete\" matTooltip=\"Remove % options\" (click)=\"removeAll()\" *ngIf=\"!readOnly\">\n <mat-icon>cancel</mat-icon>\n </div>\n </div>\n </div>\n</div>", styles: [".percentage-wrapper{border:1px solid #d5d5d5;background-color:#fff;border-radius:4px;margin:.5em 1em;position:relative;padding:.5em 0;display:flex}.percentage-wrapper .delete{position:absolute;color:#d6a9a9;cursor:pointer;top:-8px;right:-12px}.percentage-wrapper .delete:hover{color:#ed5251}.percentage-wrapper .delete mat-icon{font-size:20px}.percentage-wrapper .delete-option{color:#d6a9a9;cursor:pointer;margin-left:1rem;margin-top:4px}.percentage-wrapper .delete-option:hover{color:#ed5251}.percentage-wrapper .delete-option mat-icon{font-size:20px}.if{display:flex;align-items:center}.if>*{margin:0 .5em}.if .spacing{margin-right:44px}.if .chip{background-color:#67b1fc3d}.grow{flex-grow:1}.percentage-row{min-height:40px;margin-left:.5em}.flex{display:flex;flex-wrap:wrap;align-items:center}.value{margin-left:auto;min-width:100px}.value .value-name{display:none}input{background-color:#fff;border:1px solid #e5e5e5;min-height:28px;width:40px;margin-right:5px;font-size:15px;padding:0 5px;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;font-weight:300}.users{margin-left:20px}@media (max-width: 700px){.percentage-wrapper{margin:.5em .3em}}\n"], components: [{ type: i3$2.MatChipList, selector: "mat-chip-list", inputs: ["aria-orientation", "multiple", "compareWith", "value", "required", "placeholder", "disabled", "selectable", "tabIndex", "errorStateMatcher"], outputs: ["change", "valueChange"], exportAs: ["matChipList"] }, { type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: ValueComponent, selector: "configcat-value", inputs: ["valueFormControl", "setting"] }], directives: [{ type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3$2.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["color", "disableRipple", "tabIndex", "selected", "value", "selectable", "disabled", "removable"], outputs: ["selectionChange", "destroyed", "removed"], exportAs: ["matChip"] }, { type: i5$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i5$2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
791
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: RolloutPercentageItemsComponent, decorators: [{
792
+ type: Component,
793
+ args: [{
794
+ selector: 'configcat-rollout-percentage-items',
795
+ templateUrl: './rollout-percentage-items.component.html',
796
+ styleUrls: ['./rollout-percentage-items.component.scss']
797
+ }]
798
+ }], ctorParameters: function () { return []; }, propDecorators: { formGroup: [{
799
+ type: Input
800
+ }], readOnly: [{
801
+ type: Input
802
+ }] } });
803
+
804
+ class SettingComponent {
805
+ constructor(formHelper, cdRef) {
806
+ this.formHelper = formHelper;
807
+ this.cdRef = cdRef;
808
+ this.readOnly = false;
809
+ this.canEditSetting = false;
810
+ this.canDeleteSetting = false;
811
+ this.deleteSettingText = 'Delete setting';
812
+ this.showEnvironmentName = false;
813
+ this.editSettingRequested = new EventEmitter();
814
+ this.deleteSettingRequested = new EventEmitter();
815
+ this.rulesBeingRendered = false;
816
+ this.SettingType = SettingType;
817
+ }
818
+ ngOnInit() { }
819
+ environment() {
820
+ var _a;
821
+ return (_a = this.formGroup.root.get('environment')) === null || _a === void 0 ? void 0 : _a.value;
822
+ }
823
+ setting() {
824
+ var _a;
825
+ return (_a = this.formGroup.get('setting')) === null || _a === void 0 ? void 0 : _a.value;
826
+ }
827
+ integrationLinks() {
828
+ return this.formGroup.get('integrationLinks');
829
+ }
830
+ rolloutRules() {
831
+ return this.formGroup.get('rolloutRules');
832
+ }
833
+ rolloutPercentageItems() {
834
+ return this.formGroup.get('rolloutPercentageItems');
835
+ }
836
+ value() {
837
+ return this.formGroup.get('value');
838
+ }
839
+ addRolloutRule() {
840
+ this.rolloutRules().push(this.formHelper.createRolloutRuleFormGroup({
841
+ comparisonAttribute: 'Identifier',
842
+ comparator: RolloutRuleComparator.SensitiveIsOneOf,
843
+ comparisonValue: '',
844
+ value: this.setting().settingType === SettingType.Boolean ? false : null
845
+ }, this.readOnly, this.setting().settingType));
846
+ this.rolloutRules().markAsDirty();
847
+ }
848
+ removeRolloutRules() {
849
+ this.rolloutRules().clear();
850
+ this.rolloutRules().markAsDirty();
851
+ }
852
+ addRolloutPercentageItem() {
853
+ if (this.rolloutPercentageItems().length === 0) {
854
+ switch (this.setting().settingType) {
855
+ case SettingType.Boolean:
856
+ this.rolloutPercentageItems().push(this.formHelper.createRolloutPercentageItemFormGroup({
857
+ percentage: 0,
858
+ value: true
859
+ }, this.readOnly, this.setting().settingType));
860
+ this.rolloutPercentageItems().push(this.formHelper.createRolloutPercentageItemFormGroup({
861
+ percentage: 100,
862
+ value: false
863
+ }, this.readOnly, this.setting().settingType));
864
+ break;
865
+ case SettingType.String:
866
+ case SettingType.Int:
867
+ case SettingType.Double:
868
+ for (let i = 0; i < 4; i++) {
869
+ this.rolloutPercentageItems().push(this.formHelper.createRolloutPercentageItemFormGroup({
870
+ percentage: 25,
871
+ value: null
872
+ }, this.readOnly, this.setting().settingType));
873
+ }
874
+ break;
875
+ }
1048
876
  }
1049
- else {
1050
- return [];
877
+ else if (this.setting().settingType !== SettingType.Boolean) {
878
+ this.rolloutPercentageItems().push(this.formHelper.createRolloutPercentageItemFormGroup({
879
+ percentage: 0,
880
+ value: null
881
+ }, this.readOnly, this.setting().settingType));
1051
882
  }
883
+ this.rolloutPercentageItems().markAsDirty();
884
+ }
885
+ removeRolloutPercentageItems() {
886
+ this.rolloutPercentageItems().clear();
887
+ this.rolloutPercentageItems().markAsDirty();
888
+ }
889
+ editSetting() {
890
+ this.editSettingRequested.emit({ setting: this.setting() });
891
+ }
892
+ deleteSetting() {
893
+ this.deleteSettingRequested.emit({ setting: this.setting() });
1052
894
  }
1053
895
  }
1054
- SettingListFormHelper.ɵprov = i0.ɵɵdefineInjectable({ factory: function SettingListFormHelper_Factory() { return new SettingListFormHelper(i0.ɵɵinject(i1$1.FormBuilder)); }, token: SettingListFormHelper, providedIn: "root" });
1055
- SettingListFormHelper.decorators = [
1056
- { type: Injectable, args: [{ providedIn: 'root' },] }
1057
- ];
1058
- SettingListFormHelper.ctorParameters = () => [
1059
- { type: FormBuilder }
1060
- ];
896
+ SettingComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingComponent, deps: [{ token: SettingListFormHelper }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
897
+ SettingComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: SettingComponent, selector: "setting", inputs: { formGroup: "formGroup", readOnly: "readOnly", canEditSetting: "canEditSetting", canDeleteSetting: "canDeleteSetting", deleteSettingText: "deleteSettingText", showEnvironmentName: "showEnvironmentName" }, outputs: { editSettingRequested: "editSettingRequested", deleteSettingRequested: "deleteSettingRequested" }, ngImport: i0, template: "<div class=\"setting-full\"\n [formGroup]=\"formGroup\">\n <div class=\"delete\" (click)=\"deleteSetting()\" *ngIf=\"canDeleteSetting\" [matTooltip]=\"deleteSettingText\"\n data-robot=\"deleteSetting\">\n <mat-icon>cancel</mat-icon>\n </div>\n <div class=\"setting-header flex\">\n <div class=\"setting-name flex\">\n <div class=\"name\" data-robot=\"settingName\">\n {{setting().name}}\n <span *ngIf=\"showEnvironmentName\"> ({{environment().name}})</span>\n </div>\n <div class=\"edit\" matTooltip=\"Edit name and description\" (click)=\"editSetting()\" *ngIf=\"canEditSetting\"\n data-robot=\"renameSetting\">\n <mat-icon>edit</mat-icon>\n </div>\n </div>\n </div>\n <div class=\"setting-content\">\n <div class=\"setting-details\">\n <div class=\"setting-key\"><code data-robot=\"settingKey\">{{setting().key}}</code></div>\n <span *ngIf=\"setting().hint\" class=\"description\">{{setting().hint}}</span>\n </div>\n\n <div class=\"rules-list\">\n <div>\n <configcat-rollout-rules *ngIf=\"rolloutRules().controls.length !== 0\" [formGroup]=\"formGroup\">\n </configcat-rollout-rules>\n </div>\n\n <configcat-rollout-percentage-items *ngIf=\"rolloutPercentageItems().controls.length !== 0\"\n [formGroup]=\"formGroup\">\n </configcat-rollout-percentage-items>\n\n <div class=\"all-other flex\">\n <div *ngIf=\"rolloutPercentageItems().length === 0 && rolloutRules().length !== 0\"\n data-tooltip=\"This value will be served to all users who the above rules don't apply.\">\n <mat-chip-list>\n <mat-chip class=\"chip\">To all other users, serve:</mat-chip>\n </mat-chip-list>\n </div>\n <div *ngIf=\"rolloutPercentageItems().length !== 0\"\n data-tooltip=\"This fallback value will be served in case the user can't be identified.\">\n <mat-chip-list>\n <mat-chip class=\"chip\">To unidentified users, serve:</mat-chip>\n </mat-chip-list>\n </div>\n <div *ngIf=\"rolloutPercentageItems().length === 0 && rolloutRules().length === 0\">\n <mat-chip-list>\n <mat-chip class=\"chip\">To all users, serve:</mat-chip>\n </mat-chip-list>\n </div>\n <configcat-value class=\"served-value\" [valueFormControl]=\"value()\" [setting]=\"setting()\">\n </configcat-value>\n </div>\n <div class=\"buttons\">\n <button type=\"button\" class=\"sige-button targeting-button\" (click)=\"addRolloutRule()\" *ngIf=\"!readOnly\">\n {{rolloutRules().length === 0 ? 'Target specific users' : 'Add new targeting rule'}}\n </button>\n <button type=\"button\" class=\"sige-button targeting-button\" (click)=\"addRolloutPercentageItem()\"\n *ngIf=\"!readOnly\"\n [disabled]=\"setting().settingType === SettingType.Boolean && rolloutPercentageItems().length !== 0\">\n {{rolloutPercentageItems().length === 0 ? 'Target % of users' : 'Add new % option'}}\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: [".setting-full{position:relative;display:flex;flex-wrap:nowrap;flex-direction:column;border-radius:3px}.setting-full .delete{position:absolute;right:-15px;top:-10px;color:#d6a9a9;cursor:pointer}.setting-full .delete:hover{color:#ed5251}.setting-full .delete mat-icon{font-size:20px}.setting-full .setting-header{padding:1.5em 1.5em 0}.setting-full .setting-header .setting-name{flex-direction:row}.setting-full .setting-header .setting-name:hover>.edit{visibility:visible}.setting-full .setting-header .setting-name .name{font-weight:bold;word-wrap:break-word;max-width:100%}.setting-full .setting-header .setting-name .edit{margin:5px 0 0 5px;cursor:pointer;visibility:hidden;color:gray}.setting-full .setting-header .setting-name .edit:hover{visibility:visible}.setting-full .setting-header .setting-name .edit mat-icon{font-size:18px}.setting-full .setting-header .setting-meta{margin-left:auto;margin-right:14px;font-size:.7em;color:gray}.setting-full .setting-content{display:flex}.setting-full .setting-content .setting-details{display:flex;flex-direction:column;width:400px;padding:0 0 1em 1em}.setting-full .setting-content .setting-details>*{margin:.5em}.setting-full .setting-content .setting-details .integrations{font-size:.8rem}.setting-full .setting-content .setting-details .integrations>div{margin:3px}.setting-full .setting-content .setting-details .integrations>div>a:hover{text-decoration:underline}.setting-full .setting-content .setting-details .setting-key{word-wrap:break-word}.setting-full .setting-content .setting-details .description{font-size:.8em;white-space:pre-wrap;word-wrap:break-word;max-width:300px}.setting-full .setting-content .setting-details code{background-color:#d3d3d3;padding:3px 6px}.setting-full .setting-content .rules-list{width:100%;padding:0 1em 1em}.setting-full .setting-content .targeting-button{margin-left:.5rem;margin-right:.5rem;margin-bottom:1em}.all-other{min-height:40px;justify-content:flex-start;border:1px solid #d5d5d5;border-radius:4px;background-color:#fff;margin:.5em 1em;padding:4px 0 4px .5em}.all-other .chip{background-color:#67b1fc3d}.all-other .served-value{margin-left:auto;min-width:100px}.end{margin-left:auto}.buttons{display:flex;margin-left:.5rem;margin-right:.5rem}.buttons .updated-date{font-size:.7em;margin-left:auto;margin-right:22px;color:gray}.flex{display:flex;flex-wrap:wrap;align-items:center}.show-hide-toggle{margin-left:1em;cursor:pointer}.show-hide-toggle a{font-size:.8rem}.sige-button{background-color:#d9eadc;color:#363636;cursor:pointer;justify-content:center;padding:.375em .75em;text-align:center;white-space:nowrap;border-radius:2px;font-size:.75rem;border:1px solid #dbdbdb;box-shadow:none;display:inline-flex;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif}.sige-button:hover{background-color:#bad0be}.sige-button:disabled{background-color:#fff;color:#b3b3b3}@media (max-width: 1200px){.setting-details{width:300px}}@media (max-width: 700px){.setting-full .setting-content{flex-wrap:wrap}.setting-full .setting-content .rules-list{padding:0}.buttons{flex-direction:column}.updated-date{display:none}.setting-details{padding:.2em 0 .2em .2em}.all-other{margin:.5em .3em}}\n"], components: [{ type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: RolloutRulesComponent, selector: "configcat-rollout-rules", inputs: ["formGroup", "readOnly"] }, { type: RolloutPercentageItemsComponent, selector: "configcat-rollout-percentage-items", inputs: ["formGroup", "readOnly"] }, { type: i3$2.MatChipList, selector: "mat-chip-list", inputs: ["aria-orientation", "multiple", "compareWith", "value", "required", "placeholder", "disabled", "selectable", "tabIndex", "errorStateMatcher"], outputs: ["change", "valueChange"], exportAs: ["matChipList"] }, { type: ValueComponent, selector: "configcat-value", inputs: ["valueFormControl", "setting"] }], directives: [{ type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5$2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { type: i3$2.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["color", "disableRipple", "tabIndex", "selected", "value", "selectable", "disabled", "removable"], outputs: ["selectionChange", "destroyed", "removed"], exportAs: ["matChip"] }] });
898
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingComponent, decorators: [{
899
+ type: Component,
900
+ args: [{
901
+ selector: 'setting',
902
+ templateUrl: './setting.component.html',
903
+ styleUrls: ['./setting.component.scss']
904
+ }]
905
+ }], ctorParameters: function () { return [{ type: SettingListFormHelper }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { formGroup: [{
906
+ type: Input
907
+ }], readOnly: [{
908
+ type: Input
909
+ }], canEditSetting: [{
910
+ type: Input
911
+ }], canDeleteSetting: [{
912
+ type: Input
913
+ }], deleteSettingText: [{
914
+ type: Input
915
+ }], showEnvironmentName: [{
916
+ type: Input
917
+ }], editSettingRequested: [{
918
+ type: Output
919
+ }], deleteSettingRequested: [{
920
+ type: Output
921
+ }] } });
1061
922
 
1062
923
  class SettingListComponent {
1063
924
  constructor(formHelper, snackBar, dialog) {
1064
925
  this.formHelper = formHelper;
1065
926
  this.snackBar = snackBar;
1066
927
  this.dialog = dialog;
928
+ this.canEditSetting = false;
929
+ this.canDeleteSetting = false;
1067
930
  this.deleteSettingText = 'Delete setting';
1068
931
  this.showEnvironmentName = false;
1069
932
  this.editSettingRequested = new EventEmitter();
@@ -1109,6 +972,9 @@ class SettingListComponent {
1109
972
  settingValuesFormArray() {
1110
973
  return this.formGroup.get('settingValues');
1111
974
  }
975
+ getSettingValueFormGroups() {
976
+ return this.settingValuesFormArray().controls.map(c => c);
977
+ }
1112
978
  submit() {
1113
979
  if (!this.formGroup || !this.formGroup.valid) {
1114
980
  this.showFeedBackMessage = true;
@@ -1207,138 +1073,371 @@ class SettingListComponent {
1207
1073
  });
1208
1074
  }
1209
1075
  }
1210
- SettingListComponent.decorators = [
1211
- { type: Component, args: [{
1212
- selector: 'configcat-setting-list',
1213
- template: "<form [formGroup]=\"formGroup\" (ngSubmit)=\"submit()\" *ngIf=\"!loading\">\n <div class=\"wrapper\" *ngFor=\"let settingValueFromGroup of settingValuesFormArray().controls; let i = index\">\n <setting [formGroup]=\"settingValueFromGroup\" [readOnly]=\"readOnly\"\n [canEditSetting]=\"canEditSetting\" [canDeleteSetting]=\"canDeleteSetting\"\n (editSettingRequested)=\"onEditSettingRequested($event)\"\n (deleteSettingRequested)=\"onDeleteSettingRequested($event)\" [deleteSettingText]=\"deleteSettingText\"\n [showEnvironmentName]=\"showEnvironmentName\">\n </setting>\n </div>\n\n <div class=\"save\" *ngIf=\"!readOnly && !submitting && formGroup.dirty\">\n <div class=\"feedback\" *ngIf=\"formGroup.touched && formGroup.invalid && showFeedBackMessage\">\n <div class=\"error\" *ngIf=\"formGroup.errors && formGroup.errors.internalServerError\">\n <span>Something went wrong and we could not save your settings. You can try again or <a\n href=\"https://configcat.com/support\" target=\"_blank\" rel=\"noopener noreferrer\">contact us.</a></span>\n </div>\n <div class=\"error\" *ngIf=\"!formGroup.errors || !formGroup.errors.internalServerError\">\n <span>Oh no! A feature flag has invalid or missing data.</span>\n </div>\n </div>\n <button type=\"button\" mat-stroked-button *ngIf=\"!showRevertQuestion\" (click)=\"showRevertQuestion = true\">\n <mat-icon>history</mat-icon><span>Revert changes</span>\n </button>\n <div *ngIf=\"showRevertQuestion\">\n <span class=\"revert\">Revert to last saved state?</span>\n <button type=\"button\" class=\"yes\" mat-stroked-button (click)=\"handleRevertYes()\">Yes</button>\n <button type=\"button\" mat-stroked-button (click)=\"handleRevertCancel()\">No</button>\n </div>\n <button class=\"save-button\" mat-flat-button color=\"warn\" type=\"submit\" [disabled]=\"submitting\">\n <mat-icon>backup</mat-icon> <span> Save & publish changes</span>\n </button>\n </div>\n</form>\n",
1214
- styles: [".wrapper{background-color:#f3f3f3;border:1px solid rgba(0,0,0,.12);border-radius:4px;margin:1em 0}.show-hide a{cursor:pointer;font-size:.8rem}.save{position:sticky;bottom:0;min-height:38px;padding:.5rem .5rem .5rem 0;background-color:#fff;display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-end;z-index:1000}.save .save-button{-webkit-animation:shadow-pulse 1s 3;animation:shadow-pulse 1s 3;margin-left:1rem}.save>*{margin:.3em}.save .revert{margin:0 16px;font-size:14px}.save .yes{margin:0 5px}.error{background-color:#a32b2d;color:#fff;padding:5px;font-size:.9em}.error a{color:#fff;text-decoration:underline}@media (max-width: 700px){.save{padding:.5rem 0}}\n"]
1215
- },] }
1216
- ];
1217
- SettingListComponent.ctorParameters = () => [
1218
- { type: SettingListFormHelper },
1219
- { type: MatSnackBar },
1220
- { type: MatDialog }
1221
- ];
1222
- SettingListComponent.propDecorators = {
1223
- canEditSetting: [{ type: Input }],
1224
- canDeleteSetting: [{ type: Input }],
1225
- onSave: [{ type: Input }],
1226
- loadSettingValues: [{ type: Input }],
1227
- deleteSettingText: [{ type: Input }],
1228
- showEnvironmentName: [{ type: Input }],
1229
- editSettingRequested: [{ type: Output }],
1230
- deleteSettingRequested: [{ type: Output }],
1231
- formValuesChanged: [{ type: Output }]
1232
- };
1076
+ SettingListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingListComponent, deps: [{ token: SettingListFormHelper }, { token: i2$2.MatSnackBar }, { token: i1$1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
1077
+ SettingListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: SettingListComponent, selector: "configcat-setting-list", inputs: { canEditSetting: "canEditSetting", canDeleteSetting: "canDeleteSetting", onSave: "onSave", loadSettingValues: "loadSettingValues", deleteSettingText: "deleteSettingText", showEnvironmentName: "showEnvironmentName" }, outputs: { editSettingRequested: "editSettingRequested", deleteSettingRequested: "deleteSettingRequested", formValuesChanged: "formValuesChanged" }, usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"formGroup\" (ngSubmit)=\"submit()\" *ngIf=\"!loading\">\n <div class=\"wrapper\" *ngFor=\"let settingValueFromGroup of getSettingValueFormGroups(); let i = index\">\n <setting [formGroup]=\"settingValueFromGroup\" [readOnly]=\"readOnly\" [canEditSetting]=\"canEditSetting\"\n [canDeleteSetting]=\"canDeleteSetting\" (editSettingRequested)=\"onEditSettingRequested($event)\"\n (deleteSettingRequested)=\"onDeleteSettingRequested($event)\" [deleteSettingText]=\"deleteSettingText\"\n [showEnvironmentName]=\"showEnvironmentName\">\n </setting>\n </div>\n\n <div class=\"save\" *ngIf=\"!readOnly && !submitting && formGroup.dirty\">\n <div class=\"feedback\" *ngIf=\"formGroup.touched && formGroup.invalid && showFeedBackMessage\">\n <div class=\"error\" *ngIf=\"formGroup.errors && formGroup.errors.internalServerError\">\n <span>Something went wrong and we could not save your settings. You can try again or <a\n href=\"https://configcat.com/support\" target=\"_blank\" rel=\"noopener noreferrer\">contact us.</a></span>\n </div>\n <div class=\"error\" *ngIf=\"!formGroup.errors || !formGroup.errors.internalServerError\">\n <span>Oh no! A feature flag has invalid or missing data.</span>\n </div>\n </div>\n <button type=\"button\" mat-stroked-button *ngIf=\"!showRevertQuestion\" (click)=\"showRevertQuestion = true\">\n <mat-icon>history</mat-icon><span>Revert changes</span>\n </button>\n <div *ngIf=\"showRevertQuestion\">\n <span class=\"revert\">Revert to last saved state?</span>\n <button type=\"button\" class=\"yes\" mat-stroked-button (click)=\"handleRevertYes()\">Yes</button>\n <button type=\"button\" mat-stroked-button (click)=\"handleRevertCancel()\">No</button>\n </div>\n <button class=\"save-button\" mat-flat-button color=\"warn\" type=\"submit\" [disabled]=\"submitting\">\n <mat-icon>backup</mat-icon> <span> Save & publish changes</span>\n </button>\n </div>\n</form>", styles: [".wrapper{background-color:#f3f3f3;border:1px solid rgba(0,0,0,.12);border-radius:4px;margin:1em 0}.show-hide a{cursor:pointer;font-size:.8rem}.save{position:sticky;bottom:0;min-height:38px;padding:.5rem .5rem .5rem 0;background-color:#fff;display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-end;z-index:1000}.save .save-button{animation:shadow-pulse 1s 3;margin-left:1rem}.save>*{margin:.3em}.save .revert{margin:0 16px;font-size:14px}.save .yes{margin:0 5px}.error{background-color:#a32b2d;color:#fff;padding:5px;font-size:.9em}.error a{color:#fff;text-decoration:underline}@media (max-width: 700px){.save{padding:.5rem 0}}\n"], components: [{ type: SettingComponent, selector: "setting", inputs: ["formGroup", "readOnly", "canEditSetting", "canDeleteSetting", "deleteSettingText", "showEnvironmentName"], outputs: ["editSettingRequested", "deleteSettingRequested"] }, { type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i5$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1078
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingListComponent, decorators: [{
1079
+ type: Component,
1080
+ args: [{
1081
+ selector: 'configcat-setting-list',
1082
+ templateUrl: './setting-list.component.html',
1083
+ styleUrls: ['./setting-list.component.scss']
1084
+ }]
1085
+ }], ctorParameters: function () { return [{ type: SettingListFormHelper }, { type: i2$2.MatSnackBar }, { type: i1$1.MatDialog }]; }, propDecorators: { canEditSetting: [{
1086
+ type: Input
1087
+ }], canDeleteSetting: [{
1088
+ type: Input
1089
+ }], onSave: [{
1090
+ type: Input
1091
+ }], loadSettingValues: [{
1092
+ type: Input
1093
+ }], deleteSettingText: [{
1094
+ type: Input
1095
+ }], showEnvironmentName: [{
1096
+ type: Input
1097
+ }], editSettingRequested: [{
1098
+ type: Output
1099
+ }], deleteSettingRequested: [{
1100
+ type: Output
1101
+ }], formValuesChanged: [{
1102
+ type: Output
1103
+ }] } });
1233
1104
 
1234
- class SettingComponent {
1235
- constructor(formHelper, cdRef) {
1236
- this.formHelper = formHelper;
1237
- this.cdRef = cdRef;
1105
+ class SettingItemComponent extends BaseComponent {
1106
+ constructor(publicApiService) {
1107
+ super(publicApiService);
1108
+ this.canEditSetting = true;
1109
+ this.canDeleteSetting = true;
1238
1110
  this.deleteSettingText = 'Delete setting';
1239
- this.showEnvironmentName = false;
1111
+ this.showEnvironmentName = true;
1240
1112
  this.editSettingRequested = new EventEmitter();
1241
1113
  this.deleteSettingRequested = new EventEmitter();
1242
- this.rulesBeingRendered = false;
1243
- this.SettingType = SettingType;
1114
+ this.loadSucceeded = new EventEmitter();
1115
+ this.loadFailed = new EventEmitter();
1116
+ this.saveSucceeded = new EventEmitter();
1117
+ this.saveFailed = new EventEmitter();
1118
+ this.formValuesChanged = new EventEmitter();
1119
+ this.loadSettingValues = () => {
1120
+ return this.createSettingValuesService().getSettingValue(this.environmentId, this.settingId)
1121
+ .pipe(map(settingValue => {
1122
+ this.settingValue = settingValue;
1123
+ const settingValues = {
1124
+ settingValues: [settingValue],
1125
+ readOnly: settingValue.readOnly,
1126
+ config: settingValue.config,
1127
+ environment: settingValue.environment
1128
+ };
1129
+ this.loadSucceeded.emit(settingValues);
1130
+ return settingValues;
1131
+ }), catchError(error => {
1132
+ this.loadFailed.emit(error);
1133
+ return throwError(error);
1134
+ }));
1135
+ };
1136
+ this.handleSave = (model) => {
1137
+ if (model.settingValues.length === 0) {
1138
+ return of();
1139
+ }
1140
+ const updateModel = model.settingValues[0];
1141
+ return this.createSettingValuesService().replaceSettingValue(this.environmentId, this.settingId, updateModel, model.reason)
1142
+ .pipe(map(settingValue => {
1143
+ this.settingValue = settingValue;
1144
+ const settingValues = {
1145
+ settingValues: [settingValue],
1146
+ readOnly: settingValue.readOnly,
1147
+ config: settingValue.config,
1148
+ environment: settingValue.environment
1149
+ };
1150
+ this.saveSucceeded.emit(settingValues);
1151
+ return settingValues;
1152
+ }), catchError(error => {
1153
+ this.saveFailed.emit(error);
1154
+ return throwError(error);
1155
+ }));
1156
+ };
1244
1157
  }
1245
- ngOnInit() { }
1246
- environment() {
1247
- return this.formGroup.root.get('environment').value;
1158
+ ngOnInit() {
1248
1159
  }
1249
- setting() {
1250
- return this.formGroup.get('setting').value;
1160
+ onEditSettingRequested(setting) {
1161
+ this.editSettingRequested.emit({
1162
+ setting: setting.setting,
1163
+ environment: this.settingValue.environment,
1164
+ config: this.settingValue.config
1165
+ });
1251
1166
  }
1252
- integrationLinks() {
1253
- return this.formGroup.get('integrationLinks');
1167
+ onDeleteSettingRequested(setting) {
1168
+ this.deleteSettingRequested.emit({
1169
+ setting: setting.setting,
1170
+ environment: this.settingValue.environment,
1171
+ config: this.settingValue.config
1172
+ });
1254
1173
  }
1255
- rolloutRules() {
1256
- return this.formGroup.get('rolloutRules');
1174
+ onFormValuesChanged() {
1175
+ this.formValuesChanged.emit();
1257
1176
  }
1258
- rolloutPercentageItems() {
1259
- return this.formGroup.get('rolloutPercentageItems');
1177
+ }
1178
+ SettingItemComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingItemComponent, deps: [{ token: PublicApiService }], target: i0.ɵɵFactoryTarget.Component });
1179
+ SettingItemComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: SettingItemComponent, selector: "configcat-setting-item", inputs: { productId: "productId", environmentId: "environmentId", configId: "configId", settingId: "settingId", canEditSetting: "canEditSetting", canDeleteSetting: "canDeleteSetting", deleteSettingText: "deleteSettingText", showEnvironmentName: "showEnvironmentName" }, outputs: { editSettingRequested: "editSettingRequested", deleteSettingRequested: "deleteSettingRequested", loadSucceeded: "loadSucceeded", loadFailed: "loadFailed", saveSucceeded: "saveSucceeded", saveFailed: "saveFailed", formValuesChanged: "formValuesChanged" }, usesInheritance: true, ngImport: i0, template: "<configcat-setting-list [canEditSetting]=\"canEditSetting\" [canDeleteSetting]=\"canDeleteSetting\"\n [onSave]=\"handleSave\" [loadSettingValues]=\"loadSettingValues\"\n (editSettingRequested)=\"onEditSettingRequested($event)\" (deleteSettingRequested)=\"onDeleteSettingRequested($event)\"\n [deleteSettingText]=\"deleteSettingText\"\n [showEnvironmentName]=\"showEnvironmentName\" (formValuesChanged)=\"onFormValuesChanged()\">\n</configcat-setting-list>", styles: [".container{font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif;background-color:#f3f3f3;border:1px solid rgba(0,0,0,.12);border-radius:4px;margin:1em 0}.container .setting-full{position:relative;display:flex;flex-wrap:nowrap}.container .setting-full .setting-details{display:flex;flex-direction:column;width:400px;padding:1em 0 1em 1em}.container .setting-full .setting-details>*{margin:.5em}.container .setting-full .setting-details .setting-name{flex-direction:row}.container .setting-full .setting-details .setting-name .name{font-weight:bold;word-wrap:break-word;max-width:100%}.container .setting-full .setting-details .setting-key{word-wrap:break-word}.container .setting-full .setting-details .description{font-size:.8em}.container .setting-full .setting-details code{background-color:#d3d3d3;padding:3px 6px}.container .setting-full .rules-list{width:100%;padding:1em}.container .setting-full .targeting-button{margin-left:1rem;margin-bottom:1em}.all-other{min-height:40px;justify-content:flex-start;border:1px solid #d5d5d5;border-radius:4px;background-color:#fff;margin:.5em 1em;padding:4px 0 4px .5em}.all-other .chip{background-color:#67b1fc3d}.all-other .served-value{margin-left:auto;min-width:100px}.end{margin-left:auto}.flex{display:flex;flex-wrap:wrap;align-items:center}.show-hide-toggle{margin-left:1em;cursor:pointer}@media (max-width: 1200px){.setting-full .setting-details{width:300px}}@media (max-width: 700px){.setting-full{flex-wrap:wrap}.setting-full .setting-details{padding:.2em 0 .2em .2em}.setting-full .all-other{margin:.5em .3em}}\n"], components: [{ type: SettingListComponent, selector: "configcat-setting-list", inputs: ["canEditSetting", "canDeleteSetting", "onSave", "loadSettingValues", "deleteSettingText", "showEnvironmentName"], outputs: ["editSettingRequested", "deleteSettingRequested", "formValuesChanged"] }] });
1180
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingItemComponent, decorators: [{
1181
+ type: Component,
1182
+ args: [{
1183
+ selector: 'configcat-setting-item',
1184
+ templateUrl: './setting-item.component.html',
1185
+ styleUrls: ['./setting-item.component.scss']
1186
+ }]
1187
+ }], ctorParameters: function () { return [{ type: PublicApiService }]; }, propDecorators: { productId: [{
1188
+ type: Input
1189
+ }], environmentId: [{
1190
+ type: Input
1191
+ }], configId: [{
1192
+ type: Input
1193
+ }], settingId: [{
1194
+ type: Input
1195
+ }], canEditSetting: [{
1196
+ type: Input
1197
+ }], canDeleteSetting: [{
1198
+ type: Input
1199
+ }], deleteSettingText: [{
1200
+ type: Input
1201
+ }], showEnvironmentName: [{
1202
+ type: Input
1203
+ }], editSettingRequested: [{
1204
+ type: Output
1205
+ }], deleteSettingRequested: [{
1206
+ type: Output
1207
+ }], loadSucceeded: [{
1208
+ type: Output
1209
+ }], loadFailed: [{
1210
+ type: Output
1211
+ }], saveSucceeded: [{
1212
+ type: Output
1213
+ }], saveFailed: [{
1214
+ type: Output
1215
+ }], formValuesChanged: [{
1216
+ type: Output
1217
+ }] } });
1218
+
1219
+ class SettingSelectComponent extends BaseComponent {
1220
+ constructor(publicApiService) {
1221
+ super(publicApiService);
1222
+ this.settings = [];
1260
1223
  }
1261
- value() {
1262
- return this.formGroup.get('value');
1224
+ ngOnInit() {
1225
+ this.settings = [];
1226
+ this.reloadSettings();
1263
1227
  }
1264
- addRolloutRule() {
1265
- this.rolloutRules().push(this.formHelper.createRolloutRuleFormGroup({
1266
- comparisonAttribute: 'Identifier',
1267
- comparator: RolloutRuleComparator.SensitiveIsOneOf,
1268
- comparisonValue: '',
1269
- value: this.setting().settingType === SettingType.Boolean ? false : null
1270
- }, this.readOnly, this.setting().settingType));
1271
- this.rolloutRules().markAsDirty();
1228
+ ngOnChanges() {
1229
+ this.reloadSettings();
1272
1230
  }
1273
- removeRolloutRules() {
1274
- this.rolloutRules().clear();
1275
- this.rolloutRules().markAsDirty();
1231
+ reloadSettings() {
1232
+ if (this.configId) {
1233
+ this.createSettingsService().getSettings(this.configId).subscribe(settings => {
1234
+ this.settings = settings;
1235
+ if (this.settings.length > 0) {
1236
+ this.valueFormControl.setValue(this.settings[0].settingId);
1237
+ }
1238
+ });
1239
+ }
1276
1240
  }
1277
- addRolloutPercentageItem() {
1278
- if (this.rolloutPercentageItems().length === 0) {
1279
- switch (this.setting().settingType) {
1280
- case SettingType.Boolean:
1281
- this.rolloutPercentageItems().push(this.formHelper.createRolloutPercentageItemFormGroup({
1282
- percentage: 0,
1283
- value: true
1284
- }, this.readOnly, this.setting().settingType));
1285
- this.rolloutPercentageItems().push(this.formHelper.createRolloutPercentageItemFormGroup({
1286
- percentage: 100,
1287
- value: false
1288
- }, this.readOnly, this.setting().settingType));
1289
- break;
1290
- case SettingType.String:
1291
- case SettingType.Int:
1292
- case SettingType.Double:
1293
- for (let i = 0; i < 4; i++) {
1294
- this.rolloutPercentageItems().push(this.formHelper.createRolloutPercentageItemFormGroup({
1295
- percentage: 25,
1296
- value: null
1297
- }, this.readOnly, this.setting().settingType));
1298
- }
1299
- break;
1241
+ }
1242
+ SettingSelectComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingSelectComponent, deps: [{ token: PublicApiService }], target: i0.ɵɵFactoryTarget.Component });
1243
+ SettingSelectComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: SettingSelectComponent, selector: "configcat-setting-select", inputs: { valueFormControl: "valueFormControl", configId: "configId" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<mat-form-field class=\"form-field\">\n <mat-select placeholder=\"Feature flag or setting\" [formControl]=\"valueFormControl\" *ngIf=\"settings\" name=\"value\">\n <mat-option *ngFor=\"let setting of settings\" [value]=\"setting.settingId\">{{setting.name}}\n </mat-option>\n </mat-select>\n</mat-form-field>", styles: [".form-field{width:100%}\n"], components: [{ type: i3.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }, { type: i3$3.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { type: i4$2.MatOption, selector: "mat-option", exportAs: ["matOption"] }], directives: [{ type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i5$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1244
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: SettingSelectComponent, decorators: [{
1245
+ type: Component,
1246
+ args: [{
1247
+ selector: 'configcat-setting-select',
1248
+ templateUrl: './setting-select.component.html',
1249
+ styleUrls: ['./setting-select.component.css']
1250
+ }]
1251
+ }], ctorParameters: function () { return [{ type: PublicApiService }]; }, propDecorators: { valueFormControl: [{
1252
+ type: Input
1253
+ }], configId: [{
1254
+ type: Input
1255
+ }] } });
1256
+
1257
+ class ProductSelectComponent extends BaseComponent {
1258
+ constructor(publicApiService) {
1259
+ super(publicApiService);
1260
+ this.products = [];
1261
+ }
1262
+ ngOnInit() {
1263
+ this.products = [];
1264
+ this.createProductsService().getProducts().subscribe(products => {
1265
+ this.products = products;
1266
+ if (this.products.length > 0) {
1267
+ this.valueFormControl.setValue(this.products[0].productId, {
1268
+ emitEvent: true,
1269
+ emitModelToViewChange: true, emitViewToModelChange: true
1270
+ });
1300
1271
  }
1272
+ });
1273
+ }
1274
+ }
1275
+ ProductSelectComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ProductSelectComponent, deps: [{ token: PublicApiService }], target: i0.ɵɵFactoryTarget.Component });
1276
+ ProductSelectComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: ProductSelectComponent, selector: "configcat-product-select", inputs: { valueFormControl: "valueFormControl" }, usesInheritance: true, ngImport: i0, template: "<mat-form-field class=\"form-field\">\n <mat-select placeholder=\"Product\" [formControl]=\"valueFormControl\" name=\"productValue\">\n <mat-option *ngFor=\"let product of products\" [value]=\"product.productId\">{{product.name}}\n </mat-option>\n </mat-select>\n</mat-form-field>", styles: [".form-field{width:100%}\n"], components: [{ type: i3.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }, { type: i3$3.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { type: i4$2.MatOption, selector: "mat-option", exportAs: ["matOption"] }], directives: [{ type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i5$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1277
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ProductSelectComponent, decorators: [{
1278
+ type: Component,
1279
+ args: [{
1280
+ selector: 'configcat-product-select',
1281
+ templateUrl: './product-select.component.html',
1282
+ styleUrls: ['./product-select.component.css']
1283
+ }]
1284
+ }], ctorParameters: function () { return [{ type: PublicApiService }]; }, propDecorators: { valueFormControl: [{
1285
+ type: Input
1286
+ }] } });
1287
+
1288
+ class ConfigSelectComponent extends BaseComponent {
1289
+ constructor(publicApiService) {
1290
+ super(publicApiService);
1291
+ this.configs = [];
1292
+ }
1293
+ ngOnInit() {
1294
+ this.configs = [];
1295
+ this.reloadConfigs();
1296
+ }
1297
+ ngOnChanges() {
1298
+ this.reloadConfigs();
1299
+ }
1300
+ reloadConfigs() {
1301
+ if (this.productId) {
1302
+ this.createConfigsService().getConfigs(this.productId).subscribe(configs => {
1303
+ this.configs = configs;
1304
+ if (this.configs.length > 0) {
1305
+ this.valueFormControl.setValue(this.configs[0].configId);
1306
+ }
1307
+ });
1301
1308
  }
1302
- else if (this.setting().settingType !== SettingType.Boolean) {
1303
- this.rolloutPercentageItems().push(this.formHelper.createRolloutPercentageItemFormGroup({
1304
- percentage: 0,
1305
- value: null
1306
- }, this.readOnly, this.setting().settingType));
1309
+ }
1310
+ }
1311
+ ConfigSelectComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ConfigSelectComponent, deps: [{ token: PublicApiService }], target: i0.ɵɵFactoryTarget.Component });
1312
+ ConfigSelectComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: ConfigSelectComponent, selector: "configcat-config-select", inputs: { valueFormControl: "valueFormControl", productId: "productId" }, providers: [
1313
+ {
1314
+ provide: NG_VALUE_ACCESSOR,
1315
+ useExisting: forwardRef(() => ConfigSelectComponent),
1316
+ multi: true
1307
1317
  }
1308
- this.rolloutPercentageItems().markAsDirty();
1318
+ ], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<mat-form-field class=\"form-field\">\n <mat-select placeholder=\"Config\" [formControl]=\"valueFormControl\" *ngIf=\"configs\" name=\"value\">\n <mat-option *ngFor=\"let config of configs\" [value]=\"config.configId\">{{config.name}}</mat-option>\n </mat-select>\n</mat-form-field>", styles: [".form-field{width:100%}\n"], components: [{ type: i3.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }, { type: i3$3.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { type: i4$2.MatOption, selector: "mat-option", exportAs: ["matOption"] }], directives: [{ type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i5$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1319
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: ConfigSelectComponent, decorators: [{
1320
+ type: Component,
1321
+ args: [{
1322
+ selector: 'configcat-config-select',
1323
+ templateUrl: './config-select.component.html',
1324
+ styleUrls: ['./config-select.component.css'],
1325
+ providers: [
1326
+ {
1327
+ provide: NG_VALUE_ACCESSOR,
1328
+ useExisting: forwardRef(() => ConfigSelectComponent),
1329
+ multi: true
1330
+ }
1331
+ ]
1332
+ }]
1333
+ }], ctorParameters: function () { return [{ type: PublicApiService }]; }, propDecorators: { valueFormControl: [{
1334
+ type: Input
1335
+ }], productId: [{
1336
+ type: Input
1337
+ }] } });
1338
+
1339
+ class EnvironmentSelectComponent extends BaseComponent {
1340
+ constructor(publicApiService) {
1341
+ super(publicApiService);
1342
+ this.environments = [];
1309
1343
  }
1310
- removeRolloutPercentageItems() {
1311
- this.rolloutPercentageItems().clear();
1312
- this.rolloutPercentageItems().markAsDirty();
1344
+ ngOnInit() {
1345
+ this.environments = [];
1346
+ this.reloadEnvironments();
1313
1347
  }
1314
- editSetting() {
1315
- this.editSettingRequested.emit({ setting: this.setting() });
1348
+ ngOnChanges() {
1349
+ this.reloadEnvironments();
1316
1350
  }
1317
- deleteSetting() {
1318
- this.deleteSettingRequested.emit({ setting: this.setting() });
1351
+ reloadEnvironments() {
1352
+ if (this.productId) {
1353
+ this.createEnvironmentsService().getEnvironments(this.productId).subscribe(environments => {
1354
+ this.environments = environments;
1355
+ if (this.environments.length > 0) {
1356
+ this.valueFormControl.setValue(this.environments[0].environmentId);
1357
+ }
1358
+ });
1359
+ }
1360
+ }
1361
+ }
1362
+ EnvironmentSelectComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: EnvironmentSelectComponent, deps: [{ token: PublicApiService }], target: i0.ɵɵFactoryTarget.Component });
1363
+ EnvironmentSelectComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: EnvironmentSelectComponent, selector: "configcat-environment-select", inputs: { valueFormControl: "valueFormControl", productId: "productId" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<mat-form-field class=\"form-field\">\n <mat-select placeholder=\"Environment\" [formControl]=\"valueFormControl\" *ngIf=\"environments\" name=\"value\">\n <mat-option *ngFor=\"let environment of environments\" [value]=\"environment.environmentId\">{{environment.name}}\n </mat-option>\n </mat-select>\n</mat-form-field>", styles: [".form-field{width:100%}\n"], components: [{ type: i3.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }, { type: i3$3.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { type: i4$2.MatOption, selector: "mat-option", exportAs: ["matOption"] }], directives: [{ type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { type: i5$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1364
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: EnvironmentSelectComponent, decorators: [{
1365
+ type: Component,
1366
+ args: [{
1367
+ selector: 'configcat-environment-select',
1368
+ templateUrl: './environment-select.component.html',
1369
+ styleUrls: ['./environment-select.component.css']
1370
+ }]
1371
+ }], ctorParameters: function () { return [{ type: PublicApiService }]; }, propDecorators: { valueFormControl: [{
1372
+ type: Input
1373
+ }], productId: [{
1374
+ type: Input
1375
+ }] } });
1376
+
1377
+ class FormHelper {
1378
+ static getErrorMessage(control, hint = '') {
1379
+ if (!control || control.valid) {
1380
+ return;
1381
+ }
1382
+ if (control.hasError('required')) {
1383
+ return `${hint} The field is required.`;
1384
+ }
1385
+ if (control.hasError('pattern')) {
1386
+ return 'Not a valid e-mail address.';
1387
+ }
1388
+ if (control.hasError('minlength')) {
1389
+ return 'The field must be at least ' + control.getError('minlength').requiredLength + ' characters long';
1390
+ }
1391
+ if (control.hasError('maxlength')) {
1392
+ return 'The field must be at max ' + control.getError('maxlength').requiredLength + ' characters long';
1393
+ }
1394
+ return;
1395
+ }
1396
+ }
1397
+
1398
+ class AuthorizationComponent {
1399
+ constructor(formBuilder, publicApiService) {
1400
+ this.formBuilder = formBuilder;
1401
+ this.publicApiService = publicApiService;
1402
+ this.basePath = null;
1403
+ this.authorized = new EventEmitter();
1404
+ this.FormHelper = FormHelper;
1405
+ }
1406
+ ngOnInit() {
1407
+ this.formGroup = this.formBuilder.group({
1408
+ basicAuthUsername: ['', Validators.required],
1409
+ basicAuthPassword: ['', Validators.required],
1410
+ });
1411
+ }
1412
+ onSubmit() {
1413
+ if (!this.formGroup.valid) {
1414
+ return;
1415
+ }
1416
+ const meService = this.publicApiService.createMeService(this.formGroup.controls.basicAuthUsername.value, this.formGroup.controls.basicAuthPassword.value, this.basePath);
1417
+ meService.getMe().subscribe(me => {
1418
+ this.authorized.emit({
1419
+ basicAuthUsername: this.formGroup.controls.basicAuthUsername.value,
1420
+ basicAuthPassword: this.formGroup.controls.basicAuthPassword.value
1421
+ });
1422
+ }, error => {
1423
+ this.formGroup.setErrors({ invalidAuthorization: 'Invalid Basic auth user name or password' });
1424
+ });
1319
1425
  }
1320
1426
  }
1321
- SettingComponent.decorators = [
1322
- { type: Component, args: [{
1323
- selector: 'setting',
1324
- template: "<div class=\"setting-full\"\n [formGroup]=\"formGroup\">\n <div class=\"delete\" (click)=\"deleteSetting()\" *ngIf=\"canDeleteSetting\" [matTooltip]=\"deleteSettingText\"\n data-robot=\"deleteSetting\">\n <mat-icon>cancel</mat-icon>\n </div>\n <div class=\"setting-header flex\">\n <div class=\"setting-name flex\">\n <div class=\"name\" data-robot=\"settingName\">\n {{setting().name}}\n <span *ngIf=\"showEnvironmentName\"> ({{environment().name}})</span>\n </div>\n <div class=\"edit\" matTooltip=\"Edit name and description\" (click)=\"editSetting()\" *ngIf=\"canEditSetting\"\n data-robot=\"renameSetting\">\n <mat-icon>edit</mat-icon>\n </div>\n </div>\n </div>\n <div class=\"setting-content\">\n <div class=\"setting-details\">\n <div class=\"setting-key\"><code data-robot=\"settingKey\">{{setting().key}}</code></div>\n <span *ngIf=\"setting().hint\" class=\"description\">{{setting().hint}}</span>\n </div>\n\n <div class=\"rules-list\">\n <div>\n <configcat-rollout-rules *ngIf=\"rolloutRules().controls.length !== 0\" [formGroup]=\"formGroup\">\n </configcat-rollout-rules>\n </div>\n\n <configcat-rollout-percentage-items *ngIf=\"rolloutPercentageItems().controls.length !== 0\"\n [formGroup]=\"formGroup\">\n </configcat-rollout-percentage-items>\n\n <div class=\"all-other flex\">\n <div *ngIf=\"rolloutPercentageItems().length === 0 && rolloutRules().length !== 0\"\n data-tooltip=\"This value will be served to all users who the above rules don't apply.\">\n <mat-chip-list>\n <mat-chip class=\"chip\">To all other users, serve:</mat-chip>\n </mat-chip-list>\n </div>\n <div *ngIf=\"rolloutPercentageItems().length !== 0\"\n data-tooltip=\"This fallback value will be served in case the user can't be identified.\">\n <mat-chip-list>\n <mat-chip class=\"chip\">To unidentified users, serve:</mat-chip>\n </mat-chip-list>\n </div>\n <div *ngIf=\"rolloutPercentageItems().length === 0 && rolloutRules().length === 0\">\n <mat-chip-list>\n <mat-chip class=\"chip\">To all users, serve:</mat-chip>\n </mat-chip-list>\n </div>\n <configcat-value class=\"served-value\" [valueFormControl]=\"value()\" [setting]=\"setting()\">\n </configcat-value>\n </div>\n <div class=\"buttons\">\n <button type=\"button\" class=\"sige-button targeting-button\" (click)=\"addRolloutRule()\" *ngIf=\"!readOnly\">\n {{rolloutRules().length === 0 ? 'Target specific users' : 'Add new targeting rule'}}\n </button>\n <button type=\"button\" class=\"sige-button targeting-button\" (click)=\"addRolloutPercentageItem()\"\n *ngIf=\"!readOnly\"\n [disabled]=\"setting().settingType === SettingType.Boolean && rolloutPercentageItems().length !== 0\">\n {{rolloutPercentageItems().length === 0 ? 'Target % of users' : 'Add new % option'}}\n </button>\n </div>\n </div>\n </div>\n</div>\n",
1325
- styles: [".setting-full{position:relative;display:flex;flex-wrap:nowrap;flex-direction:column;border-radius:3px}.setting-full .delete{position:absolute;right:-15px;top:-10px;color:#d6a9a9;cursor:pointer}.setting-full .delete:hover{color:#ed5251}.setting-full .delete mat-icon{font-size:20px}.setting-full .setting-header{padding:1.5em 1.5em 0}.setting-full .setting-header .setting-name{flex-direction:row}.setting-full .setting-header .setting-name:hover>.edit{visibility:visible}.setting-full .setting-header .setting-name .name{font-weight:bold;word-wrap:break-word;max-width:100%}.setting-full .setting-header .setting-name .edit{margin:5px 0 0 5px;cursor:pointer;visibility:hidden;color:gray}.setting-full .setting-header .setting-name .edit:hover{visibility:visible}.setting-full .setting-header .setting-name .edit mat-icon{font-size:18px}.setting-full .setting-header .setting-meta{margin-left:auto;margin-right:14px;font-size:.7em;color:gray}.setting-full .setting-content{display:flex}.setting-full .setting-content .setting-details{display:flex;flex-direction:column;width:400px;padding:0 0 1em 1em}.setting-full .setting-content .setting-details>*{margin:.5em}.setting-full .setting-content .setting-details .integrations{font-size:.8rem}.setting-full .setting-content .setting-details .integrations>div{margin:3px}.setting-full .setting-content .setting-details .integrations>div>a:hover{text-decoration:underline}.setting-full .setting-content .setting-details .setting-key{word-wrap:break-word}.setting-full .setting-content .setting-details .description{font-size:.8em;white-space:pre-wrap;word-wrap:break-word;max-width:300px}.setting-full .setting-content .setting-details code{background-color:#d3d3d3;padding:3px 6px}.setting-full .setting-content .rules-list{width:100%;padding:0 1em 1em}.setting-full .setting-content .targeting-button{margin-left:.5rem;margin-right:.5rem;margin-bottom:1em}.all-other{min-height:40px;justify-content:flex-start;border:1px solid #d5d5d5;border-radius:4px;background-color:#fff;margin:.5em 1em;padding:4px 0 4px .5em}.all-other .chip{background-color:#67b1fc3d}.all-other .served-value{margin-left:auto;min-width:100px}.end{margin-left:auto}.buttons{display:flex;margin-left:.5rem;margin-right:.5rem}.buttons .updated-date{font-size:.7em;margin-left:auto;margin-right:22px;color:gray}.flex{display:flex;flex-wrap:wrap;align-items:center}.show-hide-toggle{margin-left:1em;cursor:pointer}.show-hide-toggle a{font-size:.8rem}.sige-button{background-color:#d9eadc;color:#363636;cursor:pointer;justify-content:center;padding:.375em .75em;text-align:center;white-space:nowrap;border-radius:2px;font-size:.75rem;border:1px solid #dbdbdb;box-shadow:none;display:inline-flex;font-family:Montserrat,Roboto,\"Helvetica Neue\",sans-serif}.sige-button:hover{background-color:#bad0be}.sige-button:disabled{background-color:#fff;color:#b3b3b3}@media (max-width: 1200px){.setting-details{width:300px}}@media (max-width: 700px){.setting-full .setting-content{flex-wrap:wrap}.setting-full .setting-content .rules-list{padding:0}.buttons{flex-direction:column}.updated-date{display:none}.setting-details{padding:.2em 0 .2em .2em}.all-other{margin:.5em .3em}}\n"]
1326
- },] }
1327
- ];
1328
- SettingComponent.ctorParameters = () => [
1329
- { type: SettingListFormHelper },
1330
- { type: ChangeDetectorRef }
1331
- ];
1332
- SettingComponent.propDecorators = {
1333
- formGroup: [{ type: Input }],
1334
- readOnly: [{ type: Input }],
1335
- canEditSetting: [{ type: Input }],
1336
- canDeleteSetting: [{ type: Input }],
1337
- deleteSettingText: [{ type: Input }],
1338
- showEnvironmentName: [{ type: Input }],
1339
- editSettingRequested: [{ type: Output }],
1340
- deleteSettingRequested: [{ type: Output }]
1341
- };
1427
+ AuthorizationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: AuthorizationComponent, deps: [{ token: i2.FormBuilder }, { token: PublicApiService }], target: i0.ɵɵFactoryTarget.Component });
1428
+ AuthorizationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.12", type: AuthorizationComponent, selector: "configcat-authorization", inputs: { basePath: "basePath" }, outputs: { authorized: "authorized" }, ngImport: i0, template: "<form [formGroup]=\"formGroup\" (ngSubmit)=\"onSubmit()\">\n <div class=\"container\">\n <div class=\"form\">\n <p>\n <a href=\"https://app.configcat.com/my-account/public-api-credentials\" target=\"_blank\">\n Get your Basic Auth user name and password</a> to access ConfigCat Public API.\n <br /><br />\n <b>Note!</b> Your ConfigCat account's email address and password will not work here.\n </p>\n\n <mat-form-field class=\"form-field\">\n <mat-label>Basic auth user name</mat-label>\n <input matInput placeholder=\"Basic auth user name\" formControlName=\"basicAuthUsername\">\n <mat-error *ngIf=\"formGroup.controls.basicAuthUsername.invalid\">\n {{FormHelper.getErrorMessage(formGroup.controls.basicAuthUsername)}}\n </mat-error>\n </mat-form-field>\n <mat-form-field class=\"form-field\">\n <mat-label>Basic auth password</mat-label>\n <input matInput placeholder=\"Basic auth password\" formControlName=\"basicAuthPassword\" type=\"password\"\n autocomplete=\"current-password\">\n <mat-error *ngIf=\"formGroup.controls.basicAuthPassword.invalid\">\n {{FormHelper.getErrorMessage(formGroup.controls.basicAuthPassword)}}\n </mat-error>\n </mat-form-field>\n <div>\n <button mat-raised-button color=\"primary\" type=\"submit\" class=\"submit\">Authorize</button>\n <mat-error *ngIf=\"formGroup.invalid && formGroup.errors && formGroup.errors.invalidAuthorization\">\n {{formGroup.errors.invalidAuthorization}}</mat-error>\n </div>\n </div>\n </div>\n</form>", styles: [".container{display:flex;margin:8px}.form{width:100%}.form-field{width:100%;margin-bottom:8px}.submit{margin-top:8px;margin-bottom:8px}\n"], components: [{ type: i3.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }, { type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }], directives: [{ type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i3.MatLabel, selector: "mat-label" }, { type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["id", "disabled", "required", "type", "value", "readonly", "placeholder", "errorStateMatcher", "aria-describedby"], exportAs: ["matInput"] }, { type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlName, selector: "[formControlName]", inputs: ["disabled", "formControlName", "ngModel"], outputs: ["ngModelChange"] }, { type: i5$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.MatError, selector: "mat-error", inputs: ["id"] }] });
1429
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: AuthorizationComponent, decorators: [{
1430
+ type: Component,
1431
+ args: [{
1432
+ selector: 'configcat-authorization',
1433
+ templateUrl: './authorization.component.html',
1434
+ styleUrls: ['./authorization.component.scss']
1435
+ }]
1436
+ }], ctorParameters: function () { return [{ type: i2.FormBuilder }, { type: PublicApiService }]; }, propDecorators: { basePath: [{
1437
+ type: Input
1438
+ }], authorized: [{
1439
+ type: Output
1440
+ }] } });
1342
1441
 
1343
1442
  class NgConfigCatPublicApiUIModule {
1344
1443
  constructor() { }
@@ -1349,60 +1448,119 @@ class NgConfigCatPublicApiUIModule {
1349
1448
  };
1350
1449
  }
1351
1450
  }
1352
- NgConfigCatPublicApiUIModule.decorators = [
1353
- { type: NgModule, args: [{
1354
- declarations: [
1355
- BaseComponent,
1356
- ProductSelectComponent,
1357
- ConfigSelectComponent,
1358
- EnvironmentSelectComponent,
1359
- SettingSelectComponent,
1360
- SettingItemComponent,
1361
- AuthorizationComponent,
1362
- SettingListComponent,
1363
- SettingComponent,
1364
- RolloutPercentageItemsComponent,
1365
- RolloutRulesComponent,
1366
- ValueComponent,
1367
- ReasonDialogComponent,
1368
- ConcurrentDialogComponent,
1369
- SaveSnackbarComponent,
1370
- ExpandDialogComponent
1371
- ],
1372
- imports: [
1373
- CommonModule,
1374
- FormsModule,
1375
- ReactiveFormsModule,
1376
- BrowserAnimationsModule,
1377
- HttpClientModule,
1378
- ApiModule,
1379
- MatFormFieldModule,
1380
- MatOptionModule,
1381
- MatSelectModule,
1382
- MatButtonModule,
1383
- MatInputModule,
1384
- MatIconModule,
1385
- MatTooltipModule,
1386
- MatChipsModule,
1387
- MatAutocompleteModule,
1388
- DragDropModule,
1389
- RouterModule,
1390
- MatSnackBarModule,
1391
- MatDialogModule,
1392
- MatMenuModule
1393
- ],
1394
- exports: [
1395
- ApiModule,
1396
- AuthorizationComponent,
1397
- ProductSelectComponent,
1398
- ConfigSelectComponent,
1399
- EnvironmentSelectComponent,
1400
- SettingSelectComponent,
1401
- SettingItemComponent
1402
- ]
1403
- },] }
1404
- ];
1405
- NgConfigCatPublicApiUIModule.ctorParameters = () => [];
1451
+ NgConfigCatPublicApiUIModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: NgConfigCatPublicApiUIModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1452
+ NgConfigCatPublicApiUIModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: NgConfigCatPublicApiUIModule, declarations: [BaseComponent,
1453
+ ProductSelectComponent,
1454
+ ConfigSelectComponent,
1455
+ EnvironmentSelectComponent,
1456
+ SettingSelectComponent,
1457
+ SettingItemComponent,
1458
+ AuthorizationComponent,
1459
+ SettingListComponent,
1460
+ SettingComponent,
1461
+ RolloutPercentageItemsComponent,
1462
+ RolloutRulesComponent,
1463
+ ValueComponent,
1464
+ ReasonDialogComponent,
1465
+ ConcurrentDialogComponent,
1466
+ SaveSnackbarComponent,
1467
+ ExpandDialogComponent], imports: [CommonModule,
1468
+ FormsModule,
1469
+ ReactiveFormsModule,
1470
+ BrowserAnimationsModule,
1471
+ MatFormFieldModule,
1472
+ MatOptionModule,
1473
+ MatSelectModule,
1474
+ MatButtonModule,
1475
+ MatInputModule,
1476
+ MatIconModule,
1477
+ MatTooltipModule,
1478
+ MatChipsModule,
1479
+ MatAutocompleteModule,
1480
+ DragDropModule,
1481
+ RouterModule,
1482
+ MatSnackBarModule,
1483
+ MatDialogModule,
1484
+ MatMenuModule,
1485
+ ApiModule], exports: [AuthorizationComponent,
1486
+ ProductSelectComponent,
1487
+ ConfigSelectComponent,
1488
+ EnvironmentSelectComponent,
1489
+ SettingSelectComponent,
1490
+ SettingItemComponent] });
1491
+ NgConfigCatPublicApiUIModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: NgConfigCatPublicApiUIModule, imports: [[
1492
+ CommonModule,
1493
+ FormsModule,
1494
+ ReactiveFormsModule,
1495
+ BrowserAnimationsModule,
1496
+ MatFormFieldModule,
1497
+ MatOptionModule,
1498
+ MatSelectModule,
1499
+ MatButtonModule,
1500
+ MatInputModule,
1501
+ MatIconModule,
1502
+ MatTooltipModule,
1503
+ MatChipsModule,
1504
+ MatAutocompleteModule,
1505
+ DragDropModule,
1506
+ RouterModule,
1507
+ MatSnackBarModule,
1508
+ MatDialogModule,
1509
+ MatMenuModule,
1510
+ ApiModule
1511
+ ]] });
1512
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.12", ngImport: i0, type: NgConfigCatPublicApiUIModule, decorators: [{
1513
+ type: NgModule,
1514
+ args: [{
1515
+ declarations: [
1516
+ BaseComponent,
1517
+ ProductSelectComponent,
1518
+ ConfigSelectComponent,
1519
+ EnvironmentSelectComponent,
1520
+ SettingSelectComponent,
1521
+ SettingItemComponent,
1522
+ AuthorizationComponent,
1523
+ SettingListComponent,
1524
+ SettingComponent,
1525
+ RolloutPercentageItemsComponent,
1526
+ RolloutRulesComponent,
1527
+ ValueComponent,
1528
+ ReasonDialogComponent,
1529
+ ConcurrentDialogComponent,
1530
+ SaveSnackbarComponent,
1531
+ ExpandDialogComponent
1532
+ ],
1533
+ imports: [
1534
+ CommonModule,
1535
+ FormsModule,
1536
+ ReactiveFormsModule,
1537
+ BrowserAnimationsModule,
1538
+ MatFormFieldModule,
1539
+ MatOptionModule,
1540
+ MatSelectModule,
1541
+ MatButtonModule,
1542
+ MatInputModule,
1543
+ MatIconModule,
1544
+ MatTooltipModule,
1545
+ MatChipsModule,
1546
+ MatAutocompleteModule,
1547
+ DragDropModule,
1548
+ RouterModule,
1549
+ MatSnackBarModule,
1550
+ MatDialogModule,
1551
+ MatMenuModule,
1552
+ ApiModule
1553
+ ],
1554
+ exports: [
1555
+ AuthorizationComponent,
1556
+ ProductSelectComponent,
1557
+ ConfigSelectComponent,
1558
+ EnvironmentSelectComponent,
1559
+ SettingSelectComponent,
1560
+ SettingItemComponent
1561
+ ]
1562
+ }]
1563
+ }], ctorParameters: function () { return []; } });
1406
1564
 
1407
1565
  /*
1408
1566
  * Public API Surface of ng-configcat-publicapi-ui
@@ -1412,5 +1570,5 @@ NgConfigCatPublicApiUIModule.ctorParameters = () => [];
1412
1570
  * Generated bundle index. Do not edit.
1413
1571
  */
1414
1572
 
1415
- export { AuthorizationComponent, ConfigSelectComponent, EnvironmentSelectComponent, NgConfigCatPublicApiUIModule, ProductSelectComponent, PublicApiService, SettingItemComponent, SettingSelectComponent, BaseComponent as ɵa, PublicApiService as ɵb, Configuration as ɵc, PublicApiService as ɵd, SettingListComponent as ɵe, SettingListFormHelper as ɵf, SettingComponent as ɵg, RolloutPercentageItemsComponent as ɵh, RolloutRulesComponent as ɵi, ValueComponent as ɵj, ReasonDialogComponent as ɵk, ConcurrentDialogComponent as ɵl, SaveSnackbarComponent as ɵm, ExpandDialogComponent as ɵn };
1573
+ export { AuthorizationComponent, ConfigSelectComponent, EnvironmentSelectComponent, NgConfigCatPublicApiUIModule, ProductSelectComponent, PublicApiService, SettingItemComponent, SettingSelectComponent };
1416
1574
  //# sourceMappingURL=ng-configcat-publicapi-ui.js.map