@nettyapps/ntybase 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3295 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, Injectable, inject, NgModule, Inject, signal, input, output, computed, effect, model, ViewContainerRef, ViewChild, Input, ViewEncapsulation, forwardRef, ChangeDetectionStrategy } from '@angular/core';
3
+ import * as i1$2 from '@angular/common/http';
4
+ import { HttpErrorResponse, HttpResponse, HTTP_INTERCEPTORS, HttpClient, HttpHeaders } from '@angular/common/http';
5
+ import { of, throwError, lastValueFrom, map, catchError as catchError$1, Subject, finalize, take as take$1, takeUntil } from 'rxjs';
6
+ import { catchError, map as map$1, take } from 'rxjs/operators';
7
+ import * as i5 from '@angular/router';
8
+ import { Router, ActivatedRoute, RouterLink, RouterModule } from '@angular/router';
9
+ import * as i2$1 from '@nettyapps/ntycontract';
10
+ import { EnvironmentProxy } from '@nettyapps/ntycontract';
11
+ import * as i2$2 from '@angular/common';
12
+ import { DatePipe, Location, CommonModule, TitleCasePipe } from '@angular/common';
13
+ import * as i3$1 from '@ngx-translate/core';
14
+ import { TranslateModule, TranslateService } from '@ngx-translate/core';
15
+ import { Buffer } from 'buffer';
16
+ import { marker } from '@biesbjerg/ngx-translate-extract-marker';
17
+ import * as i1 from '@angular/material/dialog';
18
+ import { MAT_DIALOG_DATA, MatDialogModule, MatDialog, MatDialogRef, MatDialogConfig } from '@angular/material/dialog';
19
+ import * as i3 from '@angular/material/divider';
20
+ import { MatDividerModule } from '@angular/material/divider';
21
+ import * as i2 from '@angular/material/icon';
22
+ import { MatIconModule } from '@angular/material/icon';
23
+ import * as i1$1 from '@angular/material/snack-bar';
24
+ import { MatSnackBarModule } from '@angular/material/snack-bar';
25
+ import { Mutex } from 'async-mutex';
26
+ import { ModuleRegistry, AllCommunityModule, ClientSideRowModelModule, HighlightChangesModule, themeQuartz } from 'ag-grid-community';
27
+ import { StatusBarModule, ClipboardModule, ExcelExportModule, ColumnMenuModule, ContextMenuModule, CellSelectionModule, RowSelectionModule } from 'ag-grid-enterprise';
28
+ import * as i3$2 from '@angular/material/tooltip';
29
+ import { MatTooltipModule } from '@angular/material/tooltip';
30
+ import * as i3$3 from '@angular/material/menu';
31
+ import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
32
+ import * as i1$3 from '@angular/forms';
33
+ import { FormBuilder, Validators, FormsModule, ReactiveFormsModule, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
34
+ import * as i1$4 from '@angular/material/input';
35
+ import { MatInputModule } from '@angular/material/input';
36
+ import { MatFormFieldModule } from '@angular/material/form-field';
37
+ import { I18nService } from '@nettyapps/ntyi18n';
38
+ import * as i3$4 from '@angular/material/list';
39
+ import { MatListModule } from '@angular/material/list';
40
+ import { SearchInput, UiBase } from '@nettyapps/ntyui';
41
+ import * as i3$5 from '@angular/material/toolbar';
42
+ import { MatToolbarModule } from '@angular/material/toolbar';
43
+ import * as i3$6 from '@angular/material/autocomplete';
44
+ import { MatAutocompleteModule, MatAutocompleteTrigger } from '@angular/material/autocomplete';
45
+ import { ErrorStateMatcher } from '@angular/material/core';
46
+ import { Clipboard } from '@angular/cdk/clipboard';
47
+
48
+ class Ntybase {
49
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Ntybase, deps: [], target: i0.ɵɵFactoryTarget.Component });
50
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: Ntybase, isStandalone: false, selector: "lib-ntybase", ngImport: i0, template: `
51
+ <p>
52
+ ntybase works!
53
+ </p>
54
+ `, isInline: true, styles: [""] });
55
+ }
56
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Ntybase, decorators: [{
57
+ type: Component,
58
+ args: [{ selector: 'lib-ntybase', standalone: false, template: `
59
+ <p>
60
+ ntybase works!
61
+ </p>
62
+ ` }]
63
+ }] });
64
+
65
+ const credentialsKey = 'credentials';
66
+ class CredentialsService {
67
+ _credentials = null;
68
+ constructor() {
69
+ const savedCredentials = sessionStorage.getItem(credentialsKey) ||
70
+ localStorage.getItem(credentialsKey);
71
+ if (savedCredentials) {
72
+ this._credentials = JSON.parse(savedCredentials);
73
+ }
74
+ }
75
+ /**
76
+ * Checks is the user is authenticated.
77
+ * @return True if the user is authenticated.
78
+ */
79
+ isAuthenticated() {
80
+ return !!this.credentials;
81
+ }
82
+ /**
83
+ * Gets the user credentials.
84
+ * @return The user credentials or null if the user is not authenticated.
85
+ */
86
+ get credentials() {
87
+ return this._credentials;
88
+ }
89
+ get token() {
90
+ return this._credentials?.token ?? null;
91
+ }
92
+ /**
93
+ * Sets the user credentials.
94
+ * The credentials may be persisted across sessions by setting the `remember` parameter to true.
95
+ * Otherwise, the credentials are only persisted for the current session.
96
+ * @param credentials The user credentials.
97
+ * @param remember True to remember credentials across sessions.
98
+ */
99
+ setCredentials(credentials, remember) {
100
+ this._credentials = credentials || null;
101
+ if (credentials) {
102
+ const storage = remember ? localStorage : sessionStorage;
103
+ storage.setItem(credentialsKey, JSON.stringify(credentials));
104
+ }
105
+ else {
106
+ sessionStorage.removeItem(credentialsKey);
107
+ localStorage.removeItem(credentialsKey);
108
+ }
109
+ }
110
+ /** Get Credentials
111
+ *
112
+ * @returns
113
+ */
114
+ getCredentials() {
115
+ let _credentialsString = sessionStorage.getItem(credentialsKey);
116
+ if (_credentialsString == null || _credentialsString == '') {
117
+ _credentialsString = localStorage.getItem(credentialsKey);
118
+ }
119
+ return JSON.parse(_credentialsString ?? '');
120
+ }
121
+ /** Get the token if available otherwise return empty string
122
+ *
123
+ * @returns
124
+ */
125
+ getToken() {
126
+ try {
127
+ let _credentials = this.getCredentials();
128
+ return _credentials.token;
129
+ }
130
+ catch (error) {
131
+ return '';
132
+ }
133
+ }
134
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CredentialsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
135
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CredentialsService, providedIn: 'root' });
136
+ }
137
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CredentialsService, decorators: [{
138
+ type: Injectable,
139
+ args: [{
140
+ providedIn: 'root',
141
+ }]
142
+ }], ctorParameters: () => [] });
143
+
144
+ class UrlHelperService {
145
+ router = inject(Router);
146
+ cleanUrl(url) {
147
+ let result = url
148
+ .replace('/mfalogin?redirect=', '')
149
+ .replace('/mfalogin?redirect=', '')
150
+ .replace('/mfalogin?redirect=', '')
151
+ .replace('/login?redirect=', '')
152
+ .replace('/login?redirect=', '')
153
+ .replace('/login?redirect=', '')
154
+ .replace(new RegExp('%25', 'g'), '%')
155
+ .replace(new RegExp('%25', 'g'), '%')
156
+ .replace(new RegExp('%25', 'g'), '%')
157
+ .replace(new RegExp('%25', 'g'), '%')
158
+ .replace(new RegExp('%25', 'g'), '%')
159
+ .replace(new RegExp('%25', 'g'), '%')
160
+ .replace(new RegExp('%22', 'g'), '"')
161
+ .replace(new RegExp('%3F', 'g'), '?')
162
+ .replace(new RegExp('%3D', 'g'), '=')
163
+ .replace(new RegExp('%2F', 'g'), '/')
164
+ .replace(new RegExp('%26', 'g'), '&')
165
+ .replace(new RegExp('/mfalogin?redirect=', 'g'), '')
166
+ .replace(new RegExp('/login?redirect=', 'g'), '');
167
+ console.log('url:', url);
168
+ console.log('result:', result);
169
+ // return url;
170
+ return result;
171
+ }
172
+ navigate(url) {
173
+ let urlParts = url.split('?');
174
+ if (urlParts.length == 1) {
175
+ return this.router.navigate(urlParts, { replaceUrl: true });
176
+ }
177
+ let parameters = urlParts[1].split('&');
178
+ return this.router.navigate([urlParts[0]], {
179
+ queryParams: {
180
+ parameters: parameters[0]?.replace('parameters=', '') ?? '',
181
+ type: parameters[1]?.replace('type=', '') ?? '',
182
+ },
183
+ replaceUrl: true,
184
+ });
185
+ }
186
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: UrlHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
187
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: UrlHelperService, providedIn: 'root' });
188
+ }
189
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: UrlHelperService, decorators: [{
190
+ type: Injectable,
191
+ args: [{
192
+ providedIn: 'root',
193
+ }]
194
+ }] });
195
+
196
+ class AuthenticationInterceptor {
197
+ router = inject(Router);
198
+ credentialsService = inject(CredentialsService);
199
+ environmentProxy = inject(EnvironmentProxy);
200
+ urlHelperService = inject(UrlHelperService);
201
+ intercept(req, next) {
202
+ if (req.headers.get('No-Auth') == 'True')
203
+ return next.handle(req.clone());
204
+ let token = this.credentialsService.token;
205
+ if (token != null) {
206
+ let appName = this.environmentProxy.getApplicationName();
207
+ const clonedreq = req.clone({
208
+ headers: req.headers.set('Authorization', 'Bearer ' + token),
209
+ // .set("NettyAppName",appName)
210
+ });
211
+ return next.handle(clonedreq).pipe(catchError((error) => {
212
+ if (error instanceof HttpErrorResponse && error.status === 401) {
213
+ // Handle 401 error, e.g., navigate to the login page
214
+ this.router.navigate(['/login'], {
215
+ queryParams: {
216
+ redirect: this.urlHelperService.cleanUrl(this.router.url),
217
+ },
218
+ replaceUrl: true,
219
+ });
220
+ // Return an observable with a successful response
221
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
222
+ }
223
+ if (error instanceof HttpErrorResponse && error.status === 403) {
224
+ this.router.navigate(['/forbidden'], {
225
+ state: { attemptedUrl: this.router.url }, // Orijinal URL'i state olarak geçme
226
+ });
227
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
228
+ }
229
+ if (error instanceof HttpErrorResponse && error.status === 428) {
230
+ // Handle 428 error, e.g., navigate to the login page
231
+ this.router.navigate(['/mfalogin'], {
232
+ queryParams: {
233
+ redirect: this.urlHelperService.cleanUrl(this.router.url),
234
+ },
235
+ replaceUrl: true,
236
+ });
237
+ // Return an observable with a successful response
238
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
239
+ }
240
+ // For other errors, re-throw the error to propagate it further
241
+ return throwError(() => error);
242
+ }));
243
+ }
244
+ else {
245
+ return next.handle(req.clone()).pipe(catchError((error) => {
246
+ if (error instanceof HttpErrorResponse && error.status === 401) {
247
+ // Handle 401 error, e.g., navigate to the login page
248
+ this.router.navigate(['/login'], {
249
+ queryParams: {
250
+ redirect: this.urlHelperService.cleanUrl(this.router.url),
251
+ },
252
+ replaceUrl: true,
253
+ });
254
+ // Return an observable with a successful response
255
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
256
+ }
257
+ if (error instanceof HttpErrorResponse && error.status === 403) {
258
+ this.router.navigate(['/forbidden'], {
259
+ state: { attemptedUrl: this.router.url }, // Orijinal URL'i state olarak geçme
260
+ });
261
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
262
+ }
263
+ if (error instanceof HttpErrorResponse && error.status === 428) {
264
+ // Handle 428 error, e.g., navigate to the login page
265
+ this.router.navigate(['/mfalogin'], {
266
+ queryParams: {
267
+ redirect: this.urlHelperService.cleanUrl(this.router.url),
268
+ },
269
+ replaceUrl: true,
270
+ });
271
+ // Return an observable with a successful response
272
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
273
+ }
274
+ // For other errors, re-throw the error to propagate it further
275
+ return throwError(() => error);
276
+ }));
277
+ }
278
+ }
279
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
280
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationInterceptor });
281
+ }
282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationInterceptor, decorators: [{
283
+ type: Injectable
284
+ }] });
285
+
286
+ class CanDeactivateGuard {
287
+ canDeactivate(component) {
288
+ return component.canDeactivate ? component.canDeactivate() : true;
289
+ }
290
+ }
291
+
292
+ class NtybaseModule {
293
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NtybaseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
294
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: NtybaseModule, declarations: [Ntybase], exports: [Ntybase] });
295
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NtybaseModule, providers: [
296
+ {
297
+ provide: HTTP_INTERCEPTORS,
298
+ useClass: AuthenticationInterceptor,
299
+ multi: true,
300
+ },
301
+ [CanDeactivateGuard],
302
+ DatePipe,
303
+ ] });
304
+ }
305
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NtybaseModule, decorators: [{
306
+ type: NgModule,
307
+ args: [{
308
+ declarations: [Ntybase],
309
+ imports: [],
310
+ exports: [Ntybase],
311
+ providers: [
312
+ {
313
+ provide: HTTP_INTERCEPTORS,
314
+ useClass: AuthenticationInterceptor,
315
+ multi: true,
316
+ },
317
+ [CanDeactivateGuard],
318
+ DatePipe,
319
+ ],
320
+ }]
321
+ }] });
322
+
323
+ class NettyHelper {
324
+ /**
325
+ * Returns null if fields are equal otherwise return second field
326
+ * @param firstField
327
+ * @param secondField
328
+ * @returns
329
+ */
330
+ static difference(firstField, secondField) {
331
+ if (firstField != secondField) {
332
+ return secondField;
333
+ }
334
+ return null;
335
+ }
336
+ /**
337
+ * Returns null if fields are equal otherwise return second field
338
+ * @param firstField
339
+ * @param secondField
340
+ * @param format
341
+ * @returns
342
+ */
343
+ static dateDifference(firstField, secondField, format) {
344
+ let culture = localStorage.getItem('languageApiCode') ?? 'tr-TR';
345
+ let datePipe = new DatePipe(culture);
346
+ if (datePipe.transform(firstField, format) !=
347
+ datePipe.transform(secondField, format)) {
348
+ return secondField;
349
+ }
350
+ return null;
351
+ }
352
+ }
353
+
354
+ class CurrentUserPreference {
355
+ coid = null;
356
+ nettyUserPreferenceGUID = null;
357
+ nettyUserGUID = null;
358
+ nettyUserName = null;
359
+ nettyUserFullName = null;
360
+ nettyPreferenceType = null;
361
+ nettyPreferenceData = null;
362
+ init() {
363
+ this.coid = null;
364
+ this.nettyUserPreferenceGUID = null;
365
+ this.nettyUserGUID = null;
366
+ this.nettyUserName = '';
367
+ this.nettyUserFullName = '';
368
+ this.nettyPreferenceType = '';
369
+ this.nettyPreferenceData = '';
370
+ }
371
+ initParameter(parameters) {
372
+ if (parameters == null || parameters == undefined) {
373
+ this.init();
374
+ return;
375
+ }
376
+ this.coid = parameters.coid;
377
+ this.nettyUserPreferenceGUID = parameters.nettyUserPreferenceGUID;
378
+ this.nettyUserGUID = parameters.nettyUserGUID;
379
+ this.nettyUserName = parameters.nettyUserName;
380
+ this.nettyUserFullName = parameters.nettyUserFullName;
381
+ this.nettyPreferenceType = parameters.nettyPreferenceType;
382
+ this.nettyPreferenceData = parameters.nettyPreferenceData;
383
+ }
384
+ compare(record) {
385
+ let newRecord = new CurrentUserPreference();
386
+ newRecord.init();
387
+ newRecord.coid = record.coid;
388
+ newRecord.nettyUserPreferenceGUID = record.nettyUserPreferenceGUID;
389
+ newRecord.nettyUserGUID = NettyHelper.difference(this.nettyUserGUID, record.nettyUserGUID);
390
+ newRecord.nettyPreferenceType = NettyHelper.difference(this.nettyPreferenceType, record.nettyPreferenceType);
391
+ newRecord.nettyPreferenceData = NettyHelper.difference(this.nettyPreferenceData, record.nettyPreferenceData);
392
+ return newRecord;
393
+ }
394
+ }
395
+
396
+ class ConfirmDialog {
397
+ dialogRef;
398
+ data;
399
+ constructor(dialogRef, data) {
400
+ this.dialogRef = dialogRef;
401
+ this.data = data;
402
+ }
403
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ConfirmDialog, deps: [{ token: i1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
404
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: ConfirmDialog, isStandalone: true, selector: "ntybase-confirm-dialog", ngImport: i0, template: "<div class=\"dialog-container\">\n <h2 mat-dialog-title class=\"dialog-title\">\n <mat-icon color=\"warn\" class=\"warning-icon\">warning</mat-icon>\n <span>{{data.title}}</span>\n </h2>\n\n <mat-divider class=\"divider\"></mat-divider>\n\n <mat-dialog-content class=\"dialog-content\">\n <p class=\"message\">{{data.message}}</p>\n </mat-dialog-content>\n\n <mat-dialog-actions align=\"end\" class=\"dialog-actions\">\n <button mat-stroked-button [mat-dialog-close]=\"false\" class=\"btn-cancel\">\n {{'@btnCancel' | translate}}\n </button>\n <button\n mat-flat-button\n color=\"warn\"\n [mat-dialog-close]=\"true\"\n class=\"btn-ok\"\n >\n {{'@btnOK' | translate}}\n </button>\n </mat-dialog-actions>\n</div>\n", styles: [".dialog-container{display:flex;flex-direction:column;min-width:400px;max-width:90vw;padding:24px;border-radius:12px!important}.dialog-title{display:flex;align-items:center;margin:0 0 12px;padding:0;color:var(--mat-sys-primary)}.dialog-title span{margin-left:8px}.warning-icon{color:var(--mat-sys-primary);transform:scale(1.2)}.divider{margin:8px 0 16px;border-top-color:var(--mat-sys-primary)}.dialog-content{padding:8px 0 24px;margin:0;color:var(--mat-sys-primary);line-height:1.6}.dialog-content .message{margin:0;white-space:pre-wrap}.dialog-actions{padding:16px 0 0;margin:0;gap:8px}.btn-ok{padding:8px 16px;border-radius:6px;font-weight:500;background-color:var(--mat-sys-primary);color:var(--mat-sys-primary-container)}.btn-cancel{padding:8px 16px;border-radius:6px;color:var(--mat-sys-secondary-container);background-color:var(--mat-sys-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3$1.TranslatePipe, name: "translate" }] });
405
+ }
406
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ConfirmDialog, decorators: [{
407
+ type: Component,
408
+ args: [{ selector: 'ntybase-confirm-dialog', imports: [
409
+ MatDialogModule,
410
+ MatSnackBarModule,
411
+ MatIconModule,
412
+ MatDividerModule,
413
+ TranslateModule,
414
+ ], template: "<div class=\"dialog-container\">\n <h2 mat-dialog-title class=\"dialog-title\">\n <mat-icon color=\"warn\" class=\"warning-icon\">warning</mat-icon>\n <span>{{data.title}}</span>\n </h2>\n\n <mat-divider class=\"divider\"></mat-divider>\n\n <mat-dialog-content class=\"dialog-content\">\n <p class=\"message\">{{data.message}}</p>\n </mat-dialog-content>\n\n <mat-dialog-actions align=\"end\" class=\"dialog-actions\">\n <button mat-stroked-button [mat-dialog-close]=\"false\" class=\"btn-cancel\">\n {{'@btnCancel' | translate}}\n </button>\n <button\n mat-flat-button\n color=\"warn\"\n [mat-dialog-close]=\"true\"\n class=\"btn-ok\"\n >\n {{'@btnOK' | translate}}\n </button>\n </mat-dialog-actions>\n</div>\n", styles: [".dialog-container{display:flex;flex-direction:column;min-width:400px;max-width:90vw;padding:24px;border-radius:12px!important}.dialog-title{display:flex;align-items:center;margin:0 0 12px;padding:0;color:var(--mat-sys-primary)}.dialog-title span{margin-left:8px}.warning-icon{color:var(--mat-sys-primary);transform:scale(1.2)}.divider{margin:8px 0 16px;border-top-color:var(--mat-sys-primary)}.dialog-content{padding:8px 0 24px;margin:0;color:var(--mat-sys-primary);line-height:1.6}.dialog-content .message{margin:0;white-space:pre-wrap}.dialog-actions{padding:16px 0 0;margin:0;gap:8px}.btn-ok{padding:8px 16px;border-radius:6px;font-weight:500;background-color:var(--mat-sys-primary);color:var(--mat-sys-primary-container)}.btn-cancel{padding:8px 16px;border-radius:6px;color:var(--mat-sys-secondary-container);background-color:var(--mat-sys-secondary)}\n"] }]
415
+ }], ctorParameters: () => [{ type: i1.MatDialogRef }, { type: undefined, decorators: [{
416
+ type: Inject,
417
+ args: [MAT_DIALOG_DATA]
418
+ }] }] });
419
+
420
+ // alert.service.ts
421
+ class AlertService {
422
+ snackBar;
423
+ dialog;
424
+ translate;
425
+ constructor(snackBar, dialog, translate) {
426
+ this.snackBar = snackBar;
427
+ this.dialog = dialog;
428
+ this.translate = translate;
429
+ }
430
+ // For simple notifications
431
+ showAlert(message, action, duration = 3000) {
432
+ const actionText = action ? action : '@btnOK';
433
+ this.snackBar.open(this.translate.instant(message), this.translate.instant(actionText), { duration });
434
+ }
435
+ // For confirmation dialogs
436
+ showConfirm(message) {
437
+ return new Promise((resolve) => {
438
+ const dialogRef = this.dialog.open(ConfirmDialog, {
439
+ width: 'auto',
440
+ height: 'auto',
441
+ data: {
442
+ message: this.translate.instant(message),
443
+ title: this.translate.instant('@Confirmation'),
444
+ },
445
+ });
446
+ dialogRef.afterClosed().subscribe((result) => {
447
+ resolve(!!result);
448
+ });
449
+ });
450
+ }
451
+ // For success notifications
452
+ showSuccess(message, duration = 3000) {
453
+ this.snackBar.open(this.translate.instant(message), this.translate.instant('@btnOK'), {
454
+ duration,
455
+ panelClass: ['success-snackbar'],
456
+ horizontalPosition: 'right',
457
+ verticalPosition: 'bottom',
458
+ });
459
+ }
460
+ // For error notifications
461
+ showError(message, duration = 5000) {
462
+ this.snackBar.open(this.translate.instant(message), this.translate.instant('@btnOK'), {
463
+ duration,
464
+ panelClass: ['error-snackbar'],
465
+ horizontalPosition: 'right',
466
+ verticalPosition: 'bottom',
467
+ });
468
+ }
469
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AlertService, deps: [{ token: i1$1.MatSnackBar }, { token: i1.MatDialog }, { token: i3$1.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable });
470
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AlertService, providedIn: 'root' });
471
+ }
472
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AlertService, decorators: [{
473
+ type: Injectable,
474
+ args: [{
475
+ providedIn: 'root',
476
+ }]
477
+ }], ctorParameters: () => [{ type: i1$1.MatSnackBar }, { type: i1.MatDialog }, { type: i3$1.TranslateService }] });
478
+
479
+ class CommonService {
480
+ router = inject(Router);
481
+ location = inject(Location);
482
+ datePipe = inject(DatePipe);
483
+ alertService = inject(AlertService);
484
+ rightSidenavOpen = signal(false, ...(ngDevMode ? [{ debugName: "rightSidenavOpen" }] : []));
485
+ toggleRightSidenav(open) {
486
+ this.rightSidenavOpen.set(open);
487
+ }
488
+ normalizeTurkish(text) {
489
+ return text
490
+ .toLocaleLowerCase('tr-TR')
491
+ .replace(/[ıİ]/g, 'i')
492
+ .replace(/[ğĞ]/g, 'g')
493
+ .replace(/[üÜ]/g, 'u')
494
+ .replace(/[şŞ]/g, 's')
495
+ .replace(/[öÖ]/g, 'o')
496
+ .replace(/[çÇ]/g, 'c');
497
+ }
498
+ /**
499
+ * Gets the clean URL path without fragments or query params
500
+ */
501
+ getCleanUrlPath() {
502
+ const tree = this.router.parseUrl(this.router.url);
503
+ // Remove all fragments and query params
504
+ return (tree.root.children['primary']?.segments.map((s) => s.path).join('/') ||
505
+ '/');
506
+ }
507
+ /** Merge columns with the given seperator
508
+ *
509
+ * @param columns string array to merge
510
+ * @param seperator seperator for the array. space if null
511
+ * @returns merged string
512
+ */
513
+ mergeColumns(columns, seperator = ' ') {
514
+ console.log('mergeColumns', columns);
515
+ let result = '';
516
+ columns.forEach((column) => {
517
+ if (!(column == undefined || column == null)) {
518
+ if (result.length > 0) {
519
+ result += seperator;
520
+ }
521
+ result += column.trim();
522
+ }
523
+ });
524
+ return result.trim();
525
+ }
526
+ /** Go back to the last page or close the tab
527
+ *
528
+ */
529
+ goBack() {
530
+ if (!document.referrer) {
531
+ this.location.back();
532
+ }
533
+ else {
534
+ window.close();
535
+ }
536
+ }
537
+ // General Functions
538
+ //--------------------------------------
539
+ /** Delay for the given duration
540
+ *
541
+ * @param ms wait duration in ms
542
+ * @returns promise that waits for the duration
543
+ */
544
+ delay(ms) {
545
+ return new Promise((resolve) => setTimeout(resolve, ms));
546
+ }
547
+ // Base64 Functions
548
+ //--------------------------------------
549
+ /** Convert string to base64
550
+ *
551
+ * @param stringForConversion
552
+ * @returns
553
+ */
554
+ convertToBase64(stringForConversion) {
555
+ return Buffer.from(stringForConversion).toString('base64');
556
+ // return Base64.encode(stringForConversion);
557
+ }
558
+ /** Convert base64 string to normal string
559
+ *
560
+ * @param base64data
561
+ * @returns
562
+ */
563
+ convertFromBase64(base64data) {
564
+ //ascii
565
+ return Buffer.from(base64data, 'base64').toString();
566
+ // return Base64.decode(base64data);
567
+ }
568
+ // Formatters
569
+ //----------------------------------------
570
+ /** Compare dates. Designed for grid date filter
571
+ *
572
+ * @param filterValue user provided filtering date
573
+ * @param cellValue cell value to compare
574
+ * @returns -1 if cell date is smaller(earlier), 0 if dates are equal and 1 if cell date is bigger(later)
575
+ */
576
+ dateComparator(filterValue, cellValue) {
577
+ try {
578
+ let filterDate = this.datePipe.transform(filterValue, 'yyyyMMdd') ?? '';
579
+ let cellDate = this.datePipe.transform(cellValue, 'yyyyMMdd') ?? '';
580
+ if (cellDate < filterDate) {
581
+ return -1;
582
+ }
583
+ else if (cellDate > filterDate) {
584
+ return 1;
585
+ }
586
+ else {
587
+ return 0;
588
+ }
589
+ }
590
+ catch (error) {
591
+ return -1;
592
+ }
593
+ }
594
+ /** Format date into 'dd-MM-yyyy' format for grids
595
+ *
596
+ * @param dateValue
597
+ * @returns
598
+ */
599
+ dateFormatter(dateValue) {
600
+ return this.datePipe.transform(dateValue, 'dd-MM-yyyy') ?? '';
601
+ }
602
+ /** Reactive Signal state for tracking grid updates */
603
+ _updates = signal(null, ...(ngDevMode ? [{ debugName: "_updates" }] : []));
604
+ /** Read-only Signal for external components to observe updates */
605
+ updates = this._updates.asReadonly();
606
+ /** Broadcasts grid data changes to subscribers */
607
+ notifyUpdate(type, action, data) {
608
+ this._updates.set({ type, action, data });
609
+ }
610
+ // Security Functions
611
+ //--------------------------------------
612
+ /** User is not allowed to access the page
613
+ *
614
+ */
615
+ async userNotAllowedToRead(title = '', message = '') {
616
+ if (title == '') {
617
+ title = marker('Erişim Kısıtı');
618
+ }
619
+ // else {title = this.nettyAlertService.GetMessageText(title);}
620
+ if (message == '') {
621
+ message = marker('Kullanıcı bu ekrana erişemez!');
622
+ }
623
+ // else {message = this.nettyAlertService.GetMessageText(message);}
624
+ await this.alertService.showAlert(title, message);
625
+ this.goBack();
626
+ }
627
+ async userNotAllowedToAdd(title = '', message = '') {
628
+ if (title == '') {
629
+ title = marker('Erişim Kısıtı');
630
+ }
631
+ // else { title = this.nettyAlertService.GetMessageText(title); }
632
+ if (message == '') {
633
+ message = marker('Kullanıcı yeni kayıt ekleyemez!');
634
+ }
635
+ // else { message = this.nettyAlertService.GetMessageText(message); }
636
+ await this.alertService.showAlert(title, message);
637
+ this.goBack();
638
+ }
639
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CommonService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
640
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CommonService, providedIn: 'root' });
641
+ }
642
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CommonService, decorators: [{
643
+ type: Injectable,
644
+ args: [{
645
+ providedIn: 'root',
646
+ }]
647
+ }] });
648
+
649
+ class NettyAgGridService {
650
+ userPreferenceLock;
651
+ http = inject(HttpClient);
652
+ nettyAlertService = inject(AlertService);
653
+ environmentProxy = inject(EnvironmentProxy);
654
+ commonService = inject(CommonService);
655
+ constructor() {
656
+ this.userPreferenceLock = new Mutex();
657
+ }
658
+ // Restapi functions
659
+ //--------------------------------------
660
+ /**
661
+ * Select user preference
662
+ * @param admuserpreference
663
+ * @returns
664
+ */
665
+ selectForUserAdmUserPreference(admuserpreference) {
666
+ let sysFunctionsURL = this.environmentProxy.getAdminLink('SelectForUser');
667
+ return this.http.post(sysFunctionsURL, admuserpreference);
668
+ }
669
+ /**
670
+ * Insert or update user preference
671
+ * @param admuserpreference
672
+ * @returns
673
+ */
674
+ insertOrUpdateAdmUserPreference(admuserpreference) {
675
+ let sysFunctionsURL = this.environmentProxy.getAdminLink('InsertOrUpdate');
676
+ return this.http.post(sysFunctionsURL, admuserpreference);
677
+ }
678
+ // AG Grid Preference Management
679
+ //--------------------------------------
680
+ /** Preference Save and Upload
681
+ *
682
+ * @param preferenceType component name or other uniqe identifier
683
+ * @param gridApi api for agGrid column management
684
+ * @returns nothing
685
+ */
686
+ async saveGridUserPreference(preferenceType, gridApi, defaultColDef) {
687
+ if (!gridApi)
688
+ return;
689
+ if (this.userPreferenceLock.isLocked())
690
+ return;
691
+ var releaseLock = await this.userPreferenceLock.acquire();
692
+ let columnState = gridApi?.getColumnState();
693
+ let jsonstring = JSON.stringify(columnState);
694
+ if (jsonstring == defaultColDef) {
695
+ releaseLock();
696
+ return;
697
+ }
698
+ let columnStateSaved = sessionStorage.getItem(preferenceType);
699
+ //If the column order identical then return
700
+ if (columnStateSaved && columnStateSaved == jsonstring) {
701
+ releaseLock();
702
+ return;
703
+ }
704
+ try {
705
+ sessionStorage.setItem(preferenceType, jsonstring);
706
+ await this.commonService.delay(100);
707
+ let admuserpreference = new CurrentUserPreference();
708
+ admuserpreference.nettyPreferenceType = preferenceType;
709
+ if (jsonstring != undefined) {
710
+ admuserpreference.nettyPreferenceData =
711
+ this.commonService.convertToBase64(jsonstring);
712
+ await lastValueFrom(this.insertOrUpdateAdmUserPreference(admuserpreference));
713
+ }
714
+ }
715
+ catch (error) {
716
+ console.log(error);
717
+ this.nettyAlertService.showAlert('Error', error.Message);
718
+ }
719
+ finally {
720
+ releaseLock();
721
+ }
722
+ }
723
+ /** Get the user preference and update grid columns
724
+ *
725
+ * @param preferenceType component name or other uniqe identifier
726
+ * @param gridApi
727
+ * @returns
728
+ */
729
+ async loadGridUserPreference(preferenceType, gridApi) {
730
+ if (!gridApi)
731
+ return;
732
+ let columnState = sessionStorage.getItem(preferenceType) ?? '';
733
+ if (columnState.length > 0) {
734
+ if (this.userPreferenceLock.isLocked())
735
+ return;
736
+ var releaseLock = await this.userPreferenceLock.acquire();
737
+ try {
738
+ gridApi.applyColumnState({ state: JSON.parse(columnState) });
739
+ }
740
+ catch { }
741
+ this.commonService.delay(100).then(() => releaseLock());
742
+ return;
743
+ }
744
+ this.copyGridUserPereferenceToLocal(preferenceType).then(() => {
745
+ try {
746
+ let jsonstring = sessionStorage.getItem(preferenceType)?.trim() ?? '';
747
+ if (jsonstring.length > 0) {
748
+ gridApi.applyColumnState(JSON.parse(jsonstring));
749
+ }
750
+ }
751
+ catch (error) { }
752
+ });
753
+ }
754
+ /** Download user preference and save to local
755
+ *
756
+ * @param preferenceType component name or other uniqe identifier
757
+ * @returns
758
+ */
759
+ async copyGridUserPereferenceToLocal(preferenceType) {
760
+ var relaseLock = await this.userPreferenceLock.acquire();
761
+ let columnState = sessionStorage.getItem(preferenceType);
762
+ if (columnState) {
763
+ relaseLock();
764
+ return;
765
+ }
766
+ try {
767
+ let admuserpreference = new CurrentUserPreference();
768
+ admuserpreference.nettyPreferenceType = preferenceType;
769
+ let data = (await lastValueFrom(this.selectForUserAdmUserPreference(admuserpreference)));
770
+ if (data != undefined &&
771
+ data != null &&
772
+ (data?.nettyPreferenceType?.length ?? 0) > 0) {
773
+ let jsonstring = this.commonService.convertFromBase64(data.nettyPreferenceType ?? '');
774
+ sessionStorage.setItem(preferenceType, jsonstring);
775
+ relaseLock();
776
+ }
777
+ else {
778
+ relaseLock();
779
+ }
780
+ }
781
+ catch (error) {
782
+ relaseLock();
783
+ }
784
+ }
785
+ /** Expand all Fit to screen all columns of the grid
786
+ *
787
+ * @param gridApi api of the aggrid to manage
788
+ * @param gridApi columns api of the grid to manage
789
+ * @param gridExpanded initial status of the grid
790
+ * @returns current status of the grid
791
+ */
792
+ expandCollapseGrid(gridApi, gridExpanded = false) {
793
+ let _gridExpanded = gridExpanded;
794
+ try {
795
+ if (gridExpanded) {
796
+ gridExpanded = false;
797
+ gridApi.sizeColumnsToFit();
798
+ }
799
+ else {
800
+ gridExpanded = true;
801
+ var allColumnIds = [];
802
+ gridApi.getColumns().forEach((column) => {
803
+ allColumnIds.push(column.colId);
804
+ });
805
+ gridApi.autoSizeColumns(allColumnIds);
806
+ }
807
+ }
808
+ catch (error) {
809
+ console.log(error);
810
+ return _gridExpanded;
811
+ }
812
+ return gridExpanded;
813
+ }
814
+ /** Set the QuickFilter of the grid and save the entry in session storage
815
+ *
816
+ * @param searchValueName Component identifier
817
+ * @param searchValue Value to search
818
+ * @param gridApi api for the current aggrid
819
+ */
820
+ setGridQuickSearch(searchValueName, searchValue, gridApi) {
821
+ if (gridApi) {
822
+ gridApi.setQuickFilter(searchValue);
823
+ }
824
+ sessionStorage.setItem(searchValueName, searchValue);
825
+ }
826
+ /** Reapply the last search value from the session storage
827
+ *
828
+ * @param searchValueName Component identifier
829
+ * @param gridApi api for the current aggrid
830
+ * @returns
831
+ */
832
+ reapplyGridQuickSearch(searchValueName, gridApi) {
833
+ if (!gridApi)
834
+ return '';
835
+ // Reapply the last filter
836
+ let searchValue = sessionStorage.getItem(searchValueName) ?? '';
837
+ if (searchValue.length > 0) {
838
+ gridApi.setQuickFilter(searchValue);
839
+ }
840
+ return searchValue;
841
+ }
842
+ /** Get Grid Column Default parameters for the given component
843
+ *
844
+ * @param componentName Name of the component we are initializing
845
+ * @returns
846
+ */
847
+ getGridColumnDefault(componentName = '') {
848
+ return {
849
+ flex: 1,
850
+ filter: true,
851
+ resizable: true,
852
+ sortable: true,
853
+ };
854
+ }
855
+ /** Return excel style for ag-grid
856
+ *
857
+ * @returns
858
+ */
859
+ getExcelStyles() {
860
+ return [
861
+ { id: 'text-format', dataType: 'string' },
862
+ {
863
+ id: 'numberType',
864
+ dataType: 'number',
865
+ numberFormat: { format: '#,##0.00' },
866
+ alignment: { horizontal: 'right', vertical: 'Center' },
867
+ },
868
+ {
869
+ id: 'date-format',
870
+ dataType: 'dateTime',
871
+ numberFormat: { format: 'dd/mm/yyyy;@' },
872
+ },
873
+ {
874
+ id: 'dateTime-format',
875
+ dataType: 'dateTime',
876
+ numberFormat: { format: 'dd/mm/yyyy hh:MM:ss;@' },
877
+ },
878
+ ];
879
+ }
880
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NettyAgGridService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
881
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NettyAgGridService, providedIn: 'root' });
882
+ }
883
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NettyAgGridService, decorators: [{
884
+ type: Injectable,
885
+ args: [{
886
+ providedIn: 'root',
887
+ }]
888
+ }], ctorParameters: () => [] });
889
+
890
+ class SecurityDto {
891
+ coid = null;
892
+ userGUID = null;
893
+ application = '';
894
+ formCode = '';
895
+ }
896
+
897
+ class SysfunctionProxy {
898
+ http;
899
+ environmentProxy;
900
+ constructor(http, environmentProxy) {
901
+ this.http = http;
902
+ this.environmentProxy = environmentProxy;
903
+ }
904
+ /** Get the authentication list for the given form
905
+ *
906
+ * @param formCode
907
+ * @returns
908
+ */
909
+ getAuthentication(formCode) {
910
+ let sysFunctionsURL = this.environmentProxy.getAdminLink('getAuthentication');
911
+ let securityDto = new SecurityDto();
912
+ securityDto.formCode = formCode;
913
+ securityDto.application = this.environmentProxy.getApplicationName();
914
+ let call$ = this.http.post(sysFunctionsURL, securityDto);
915
+ return call$.pipe(map((result) => result), catchError$1((error) => {
916
+ let errorMessage = error.message; //.error.title;
917
+ return throwError(() => new Error(errorMessage ?? ''));
918
+ }));
919
+ }
920
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: SysfunctionProxy, deps: [{ token: i1$2.HttpClient }, { token: i2$1.EnvironmentProxy }], target: i0.ɵɵFactoryTarget.Injectable });
921
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: SysfunctionProxy, providedIn: 'root' });
922
+ }
923
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: SysfunctionProxy, decorators: [{
924
+ type: Injectable,
925
+ args: [{
926
+ providedIn: 'root',
927
+ }]
928
+ }], ctorParameters: () => [{ type: i1$2.HttpClient }, { type: i2$1.EnvironmentProxy }] });
929
+
930
+ ModuleRegistry.registerModules([
931
+ AllCommunityModule,
932
+ StatusBarModule,
933
+ ClientSideRowModelModule,
934
+ ClipboardModule,
935
+ ExcelExportModule,
936
+ ColumnMenuModule,
937
+ ContextMenuModule,
938
+ CellSelectionModule,
939
+ HighlightChangesModule,
940
+ RowSelectionModule,
941
+ ]);
942
+ // AgGrid Dark Mode Row Style
943
+ const myTheme = themeQuartz.withParams({
944
+ backgroundColor: 'black',
945
+ browserColorScheme: 'dark',
946
+ }, 'dark');
947
+ class AgGridBase {
948
+ // Input signals
949
+ readOnly = input(false, ...(ngDevMode ? [{ debugName: "readOnly" }] : []));
950
+ popupFilterValid = input(false, ...(ngDevMode ? [{ debugName: "popupFilterValid" }] : []));
951
+ popupValid = input(false, ...(ngDevMode ? [{ debugName: "popupValid" }] : []));
952
+ params = input('', ...(ngDevMode ? [{ debugName: "params" }] : []));
953
+ // Authentication
954
+ authenticationList = [];
955
+ // User access writes
956
+ accessRightsProcessed = signal(false, ...(ngDevMode ? [{ debugName: "accessRightsProcessed" }] : []));
957
+ allowAdd = signal(false, ...(ngDevMode ? [{ debugName: "allowAdd" }] : []));
958
+ allowEdit = signal(false, ...(ngDevMode ? [{ debugName: "allowEdit" }] : []));
959
+ allowDelete = signal(false, ...(ngDevMode ? [{ debugName: "allowDelete" }] : []));
960
+ allowLog = signal(false, ...(ngDevMode ? [{ debugName: "allowLog" }] : []));
961
+ allowRead = signal(true, ...(ngDevMode ? [{ debugName: "allowRead" }] : []));
962
+ // Button action management variables
963
+ menuValid = signal(false, ...(ngDevMode ? [{ debugName: "menuValid" }] : []));
964
+ deleteValid = signal(false, ...(ngDevMode ? [{ debugName: "deleteValid" }] : []));
965
+ selectionValid = signal(false, ...(ngDevMode ? [{ debugName: "selectionValid" }] : []));
966
+ refreshButtonValid = signal(false, ...(ngDevMode ? [{ debugName: "refreshButtonValid" }] : []));
967
+ // Parameters for embeded components
968
+ selectedElement = output();
969
+ // Filter Parameters
970
+ filterField = input(null, ...(ngDevMode ? [{ debugName: "filterField" }] : []));
971
+ filterFieldValue = input(null, ...(ngDevMode ? [{ debugName: "filterFieldValue" }] : []));
972
+ filterFieldNumeric = input(false, ...(ngDevMode ? [{ debugName: "filterFieldNumeric" }] : []));
973
+ filterFieldEquality = input('=', ...(ngDevMode ? [{ debugName: "filterFieldEquality" }] : []));
974
+ // AG-Grid theme setting (dark mode)
975
+ theme = myTheme;
976
+ // Grid references
977
+ gridApi;
978
+ userGridApi;
979
+ excelStyles = null;
980
+ // Column definitions
981
+ columnDefs = signal(null, ...(ngDevMode ? [{ debugName: "columnDefs" }] : []));
982
+ userColumnDefs = signal(null, ...(ngDevMode ? [{ debugName: "userColumnDefs" }] : []));
983
+ groupMembersColumnDefs = signal(null, ...(ngDevMode ? [{ debugName: "groupMembersColumnDefs" }] : []));
984
+ // Framework components
985
+ frameworkComponents = null;
986
+ // Selection management
987
+ selectedRows = signal([], ...(ngDevMode ? [{ debugName: "selectedRows" }] : []));
988
+ isSingleRowSelected = computed(() => this.selectedRows().length === 1, ...(ngDevMode ? [{ debugName: "isSingleRowSelected" }] : []));
989
+ isMultipleRowSelected = computed(() => this.selectedRows().length > 0, ...(ngDevMode ? [{ debugName: "isMultipleRowSelected" }] : []));
990
+ // Component identifiers
991
+ gridExpanded = signal(false, ...(ngDevMode ? [{ debugName: "gridExpanded" }] : []));
992
+ componentName = signal('Invalid', ...(ngDevMode ? [{ debugName: "componentName" }] : []));
993
+ searchValueName = signal('Invalid_searchValue', ...(ngDevMode ? [{ debugName: "searchValueName" }] : []));
994
+ preferenceType = signal('Invalid_columnState', ...(ngDevMode ? [{ debugName: "preferenceType" }] : []));
995
+ searchValue = signal('', ...(ngDevMode ? [{ debugName: "searchValue" }] : []));
996
+ // Services
997
+ nettyAgGridService = inject(NettyAgGridService);
998
+ translateService = inject(TranslateService);
999
+ commonService = inject(CommonService);
1000
+ router = inject(Router);
1001
+ routerActive = inject(ActivatedRoute);
1002
+ alertService = inject(AlertService);
1003
+ dialog = inject(MatDialog);
1004
+ sysFunctionProxy = inject(SysfunctionProxy);
1005
+ initAgGrid_extension() {
1006
+ if (this.allowEdit() == false && (this.popupValid() ?? false) == false) {
1007
+ this.columnDefs().pop();
1008
+ this.columnDefs().shift();
1009
+ this.columnDefs().unshift({
1010
+ headerName: '',
1011
+ headerCheckboxSelection: true,
1012
+ sortable: true,
1013
+ resizable: true,
1014
+ filter: true,
1015
+ minWidth: 120,
1016
+ maxWidth: 120,
1017
+ suppressSizeToFit: true,
1018
+ colId: 'editColumn',
1019
+ cellRenderer: 'buttonRenderer',
1020
+ cellRendererParams: {
1021
+ //onClick: this.onBtnClick.bind(this),
1022
+ label: '1',
1023
+ type: 'edit',
1024
+ },
1025
+ });
1026
+ this.gridApi.setGridOption('rowSelection', {
1027
+ mode: 'multiRow',
1028
+ checkboxes: true,
1029
+ });
1030
+ }
1031
+ if (this.popupValid() == true) {
1032
+ this.columnDefs().pop();
1033
+ this.columnDefs().shift();
1034
+ if (this.popupFilterValid() == true) {
1035
+ if (this.allowEdit()) {
1036
+ this.columnDefs().unshift({
1037
+ headerName: '',
1038
+ sortable: false,
1039
+ resizable: false,
1040
+ filter: false,
1041
+ minWidth: 50,
1042
+ maxWidth: 50,
1043
+ suppressSizeToFit: true,
1044
+ cellRenderer: 'buttonRenderer',
1045
+ cellRendererParams: {
1046
+ onClick: this.onBtnClick?.bind(this),
1047
+ label: this.translateService.instant('@Update'),
1048
+ type: 'popupEdit',
1049
+ },
1050
+ });
1051
+ this.gridApi.setGridOption('rowSelection', {
1052
+ mode: 'multiRow',
1053
+ checkboxes: true,
1054
+ });
1055
+ }
1056
+ else {
1057
+ this.columnDefs().unshift({
1058
+ headerName: '',
1059
+ sortable: false,
1060
+ resizable: false,
1061
+ filter: false,
1062
+ minWidth: 50,
1063
+ maxWidth: 50,
1064
+ suppressSizeToFit: true,
1065
+ cellStyle: { 'padding-top': '5px' },
1066
+ });
1067
+ this.gridApi.setGridOption('rowSelection', {
1068
+ mode: 'multiRow',
1069
+ checkboxes: true,
1070
+ });
1071
+ }
1072
+ }
1073
+ else {
1074
+ if (this.allowEdit() == false) {
1075
+ this.columnDefs().unshift({
1076
+ headerName: '',
1077
+ sortable: false,
1078
+ resizable: false,
1079
+ filter: false,
1080
+ minWidth: 50,
1081
+ maxWidth: 50,
1082
+ suppressSizeToFit: true,
1083
+ cellRenderer: 'buttonRenderer',
1084
+ cellRendererParams: {
1085
+ onClick: this.onBtnClick?.bind(this),
1086
+ label: this.translateService.instant('@Update'),
1087
+ type: 'popupEdit',
1088
+ },
1089
+ });
1090
+ this.gridApi.setGridOption('rowSelection', {
1091
+ mode: 'singleRow',
1092
+ checkboxes: false,
1093
+ });
1094
+ }
1095
+ if (!this.readOnly()) {
1096
+ this.columnDefs().unshift({
1097
+ headerName: this.translateService.instant('@select'),
1098
+ sortable: false,
1099
+ resizable: false,
1100
+ filter: false,
1101
+ minWidth: 80,
1102
+ maxWidth: 80,
1103
+ suppressSizeToFit: true,
1104
+ cellRenderer: 'buttonRenderer',
1105
+ cellStyle: { 'margin-top': '8px' },
1106
+ cellRendererParams: {
1107
+ onClick: this.onBtnClick?.bind(this),
1108
+ label: this.translateService.instant('@select'),
1109
+ type: 'popupSelect',
1110
+ },
1111
+ });
1112
+ this.gridApi.setGridOption('rowSelection', {
1113
+ mode: 'singleRow',
1114
+ checkboxes: false,
1115
+ });
1116
+ }
1117
+ }
1118
+ }
1119
+ }
1120
+ // Grid configuration options
1121
+ gridOptions = {
1122
+ rowSelection: {
1123
+ mode: 'multiRow',
1124
+ checkboxes: true,
1125
+ headerCheckbox: true,
1126
+ selectAll: 'filtered',
1127
+ hideDisabledCheckboxes: true,
1128
+ suppressRowClickSelection: false,
1129
+ enableCellChangeFlash: true,
1130
+ },
1131
+ localeText: {
1132
+ // Status panel translate
1133
+ totalRows: this.translateService.instant('AG_GRID.TOTAL_ROWS'),
1134
+ filteredRows: this.translateService.instant('AG_GRID.FILTERED_ROWS'),
1135
+ selectedRows: this.translateService.instant('AG_GRID.SELECTED_ROWS'),
1136
+ noRowsToShow: this.translateService.instant('AG_GRID.NO_ROWS_TO_SHOW'),
1137
+ loadingOoo: this.translateService.instant('AG_GRID.LOADING'),
1138
+ // Context menu translations
1139
+ copy: this.translateService.instant('AG_GRID.COPY'),
1140
+ copyWithHeaders: this.translateService.instant('AG_GRID.COPY_WITH_HEADERS'),
1141
+ copyWithGroupHeaders: this.translateService.instant('AG_GRID.COPY_WITH_GROUP_HEADERS'),
1142
+ paste: this.translateService.instant('AG_GRID.PASTE'),
1143
+ export: this.translateService.instant('AG_GRID.EXPORT'),
1144
+ csvExport: this.translateService.instant('AG_GRID.CSV_EXPORT'),
1145
+ excelExport: this.translateService.instant('AG_GRID.EXCEL_EXPORT'),
1146
+ pinColumn: this.translateService.instant('AG_GRID.PIN_COLUMN'),
1147
+ pinLeft: this.translateService.instant('AG_GRID.PIN_LEFT'),
1148
+ pinRight: this.translateService.instant('AG_GRID.PIN_RIGHT'),
1149
+ noPin: this.translateService.instant('AG_GRID.NO_PIN'),
1150
+ sortAscending: this.translateService.instant('AG_GRID.SORT_ASCENDING'),
1151
+ sortDescending: this.translateService.instant('AG_GRID.SORT_DESCENDING'),
1152
+ autosizeThisColumn: this.translateService.instant('AG_GRID.AUTOSIZE_THIS_COLUMN'),
1153
+ autosizeAllColumns: this.translateService.instant('AG_GRID.AUTOSIZE_ALL_COLUMNS'),
1154
+ resetColumns: this.translateService.instant('AG_GRID.RESET_COLUMNS'),
1155
+ expandAll: this.translateService.instant('AG_GRID.EXPAND_ALL'),
1156
+ collapseAll: this.translateService.instant('AG_GRID.COLLAPSE_ALL'),
1157
+ exportToExcel: this.translateService.instant('AG_GRID.EXPORT_TO_EXCEL'),
1158
+ sheetName: this.translateService.instant('AG_GRID.SHEET_NAME'),
1159
+ exportAsExcelTable: this.translateService.instant('AG_GRID.EXPORT_AS_EXCEL_TABLE'),
1160
+ rangeSelectTo: this.translateService.instant('AG_GRID.RANGE_SELECT_TO'),
1161
+ rangeSelectFrom: this.translateService.instant('AG_GRID.RANGE_SELECT_FROM'),
1162
+ rangeSelectCopy: this.translateService.instant('AG_GRID.RANGE_SELECT_COPY'),
1163
+ rangeSelectChart: this.translateService.instant('AG_GRID.RANGE_SELECT_CHART'),
1164
+ pasteAll: this.translateService.instant('AG_GRID.CLIPBOARD_PASTE'),
1165
+ pasteWithoutHeader: this.translateService.instant('AG_GRID.CLIPBOARD_PASTE_WITHOUT_HEADER'),
1166
+ copyAll: this.translateService.instant('AG_GRID.CLIPBOARD_COPY'),
1167
+ cut: this.translateService.instant('AG_GRID.CLIPBOARD_CUT'),
1168
+ // Pagination translations
1169
+ page: this.translateService.instant('AG_GRID.PAGE'),
1170
+ more: this.translateService.instant('AG_GRID.MORE'),
1171
+ to: this.translateService.instant('AG_GRID.TO'),
1172
+ of: this.translateService.instant('AG_GRID.OF'),
1173
+ next: this.translateService.instant('AG_GRID.NEXT'),
1174
+ last: this.translateService.instant('AG_GRID.LAST'),
1175
+ first: this.translateService.instant('AG_GRID.FIRST'),
1176
+ previous: this.translateService.instant('AG_GRID.PREVIOUS'),
1177
+ pageSizeSelectorLabel: this.translateService.instant('AG_GRID.PAGE_SIZE_SELECTOR_LABEL'),
1178
+ // Set Filter
1179
+ selectAll: this.translateService.instant('AG_GRID.SELECT_ALL'),
1180
+ selectAllSearchResults: this.translateService.instant('AG_GRID.SELECT_ALL_SEARCH_RESULTS'),
1181
+ searchOoo: this.translateService.instant('AG_GRID.SEARCH_OOO'),
1182
+ blanks: this.translateService.instant('AG_GRID.BLANKS'),
1183
+ noMatches: this.translateService.instant('AG_GRID.NO_MATCHES'),
1184
+ // Number Filter & Text Filter
1185
+ filterOoo: this.translateService.instant('AG_GRID.FILTER_OOO'),
1186
+ equals: this.translateService.instant('AG_GRID.EQUALS'),
1187
+ notEqual: this.translateService.instant('AG_GRID.NOT_EQUAL'),
1188
+ blank: this.translateService.instant('AG_GRID.BLANK'),
1189
+ notBlank: this.translateService.instant('AG_GRID.NOT_BLANK'),
1190
+ empty: this.translateService.instant('AG_GRID.EMPTY'),
1191
+ // Number Filter
1192
+ lessThan: this.translateService.instant('AG_GRID.LESS_THAN'),
1193
+ greaterThan: this.translateService.instant('AG_GRID.GREATER_THAN'),
1194
+ lessThanOrEqual: this.translateService.instant('AG_GRID.LESS_THAN_OR_EQUAL'),
1195
+ greaterThanOrEqual: this.translateService.instant('AG_GRID.GREATER_THAN_OR_EQUAL'),
1196
+ inRange: this.translateService.instant('AG_GRID.IN_RANGE'),
1197
+ inRangeStart: this.translateService.instant('AG_GRID.IN_RANGE_START'),
1198
+ inRangeEnd: this.translateService.instant('AG_GRID.IN_RANGE_END'),
1199
+ // Text Filter
1200
+ contains: this.translateService.instant('AG_GRID.CONTAINS'),
1201
+ notContains: this.translateService.instant('AG_GRID.NOT_CONTAINS'),
1202
+ startsWith: this.translateService.instant('AG_GRID.STARTS_WITH'),
1203
+ endsWith: this.translateService.instant('AG_GRID.ENDS_WITH'),
1204
+ // Date Filter
1205
+ dateFormatOoo: this.translateService.instant('AG_GRID.DATE_FORMAT_OOO'),
1206
+ // Filter Conditions
1207
+ andCondition: this.translateService.instant('AG_GRID.AND_CONDITION'),
1208
+ orCondition: this.translateService.instant('AG_GRID.OR_CONDITION'),
1209
+ // Filter Buttons
1210
+ applyFilter: this.translateService.instant('AG_GRID.APPLY_FILTER'),
1211
+ resetFilter: this.translateService.instant('AG_GRID.RESET_FILTER'),
1212
+ clearFilter: this.translateService.instant('AG_GRID.CLEAR_FILTER'),
1213
+ cancelFilter: this.translateService.instant('AG_GRID.CANCEL_FILTER'),
1214
+ // Filter Titles
1215
+ textFilter: this.translateService.instant('AG_GRID.TEXT_FILTER'),
1216
+ numberFilter: this.translateService.instant('AG_GRID.NUMBER_FILTER'),
1217
+ dateFilter: this.translateService.instant('AG_GRID.DATE_FILTER'),
1218
+ setFilter: this.translateService.instant('AG_GRID.SET_FILTER'),
1219
+ },
1220
+ };
1221
+ // Statusbar
1222
+ statusBar = {
1223
+ statusPanels: [
1224
+ {
1225
+ statusPanel: 'agTotalRowCountComponent',
1226
+ align: 'left',
1227
+ key: 'rowCount',
1228
+ },
1229
+ { statusPanel: 'agFilteredRowCountComponent' },
1230
+ { statusPanel: 'agSelectedRowCountComponent' },
1231
+ { statusPanel: 'agAggregationComponent' },
1232
+ ],
1233
+ };
1234
+ // Common grid ready handler
1235
+ onGridReady(params) {
1236
+ this.gridApi = params.api;
1237
+ this.gridApi.addEventListener('selectionChanged', () => {
1238
+ const selectedNodes = this.gridApi.getSelectedNodes();
1239
+ this.selectedRows.set(selectedNodes.map((node) => node.data));
1240
+ });
1241
+ // Apply the saved filter when the grid is ready
1242
+ if (this.searchValue()) {
1243
+ this.gridApi.setGridOption('quickFilterText', this.searchValue());
1244
+ }
1245
+ }
1246
+ onFirstDataRendered(params) {
1247
+ //TODO: Fix this
1248
+ try {
1249
+ this.nettyAgGridService.loadGridUserPreference(this.preferenceType(), this.gridApi);
1250
+ this.expandCollapse();
1251
+ }
1252
+ catch (error) {
1253
+ console.log('OnFirstDataRendered error:', error);
1254
+ }
1255
+ }
1256
+ // Save the user parameters
1257
+ saveGrid(params) {
1258
+ const gridState = {
1259
+ columnState: this.gridApi.getColumnState(),
1260
+ };
1261
+ this.nettyAgGridService.saveGridUserPreference(this.preferenceType(), this.gridApi, JSON.stringify(gridState));
1262
+ }
1263
+ // Grid utilities
1264
+ expandCollapse() {
1265
+ this.gridExpanded.set(this.nettyAgGridService.expandCollapseGrid(this.gridApi, this.gridExpanded()));
1266
+ }
1267
+ quickSearch(searchStr = '') {
1268
+ this.searchValue.set(searchStr);
1269
+ if (this.gridApi) {
1270
+ this.gridApi.setGridOption('quickFilterText', this.searchValue());
1271
+ }
1272
+ sessionStorage.setItem(this.searchValueName(), this.searchValue());
1273
+ }
1274
+ /**
1275
+ * Handle back button click
1276
+ */
1277
+ backClicked() {
1278
+ this.commonService.goBack();
1279
+ }
1280
+ async deleteSelected() {
1281
+ if (!this.gridApi)
1282
+ return;
1283
+ const selectedNodes = this.gridApi.getSelectedNodes();
1284
+ if (selectedNodes.length === 0) {
1285
+ await this.alertService.showAlert('@PleaseSelectRowToDelete');
1286
+ return;
1287
+ }
1288
+ const confirmed = await this.alertService.showConfirm('@ConfirmDeleteSelectedRecords');
1289
+ if (confirmed) {
1290
+ const selectedRows = selectedNodes.map((node) => node.data);
1291
+ this.deleteRows(selectedRows);
1292
+ }
1293
+ }
1294
+ async refreshData() {
1295
+ try {
1296
+ const freshData = await this.loadFreshData();
1297
+ if (this.gridApi?.setGridOption) {
1298
+ this.gridApi.setGridOption('rowData', freshData);
1299
+ }
1300
+ await this.alertService.showAlert('@DataRefreshedSuccessfully');
1301
+ }
1302
+ catch (err) {
1303
+ this.alertService.showError(err);
1304
+ }
1305
+ }
1306
+ gotoURL(routePrefix, rightSidenav = [], parameters, type, isNewTab = false, embedded = false, dialogComponent) {
1307
+ const navigationExtras = {
1308
+ queryParams: {
1309
+ parameters: JSON.stringify(parameters),
1310
+ ...(type && { type }),
1311
+ isNewTab: isNewTab || undefined,
1312
+ },
1313
+ queryParamsHandling: 'merge',
1314
+ };
1315
+ if (isNewTab) {
1316
+ const fullUrl = this.router
1317
+ .createUrlTree([...routePrefix, ...rightSidenav], navigationExtras)
1318
+ .toString();
1319
+ window.open(fullUrl, '_blank');
1320
+ return;
1321
+ }
1322
+ if (embedded && dialogComponent) {
1323
+ this.dialog
1324
+ .open(dialogComponent, {
1325
+ data: {
1326
+ parameters: parameters,
1327
+ mode: type || 'edit',
1328
+ embedded: true,
1329
+ },
1330
+ maxWidth: '100vw',
1331
+ disableClose: true,
1332
+ })
1333
+ .afterClosed()
1334
+ .subscribe((result) => {
1335
+ if (result === 'saved') {
1336
+ this.refreshData();
1337
+ }
1338
+ });
1339
+ return;
1340
+ }
1341
+ // Log control
1342
+ if (rightSidenav[0] === 'log') {
1343
+ this.router.navigate([...routePrefix, ...rightSidenav], navigationExtras);
1344
+ return;
1345
+ }
1346
+ // In all other cases, open the side menu
1347
+ this.router
1348
+ .navigate([
1349
+ {
1350
+ outlets: {
1351
+ primary: routePrefix,
1352
+ rightSidenav: [...routePrefix, ...rightSidenav],
1353
+ },
1354
+ },
1355
+ ], navigationExtras)
1356
+ .then(() => {
1357
+ // Ensure sidenav is opened after navigation
1358
+ this.commonService.toggleRightSidenav(true);
1359
+ });
1360
+ }
1361
+ popupGotoURL(urlSegments) {
1362
+ this.router.navigate([
1363
+ {
1364
+ outlets: {
1365
+ primary: urlSegments,
1366
+ rightSidenav: null,
1367
+ },
1368
+ },
1369
+ ]);
1370
+ }
1371
+ /**
1372
+ * Listens for update events from CommonService and refreshes the grid accordingly.
1373
+ * - For 'add' or 'update' actions: Updates the relevant row
1374
+ * - For 'delete' action: Removes the row
1375
+ * - Performs no action if:
1376
+ * - Grid API is not ready, or
1377
+ * - The update type doesn't match
1378
+ */
1379
+ constructor() {
1380
+ effect(() => {
1381
+ const update = this.commonService.updates();
1382
+ if (!update || !this.gridApi)
1383
+ return;
1384
+ if (update.type === this.getEntityType?.()) {
1385
+ switch (update.action) {
1386
+ case 'add':
1387
+ case 'update':
1388
+ this.updateRowInGrid(update.data);
1389
+ break;
1390
+ case 'delete':
1391
+ this.deleteRowFromGrid(update.data);
1392
+ break;
1393
+ }
1394
+ }
1395
+ });
1396
+ }
1397
+ /**
1398
+ * Update a single row in the grid
1399
+ * @param rowData The updated row data
1400
+ * @param idField The field name that serves as unique identifier (default: 'id')
1401
+ */
1402
+ updateRowInGrid(rowData, idField = 'id') {
1403
+ const rowNode = this.gridApi.getRowNode(rowData[idField]);
1404
+ if (rowNode) {
1405
+ rowNode.setData(rowData);
1406
+ const params = {
1407
+ rowNodes: [rowNode],
1408
+ force: true,
1409
+ suppressFlash: false,
1410
+ };
1411
+ this.gridApi.refreshCells(params);
1412
+ }
1413
+ else {
1414
+ this.loadData(); // Reload all data if row not found
1415
+ }
1416
+ }
1417
+ /**
1418
+ * Delete a row from the grid
1419
+ * @param rowData The row to delete
1420
+ */
1421
+ deleteRowFromGrid(rowData) {
1422
+ this.gridApi.applyTransaction({ remove: [rowData] });
1423
+ }
1424
+ async setAccessRights() {
1425
+ this.authenticationList = await lastValueFrom(this.sysFunctionProxy.getAuthentication(this.componentName())).catch((e) => {
1426
+ return throwError(() => new Error(e));
1427
+ });
1428
+ try {
1429
+ this.allowRead.set(this.authenticationList.find((f) => f.action == 'read')?.allow ?? false);
1430
+ if (this.allowRead() == false) {
1431
+ this.commonService.userNotAllowedToRead();
1432
+ return false;
1433
+ }
1434
+ if (this.allowRead() == true && !this.readOnly()) {
1435
+ this.allowAdd.set(this.authenticationList.find((f) => f.action == 'add')?.allow ?? false);
1436
+ this.allowEdit.set(this.authenticationList.find((f) => f.action == 'edit')?.allow ??
1437
+ false);
1438
+ this.allowDelete.set(this.authenticationList.find((f) => f.action == 'delete')?.allow ??
1439
+ false);
1440
+ this.allowLog.set(this.authenticationList.find((f) => f.action == 'logAccess')?.allow ??
1441
+ false);
1442
+ }
1443
+ else {
1444
+ this.allowAdd.set(false);
1445
+ this.allowEdit.set(false);
1446
+ this.allowDelete.set(false);
1447
+ this.allowLog.set(false);
1448
+ }
1449
+ }
1450
+ catch (error) {
1451
+ this.allowAdd.set(false);
1452
+ this.allowEdit.set(false);
1453
+ this.allowDelete.set(false);
1454
+ this.allowLog.set(false);
1455
+ return false;
1456
+ }
1457
+ this.accessRightsProcessed.set(true);
1458
+ return true;
1459
+ }
1460
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AgGridBase, deps: [], target: i0.ɵɵFactoryTarget.Component });
1461
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.7", type: AgGridBase, isStandalone: true, selector: "ntybase-ag-grid-base", inputs: { readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, popupFilterValid: { classPropertyName: "popupFilterValid", publicName: "popupFilterValid", isSignal: true, isRequired: false, transformFunction: null }, popupValid: { classPropertyName: "popupValid", publicName: "popupValid", isSignal: true, isRequired: false, transformFunction: null }, params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: false, transformFunction: null }, filterField: { classPropertyName: "filterField", publicName: "filterField", isSignal: true, isRequired: false, transformFunction: null }, filterFieldValue: { classPropertyName: "filterFieldValue", publicName: "filterFieldValue", isSignal: true, isRequired: false, transformFunction: null }, filterFieldNumeric: { classPropertyName: "filterFieldNumeric", publicName: "filterFieldNumeric", isSignal: true, isRequired: false, transformFunction: null }, filterFieldEquality: { classPropertyName: "filterFieldEquality", publicName: "filterFieldEquality", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedElement: "selectedElement" }, ngImport: i0, template: "<p>ag-grid-base works!</p>\n", styles: [""] });
1462
+ }
1463
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AgGridBase, decorators: [{
1464
+ type: Component,
1465
+ args: [{ selector: 'ntybase-ag-grid-base', imports: [], template: "<p>ag-grid-base works!</p>\n" }]
1466
+ }], ctorParameters: () => [] });
1467
+
1468
+ class AgGridSaveBase {
1469
+ // Services
1470
+ router = inject(Router);
1471
+ route = inject(ActivatedRoute);
1472
+ commonService = inject(CommonService);
1473
+ alertService = inject(AlertService);
1474
+ currentItem = signal({}, ...(ngDevMode ? [{ debugName: "currentItem" }] : []));
1475
+ initialItem = {};
1476
+ // Form tracking
1477
+ formChanged = false;
1478
+ // Signals
1479
+ viewMode = signal('sidenav', ...(ngDevMode ? [{ debugName: "viewMode" }] : []));
1480
+ // Dialog related properties
1481
+ dialogRef = inject((MatDialogRef), { optional: true });
1482
+ dialogData = inject(MAT_DIALOG_DATA, { optional: true });
1483
+ // Embedded mode
1484
+ embedded = input(false, ...(ngDevMode ? [{ debugName: "embedded" }] : []));
1485
+ // Controls whether the panel should automatically close after saving user data
1486
+ closeAfterSave = model(true, ...(ngDevMode ? [{ debugName: "closeAfterSave" }] : []));
1487
+ setCloseAfterSave(value) {
1488
+ this.closeAfterSave.set(value);
1489
+ }
1490
+ // Controls the visibility of additional editable fields (e.g., password, extra settings)
1491
+ // When true, certain input fields become visible for editing
1492
+ updateValid = signal(false, ...(ngDevMode ? [{ debugName: "updateValid" }] : []));
1493
+ setUpdateValid(value) {
1494
+ this.updateValid.set(value);
1495
+ }
1496
+ /**
1497
+ * Determine view mode based on current route
1498
+ */
1499
+ determineViewMode() {
1500
+ if (this.embedded() || this.dialogData?.embedded) {
1501
+ this.viewMode.set('dialog');
1502
+ }
1503
+ else if (this.router.url.includes('(rightSidenav:')) {
1504
+ this.viewMode.set('sidenav');
1505
+ }
1506
+ else {
1507
+ this.viewMode.set('fullscreen');
1508
+ }
1509
+ }
1510
+ /**
1511
+ * Check for form changes
1512
+ */
1513
+ ngDoCheck() {
1514
+ this.formChanged = !this.isEqual(this.currentItem(), this.initialItem);
1515
+ }
1516
+ isEqual(item1, item2) {
1517
+ return JSON.stringify(item1) === JSON.stringify(item2);
1518
+ }
1519
+ /**
1520
+ * Check if can deactivate component
1521
+ */
1522
+ async canDeactivate() {
1523
+ if (this.formChanged) {
1524
+ return await this.alertService.showConfirm('@UnsavedChangesConfirm');
1525
+ }
1526
+ return true;
1527
+ }
1528
+ /**
1529
+ * Close sidenav or navigate back
1530
+ */
1531
+ closeSidenav() {
1532
+ if (this.viewMode() === 'dialog' && this.dialogRef) {
1533
+ this.dialogRef.close('saved');
1534
+ }
1535
+ else if (this.viewMode() === 'sidenav') {
1536
+ this.router.navigate([{ outlets: { rightSidenav: null } }]);
1537
+ }
1538
+ else {
1539
+ this.router.navigate(['/sys/NettyUser']);
1540
+ this.commonService.toggleRightSidenav(false);
1541
+ }
1542
+ }
1543
+ backClicked() {
1544
+ if (this.viewMode() === 'dialog' && this.dialogRef) {
1545
+ this.dialogRef.close('back');
1546
+ }
1547
+ else {
1548
+ this.commonService.goBack();
1549
+ }
1550
+ }
1551
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AgGridSaveBase, deps: [], target: i0.ɵɵFactoryTarget.Component });
1552
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.7", type: AgGridSaveBase, isStandalone: true, selector: "ntybase-ag-grid-save-base", inputs: { embedded: { classPropertyName: "embedded", publicName: "embedded", isSignal: true, isRequired: false, transformFunction: null }, closeAfterSave: { classPropertyName: "closeAfterSave", publicName: "closeAfterSave", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeAfterSave: "closeAfterSaveChange" }, ngImport: i0, template: "<p>ag-grid-save-base works!</p>\n", styles: [""] });
1553
+ }
1554
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AgGridSaveBase, decorators: [{
1555
+ type: Component,
1556
+ args: [{ selector: 'ntybase-ag-grid-save-base', imports: [], template: "<p>ag-grid-save-base works!</p>\n" }]
1557
+ }] });
1558
+
1559
+ class ButtonRenderer {
1560
+ params = null;
1561
+ label = '';
1562
+ type = '';
1563
+ toggleValue = null;
1564
+ editValid = false;
1565
+ historyValid = false;
1566
+ lineValid = false;
1567
+ popupSelectValid = false;
1568
+ popupEditValid = false;
1569
+ toggleValid = false;
1570
+ toggle_icon = '';
1571
+ none = false;
1572
+ addValid = false;
1573
+ deleteValid = false;
1574
+ agInit(params) {
1575
+ this.params = params;
1576
+ this.type = this.params.type || null;
1577
+ this.label = this.params.label || null;
1578
+ this.toggleValue = this.params.value || null;
1579
+ switch (this.toggleValue) {
1580
+ case true:
1581
+ this.toggle_icon = 'select_check_box';
1582
+ break;
1583
+ case false:
1584
+ this.toggle_icon = 'check_box_outline_blank';
1585
+ break;
1586
+ default:
1587
+ this.toggle_icon = '';
1588
+ break;
1589
+ }
1590
+ this.resetValids();
1591
+ switch (this.type.toLowerCase().trim()) {
1592
+ case 'edit':
1593
+ this.editValid = true;
1594
+ break;
1595
+ case 'log':
1596
+ this.historyValid = true;
1597
+ break;
1598
+ case 'line':
1599
+ this.lineValid = true;
1600
+ break;
1601
+ case 'popupselect':
1602
+ this.popupSelectValid = true;
1603
+ break;
1604
+ case 'toggle':
1605
+ this.toggleValid = true;
1606
+ break;
1607
+ case 'none':
1608
+ this.none = true;
1609
+ break;
1610
+ case 'add':
1611
+ this.addValid = true;
1612
+ break;
1613
+ case 'delete':
1614
+ this.deleteValid = true;
1615
+ break;
1616
+ default:
1617
+ this.popupEditValid = true;
1618
+ break;
1619
+ }
1620
+ }
1621
+ /** Refresh the display
1622
+ *
1623
+ * @param params
1624
+ * @returns
1625
+ */
1626
+ refresh(params) {
1627
+ return false;
1628
+ }
1629
+ resetValids() {
1630
+ this.editValid = false;
1631
+ this.historyValid = false;
1632
+ this.lineValid = false;
1633
+ this.popupSelectValid = false;
1634
+ this.popupEditValid = false;
1635
+ this.toggleValid = false;
1636
+ }
1637
+ onClick(event) {
1638
+ if (this.params.onClick instanceof Function) {
1639
+ // put anything into params u want pass into parents component
1640
+ const params = {
1641
+ event: event,
1642
+ rowData: this.params.node.data,
1643
+ type: this.type,
1644
+ // ...something
1645
+ };
1646
+ this.params.onClick(params);
1647
+ }
1648
+ }
1649
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ButtonRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
1650
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: ButtonRenderer, isStandalone: true, selector: "ntybase-button-renderer", ngImport: i0, template: "<mat-icon\n *ngIf=\"(editValid || popupEditValid)\"\n class=\"cursor center edit\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"4000\"\n >edit</mat-icon\n>\n\n<mat-icon\n *ngIf=\"historyValid\"\n class=\"cursor center-log\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >log</mat-icon\n>\n\n<mat-icon\n *ngIf=\"historyValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >history</mat-icon\n>\n<mat-icon\n *ngIf=\"lineValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >menu_open</mat-icon\n>\n<mat-icon\n *ngIf=\"popupSelectValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >content_copy</mat-icon\n>\n<mat-icon\n *ngIf=\"toggleValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >{{toggleValue ? 'check_box' : 'check_box_outline_blank'}}</mat-icon\n>\n<mat-icon\n *ngIf=\"none\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n [matTooltipShowDelay]=\"3000\"\n >menu_open</mat-icon\n>\n<mat-icon\n *ngIf=\"addValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >playlist_add</mat-icon\n>\n<mat-icon\n *ngIf=\"deleteValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >delete_outline</mat-icon\n>\n", styles: [".cursor{cursor:pointer}.center{display:flex;justify-content:center;align-items:center;width:100%}.center-log{display:flex;justify-content:center;align-items:center;margin-bottom:-12px}.edit{margin-top:8px}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatMenuModule }] });
1651
+ }
1652
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ButtonRenderer, decorators: [{
1653
+ type: Component,
1654
+ args: [{ selector: 'ntybase-button-renderer', imports: [MatIconModule, CommonModule, MatTooltipModule, MatMenuModule], template: "<mat-icon\n *ngIf=\"(editValid || popupEditValid)\"\n class=\"cursor center edit\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"4000\"\n >edit</mat-icon\n>\n\n<mat-icon\n *ngIf=\"historyValid\"\n class=\"cursor center-log\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >log</mat-icon\n>\n\n<mat-icon\n *ngIf=\"historyValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >history</mat-icon\n>\n<mat-icon\n *ngIf=\"lineValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >menu_open</mat-icon\n>\n<mat-icon\n *ngIf=\"popupSelectValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >content_copy</mat-icon\n>\n<mat-icon\n *ngIf=\"toggleValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >{{toggleValue ? 'check_box' : 'check_box_outline_blank'}}</mat-icon\n>\n<mat-icon\n *ngIf=\"none\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n [matTooltipShowDelay]=\"3000\"\n >menu_open</mat-icon\n>\n<mat-icon\n *ngIf=\"addValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >playlist_add</mat-icon\n>\n<mat-icon\n *ngIf=\"deleteValid\"\n class=\"cursor center\"\n matTooltip=\"{{label}}\"\n (click)=\"onClick($event)\"\n [matTooltipShowDelay]=\"3000\"\n >delete_outline</mat-icon\n>\n", styles: [".cursor{cursor:pointer}.center{display:flex;justify-content:center;align-items:center;width:100%}.center-log{display:flex;justify-content:center;align-items:center;margin-bottom:-12px}.edit{margin-top:8px}\n"] }]
1655
+ }] });
1656
+
1657
+ class CheckboxRenderer {
1658
+ params = null;
1659
+ label = '';
1660
+ type = '';
1661
+ supportClick = false;
1662
+ checked = null;
1663
+ agInit(params) {
1664
+ this.onProcess(params);
1665
+ }
1666
+ refresh(params) {
1667
+ if (params != null) {
1668
+ this.onProcess(params);
1669
+ }
1670
+ return true;
1671
+ }
1672
+ onProcess(params) {
1673
+ this.params = params;
1674
+ this.checked = this.params.value ?? false;
1675
+ this.type = this.params.type || null;
1676
+ this.label = this.params.label || null;
1677
+ if (this.type != null) {
1678
+ if (this.type.toLowerCase().trim() == 'click') {
1679
+ this.supportClick = true;
1680
+ }
1681
+ }
1682
+ }
1683
+ onClick(event) {
1684
+ this.checked = !this.checked;
1685
+ if (this.params.onClick instanceof Function) {
1686
+ // put anything into params u want pass into parents component
1687
+ const params = {
1688
+ event: event,
1689
+ rowData: this.params.node.data,
1690
+ type: this.type,
1691
+ checked: this.checked,
1692
+ // ...something
1693
+ };
1694
+ this.params.onClick(params);
1695
+ }
1696
+ }
1697
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CheckboxRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
1698
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: CheckboxRenderer, isStandalone: true, selector: "ntybase-checkbox-renderer", ngImport: i0, template: "<input\n *ngIf=\"supportClick\"\n id=\"checkbox\"\n type=\"checkbox\"\n [checked]=\"checked\"\n (click)=\"onClick($event)\"\n/>\n<input\n *ngIf=\"!supportClick\"\n id=\"checkbox\"\n type=\"checkbox\"\n [checked]=\"params.value\"\n disabled\n/>\n<label for=\"checkbox\">{{label}}</label>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
1699
+ }
1700
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CheckboxRenderer, decorators: [{
1701
+ type: Component,
1702
+ args: [{ selector: 'ntybase-checkbox-renderer', imports: [CommonModule], template: "<input\n *ngIf=\"supportClick\"\n id=\"checkbox\"\n type=\"checkbox\"\n [checked]=\"checked\"\n (click)=\"onClick($event)\"\n/>\n<input\n *ngIf=\"!supportClick\"\n id=\"checkbox\"\n type=\"checkbox\"\n [checked]=\"params.value\"\n disabled\n/>\n<label for=\"checkbox\">{{label}}</label>\n" }]
1703
+ }] });
1704
+
1705
+ class AuthenticationGuard {
1706
+ router = inject(Router);
1707
+ credentialsService = inject(CredentialsService);
1708
+ canActivate(route, state) {
1709
+ if (this.credentialsService.isAuthenticated()) {
1710
+ return true;
1711
+ }
1712
+ if (state.url.includes('login')) {
1713
+ this.router.navigate([state.url], { replaceUrl: false });
1714
+ }
1715
+ else {
1716
+ this.router.navigate(['/login'], {
1717
+ queryParams: { redirect: state.url },
1718
+ replaceUrl: true,
1719
+ });
1720
+ }
1721
+ return false;
1722
+ }
1723
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1724
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationGuard, providedIn: 'root' });
1725
+ }
1726
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationGuard, decorators: [{
1727
+ type: Injectable,
1728
+ args: [{ providedIn: 'root' }]
1729
+ }] });
1730
+
1731
+ class LoginDto {
1732
+ userName = '';
1733
+ password = '';
1734
+ application = '';
1735
+ constructor(_userName, _password, _application) {
1736
+ this.userName = _userName ?? '';
1737
+ this.password = _password ?? '';
1738
+ this.application = _application ?? '';
1739
+ }
1740
+ }
1741
+
1742
+ class MFACodeDto {
1743
+ sid = '';
1744
+ mfacode = '';
1745
+ constructor(_sid, _mfacode) {
1746
+ this.sid = _sid;
1747
+ this.mfacode = _mfacode;
1748
+ }
1749
+ }
1750
+
1751
+ class AuthenticationService {
1752
+ credentialsService = inject(CredentialsService);
1753
+ environmentProxy = inject(EnvironmentProxy);
1754
+ http = inject(HttpClient);
1755
+ static counter = 0;
1756
+ /**
1757
+ * Authenticates the user.
1758
+ * @param context The login parameters.
1759
+ * @return The user credentials.
1760
+ */
1761
+ login(context) {
1762
+ // Prepare Connection URL
1763
+ let loginUrl = this.environmentProxy.getAdminLink('Login');
1764
+ let loginDto = new LoginDto(context.username, context.password, this.environmentProxy.getApplicationName());
1765
+ let reqHeader = new HttpHeaders({
1766
+ 'Content-Type': 'application/json',
1767
+ 'No-Auth': 'True',
1768
+ });
1769
+ let loginCall$ = this.http.post(loginUrl, loginDto, {
1770
+ headers: reqHeader,
1771
+ });
1772
+ // Return an observable for the connection which has pipes to
1773
+ // process the incoming data before passing it to the caller
1774
+ // This process maps the incoming data into Credentials format
1775
+ // and saves the credentials into localstorage
1776
+ return loginCall$.pipe(map$1((record) => {
1777
+ let token = record.toString();
1778
+ let decodedToken = this.decodeToken(token);
1779
+ let data = {
1780
+ username: context.username,
1781
+ token: token,
1782
+ role: this.getClaim(decodedToken, 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role'),
1783
+ authenticationMethod: this.getClaim(decodedToken, 'http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod'),
1784
+ sid: this.getClaim(decodedToken, 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid'),
1785
+ };
1786
+ // Parse JWT
1787
+ this.credentialsService.setCredentials(data, context.remember);
1788
+ return data;
1789
+ }), take(1), catchError((error) => {
1790
+ let errorMessage = '';
1791
+ switch (error.status) {
1792
+ case '404':
1793
+ if (AuthenticationService.counter == 0) {
1794
+ AuthenticationService.counter++;
1795
+ return this.login(context);
1796
+ }
1797
+ break;
1798
+ case '415':
1799
+ case '500':
1800
+ errorMessage =
1801
+ error.status + ' ' + (error.error?.detail ?? error.message ?? '');
1802
+ break;
1803
+ default:
1804
+ errorMessage =
1805
+ error.status +
1806
+ ' ' +
1807
+ (error?.error?.title ?? error?.message ?? error ?? 'Error.');
1808
+ break;
1809
+ }
1810
+ return throwError(() => new Error(errorMessage ?? ''));
1811
+ }));
1812
+ }
1813
+ /** Parse the claim sections of the given JWT token
1814
+ *
1815
+ * @param token Token to parse
1816
+ * @returns
1817
+ */
1818
+ decodeToken(token) {
1819
+ let parts = token.split('.');
1820
+ let decodedTokenStr = Buffer.from(parts[1], 'base64').toString();
1821
+ let decodedToken = JSON.parse(decodedTokenStr);
1822
+ // console.log('decoded Token:', decodedToken);
1823
+ return decodedToken;
1824
+ }
1825
+ /** Get the claim data from the given token claim json
1826
+ *
1827
+ * @param decodedToken
1828
+ * @param claimKey
1829
+ * @returns
1830
+ */
1831
+ getClaim(decodedToken, claimKey) {
1832
+ try {
1833
+ return decodedToken ? decodedToken[claimKey] : null;
1834
+ }
1835
+ catch (error) {
1836
+ return null;
1837
+ }
1838
+ }
1839
+ mfaLogin(context) {
1840
+ // Prepare Connection URL
1841
+ let mfaCodeUrl = this.environmentProxy.getAdminLink('ValidateMFACode');
1842
+ let credentials = this.credentialsService.getCredentials();
1843
+ let mfaCodeDto = new MFACodeDto(credentials.sid, context.mfaCode);
1844
+ let reqHeader = new HttpHeaders({
1845
+ 'Content-Type': 'application/json',
1846
+ 'No-Auth': 'True',
1847
+ });
1848
+ let mfaCodeCall$ = this.http.post(mfaCodeUrl, mfaCodeDto, {
1849
+ headers: reqHeader,
1850
+ });
1851
+ return mfaCodeCall$.pipe(map$1((message) => {
1852
+ return message;
1853
+ }), take(1), catchError((error) => {
1854
+ let errorMessage = '';
1855
+ errorMessage =
1856
+ error.status +
1857
+ ' ' +
1858
+ (error?.error?.title ?? error?.message ?? error ?? 'Error.');
1859
+ return throwError(() => new Error(errorMessage ?? ''));
1860
+ }));
1861
+ }
1862
+ resendMFACode(context) {
1863
+ // Prepare Connection URL
1864
+ let mfaCodeUrl = this.environmentProxy.getAdminLink('ResendMFACode');
1865
+ let credentials = this.credentialsService.getCredentials();
1866
+ let mfaCodeDto = new MFACodeDto(credentials.sid, context.mfaCode);
1867
+ let reqHeader = new HttpHeaders({
1868
+ 'Content-Type': 'application/json',
1869
+ 'No-Auth': 'True',
1870
+ });
1871
+ let resendMFACodeCall$ = this.http.post(mfaCodeUrl, mfaCodeDto, {
1872
+ headers: reqHeader,
1873
+ });
1874
+ console.log('mfaCodeUrl', mfaCodeUrl);
1875
+ return resendMFACodeCall$.pipe(map$1((message) => {
1876
+ return message;
1877
+ }), take(1), catchError((error) => {
1878
+ let errorMessage = '';
1879
+ errorMessage =
1880
+ error.status +
1881
+ ' ' +
1882
+ (error?.error?.title ?? error?.message ?? error ?? 'Error.');
1883
+ return throwError(() => new Error(errorMessage ?? ''));
1884
+ }));
1885
+ }
1886
+ /**
1887
+ * Logs out the user and clear credentials.
1888
+ * @return True if the user was logged out successfully.
1889
+ */
1890
+ logout() {
1891
+ // Customize credentials invalidation here
1892
+ this.credentialsService.setCredentials();
1893
+ localStorage.clear();
1894
+ return of(true);
1895
+ }
1896
+ /** Send a pasword reset e-mail
1897
+ *
1898
+ * @param context
1899
+ * @returns
1900
+ */
1901
+ forgotPassword(context) {
1902
+ // Prepare Connection URL
1903
+ let loginUrl = this.environmentProxy.getAdminLink('ForgotPassword');
1904
+ let loginDto = new LoginDto(context.username, '', this.environmentProxy.getApplicationName());
1905
+ let reqHeader = new HttpHeaders({
1906
+ 'Content-Type': 'application/json',
1907
+ 'No-Auth': 'True',
1908
+ });
1909
+ let loginCall$ = this.http.post(loginUrl, loginDto, { headers: reqHeader });
1910
+ // Return an observable for the connection which has pipes to
1911
+ // process the incoming data before passing it to the caller
1912
+ // This process maps the incoming data into Credentials format
1913
+ // and saves the credentials into localstorage
1914
+ return loginCall$.pipe(map$1((message) => {
1915
+ return message;
1916
+ }), take(1), catchError((error) => {
1917
+ let errorMessage = '';
1918
+ errorMessage =
1919
+ error.status +
1920
+ ' ' +
1921
+ (error?.error?.title ?? error?.message ?? error ?? 'Error.');
1922
+ return throwError(() => new Error(errorMessage ?? ''));
1923
+ }));
1924
+ }
1925
+ getAdminLink(path = '') {
1926
+ return this.environmentProxy.getAdminLink(path);
1927
+ }
1928
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1929
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationService, providedIn: 'root' });
1930
+ }
1931
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthenticationService, decorators: [{
1932
+ type: Injectable,
1933
+ args: [{
1934
+ providedIn: 'root',
1935
+ }]
1936
+ }] });
1937
+
1938
+ class AuthBase {
1939
+ // Inject dependencies
1940
+ router = inject(Router);
1941
+ route = inject(ActivatedRoute);
1942
+ environmentProxy = inject(EnvironmentProxy);
1943
+ i18nService = inject(I18nService);
1944
+ alertService = inject(AlertService);
1945
+ urlHelperService = inject(UrlHelperService);
1946
+ onDestroy$ = new Subject();
1947
+ ngOnDestroy() {
1948
+ //Called once, before the instance is destroyed.
1949
+ //Add 'implements OnDestroy' to the class.
1950
+ this.onDestroy$.next();
1951
+ this.onDestroy$.complete();
1952
+ }
1953
+ // Language
1954
+ icon = model(false, ...(ngDevMode ? [{ debugName: "icon" }] : []));
1955
+ get currentLanguage() {
1956
+ return this.i18nService.language;
1957
+ }
1958
+ get languages() {
1959
+ return this.i18nService.supportedLanguages;
1960
+ }
1961
+ setLanguage(language) {
1962
+ this.i18nService.language = language;
1963
+ window.location.reload();
1964
+ }
1965
+ getCurrentLanguageIcon() {
1966
+ switch (this.i18nService.language) {
1967
+ case 'Türkçe':
1968
+ return 'fi fi-tr';
1969
+ case 'English':
1970
+ return 'fi fi-us';
1971
+ default:
1972
+ return 'fi fi-tr';
1973
+ }
1974
+ }
1975
+ getLanguageIcon(language) {
1976
+ switch (language) {
1977
+ case 'Türkçe':
1978
+ return 'fi fi-tr';
1979
+ case 'English':
1980
+ return 'fi fi-us';
1981
+ default:
1982
+ return 'fi fi-tr';
1983
+ }
1984
+ }
1985
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthBase, deps: [], target: i0.ɵɵFactoryTarget.Component });
1986
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.7", type: AuthBase, isStandalone: true, selector: "ntybase-auth-base", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { icon: "iconChange" }, ngImport: i0, template: "<p>auth-base works!</p>\n", styles: [""] });
1987
+ }
1988
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AuthBase, decorators: [{
1989
+ type: Component,
1990
+ args: [{ selector: 'ntybase-auth-base', imports: [], template: "<p>auth-base works!</p>\n" }]
1991
+ }] });
1992
+
1993
+ class Login extends AuthBase {
1994
+ // Inject dependencies
1995
+ authService = inject(AuthenticationService);
1996
+ formBuilder = inject(FormBuilder);
1997
+ loginForm = this.formBuilder.group({
1998
+ username: ['', Validators.required],
1999
+ password: ['', Validators.required],
2000
+ remember: [false],
2001
+ });
2002
+ version = this.environmentProxy.version();
2003
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2004
+ error = signal(undefined, ...(ngDevMode ? [{ debugName: "error" }] : []));
2005
+ credentials = {
2006
+ username: '',
2007
+ password: '',
2008
+ remember: false,
2009
+ };
2010
+ ngOnInit() {
2011
+ this.authService.getAdminLink('Login');
2012
+ this.timeout(500).then(() => this.authService.getAdminLink('Login'));
2013
+ }
2014
+ timeout(ms) {
2015
+ return new Promise((resolve) => setTimeout(resolve, ms));
2016
+ }
2017
+ login() {
2018
+ this.isLoading.set(true);
2019
+ // Get the form values
2020
+ const formValues = this.loginForm.value;
2021
+ this.authService
2022
+ .login(formValues) // Use the form values directly
2023
+ .pipe(finalize(() => {
2024
+ this.isLoading.set(false);
2025
+ this.loginForm.markAsPristine();
2026
+ }))
2027
+ .subscribe({
2028
+ next: (credentials) => {
2029
+ try {
2030
+ let redirectUrl = '/mfalogin';
2031
+ if (credentials.authenticationMethod === 'NONE') {
2032
+ redirectUrl = this.route.snapshot.queryParams['redirect'] || '/';
2033
+ redirectUrl = this.urlHelperService.cleanUrl(redirectUrl);
2034
+ }
2035
+ this.urlHelperService
2036
+ .navigate(redirectUrl)
2037
+ .catch(() => this.router.navigate(['/'], { replaceUrl: true }));
2038
+ }
2039
+ catch (error) {
2040
+ this.router.navigate(['/'], { replaceUrl: true });
2041
+ }
2042
+ },
2043
+ error: (error) => {
2044
+ const errorMessage = error.message || '@LoginFailed';
2045
+ this.error.set(errorMessage); // Set the error signal
2046
+ this.alertService.showError(errorMessage);
2047
+ },
2048
+ });
2049
+ }
2050
+ onForgotPassword() {
2051
+ this.isLoading.set(true);
2052
+ const forgotPassword$ = this.authService.forgotPassword(this.loginForm.value);
2053
+ forgotPassword$
2054
+ .pipe(finalize(() => {
2055
+ this.loginForm.markAsPristine();
2056
+ this.isLoading.set(false);
2057
+ }))
2058
+ .subscribe({
2059
+ next: (message) => {
2060
+ this.alertService.showSuccess(message);
2061
+ },
2062
+ error: (error) => {
2063
+ this.error.set(error.message);
2064
+ },
2065
+ });
2066
+ }
2067
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Login, deps: null, target: i0.ɵɵFactoryTarget.Component });
2068
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: Login, isStandalone: true, selector: "ntybase-login", usesInheritance: true, ngImport: i0, template: "<div class=\"login-page-container\">\n <div class=\"login-image-container\"></div>\n\n <div class=\"login-container glass-effect\">\n <!-- Language Button -->\n <div class=\"language-toggle\">\n <button\n *ngIf=\"icon; else text\"\n mat-icon-button\n [matMenuTriggerFor]=\"languageMenu\"\n >\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n <ng-template #text>\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n </ng-template>\n </div>\n\n <h2>Netty Admin Core</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let language of languages\"\n (click)=\"setLanguage(language)\"\n >\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n </mat-menu>\n\n <form\n (ngSubmit)=\"login()\"\n [formGroup]=\"loginForm\"\n *ngIf=\"!isLoading(); else loading\"\n novalidate\n >\n <div class=\"error-message\" *ngIf=\"error()\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n\n <div class=\"form-group\">\n <label for=\"username\">{{'@username' | translate}}</label>\n <input\n type=\"text\"\n id=\"username\"\n formControlName=\"username\"\n [placeholder]=\"'@username' | translate\"\n required\n />\n <mat-error\n class=\"validation-error\"\n *ngIf=\"loginForm.controls['username'].invalid && loginForm.controls['username'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'Username is required' | translate }}\n </mat-error>\n </div>\n\n <div class=\"form-group\">\n <label for=\"password\">{{'@password' | translate}}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n [placeholder]=\"'@password' | translate\"\n required\n />\n <div\n class=\"validation-error\"\n *ngIf=\"loginForm.controls['password'].invalid && loginForm.controls['password'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'Password is required' | translate }}\n </div>\n </div>\n\n <div class=\"form-group remember\">\n <input type=\"checkbox\" id=\"remember\" formControlName=\"remember\" />\n <label for=\"remember\">{{ '@rememberMe' | translate }}</label>\n <a class=\"forgot-password\" (click)=\"onForgotPassword()\"\n >{{ '@forgotPassword' | translate }}</a\n >\n </div>\n\n <button\n type=\"submit\"\n class=\"login-button\"\n [disabled]=\"loginForm.invalid || isLoading()\"\n >\n {{ '@login' | translate }}\n </button>\n </form>\n\n <ng-template #loading>\n <div class=\"loading-spinner\">\n <span>{{ '@loggingIn' | translate }}</span>\n </div>\n </ng-template>\n\n <div class=\"footer\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n</div>\n", styles: [".login-page-container{position:relative;display:flex;height:100vh;background-image:var(--login-bg-image, url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-position:center;background-repeat:no-repeat;background-size:cover;background-attachment:fixed;justify-content:flex-end;padding-right:5%}.login-image-container{display:none}.login-container{width:100%;max-width:400px;padding:2.5rem;display:flex;flex-direction:column;justify-content:center;background:#ffffff1a;border-radius:16px;box-shadow:0 8px 32px #0003;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.2);margin:20px;height:fit-content;align-self:center}.login-container h2{color:#fff;text-align:center;margin-bottom:2rem;font-size:1.8rem;font-weight:600;text-shadow:0 2px 4px rgba(0,0,0,.3)}.form-group{margin-bottom:1.5rem}.form-group label{display:block;font-weight:500;color:#fff;font-size:.9rem;text-shadow:0 1px 2px rgba(0,0,0,.3)}.form-group input[type=text],.form-group input[type=password]{width:100%;padding:.85rem 1rem;margin-top:.5rem;border:1px solid rgba(255,255,255,.3);border-radius:8px;font-size:.95rem;background-color:#ffffff26;color:#fff}.form-group input::placeholder{color:#ffffffb3}.form-group input[type=text]:focus,.form-group input[type=password]:focus{outline:none;border-color:#ffffff80;background-color:#ffffff40;box-shadow:0 0 0 3px #ffffff1a}.remember{display:flex;align-items:center;margin-bottom:1.5rem}.remember input{width:18px;height:18px;margin-right:.75rem;cursor:pointer}.remember label{color:#fff;font-size:.9rem;cursor:pointer;-webkit-user-select:none;user-select:none;text-shadow:0 1px 2px rgba(0,0,0,.3)}.forgot-password{margin-left:auto;color:#ffffffe6;font-size:.85rem;text-decoration:none}.forgot-password:hover{text-decoration:underline}.login-button{width:100%;padding:1rem;background-color:#fff3;color:#fff;border:1px solid rgba(255,255,255,.4);border-radius:8px;font-size:1rem;font-weight:500;cursor:pointer;transition:all .2s}.login-button:hover{background-color:#ffffff4d}.loading-spinner{text-align:center;padding:2rem}.loading-spinner p{margin-top:1rem;color:#fff}.footer{margin-top:2rem;text-align:center}.version{color:#ffffffb3;font-size:.8rem;display:block;text-shadow:0 1px 2px rgba(0,0,0,.3)}@media (max-width: 768px){.login-page-container{justify-content:center;padding-right:0;align-items:center}.login-container{max-width:90%;margin:20px auto}}.language-toggle{position:absolute;top:20px;right:20px;z-index:10}.language-toggle button{background:#fff3;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:1px solid rgba(255,255,255,.3);cursor:pointer}.language-toggle button:hover{background:#ffffff4d}.language-toggle button mat-icon{color:#fff}.login-container h2{margin-top:.5rem}.flag-icon{width:24px;height:16px;margin-right:8px;border:1px solid #ddd}.error-message{background-color:#f8d7da;color:#721c24;padding:12px 15px;border-radius:4px;margin-bottom:20px;display:flex;align-items:center;border:1px solid #f5c6cb;font-size:14px;animation:fadeIn .3s ease-in-out}.error-message .error-icon{display:inline-block;width:20px;height:20px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:20px;margin-right:10px;font-weight:700;font-size:12px}.validation-error{color:#dc3545;font-size:12px;margin-top:5px;display:flex;align-items:center;animation:fadeIn .3s ease-in-out}.validation-error .error-icon{display:inline-block;width:16px;height:16px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:16px;margin-right:6px;font-weight:700;font-size:10px}input.ng-invalid.ng-touched{border:1px solid #dc3545!important}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3$3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i1$4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3$1.TranslatePipe, name: "translate" }] });
2069
+ }
2070
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Login, decorators: [{
2071
+ type: Component,
2072
+ args: [{ selector: 'ntybase-login', imports: [
2073
+ FormsModule,
2074
+ ReactiveFormsModule,
2075
+ CommonModule,
2076
+ MatMenuModule,
2077
+ MatIconModule,
2078
+ MatInputModule,
2079
+ MatFormFieldModule,
2080
+ TranslateModule,
2081
+ ], template: "<div class=\"login-page-container\">\n <div class=\"login-image-container\"></div>\n\n <div class=\"login-container glass-effect\">\n <!-- Language Button -->\n <div class=\"language-toggle\">\n <button\n *ngIf=\"icon; else text\"\n mat-icon-button\n [matMenuTriggerFor]=\"languageMenu\"\n >\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n <ng-template #text>\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n </ng-template>\n </div>\n\n <h2>Netty Admin Core</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let language of languages\"\n (click)=\"setLanguage(language)\"\n >\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n </mat-menu>\n\n <form\n (ngSubmit)=\"login()\"\n [formGroup]=\"loginForm\"\n *ngIf=\"!isLoading(); else loading\"\n novalidate\n >\n <div class=\"error-message\" *ngIf=\"error()\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n\n <div class=\"form-group\">\n <label for=\"username\">{{'@username' | translate}}</label>\n <input\n type=\"text\"\n id=\"username\"\n formControlName=\"username\"\n [placeholder]=\"'@username' | translate\"\n required\n />\n <mat-error\n class=\"validation-error\"\n *ngIf=\"loginForm.controls['username'].invalid && loginForm.controls['username'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'Username is required' | translate }}\n </mat-error>\n </div>\n\n <div class=\"form-group\">\n <label for=\"password\">{{'@password' | translate}}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n [placeholder]=\"'@password' | translate\"\n required\n />\n <div\n class=\"validation-error\"\n *ngIf=\"loginForm.controls['password'].invalid && loginForm.controls['password'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'Password is required' | translate }}\n </div>\n </div>\n\n <div class=\"form-group remember\">\n <input type=\"checkbox\" id=\"remember\" formControlName=\"remember\" />\n <label for=\"remember\">{{ '@rememberMe' | translate }}</label>\n <a class=\"forgot-password\" (click)=\"onForgotPassword()\"\n >{{ '@forgotPassword' | translate }}</a\n >\n </div>\n\n <button\n type=\"submit\"\n class=\"login-button\"\n [disabled]=\"loginForm.invalid || isLoading()\"\n >\n {{ '@login' | translate }}\n </button>\n </form>\n\n <ng-template #loading>\n <div class=\"loading-spinner\">\n <span>{{ '@loggingIn' | translate }}</span>\n </div>\n </ng-template>\n\n <div class=\"footer\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n</div>\n", styles: [".login-page-container{position:relative;display:flex;height:100vh;background-image:var(--login-bg-image, url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-position:center;background-repeat:no-repeat;background-size:cover;background-attachment:fixed;justify-content:flex-end;padding-right:5%}.login-image-container{display:none}.login-container{width:100%;max-width:400px;padding:2.5rem;display:flex;flex-direction:column;justify-content:center;background:#ffffff1a;border-radius:16px;box-shadow:0 8px 32px #0003;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.2);margin:20px;height:fit-content;align-self:center}.login-container h2{color:#fff;text-align:center;margin-bottom:2rem;font-size:1.8rem;font-weight:600;text-shadow:0 2px 4px rgba(0,0,0,.3)}.form-group{margin-bottom:1.5rem}.form-group label{display:block;font-weight:500;color:#fff;font-size:.9rem;text-shadow:0 1px 2px rgba(0,0,0,.3)}.form-group input[type=text],.form-group input[type=password]{width:100%;padding:.85rem 1rem;margin-top:.5rem;border:1px solid rgba(255,255,255,.3);border-radius:8px;font-size:.95rem;background-color:#ffffff26;color:#fff}.form-group input::placeholder{color:#ffffffb3}.form-group input[type=text]:focus,.form-group input[type=password]:focus{outline:none;border-color:#ffffff80;background-color:#ffffff40;box-shadow:0 0 0 3px #ffffff1a}.remember{display:flex;align-items:center;margin-bottom:1.5rem}.remember input{width:18px;height:18px;margin-right:.75rem;cursor:pointer}.remember label{color:#fff;font-size:.9rem;cursor:pointer;-webkit-user-select:none;user-select:none;text-shadow:0 1px 2px rgba(0,0,0,.3)}.forgot-password{margin-left:auto;color:#ffffffe6;font-size:.85rem;text-decoration:none}.forgot-password:hover{text-decoration:underline}.login-button{width:100%;padding:1rem;background-color:#fff3;color:#fff;border:1px solid rgba(255,255,255,.4);border-radius:8px;font-size:1rem;font-weight:500;cursor:pointer;transition:all .2s}.login-button:hover{background-color:#ffffff4d}.loading-spinner{text-align:center;padding:2rem}.loading-spinner p{margin-top:1rem;color:#fff}.footer{margin-top:2rem;text-align:center}.version{color:#ffffffb3;font-size:.8rem;display:block;text-shadow:0 1px 2px rgba(0,0,0,.3)}@media (max-width: 768px){.login-page-container{justify-content:center;padding-right:0;align-items:center}.login-container{max-width:90%;margin:20px auto}}.language-toggle{position:absolute;top:20px;right:20px;z-index:10}.language-toggle button{background:#fff3;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:1px solid rgba(255,255,255,.3);cursor:pointer}.language-toggle button:hover{background:#ffffff4d}.language-toggle button mat-icon{color:#fff}.login-container h2{margin-top:.5rem}.flag-icon{width:24px;height:16px;margin-right:8px;border:1px solid #ddd}.error-message{background-color:#f8d7da;color:#721c24;padding:12px 15px;border-radius:4px;margin-bottom:20px;display:flex;align-items:center;border:1px solid #f5c6cb;font-size:14px;animation:fadeIn .3s ease-in-out}.error-message .error-icon{display:inline-block;width:20px;height:20px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:20px;margin-right:10px;font-weight:700;font-size:12px}.validation-error{color:#dc3545;font-size:12px;margin-top:5px;display:flex;align-items:center;animation:fadeIn .3s ease-in-out}.validation-error .error-icon{display:inline-block;width:16px;height:16px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:16px;margin-right:6px;font-weight:700;font-size:10px}input.ng-invalid.ng-touched{border:1px solid #dc3545!important}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}\n"] }]
2082
+ }] });
2083
+
2084
+ class MfaLogin extends AuthBase {
2085
+ // Inject dependencies
2086
+ authService = inject(AuthenticationService);
2087
+ formBuilder = inject(FormBuilder);
2088
+ // Component state
2089
+ version = this.environmentProxy.version();
2090
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2091
+ error = signal(undefined, ...(ngDevMode ? [{ debugName: "error" }] : []));
2092
+ resendable = signal(false, ...(ngDevMode ? [{ debugName: "resendable" }] : []));
2093
+ startFrom = signal(120, ...(ngDevMode ? [{ debugName: "startFrom" }] : []));
2094
+ remainingTime = signal(0, ...(ngDevMode ? [{ debugName: "remainingTime" }] : []));
2095
+ countdownInterval;
2096
+ loginForm = this.formBuilder.group({
2097
+ mfaCode: ['', Validators.required],
2098
+ });
2099
+ ngOnInit() {
2100
+ this.startCountdown(this.startFrom());
2101
+ }
2102
+ // Yeni: Geri sayım fonksiyonu
2103
+ startCountdown(seconds) {
2104
+ clearInterval(this.countdownInterval);
2105
+ this.remainingTime.set(seconds);
2106
+ this.resendable.set(false);
2107
+ this.countdownInterval = setInterval(() => {
2108
+ this.remainingTime.update((t) => {
2109
+ const newTime = t - 1;
2110
+ if (newTime <= 0) {
2111
+ clearInterval(this.countdownInterval);
2112
+ this.resendable.set(true);
2113
+ return 0;
2114
+ }
2115
+ return newTime;
2116
+ });
2117
+ }, 1000);
2118
+ }
2119
+ // Yeni: Formatlı zaman gösterimi
2120
+ getFormattedTime() {
2121
+ const mins = Math.floor(this.remainingTime() / 60);
2122
+ const secs = this.remainingTime() % 60;
2123
+ return `${mins}:${secs < 10 ? '0' + secs : secs}`;
2124
+ }
2125
+ login() {
2126
+ if (this.resendable()) {
2127
+ this.error.set('MFACode expired. Please request a new one.');
2128
+ return;
2129
+ }
2130
+ this.isLoading.set(true);
2131
+ this.authService
2132
+ .mfaLogin(this.loginForm.value)
2133
+ .pipe(finalize(() => {
2134
+ this.isLoading.set(false);
2135
+ this.loginForm.markAsPristine();
2136
+ }))
2137
+ .subscribe({
2138
+ next: (credentials) => {
2139
+ if (credentials) {
2140
+ try {
2141
+ let redirectUrl = this.route.snapshot.queryParams['redirect'] || '/';
2142
+ redirectUrl = this.urlHelperService.cleanUrl(redirectUrl);
2143
+ this.urlHelperService
2144
+ .navigate(redirectUrl)
2145
+ .catch(() => this.router.navigate(['/'], { replaceUrl: true }));
2146
+ }
2147
+ catch (error) {
2148
+ this.router.navigate(['/'], { replaceUrl: true });
2149
+ }
2150
+ }
2151
+ },
2152
+ error: (error) => {
2153
+ this.error.set(error.message);
2154
+ },
2155
+ });
2156
+ }
2157
+ onResendMFACode() {
2158
+ this.startFrom.set(121);
2159
+ this.authService
2160
+ .resendMFACode(this.loginForm.value)
2161
+ .pipe(finalize(() => {
2162
+ this.isLoading.set(false);
2163
+ this.loginForm.markAsPristine();
2164
+ this.startCountdown(120);
2165
+ }))
2166
+ .subscribe({
2167
+ next: (message) => {
2168
+ this.startFrom.set(120);
2169
+ if (message.length > 0) {
2170
+ this.alertService.showAlert('', message);
2171
+ }
2172
+ },
2173
+ error: (error) => {
2174
+ this.error.set(error.message);
2175
+ },
2176
+ });
2177
+ }
2178
+ ngOnDestroy() {
2179
+ clearInterval(this.countdownInterval);
2180
+ }
2181
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: MfaLogin, deps: null, target: i0.ɵɵFactoryTarget.Component });
2182
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: MfaLogin, isStandalone: true, selector: "ntybase-mfa-login", usesInheritance: true, ngImport: i0, template: "<div class=\"login-page-container\">\n <div class=\"login-image-container\"></div>\n\n <div class=\"login-container glass-effect\">\n <!-- Language Button -->\n <div class=\"language-toggle\">\n <button\n *ngIf=\"icon; else text\"\n mat-icon-button\n [matMenuTriggerFor]=\"languageMenu\"\n >\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n <ng-template #text>\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n </ng-template>\n </div>\n\n <h2>Netty Admin Core</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let language of languages\"\n (click)=\"setLanguage(language)\"\n >\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n </mat-menu>\n\n <form\n (ngSubmit)=\"login()\"\n [formGroup]=\"loginForm\"\n *ngIf=\"!isLoading(); else loading\"\n novalidate\n >\n <div class=\"error-message\" *ngIf=\"error()\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n\n <div class=\"form-group\">\n <label for=\"mfaCode\">{{'MFACode' | translate}}</label>\n <input\n type=\"text\"\n id=\"mfaCode\"\n formControlName=\"mfaCode\"\n [placeholder]=\"'MFACode' | translate\"\n required\n />\n <div\n class=\"validation-error\"\n *ngIf=\"loginForm.controls['mfaCode'].invalid && loginForm.controls['mfaCode'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'MFACode is required' | translate }}\n </div>\n <!-- Time -->\n <div class=\"time-remaining\" *ngIf=\"!resendable()\">\n {{ '@TokenCount' | translate }}: {{ getFormattedTime() }}\n </div>\n </div>\n\n <button\n type=\"submit\"\n class=\"login-button\"\n [disabled]=\"loginForm.invalid || isLoading() || !resendable()\"\n >\n {{ '@login' | translate }}\n </button>\n\n <div class=\"resend-container\" *ngIf=\"resendable()\">\n <span (click)=\"onResendMFACode()\" class=\"resend-link\">\n {{ 'Resend MFACode' | translate }}\n </span>\n </div>\n </form>\n\n <ng-template #loading>\n <div class=\"loading-spinner\">\n <span>{{ '@loggingIn' | translate }}</span>\n </div>\n </ng-template>\n\n <div class=\"footer\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n</div>\n", styles: [".login-page-container{position:relative;display:flex;height:100vh;background-image:var(--mfa-login-bg-image, url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-position:center;background-repeat:no-repeat;background-size:cover;background-attachment:fixed;justify-content:flex-end;padding-right:5%}.login-image-container{display:none}.login-container{width:100%;max-width:400px;padding:2.5rem;display:flex;flex-direction:column;justify-content:center;background:#ffffff1a;border-radius:16px;box-shadow:0 8px 32px #0003;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.2);margin:20px;height:fit-content;align-self:center}.login-container h2{color:#fff;text-align:center;margin-bottom:2rem;font-size:1.8rem;font-weight:600;text-shadow:0 2px 4px rgba(0,0,0,.3)}.form-group{margin-bottom:1.5rem}.form-group label{display:block;font-weight:500;color:#fff;font-size:.9rem;text-shadow:0 1px 2px rgba(0,0,0,.3)}.form-group input[type=text],.form-group input[type=password]{width:100%;padding:.85rem 1rem;margin-top:.5rem;border:1px solid rgba(255,255,255,.3);border-radius:8px;font-size:.95rem;background-color:#ffffff26;color:#fff}.form-group input::placeholder{color:#ffffffb3}.form-group input[type=text]:focus,.form-group input[type=password]:focus{outline:none;border-color:#ffffff80;background-color:#ffffff40;box-shadow:0 0 0 3px #ffffff1a}.login-button{width:100%;padding:1rem;background-color:#fff3;color:#fff;border:1px solid rgba(255,255,255,.4);border-radius:8px;font-size:1rem;font-weight:500;cursor:pointer;transition:all .2s}.login-button:hover{background-color:#ffffff4d}.loading-spinner{text-align:center;padding:2rem}.loading-spinner p{margin-top:1rem;color:#fff}.footer{margin-top:2rem;text-align:center}.version{color:#ffffffb3;font-size:.8rem;display:block;text-shadow:0 1px 2px rgba(0,0,0,.3)}.language-toggle{position:absolute;top:20px;right:20px;z-index:10}.language-toggle button{background:#fff3;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:1px solid rgba(255,255,255,.3);cursor:pointer}.language-toggle button:hover{background:#ffffff4d}.language-toggle button mat-icon{color:#fff}.login-container h2{margin-top:.5rem}.flag-icon{width:24px;height:16px;margin-right:8px;border:1px solid #ddd}.error-message{background-color:#f8d7da;color:#721c24;padding:12px 15px;border-radius:4px;margin-bottom:20px;display:flex;align-items:center;border:1px solid #f5c6cb;font-size:14px;animation:fadeIn .3s ease-in-out}.error-message .error-icon{display:inline-block;width:20px;height:20px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:20px;margin-right:10px;font-weight:700;font-size:12px}.validation-error{color:#dc3545;font-size:12px;margin-top:5px;display:flex;align-items:center;animation:fadeIn .3s ease-in-out}.validation-error .error-icon{display:inline-block;width:16px;height:16px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:16px;margin-right:6px;font-weight:700;font-size:10px}.invalid-input{border:1px solid #dc3545!important}.invalid-input:focus{box-shadow:0 0 0 .2rem #dc354540}.resend-container{margin-top:15px;text-align:center}.resend-container .resend-link{color:#007bff;cursor:pointer;text-decoration:underline;font-size:13px}.resend-container .resend-link:hover{color:#0056b3}.time-remaining{margin-top:16px;font-size:1rem;color:#f5f5f5}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}@media (max-width: 768px){.login-page-container{justify-content:center;padding-right:0;align-items:center}.login-container{max-width:90%;margin:20px auto}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3$3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3$1.TranslatePipe, name: "translate" }] });
2183
+ }
2184
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: MfaLogin, decorators: [{
2185
+ type: Component,
2186
+ args: [{ selector: 'ntybase-mfa-login', imports: [
2187
+ FormsModule,
2188
+ ReactiveFormsModule,
2189
+ CommonModule,
2190
+ MatMenuModule,
2191
+ MatIconModule,
2192
+ TranslateModule,
2193
+ ], template: "<div class=\"login-page-container\">\n <div class=\"login-image-container\"></div>\n\n <div class=\"login-container glass-effect\">\n <!-- Language Button -->\n <div class=\"language-toggle\">\n <button\n *ngIf=\"icon; else text\"\n mat-icon-button\n [matMenuTriggerFor]=\"languageMenu\"\n >\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n <ng-template #text>\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n </ng-template>\n </div>\n\n <h2>Netty Admin Core</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let language of languages\"\n (click)=\"setLanguage(language)\"\n >\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n </mat-menu>\n\n <form\n (ngSubmit)=\"login()\"\n [formGroup]=\"loginForm\"\n *ngIf=\"!isLoading(); else loading\"\n novalidate\n >\n <div class=\"error-message\" *ngIf=\"error()\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n\n <div class=\"form-group\">\n <label for=\"mfaCode\">{{'MFACode' | translate}}</label>\n <input\n type=\"text\"\n id=\"mfaCode\"\n formControlName=\"mfaCode\"\n [placeholder]=\"'MFACode' | translate\"\n required\n />\n <div\n class=\"validation-error\"\n *ngIf=\"loginForm.controls['mfaCode'].invalid && loginForm.controls['mfaCode'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'MFACode is required' | translate }}\n </div>\n <!-- Time -->\n <div class=\"time-remaining\" *ngIf=\"!resendable()\">\n {{ '@TokenCount' | translate }}: {{ getFormattedTime() }}\n </div>\n </div>\n\n <button\n type=\"submit\"\n class=\"login-button\"\n [disabled]=\"loginForm.invalid || isLoading() || !resendable()\"\n >\n {{ '@login' | translate }}\n </button>\n\n <div class=\"resend-container\" *ngIf=\"resendable()\">\n <span (click)=\"onResendMFACode()\" class=\"resend-link\">\n {{ 'Resend MFACode' | translate }}\n </span>\n </div>\n </form>\n\n <ng-template #loading>\n <div class=\"loading-spinner\">\n <span>{{ '@loggingIn' | translate }}</span>\n </div>\n </ng-template>\n\n <div class=\"footer\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n</div>\n", styles: [".login-page-container{position:relative;display:flex;height:100vh;background-image:var(--mfa-login-bg-image, url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-position:center;background-repeat:no-repeat;background-size:cover;background-attachment:fixed;justify-content:flex-end;padding-right:5%}.login-image-container{display:none}.login-container{width:100%;max-width:400px;padding:2.5rem;display:flex;flex-direction:column;justify-content:center;background:#ffffff1a;border-radius:16px;box-shadow:0 8px 32px #0003;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.2);margin:20px;height:fit-content;align-self:center}.login-container h2{color:#fff;text-align:center;margin-bottom:2rem;font-size:1.8rem;font-weight:600;text-shadow:0 2px 4px rgba(0,0,0,.3)}.form-group{margin-bottom:1.5rem}.form-group label{display:block;font-weight:500;color:#fff;font-size:.9rem;text-shadow:0 1px 2px rgba(0,0,0,.3)}.form-group input[type=text],.form-group input[type=password]{width:100%;padding:.85rem 1rem;margin-top:.5rem;border:1px solid rgba(255,255,255,.3);border-radius:8px;font-size:.95rem;background-color:#ffffff26;color:#fff}.form-group input::placeholder{color:#ffffffb3}.form-group input[type=text]:focus,.form-group input[type=password]:focus{outline:none;border-color:#ffffff80;background-color:#ffffff40;box-shadow:0 0 0 3px #ffffff1a}.login-button{width:100%;padding:1rem;background-color:#fff3;color:#fff;border:1px solid rgba(255,255,255,.4);border-radius:8px;font-size:1rem;font-weight:500;cursor:pointer;transition:all .2s}.login-button:hover{background-color:#ffffff4d}.loading-spinner{text-align:center;padding:2rem}.loading-spinner p{margin-top:1rem;color:#fff}.footer{margin-top:2rem;text-align:center}.version{color:#ffffffb3;font-size:.8rem;display:block;text-shadow:0 1px 2px rgba(0,0,0,.3)}.language-toggle{position:absolute;top:20px;right:20px;z-index:10}.language-toggle button{background:#fff3;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:1px solid rgba(255,255,255,.3);cursor:pointer}.language-toggle button:hover{background:#ffffff4d}.language-toggle button mat-icon{color:#fff}.login-container h2{margin-top:.5rem}.flag-icon{width:24px;height:16px;margin-right:8px;border:1px solid #ddd}.error-message{background-color:#f8d7da;color:#721c24;padding:12px 15px;border-radius:4px;margin-bottom:20px;display:flex;align-items:center;border:1px solid #f5c6cb;font-size:14px;animation:fadeIn .3s ease-in-out}.error-message .error-icon{display:inline-block;width:20px;height:20px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:20px;margin-right:10px;font-weight:700;font-size:12px}.validation-error{color:#dc3545;font-size:12px;margin-top:5px;display:flex;align-items:center;animation:fadeIn .3s ease-in-out}.validation-error .error-icon{display:inline-block;width:16px;height:16px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:16px;margin-right:6px;font-weight:700;font-size:10px}.invalid-input{border:1px solid #dc3545!important}.invalid-input:focus{box-shadow:0 0 0 .2rem #dc354540}.resend-container{margin-top:15px;text-align:center}.resend-container .resend-link{color:#007bff;cursor:pointer;text-decoration:underline;font-size:13px}.resend-container .resend-link:hover{color:#0056b3}.time-remaining{margin-top:16px;font-size:1rem;color:#f5f5f5}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}@media (max-width: 768px){.login-page-container{justify-content:center;padding-right:0;align-items:center}.login-container{max-width:90%;margin:20px auto}}\n"] }]
2194
+ }] });
2195
+
2196
+ class PasswordProxy {
2197
+ noAuthReqHeader;
2198
+ http = inject(HttpClient);
2199
+ environmentProxy = inject(EnvironmentProxy);
2200
+ constructor() {
2201
+ this.noAuthReqHeader = new HttpHeaders({
2202
+ 'Content-Type': 'application/json',
2203
+ 'No-Auth': 'True',
2204
+ });
2205
+ }
2206
+ /** Get forgot password record with the given recordGUID, if its still valid.
2207
+ *
2208
+ * @param recordGUID
2209
+ * @returns
2210
+ */
2211
+ getForgotPasswordRecord(recordGUID) {
2212
+ let functionUrl = this.environmentProxy.getAdminLink('GetForgotPasswordRecord');
2213
+ return this.http.get(functionUrl + '/' + recordGUID, {
2214
+ headers: this.noAuthReqHeader,
2215
+ });
2216
+ }
2217
+ /** Send reset password request for password change
2218
+ *
2219
+ * @param newPasswordDto
2220
+ * @returns
2221
+ */
2222
+ resetPassword(newPasswordDto) {
2223
+ let resetPasswordUrl = this.environmentProxy.getAdminLink('UpdatePassword');
2224
+ let resertPasswordCall$ = this.http.post(resetPasswordUrl, newPasswordDto, {
2225
+ headers: this.noAuthReqHeader,
2226
+ });
2227
+ return resertPasswordCall$.pipe(map((message) => {
2228
+ return message;
2229
+ }), take$1(1), catchError$1((error) => {
2230
+ let errorMessage = '';
2231
+ errorMessage =
2232
+ error.status +
2233
+ ' ' +
2234
+ (error?.error?.title ?? error?.message ?? error ?? 'Error.');
2235
+ return throwError(() => new Error(errorMessage ?? ''));
2236
+ }));
2237
+ }
2238
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PasswordProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2239
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PasswordProxy, providedIn: 'root' });
2240
+ }
2241
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: PasswordProxy, decorators: [{
2242
+ type: Injectable,
2243
+ args: [{
2244
+ providedIn: 'root',
2245
+ }]
2246
+ }], ctorParameters: () => [] });
2247
+
2248
+ class ForgotPasswordDto {
2249
+ nettyPasswordChangeGUID = null;
2250
+ nettyUserGUID = null;
2251
+ nettyUserName = null;
2252
+ nettyUserFullName = null;
2253
+ nettyValidUntil = null;
2254
+ nettyEMailParametersGUID = null;
2255
+ nettyIsUsed = null;
2256
+ nettyUsedDate = null;
2257
+ init() {
2258
+ this.nettyPasswordChangeGUID = null;
2259
+ this.nettyUserGUID = null;
2260
+ this.nettyUserName = '';
2261
+ this.nettyUserFullName = '';
2262
+ this.nettyValidUntil = null;
2263
+ this.nettyEMailParametersGUID = null;
2264
+ this.nettyIsUsed = true;
2265
+ this.nettyUsedDate = null;
2266
+ }
2267
+ }
2268
+
2269
+ class NewPasswordDto {
2270
+ nettyPasswordChangeGUID = null;
2271
+ nettyUserGUID = null;
2272
+ nettyUserName = '';
2273
+ password = '';
2274
+ passwordCheck = '';
2275
+ init() {
2276
+ this.nettyPasswordChangeGUID = null;
2277
+ this.nettyUserGUID = null;
2278
+ this.nettyUserName = '';
2279
+ this.password = '';
2280
+ this.passwordCheck = '';
2281
+ }
2282
+ constructor() { }
2283
+ static create(_nettyUserGUID, _nettyPasswordChangeGUID, _password, _passwordCheck) {
2284
+ let obj = new NewPasswordDto();
2285
+ obj.nettyUserGUID = _nettyUserGUID;
2286
+ obj.nettyPasswordChangeGUID = _nettyPasswordChangeGUID;
2287
+ obj.password = _password;
2288
+ obj.passwordCheck = _passwordCheck;
2289
+ return obj;
2290
+ }
2291
+ }
2292
+
2293
+ class ForgotPassword extends AuthBase {
2294
+ // Inject dependencies
2295
+ formBuilder = inject(FormBuilder);
2296
+ passwordService = inject(PasswordProxy);
2297
+ version = this.environmentProxy.version();
2298
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2299
+ error = signal(undefined, ...(ngDevMode ? [{ debugName: "error" }] : []));
2300
+ updatePasswordForm;
2301
+ //URL parameters
2302
+ urlOption = null;
2303
+ forgotPasswordRecord = new ForgotPasswordDto();
2304
+ constructor() {
2305
+ super();
2306
+ this.updatePasswordForm = this.formBuilder.group({
2307
+ username: [{ value: '', disabled: true }, Validators.required],
2308
+ newPassword: ['', Validators.required],
2309
+ newPasswordCheck: ['', Validators.required],
2310
+ });
2311
+ }
2312
+ ngOnInit() {
2313
+ this.route.paramMap.pipe(takeUntil(this.onDestroy$)).subscribe((params) => {
2314
+ this.urlOption = params.get('requestguid');
2315
+ this.getData();
2316
+ });
2317
+ }
2318
+ getData() {
2319
+ this.isLoading.set(true);
2320
+ this.passwordService
2321
+ .getForgotPasswordRecord(this.urlOption)
2322
+ .pipe(finalize(() => {
2323
+ this.isLoading.set(false);
2324
+ }))
2325
+ .subscribe({
2326
+ next: (data) => {
2327
+ this.isLoading.set(false);
2328
+ this.updatePasswordForm.get('username')?.setValue(data.nettyUserName);
2329
+ this.forgotPasswordRecord = data;
2330
+ },
2331
+ error: (error) => {
2332
+ this.alertService.showError(error.message);
2333
+ },
2334
+ });
2335
+ }
2336
+ updatePassword() {
2337
+ let newPasswordDto = NewPasswordDto.create(this.forgotPasswordRecord.nettyUserGUID, this.forgotPasswordRecord.nettyPasswordChangeGUID, this.updatePasswordForm.get('newPassword')?.value, this.updatePasswordForm.get('newPasswordCheck')?.value);
2338
+ this.isLoading.set(true);
2339
+ this.passwordService
2340
+ .resetPassword(newPasswordDto)
2341
+ .pipe(finalize(() => {
2342
+ this.isLoading.set(false);
2343
+ }))
2344
+ .subscribe({
2345
+ next: (result) => {
2346
+ if (result) {
2347
+ this.alertService.showSuccess('@PasswordChangeSuccess');
2348
+ this.router.navigate(['/login']);
2349
+ }
2350
+ },
2351
+ error: (error) => {
2352
+ this.error.set(error.message);
2353
+ },
2354
+ });
2355
+ }
2356
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ForgotPassword, deps: [], target: i0.ɵɵFactoryTarget.Component });
2357
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: ForgotPassword, isStandalone: true, selector: "ntybase-forgot-password", usesInheritance: true, ngImport: i0, template: "<div class=\"login-page-container\">\n <div class=\"login-image-container\"></div>\n\n <div class=\"login-container glass-effect\">\n <!-- Language Button -->\n <div class=\"language-toggle\">\n <button\n *ngIf=\"icon; else text\"\n mat-icon-button\n [matMenuTriggerFor]=\"languageMenu\"\n >\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n <ng-template #text>\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n </ng-template>\n </div>\n\n <h2>Netty Admin Core</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let language of languages\"\n (click)=\"setLanguage(language)\"\n >\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n </mat-menu>\n\n <form\n (ngSubmit)=\"updatePassword()\"\n [formGroup]=\"updatePasswordForm\"\n *ngIf=\"!isLoading(); else loading\"\n novalidate\n >\n <div class=\"error-message\" *ngIf=\"error()\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n <div class=\"form-group\">\n <label for=\"username\">{{'@username' | translate}}</label>\n <input\n type=\"text\"\n id=\"username\"\n [placeholder]=\"'@username' | translate\"\n formControlName=\"username\"\n required\n />\n </div>\n\n <div class=\"form-group\">\n <label for=\"newPassword\">{{'@password' | translate}}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n [placeholder]=\"'@password' | translate\"\n formControlName=\"newPassword\"\n required\n />\n <mat-error\n class=\"validation-error\"\n *ngIf=\"updatePasswordForm.controls['newPassword'].invalid && updatePasswordForm.controls['newPassword'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'Password is required' | translate }}\n </mat-error>\n </div>\n\n <div class=\"form-group\">\n <label for=\"newPasswordCheck\">{{'@passwordCheck' | translate}}</label>\n <input\n type=\"password\"\n id=\"newPasswordCheck\"\n [placeholder]=\"'@passwordCheck' | translate\"\n formControlName=\"newPasswordCheck\"\n required\n />\n <mat-error\n class=\"validation-error\"\n *ngIf=\"updatePasswordForm.controls['newPasswordCheck'].invalid && updatePasswordForm.controls['newPasswordCheck'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'Password is required' | translate }}\n </mat-error>\n </div>\n\n <button\n type=\"submit\"\n class=\"login-button\"\n [disabled]=\"updatePasswordForm.invalid\"\n >\n {{ '@setPassword' | translate }}\n </button>\n </form>\n\n <ng-template #loading>\n <div class=\"loading-spinner\">\n <span>{{ '@updatingPassword' | translate }}</span>\n </div>\n </ng-template>\n\n <div class=\"footer\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n</div>\n", styles: [".login-page-container{position:relative;display:flex;height:100vh;background-image:var(--forgot-password-bg-image, url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-position:center;background-repeat:no-repeat;background-size:cover;background-attachment:fixed;justify-content:flex-end;padding-right:5%}.login-image-container{display:none}.login-container{width:100%;max-width:400px;padding:2.5rem;display:flex;flex-direction:column;justify-content:center;background:#ffffff1a;border-radius:16px;box-shadow:0 8px 32px #0003;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.2);margin:20px;height:fit-content;align-self:center}.login-container h2{color:#fff;text-align:center;margin-bottom:2rem;font-size:1.8rem;font-weight:600;text-shadow:0 2px 4px rgba(0,0,0,.3)}.form-group{margin-bottom:1.5rem}.form-group label{display:block;font-weight:500;color:#fff;font-size:.9rem;text-shadow:0 1px 2px rgba(0,0,0,.3)}.form-group input[type=text],.form-group input[type=password]{width:100%;padding:.85rem 1rem;margin-top:.5rem;border:1px solid rgba(255,255,255,.3);border-radius:8px;font-size:.95rem;background-color:#ffffff26;color:#fff}.form-group input::placeholder{color:#ffffffb3}.form-group input[type=text]:focus,.form-group input[type=password]:focus{outline:none;border-color:#ffffff80;background-color:#ffffff40;box-shadow:0 0 0 3px #ffffff1a}.remember{display:flex;align-items:center;margin-bottom:1.5rem}.remember input{width:18px;height:18px;margin-right:.75rem;cursor:pointer}.remember label{color:#fff;font-size:.9rem;cursor:pointer;-webkit-user-select:none;user-select:none;text-shadow:0 1px 2px rgba(0,0,0,.3)}.forgot-password{margin-left:auto;color:#ffffffe6;font-size:.85rem;text-decoration:none}.forgot-password:hover{text-decoration:underline}.login-button{width:100%;padding:1rem;background-color:#fff3;color:#fff;border:1px solid rgba(255,255,255,.4);border-radius:8px;font-size:1rem;font-weight:500;cursor:pointer;transition:all .2s}.login-button:hover{background-color:#ffffff4d}.loading-spinner{text-align:center;padding:2rem}.loading-spinner p{margin-top:1rem;color:#fff}.footer{margin-top:2rem;text-align:center}.version{color:#ffffffb3;font-size:.8rem;display:block;text-shadow:0 1px 2px rgba(0,0,0,.3)}@media (max-width: 768px){.login-page-container{justify-content:center;padding-right:0;align-items:center}.login-container{max-width:90%;margin:20px auto}}.language-toggle{position:absolute;top:20px;right:20px;z-index:10}.language-toggle button{background:#fff3;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:1px solid rgba(255,255,255,.3);cursor:pointer}.language-toggle button:hover{background:#ffffff4d}.language-toggle button mat-icon{color:#fff}.login-container h2{margin-top:.5rem}.flag-icon{width:24px;height:16px;margin-right:8px;border:1px solid #ddd}.error-message{background-color:#f8d7da;color:#721c24;padding:12px 15px;border-radius:4px;margin-bottom:20px;display:flex;align-items:center;border:1px solid #f5c6cb;font-size:14px;animation:fadeIn .3s ease-in-out}.error-message .error-icon{display:inline-block;width:20px;height:20px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:20px;margin-right:10px;font-weight:700;font-size:12px}.validation-error{color:#dc3545;font-size:12px;margin-top:5px;display:flex;align-items:center;animation:fadeIn .3s ease-in-out}.validation-error .error-icon{display:inline-block;width:16px;height:16px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:16px;margin-right:6px;font-weight:700;font-size:10px}input.ng-invalid.ng-touched{border:1px solid #dc3545!important}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3$3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i1$4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3$1.TranslatePipe, name: "translate" }] });
2358
+ }
2359
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ForgotPassword, decorators: [{
2360
+ type: Component,
2361
+ args: [{ selector: 'ntybase-forgot-password', imports: [
2362
+ FormsModule,
2363
+ ReactiveFormsModule,
2364
+ CommonModule,
2365
+ MatMenuModule,
2366
+ MatIconModule,
2367
+ MatInputModule,
2368
+ MatFormFieldModule,
2369
+ TranslateModule,
2370
+ ], template: "<div class=\"login-page-container\">\n <div class=\"login-image-container\"></div>\n\n <div class=\"login-container glass-effect\">\n <!-- Language Button -->\n <div class=\"language-toggle\">\n <button\n *ngIf=\"icon; else text\"\n mat-icon-button\n [matMenuTriggerFor]=\"languageMenu\"\n >\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n <ng-template #text>\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n </ng-template>\n </div>\n\n <h2>Netty Admin Core</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let language of languages\"\n (click)=\"setLanguage(language)\"\n >\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n </mat-menu>\n\n <form\n (ngSubmit)=\"updatePassword()\"\n [formGroup]=\"updatePasswordForm\"\n *ngIf=\"!isLoading(); else loading\"\n novalidate\n >\n <div class=\"error-message\" *ngIf=\"error()\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n <div class=\"form-group\">\n <label for=\"username\">{{'@username' | translate}}</label>\n <input\n type=\"text\"\n id=\"username\"\n [placeholder]=\"'@username' | translate\"\n formControlName=\"username\"\n required\n />\n </div>\n\n <div class=\"form-group\">\n <label for=\"newPassword\">{{'@password' | translate}}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n [placeholder]=\"'@password' | translate\"\n formControlName=\"newPassword\"\n required\n />\n <mat-error\n class=\"validation-error\"\n *ngIf=\"updatePasswordForm.controls['newPassword'].invalid && updatePasswordForm.controls['newPassword'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'Password is required' | translate }}\n </mat-error>\n </div>\n\n <div class=\"form-group\">\n <label for=\"newPasswordCheck\">{{'@passwordCheck' | translate}}</label>\n <input\n type=\"password\"\n id=\"newPasswordCheck\"\n [placeholder]=\"'@passwordCheck' | translate\"\n formControlName=\"newPasswordCheck\"\n required\n />\n <mat-error\n class=\"validation-error\"\n *ngIf=\"updatePasswordForm.controls['newPasswordCheck'].invalid && updatePasswordForm.controls['newPasswordCheck'].touched\"\n >\n <span class=\"error-icon\">!</span>\n {{ 'Password is required' | translate }}\n </mat-error>\n </div>\n\n <button\n type=\"submit\"\n class=\"login-button\"\n [disabled]=\"updatePasswordForm.invalid\"\n >\n {{ '@setPassword' | translate }}\n </button>\n </form>\n\n <ng-template #loading>\n <div class=\"loading-spinner\">\n <span>{{ '@updatingPassword' | translate }}</span>\n </div>\n </ng-template>\n\n <div class=\"footer\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n</div>\n", styles: [".login-page-container{position:relative;display:flex;height:100vh;background-image:var(--forgot-password-bg-image, url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-position:center;background-repeat:no-repeat;background-size:cover;background-attachment:fixed;justify-content:flex-end;padding-right:5%}.login-image-container{display:none}.login-container{width:100%;max-width:400px;padding:2.5rem;display:flex;flex-direction:column;justify-content:center;background:#ffffff1a;border-radius:16px;box-shadow:0 8px 32px #0003;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.2);margin:20px;height:fit-content;align-self:center}.login-container h2{color:#fff;text-align:center;margin-bottom:2rem;font-size:1.8rem;font-weight:600;text-shadow:0 2px 4px rgba(0,0,0,.3)}.form-group{margin-bottom:1.5rem}.form-group label{display:block;font-weight:500;color:#fff;font-size:.9rem;text-shadow:0 1px 2px rgba(0,0,0,.3)}.form-group input[type=text],.form-group input[type=password]{width:100%;padding:.85rem 1rem;margin-top:.5rem;border:1px solid rgba(255,255,255,.3);border-radius:8px;font-size:.95rem;background-color:#ffffff26;color:#fff}.form-group input::placeholder{color:#ffffffb3}.form-group input[type=text]:focus,.form-group input[type=password]:focus{outline:none;border-color:#ffffff80;background-color:#ffffff40;box-shadow:0 0 0 3px #ffffff1a}.remember{display:flex;align-items:center;margin-bottom:1.5rem}.remember input{width:18px;height:18px;margin-right:.75rem;cursor:pointer}.remember label{color:#fff;font-size:.9rem;cursor:pointer;-webkit-user-select:none;user-select:none;text-shadow:0 1px 2px rgba(0,0,0,.3)}.forgot-password{margin-left:auto;color:#ffffffe6;font-size:.85rem;text-decoration:none}.forgot-password:hover{text-decoration:underline}.login-button{width:100%;padding:1rem;background-color:#fff3;color:#fff;border:1px solid rgba(255,255,255,.4);border-radius:8px;font-size:1rem;font-weight:500;cursor:pointer;transition:all .2s}.login-button:hover{background-color:#ffffff4d}.loading-spinner{text-align:center;padding:2rem}.loading-spinner p{margin-top:1rem;color:#fff}.footer{margin-top:2rem;text-align:center}.version{color:#ffffffb3;font-size:.8rem;display:block;text-shadow:0 1px 2px rgba(0,0,0,.3)}@media (max-width: 768px){.login-page-container{justify-content:center;padding-right:0;align-items:center}.login-container{max-width:90%;margin:20px auto}}.language-toggle{position:absolute;top:20px;right:20px;z-index:10}.language-toggle button{background:#fff3;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:1px solid rgba(255,255,255,.3);cursor:pointer}.language-toggle button:hover{background:#ffffff4d}.language-toggle button mat-icon{color:#fff}.login-container h2{margin-top:.5rem}.flag-icon{width:24px;height:16px;margin-right:8px;border:1px solid #ddd}.error-message{background-color:#f8d7da;color:#721c24;padding:12px 15px;border-radius:4px;margin-bottom:20px;display:flex;align-items:center;border:1px solid #f5c6cb;font-size:14px;animation:fadeIn .3s ease-in-out}.error-message .error-icon{display:inline-block;width:20px;height:20px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:20px;margin-right:10px;font-weight:700;font-size:12px}.validation-error{color:#dc3545;font-size:12px;margin-top:5px;display:flex;align-items:center;animation:fadeIn .3s ease-in-out}.validation-error .error-icon{display:inline-block;width:16px;height:16px;background-color:#dc3545;color:#fff;border-radius:50%;text-align:center;line-height:16px;margin-right:6px;font-weight:700;font-size:10px}input.ng-invalid.ng-touched{border:1px solid #dc3545!important}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}\n"] }]
2371
+ }], ctorParameters: () => [] });
2372
+
2373
+ class HttpError403 {
2374
+ attemptedUrl = '';
2375
+ routeSubscription;
2376
+ router = inject(Router);
2377
+ route = inject(ActivatedRoute);
2378
+ location = inject(Location);
2379
+ commonService = inject(CommonService);
2380
+ constructor() {
2381
+ // Router'dan state'i al
2382
+ const navigation = this.router.getCurrentNavigation();
2383
+ this.attemptedUrl = navigation?.extras.state?.['attemptedUrl'] || '';
2384
+ // Eğer state'ten gelmediyse, mevcut URL'i temizleyerek kullan
2385
+ if (!this.attemptedUrl) {
2386
+ this.attemptedUrl = this.getCleanUrlPath();
2387
+ }
2388
+ this.routeSubscription = this.router.events.subscribe(() => {
2389
+ this.updateAttemptedUrl();
2390
+ });
2391
+ }
2392
+ ngOnInit() {
2393
+ // 1. URL'deki secondary outlet'leri temizle (örneğin: "(rightSidenav:...)")
2394
+ this.cleanUrlFromFragments();
2395
+ // 2. Hata mesajı için attemptedUrl'i güncelle
2396
+ this.updateAttemptedUrl();
2397
+ // 3. Right sidenav'ı kapat
2398
+ this.commonService.toggleRightSidenav(false);
2399
+ }
2400
+ ngOnDestroy() {
2401
+ if (this.routeSubscription) {
2402
+ this.routeSubscription.unsubscribe();
2403
+ }
2404
+ }
2405
+ /**
2406
+ * URL'deki fragment ve outlet'leri temizler.
2407
+ * Örnek: "/sys/NettyUserGroup(rightSidenav:...)" → "/sys/NettyUserGroup"
2408
+ */
2409
+ cleanUrlFromFragments() {
2410
+ const currentUrl = this.router.url;
2411
+ const cleanUrl = currentUrl.split('(')[0]; // "(" sonrasını sil
2412
+ if (currentUrl !== cleanUrl) {
2413
+ // URL'yi temizlenmiş haliyle değiştir (tarayıcı geçmişine eklemeden)
2414
+ this.router.navigateByUrl(cleanUrl, {
2415
+ replaceUrl: true,
2416
+ skipLocationChange: false,
2417
+ });
2418
+ }
2419
+ }
2420
+ /**
2421
+ * attemptedUrl'i güncel router URL'ine göre günceller
2422
+ */
2423
+ updateAttemptedUrl() {
2424
+ this.attemptedUrl = this.getCleanUrlPath();
2425
+ }
2426
+ /**
2427
+ * Temizlenmiş URL yolunu döndürür (queryParams ve fragment olmadan).
2428
+ */
2429
+ getCleanUrlPath() {
2430
+ return this.router.url.split('?')[0].split('(')[0];
2431
+ }
2432
+ goHome() {
2433
+ this.router.navigate(['/'], { replaceUrl: true });
2434
+ this.commonService.toggleRightSidenav(false);
2435
+ }
2436
+ goBack() {
2437
+ this.location.back();
2438
+ }
2439
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: HttpError403, deps: [], target: i0.ɵɵFactoryTarget.Component });
2440
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: HttpError403, isStandalone: true, selector: "ntybase-http-error403", ngImport: i0, template: "<div class=\"error-container\">\n <div class=\"error-page\">\n <h1>403 - Eri\u015Fim Engellendi</h1>\n\n <p>\n <strong>\u00DCzg\u00FCn\u00FCz, {{ attemptedUrl }}</strong> sayfas\u0131na eri\u015Fim izniniz\n bulunmamaktad\u0131r.\n </p>\n\n <p class=\"error-detail\">\n Bu sayfay\u0131 g\u00F6r\u00FCnt\u00FClemek i\u00E7in gerekli yetkilere sahip de\u011Filsiniz. E\u011Fer bu\n bir hata oldu\u011Funu d\u00FC\u015F\u00FCn\u00FCyorsan\u0131z, l\u00FCtfen sistem y\u00F6neticinizle ileti\u015Fime\n ge\u00E7in.\n </p>\n <button mat-raised-button (click)=\"goHome()\" color=\"primary\" routerLink=\"/\">\n Ana Sayfaya D\u00F6n\n </button>\n <button mat-raised-button (click)=\"goBack()\" color=\"primary\" routerLink=\"/\">\n Geri D\u00F6n\n </button>\n </div>\n</div>\n", styles: [".error-container{background-image:var(--httperror403-bg-image, linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, .6)), url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-blend-mode:overlay;height:100vh;width:100%;background-position:center;background-repeat:no-repeat;background-size:cover;display:flex;justify-content:center;align-items:center;color:#fff;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.error-page{text-align:center;padding:3rem;max-width:650px;margin:0 auto;background-color:#000000b3;border-radius:16px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 8px 32px #0000004d;animation:fadeIn .8s ease-in-out}.error-page h1{font-size:6rem;margin:0;color:var(--mat-sys-primary-container);text-shadow:0 4px 8px rgba(0,0,0,.3);font-weight:700}.error-page h3{font-size:1.8rem;margin-top:0;margin-bottom:1.5rem;color:#f8f9fa}.error-page p{margin:1.5rem 0;font-size:1.1rem;line-height:1.6;color:#e9ecef}.error-page strong{word-break:break-all;color:#ff6b6b;font-weight:500}button{margin:.5rem;padding:.8rem 2rem;font-size:1rem;font-weight:500;border-radius:50px;transition:all .3s ease;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}button:hover{transform:translateY(-2px);box-shadow:0 6px 12px #00000026}button:active{transform:translateY(0)}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (max-width: 600px){.error-page{padding:2rem;width:90%}.error-page h1{font-size:4rem}.error-page h3{font-size:1.5rem}button{display:block;width:100%;margin:.5rem 0}}\n"] });
2441
+ }
2442
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: HttpError403, decorators: [{
2443
+ type: Component,
2444
+ args: [{ selector: 'ntybase-http-error403', imports: [], template: "<div class=\"error-container\">\n <div class=\"error-page\">\n <h1>403 - Eri\u015Fim Engellendi</h1>\n\n <p>\n <strong>\u00DCzg\u00FCn\u00FCz, {{ attemptedUrl }}</strong> sayfas\u0131na eri\u015Fim izniniz\n bulunmamaktad\u0131r.\n </p>\n\n <p class=\"error-detail\">\n Bu sayfay\u0131 g\u00F6r\u00FCnt\u00FClemek i\u00E7in gerekli yetkilere sahip de\u011Filsiniz. E\u011Fer bu\n bir hata oldu\u011Funu d\u00FC\u015F\u00FCn\u00FCyorsan\u0131z, l\u00FCtfen sistem y\u00F6neticinizle ileti\u015Fime\n ge\u00E7in.\n </p>\n <button mat-raised-button (click)=\"goHome()\" color=\"primary\" routerLink=\"/\">\n Ana Sayfaya D\u00F6n\n </button>\n <button mat-raised-button (click)=\"goBack()\" color=\"primary\" routerLink=\"/\">\n Geri D\u00F6n\n </button>\n </div>\n</div>\n", styles: [".error-container{background-image:var(--httperror403-bg-image, linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, .6)), url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-blend-mode:overlay;height:100vh;width:100%;background-position:center;background-repeat:no-repeat;background-size:cover;display:flex;justify-content:center;align-items:center;color:#fff;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.error-page{text-align:center;padding:3rem;max-width:650px;margin:0 auto;background-color:#000000b3;border-radius:16px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 8px 32px #0000004d;animation:fadeIn .8s ease-in-out}.error-page h1{font-size:6rem;margin:0;color:var(--mat-sys-primary-container);text-shadow:0 4px 8px rgba(0,0,0,.3);font-weight:700}.error-page h3{font-size:1.8rem;margin-top:0;margin-bottom:1.5rem;color:#f8f9fa}.error-page p{margin:1.5rem 0;font-size:1.1rem;line-height:1.6;color:#e9ecef}.error-page strong{word-break:break-all;color:#ff6b6b;font-weight:500}button{margin:.5rem;padding:.8rem 2rem;font-size:1rem;font-weight:500;border-radius:50px;transition:all .3s ease;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}button:hover{transform:translateY(-2px);box-shadow:0 6px 12px #00000026}button:active{transform:translateY(0)}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (max-width: 600px){.error-page{padding:2rem;width:90%}.error-page h1{font-size:4rem}.error-page h3{font-size:1.5rem}button{display:block;width:100%;margin:.5rem 0}}\n"] }]
2445
+ }], ctorParameters: () => [] });
2446
+
2447
+ class HttpError404 {
2448
+ attemptedUrl = '';
2449
+ routeSubscription;
2450
+ router = inject(Router);
2451
+ route = inject(ActivatedRoute);
2452
+ location = inject(Location);
2453
+ commonService = inject(CommonService);
2454
+ constructor() {
2455
+ this.routeSubscription = this.router.events.subscribe(() => {
2456
+ this.updateAttemptedUrl();
2457
+ });
2458
+ }
2459
+ ngOnInit() {
2460
+ // 1. URL'deki secondary outlet'leri temizle (örneğin: "(rightSidenav:...)")
2461
+ this.cleanUrlFromFragments();
2462
+ // 2. Hata mesajı için attemptedUrl'i güncelle
2463
+ this.updateAttemptedUrl();
2464
+ // 3. Right sidenav'ı kapat
2465
+ this.commonService.toggleRightSidenav(false);
2466
+ }
2467
+ ngOnDestroy() {
2468
+ if (this.routeSubscription) {
2469
+ this.routeSubscription.unsubscribe();
2470
+ }
2471
+ }
2472
+ /**
2473
+ * URL'deki fragment ve outlet'leri temizler.
2474
+ * Örnek: "/sys/NettyUserGroup(rightSidenav:...)" → "/sys/NettyUserGroup"
2475
+ */
2476
+ cleanUrlFromFragments() {
2477
+ const currentUrl = this.router.url;
2478
+ const cleanUrl = currentUrl.split('(')[0]; // "(" sonrasını sil
2479
+ if (currentUrl !== cleanUrl) {
2480
+ // URL'yi temizlenmiş haliyle değiştir (tarayıcı geçmişine eklemeden)
2481
+ this.router.navigateByUrl(cleanUrl, {
2482
+ replaceUrl: true,
2483
+ skipLocationChange: false,
2484
+ });
2485
+ }
2486
+ }
2487
+ /**
2488
+ * Temizlenmiş URL yolunu döndürür (queryParams ve fragment olmadan).
2489
+ */
2490
+ getCleanUrlPath() {
2491
+ return this.router.url.split('?')[0].split('(')[0];
2492
+ }
2493
+ /**
2494
+ * attemptedUrl'i güncel router URL'ine göre günceller
2495
+ */
2496
+ updateAttemptedUrl() {
2497
+ this.attemptedUrl = this.getCleanUrlPath();
2498
+ }
2499
+ goHome() {
2500
+ this.router.navigate(['/'], { replaceUrl: true });
2501
+ this.commonService.toggleRightSidenav(false);
2502
+ }
2503
+ goBack() {
2504
+ this.location.back();
2505
+ }
2506
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: HttpError404, deps: [], target: i0.ɵɵFactoryTarget.Component });
2507
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: HttpError404, isStandalone: true, selector: "ntybase-http-error404", ngImport: i0, template: "<div class=\"error-container\">\n <div class=\"error-page\">\n <h1>404</h1>\n <h3>Sayfa Bulunamad\u0131</h3>\n <p><strong>{{ attemptedUrl }}</strong> adresi bulunamad\u0131.</p>\n <p>Bu i\u00E7erik ta\u015F\u0131nm\u0131\u015F veya silinmi\u015F olabilir.</p>\n <button mat-raised-button (click)=\"goHome()\" color=\"primary\" routerLink=\"/\">\n Ana Sayfaya D\u00F6n\n </button>\n <button mat-raised-button (click)=\"goBack()\" color=\"primary\" routerLink=\"/\">\n Geri D\u00F6n\n </button>\n </div>\n</div>\n", styles: [".error-container{background-image:var(--httperror404-bg-image, linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, .6)), url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-blend-mode:overlay;height:100vh;width:100%;background-position:center;background-repeat:no-repeat;background-size:cover;display:flex;justify-content:center;align-items:center;color:#fff;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.error-page{text-align:center;padding:3rem;max-width:650px;margin:0 auto;background-color:#000000b3;border-radius:16px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 8px 32px #0000004d;animation:fadeIn .8s ease-in-out}.error-page h1{font-size:6rem;margin:0;color:var(--mat-sys-primary-container);text-shadow:0 4px 8px rgba(0,0,0,.3);font-weight:700}.error-page h3{font-size:1.8rem;margin-top:0;margin-bottom:1.5rem;color:#f8f9fa}.error-page p{margin:1.5rem 0;font-size:1.1rem;line-height:1.6;color:#e9ecef}.error-page strong{word-break:break-all;color:#ff6b6b;font-weight:500}button{margin:.5rem;padding:.8rem 2rem;font-size:1rem;font-weight:500;border-radius:50px;transition:all .3s ease;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}button:hover{transform:translateY(-2px);box-shadow:0 6px 12px #00000026}button:active{transform:translateY(0)}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (max-width: 600px){.error-page{padding:2rem;width:90%}.error-page h1{font-size:4rem}.error-page h3{font-size:1.5rem}button{display:block;width:100%;margin:.5rem 0}}\n"] });
2508
+ }
2509
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: HttpError404, decorators: [{
2510
+ type: Component,
2511
+ args: [{ selector: 'ntybase-http-error404', imports: [], template: "<div class=\"error-container\">\n <div class=\"error-page\">\n <h1>404</h1>\n <h3>Sayfa Bulunamad\u0131</h3>\n <p><strong>{{ attemptedUrl }}</strong> adresi bulunamad\u0131.</p>\n <p>Bu i\u00E7erik ta\u015F\u0131nm\u0131\u015F veya silinmi\u015F olabilir.</p>\n <button mat-raised-button (click)=\"goHome()\" color=\"primary\" routerLink=\"/\">\n Ana Sayfaya D\u00F6n\n </button>\n <button mat-raised-button (click)=\"goBack()\" color=\"primary\" routerLink=\"/\">\n Geri D\u00F6n\n </button>\n </div>\n</div>\n", styles: [".error-container{background-image:var(--httperror404-bg-image, linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, .6)), url(https://images.unsplash.com/photo-1519681393784-d120267933ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1124&q=100));background-blend-mode:overlay;height:100vh;width:100%;background-position:center;background-repeat:no-repeat;background-size:cover;display:flex;justify-content:center;align-items:center;color:#fff;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.error-page{text-align:center;padding:3rem;max-width:650px;margin:0 auto;background-color:#000000b3;border-radius:16px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 8px 32px #0000004d;animation:fadeIn .8s ease-in-out}.error-page h1{font-size:6rem;margin:0;color:var(--mat-sys-primary-container);text-shadow:0 4px 8px rgba(0,0,0,.3);font-weight:700}.error-page h3{font-size:1.8rem;margin-top:0;margin-bottom:1.5rem;color:#f8f9fa}.error-page p{margin:1.5rem 0;font-size:1.1rem;line-height:1.6;color:#e9ecef}.error-page strong{word-break:break-all;color:#ff6b6b;font-weight:500}button{margin:.5rem;padding:.8rem 2rem;font-size:1rem;font-weight:500;border-radius:50px;transition:all .3s ease;background-color:var(--mat-sys-primary);color:var(--mat-sys-on-primary)}button:hover{transform:translateY(-2px);box-shadow:0 6px 12px #00000026}button:active{transform:translateY(0)}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (max-width: 600px){.error-page{padding:2rem;width:90%}.error-page h1{font-size:4rem}.error-page h3{font-size:1.5rem}button{display:block;width:100%;margin:.5rem 0}}\n"] }]
2512
+ }], ctorParameters: () => [] });
2513
+
2514
+ class NettyMenuService {
2515
+ http = inject(HttpClient);
2516
+ environmentProxy = inject(EnvironmentProxy);
2517
+ menuName = signal('', ...(ngDevMode ? [{ debugName: "menuName" }] : []));
2518
+ menu = signal(null, ...(ngDevMode ? [{ debugName: "menu" }] : []));
2519
+ constructor() {
2520
+ effect(() => {
2521
+ const currentMenuName = this.menuName();
2522
+ let functionUrl = this.environmentProxy.getAdminLink('/GetMenu');
2523
+ if (currentMenuName.trim().length > 0) {
2524
+ functionUrl += '/' + currentMenuName.trim();
2525
+ }
2526
+ this.http.get(functionUrl).subscribe({
2527
+ next: (data) => this.menu.set(data),
2528
+ error: (err) => {
2529
+ console.error('Failed to fetch menu:', err);
2530
+ this.menu.set(null);
2531
+ },
2532
+ });
2533
+ });
2534
+ }
2535
+ setMenuName(name) {
2536
+ this.menuName.set(name);
2537
+ }
2538
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NettyMenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2539
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NettyMenuService, providedIn: 'root' });
2540
+ }
2541
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NettyMenuService, decorators: [{
2542
+ type: Injectable,
2543
+ args: [{
2544
+ providedIn: 'root',
2545
+ }]
2546
+ }], ctorParameters: () => [] });
2547
+
2548
+ class LeftSidenav {
2549
+ authService = inject(AuthenticationService);
2550
+ router = inject(Router);
2551
+ menuService = inject(NettyMenuService);
2552
+ commonService = inject(CommonService);
2553
+ environmentProxy = inject(EnvironmentProxy);
2554
+ version = this.environmentProxy.version();
2555
+ isMinimized = input(false, ...(ngDevMode ? [{ debugName: "isMinimized" }] : []));
2556
+ toggleMinimize = output();
2557
+ onToggleMinimize() {
2558
+ this.toggleMinimize.emit();
2559
+ }
2560
+ logout() {
2561
+ this.authService.logout().subscribe({
2562
+ next: (success) => {
2563
+ if (success) {
2564
+ const cleanUrl = this.commonService.getCleanUrlPath();
2565
+ this.router.navigateByUrl(cleanUrl, { replaceUrl: true }).then(() => {
2566
+ this.router.navigate(['/login']);
2567
+ });
2568
+ }
2569
+ },
2570
+ error: (err) => {
2571
+ console.error('Logout failed:', err);
2572
+ },
2573
+ });
2574
+ }
2575
+ // Menu Search
2576
+ menuItems = signal([], ...(ngDevMode ? [{ debugName: "menuItems" }] : []));
2577
+ searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
2578
+ originalMenuItems = signal([], ...(ngDevMode ? [{ debugName: "originalMenuItems" }] : []));
2579
+ constructor() {
2580
+ effect(() => {
2581
+ const fetchedMenu = this.menuService.menu();
2582
+ if (fetchedMenu) {
2583
+ const processedItems = this.processMenuItems(fetchedMenu);
2584
+ this.originalMenuItems.set(processedItems);
2585
+ }
2586
+ else {
2587
+ this.originalMenuItems.set([]);
2588
+ }
2589
+ });
2590
+ }
2591
+ ngOnInit() {
2592
+ this.menuService.setMenuName('');
2593
+ }
2594
+ processMenuItems(apiResponse) {
2595
+ if (!apiResponse || !Array.isArray(apiResponse)) {
2596
+ return [];
2597
+ }
2598
+ return apiResponse.map((item) => ({
2599
+ name: item.label || item.name,
2600
+ icon: item.icon || '',
2601
+ link: item.route || item.link || '#',
2602
+ expanded: false,
2603
+ children: item.children ? this.processMenuItems(item.children) : [],
2604
+ }));
2605
+ }
2606
+ filteredMenuItems = computed(() => {
2607
+ const term = this.searchTerm().toLowerCase();
2608
+ if (!term)
2609
+ return this.originalMenuItems();
2610
+ return this.filterMenuItems(this.originalMenuItems(), term);
2611
+ }, ...(ngDevMode ? [{ debugName: "filteredMenuItems" }] : []));
2612
+ filterMenuItems(items, term) {
2613
+ const normalizedTerm = this.commonService.normalizeTurkish(term);
2614
+ return items
2615
+ .map((item) => {
2616
+ const filteredChildren = item.children
2617
+ ? this.filterMenuItems(item.children, term)
2618
+ : [];
2619
+ const normalizedName = this.commonService.normalizeTurkish(item.name);
2620
+ const matches = normalizedName.includes(normalizedTerm) ||
2621
+ filteredChildren.length > 0;
2622
+ return matches
2623
+ ? {
2624
+ ...item,
2625
+ children: filteredChildren,
2626
+ expanded: matches || filteredChildren.length > 0,
2627
+ }
2628
+ : null;
2629
+ })
2630
+ .filter((item) => item !== null);
2631
+ }
2632
+ toggleSubMenu(item) {
2633
+ item.expanded = !item.expanded;
2634
+ }
2635
+ onSearch(term) {
2636
+ this.searchTerm.set(term);
2637
+ }
2638
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LeftSidenav, deps: [], target: i0.ɵɵFactoryTarget.Component });
2639
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.7", type: LeftSidenav, isStandalone: true, selector: "ntybase-left-sidenav", inputs: { isMinimized: { classPropertyName: "isMinimized", publicName: "isMinimized", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleMinimize: "toggleMinimize" }, ngImport: i0, template: "<div class=\"sidenav-content-wrapper\">\n <!-- Minimize Icon -->\n <button mat-icon-button (click)=\"onToggleMinimize()\" class=\"minimize-button\">\n <mat-icon class=\"minimize-icon\">\n {{ isMinimized() ? \"chevron_right\" : \"chevron_left\" }}\n </mat-icon>\n </button>\n\n <!-- Profile -->\n <div class=\"profile-section\">\n <button mat-button [matMenuTriggerFor]=\"profileMenu\" class=\"profile-button\">\n <img\n src=\"https://images.unsplash.com/photo-1654110455429-cf322b40a906?q=80&w=2080&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n alt=\"Profil Image\"\n class=\"profile-image\"\n />\n <div class=\"profile-info\" *ngIf=\"!isMinimized()\">\n <p class=\"profile-name\">Mustafa Samet \u00C7al\u0131\u015F\u0131r</p>\n </div>\n </button>\n\n <mat-menu #profileMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item>\n <mat-icon>account_circle</mat-icon>\n <span>{{ '@profile' | translate }}</span>\n </button>\n <button mat-menu-item mat-button (click)=\"logout()\">\n <mat-icon>logout</mat-icon>\n <span>{{ '@logout' | translate }}</span>\n </button>\n </mat-menu>\n </div>\n\n <!-- Search Input -->\n <ntyui-search-input\n [label]=\"'@search' | translate\"\n [placeholder]=\"'@placeholderSearch' | translate\"\n [appearance]=\"'outline'\"\n *ngIf=\"!isMinimized()\"\n (search)=\"onSearch($event)\"\n >\n </ntyui-search-input>\n\n <!-- Menu -->\n <mat-nav-list *ngIf=\"!isMinimized()\">\n <div class=\"sidebar\">\n <ul>\n <ng-container *ngFor=\"let item of filteredMenuItems()\">\n <li>\n <a\n *ngIf=\"!item.children || item.children.length === 0\"\n [routerLink]=\"item.link\"\n routerLinkActive=\"active\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n <span>{{ item.name }}</span>\n </a>\n\n <a\n *ngIf=\"item.children && item.children.length > 0\"\n (click)=\"toggleSubMenu(item)\"\n [class.expanded]=\"item.expanded\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n <span>{{ item.name }}</span>\n <mat-icon [class.rotated]=\"item.expanded\">\n {{ item.expanded ? 'expand_less' : 'expand_more' }}\n </mat-icon>\n </a>\n\n <ul\n *ngIf=\"item.children && item.children.length > 0 && item.expanded\"\n class=\"submenu\"\n >\n <li *ngFor=\"let child of item.children\">\n <a [routerLink]=\"child.link\" routerLinkActive=\"active\">\n <mat-icon>{{ child.icon }}</mat-icon>\n <span>{{ child.name }}</span>\n </a>\n </li>\n </ul>\n </li>\n </ng-container>\n </ul>\n </div>\n </mat-nav-list>\n\n <!-- Footer -->\n <div class=\"sidenav-footer\">\n <button mat-button [matMenuTriggerFor]=\"footerMenu\" class=\"footer-button\">\n <div class=\"footer-text\" *ngIf=\"!isMinimized()\">\n <div class=\"company-name\">\n <p>&copy; 2025 AXIS</p>\n </div>\n </div>\n </button>\n <div class=\"version\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n\n <mat-menu #footerMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item>\n <mat-icon>swap_horiz</mat-icon>\n {{ '@changeCompany' | translate }}\n </button>\n <button mat-menu-item mat-button (click)=\"logout()\">\n <mat-icon>logout</mat-icon>\n <span>{{ '@logout' | translate }}</span>\n </button>\n </mat-menu>\n</div>\n", styles: [".profile-section{text-align:center;border-bottom:1px solid #e0e0e0;cursor:pointer;transition:all .3s ease;display:flex;align-items:center;flex-direction:column;position:relative}.profile-section:hover{transform:scale(.98)}.profile-section .profile-button{display:flex;flex-direction:column;align-items:center;background:none;border:none;padding:0;cursor:pointer;width:100%}.profile-image{width:80px;height:80px;border-radius:50%;object-fit:cover;margin:0 auto 8px;display:block;border:3px solid white;box-shadow:0 2px 4px #0000001a;transition:all .3s ease}.profile-name{font-weight:500;font-size:1rem;color:inherit;transition:opacity .3s ease;margin-top:8px}:host-context(.minimized) .profile-section{padding:10px 0}:host-context(.minimized) .profile-image{width:40px;height:40px;margin:0 auto}.sidenav-footer{position:static;bottom:0;left:0;right:0;padding:16px;color:inherit;font-size:12px;border-top:1px solid rgba(0,0,0,.1);transition:all .3s ease;display:flex;align-items:center;justify-content:space-between;margin-top:auto}.sidenav-footer .version{font-size:10px;margin-top:4px}.footer-button{border:none;background:none;cursor:pointer}:host-context(.minimized) .version{margin-right:7px}:host-context(.minimized) .sidenav-content-wrapper{overflow:hidden}.sidenav-content-wrapper{position:relative;height:100%;display:flex;flex-direction:column;overflow-x:hidden}.sidebar{flex:1;overflow:auto}.sidebar ul{list-style:none;padding:0;margin:0}.sidebar li{margin-bottom:4px;position:relative}.sidebar li a{display:flex;align-items:center;padding:12px 0 0;border-radius:6px;color:var(--mat-sys-primary);text-decoration:none;transition:all .2s ease;position:relative;overflow:hidden}.sidebar li a:hover{background-color:var(--mat-sys-user-info);color:var(--mat-sys-user-text)}.sidebar li a.active{background-color:var(--mat-sys-user-info);color:var(--mat-sys-user-text);font-weight:500}.sidebar li a.active:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background-color:var(--mat-sys-user-info);border-radius:3px 0 0 3px}.sidebar mat-icon{font-size:20px;width:20px;height:20px;color:inherit}.sidebar span{font-size:.875rem;white-space:nowrap}.submenu{padding-left:12px;margin-top:4px}.submenu li a{padding:10px 16px 0 20px}.submenu mat-icon{font-size:18px}.sidebar li a mat-icon:last-child{margin-left:auto;margin-right:0;font-size:18px;color:#5f6368}:host-context(.minimized) .sidebar li a{justify-content:center;padding:12px 0}:host-context(.minimized) .sidebar mat-icon{margin-right:0}:host-context(.minimized) .sidebar span,:host-context(.minimized) .sidebar li a mat-icon:last-child{display:none}:host-context(.minimized) .submenu{display:none}.sidenav-content-wrapper button[mat-icon-button]{position:absolute;right:-8px;top:10%;z-index:5}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i3$4.MatNavList, selector: "mat-nav-list", exportAs: ["matNavList"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3$3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i5.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: SearchInput, selector: "ntyui-search-input", outputs: ["search"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3$1.TranslatePipe, name: "translate" }] });
2640
+ }
2641
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: LeftSidenav, decorators: [{
2642
+ type: Component,
2643
+ args: [{ selector: 'ntybase-left-sidenav', imports: [
2644
+ CommonModule,
2645
+ MatIconModule,
2646
+ MatListModule,
2647
+ MatMenuModule,
2648
+ MatMenuTrigger,
2649
+ RouterLink,
2650
+ RouterModule,
2651
+ SearchInput,
2652
+ TranslateModule,
2653
+ ], template: "<div class=\"sidenav-content-wrapper\">\n <!-- Minimize Icon -->\n <button mat-icon-button (click)=\"onToggleMinimize()\" class=\"minimize-button\">\n <mat-icon class=\"minimize-icon\">\n {{ isMinimized() ? \"chevron_right\" : \"chevron_left\" }}\n </mat-icon>\n </button>\n\n <!-- Profile -->\n <div class=\"profile-section\">\n <button mat-button [matMenuTriggerFor]=\"profileMenu\" class=\"profile-button\">\n <img\n src=\"https://images.unsplash.com/photo-1654110455429-cf322b40a906?q=80&w=2080&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n alt=\"Profil Image\"\n class=\"profile-image\"\n />\n <div class=\"profile-info\" *ngIf=\"!isMinimized()\">\n <p class=\"profile-name\">Mustafa Samet \u00C7al\u0131\u015F\u0131r</p>\n </div>\n </button>\n\n <mat-menu #profileMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item>\n <mat-icon>account_circle</mat-icon>\n <span>{{ '@profile' | translate }}</span>\n </button>\n <button mat-menu-item mat-button (click)=\"logout()\">\n <mat-icon>logout</mat-icon>\n <span>{{ '@logout' | translate }}</span>\n </button>\n </mat-menu>\n </div>\n\n <!-- Search Input -->\n <ntyui-search-input\n [label]=\"'@search' | translate\"\n [placeholder]=\"'@placeholderSearch' | translate\"\n [appearance]=\"'outline'\"\n *ngIf=\"!isMinimized()\"\n (search)=\"onSearch($event)\"\n >\n </ntyui-search-input>\n\n <!-- Menu -->\n <mat-nav-list *ngIf=\"!isMinimized()\">\n <div class=\"sidebar\">\n <ul>\n <ng-container *ngFor=\"let item of filteredMenuItems()\">\n <li>\n <a\n *ngIf=\"!item.children || item.children.length === 0\"\n [routerLink]=\"item.link\"\n routerLinkActive=\"active\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n <span>{{ item.name }}</span>\n </a>\n\n <a\n *ngIf=\"item.children && item.children.length > 0\"\n (click)=\"toggleSubMenu(item)\"\n [class.expanded]=\"item.expanded\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n <span>{{ item.name }}</span>\n <mat-icon [class.rotated]=\"item.expanded\">\n {{ item.expanded ? 'expand_less' : 'expand_more' }}\n </mat-icon>\n </a>\n\n <ul\n *ngIf=\"item.children && item.children.length > 0 && item.expanded\"\n class=\"submenu\"\n >\n <li *ngFor=\"let child of item.children\">\n <a [routerLink]=\"child.link\" routerLinkActive=\"active\">\n <mat-icon>{{ child.icon }}</mat-icon>\n <span>{{ child.name }}</span>\n </a>\n </li>\n </ul>\n </li>\n </ng-container>\n </ul>\n </div>\n </mat-nav-list>\n\n <!-- Footer -->\n <div class=\"sidenav-footer\">\n <button mat-button [matMenuTriggerFor]=\"footerMenu\" class=\"footer-button\">\n <div class=\"footer-text\" *ngIf=\"!isMinimized()\">\n <div class=\"company-name\">\n <p>&copy; 2025 AXIS</p>\n </div>\n </div>\n </button>\n <div class=\"version\">\n <span class=\"version\">v{{version}}</span>\n </div>\n </div>\n\n <mat-menu #footerMenu=\"matMenu\" xPosition=\"before\">\n <button mat-menu-item>\n <mat-icon>swap_horiz</mat-icon>\n {{ '@changeCompany' | translate }}\n </button>\n <button mat-menu-item mat-button (click)=\"logout()\">\n <mat-icon>logout</mat-icon>\n <span>{{ '@logout' | translate }}</span>\n </button>\n </mat-menu>\n</div>\n", styles: [".profile-section{text-align:center;border-bottom:1px solid #e0e0e0;cursor:pointer;transition:all .3s ease;display:flex;align-items:center;flex-direction:column;position:relative}.profile-section:hover{transform:scale(.98)}.profile-section .profile-button{display:flex;flex-direction:column;align-items:center;background:none;border:none;padding:0;cursor:pointer;width:100%}.profile-image{width:80px;height:80px;border-radius:50%;object-fit:cover;margin:0 auto 8px;display:block;border:3px solid white;box-shadow:0 2px 4px #0000001a;transition:all .3s ease}.profile-name{font-weight:500;font-size:1rem;color:inherit;transition:opacity .3s ease;margin-top:8px}:host-context(.minimized) .profile-section{padding:10px 0}:host-context(.minimized) .profile-image{width:40px;height:40px;margin:0 auto}.sidenav-footer{position:static;bottom:0;left:0;right:0;padding:16px;color:inherit;font-size:12px;border-top:1px solid rgba(0,0,0,.1);transition:all .3s ease;display:flex;align-items:center;justify-content:space-between;margin-top:auto}.sidenav-footer .version{font-size:10px;margin-top:4px}.footer-button{border:none;background:none;cursor:pointer}:host-context(.minimized) .version{margin-right:7px}:host-context(.minimized) .sidenav-content-wrapper{overflow:hidden}.sidenav-content-wrapper{position:relative;height:100%;display:flex;flex-direction:column;overflow-x:hidden}.sidebar{flex:1;overflow:auto}.sidebar ul{list-style:none;padding:0;margin:0}.sidebar li{margin-bottom:4px;position:relative}.sidebar li a{display:flex;align-items:center;padding:12px 0 0;border-radius:6px;color:var(--mat-sys-primary);text-decoration:none;transition:all .2s ease;position:relative;overflow:hidden}.sidebar li a:hover{background-color:var(--mat-sys-user-info);color:var(--mat-sys-user-text)}.sidebar li a.active{background-color:var(--mat-sys-user-info);color:var(--mat-sys-user-text);font-weight:500}.sidebar li a.active:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background-color:var(--mat-sys-user-info);border-radius:3px 0 0 3px}.sidebar mat-icon{font-size:20px;width:20px;height:20px;color:inherit}.sidebar span{font-size:.875rem;white-space:nowrap}.submenu{padding-left:12px;margin-top:4px}.submenu li a{padding:10px 16px 0 20px}.submenu mat-icon{font-size:18px}.sidebar li a mat-icon:last-child{margin-left:auto;margin-right:0;font-size:18px;color:#5f6368}:host-context(.minimized) .sidebar li a{justify-content:center;padding:12px 0}:host-context(.minimized) .sidebar mat-icon{margin-right:0}:host-context(.minimized) .sidebar span,:host-context(.minimized) .sidebar li a mat-icon:last-child{display:none}:host-context(.minimized) .submenu{display:none}.sidenav-content-wrapper button[mat-icon-button]{position:absolute;right:-8px;top:10%;z-index:5}\n"] }]
2654
+ }], ctorParameters: () => [] });
2655
+
2656
+ class Theme {
2657
+ appTheme = signal('light', ...(ngDevMode ? [{ debugName: "appTheme" }] : []));
2658
+ themes = [
2659
+ { name: 'light', icon: 'light_mode' },
2660
+ { name: 'dark', icon: 'dark_mode' },
2661
+ { name: 'system', icon: 'desktop_windows' },
2662
+ ];
2663
+ selectedTheme = computed(() => this.themes.find((theme) => theme.name === this.appTheme()), ...(ngDevMode ? [{ debugName: "selectedTheme" }] : []));
2664
+ getThemes() {
2665
+ return this.themes;
2666
+ }
2667
+ setTheme(theme) {
2668
+ this.appTheme.set(theme);
2669
+ }
2670
+ constructor() {
2671
+ effect(() => {
2672
+ const appTheme = this.appTheme();
2673
+ const colorScheme = appTheme === 'system' ? 'light dark' : appTheme;
2674
+ const agGridThemeMode = colorScheme === 'dark' ? 'dark' : 'light'; // AG Grid tema modunu belirle
2675
+ document.body.style.setProperty('color-scheme', colorScheme);
2676
+ document.body.setAttribute('data-ag-theme-mode', agGridThemeMode);
2677
+ });
2678
+ }
2679
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Theme, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2680
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Theme, providedIn: 'root' });
2681
+ }
2682
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Theme, decorators: [{
2683
+ type: Injectable,
2684
+ args: [{
2685
+ providedIn: 'root',
2686
+ }]
2687
+ }], ctorParameters: () => [] });
2688
+
2689
+ class ColorPalette {
2690
+ themes = [];
2691
+ currentTheme = signal(null, ...(ngDevMode ? [{ debugName: "currentTheme" }] : []));
2692
+ setThemes(customThemes) {
2693
+ this.themes = customThemes;
2694
+ if (this.themes.length > 0 && !this.currentTheme()) {
2695
+ this.currentTheme.set(this.themes[0]);
2696
+ }
2697
+ }
2698
+ getThemes() {
2699
+ return this.themes;
2700
+ }
2701
+ setTheme(themeId) {
2702
+ const theme = this.themes.find((t) => t.id === themeId);
2703
+ if (theme) {
2704
+ this.currentTheme.set(theme);
2705
+ }
2706
+ }
2707
+ updateThemeClass = effect(() => {
2708
+ const theme = this.currentTheme();
2709
+ if (theme) {
2710
+ document.body.classList.remove(...this.themes.map((t) => `${t.id}-theme`));
2711
+ document.body.classList.add(`${theme.id}-theme`);
2712
+ }
2713
+ }, ...(ngDevMode ? [{ debugName: "updateThemeClass" }] : []));
2714
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ColorPalette, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2715
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ColorPalette, providedIn: 'root' });
2716
+ }
2717
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: ColorPalette, decorators: [{
2718
+ type: Injectable,
2719
+ args: [{
2720
+ providedIn: 'root',
2721
+ }]
2722
+ }] });
2723
+
2724
+ class Toolbar {
2725
+ themeService = inject(Theme);
2726
+ colorPaletteService = inject(ColorPalette);
2727
+ i18nService = inject(I18nService);
2728
+ toggleSidenav = output();
2729
+ onToggleSidenav() {
2730
+ this.toggleSidenav.emit();
2731
+ }
2732
+ // Language
2733
+ icon = model(false, ...(ngDevMode ? [{ debugName: "icon" }] : []));
2734
+ get currentLanguage() {
2735
+ return this.i18nService.language;
2736
+ }
2737
+ get languages() {
2738
+ return this.i18nService.supportedLanguages;
2739
+ }
2740
+ setLanguage(language) {
2741
+ this.i18nService.language = language;
2742
+ window.location.reload();
2743
+ }
2744
+ getCurrentLanguageIcon() {
2745
+ switch (this.i18nService.language) {
2746
+ case 'Türkçe':
2747
+ return 'fi fi-tr';
2748
+ case 'English':
2749
+ return 'fi fi-us';
2750
+ default:
2751
+ return 'fi fi-tr';
2752
+ }
2753
+ }
2754
+ getLanguageIcon(language) {
2755
+ switch (language) {
2756
+ case 'Türkçe':
2757
+ return 'fi fi-tr';
2758
+ case 'English':
2759
+ return 'fi fi-us';
2760
+ default:
2761
+ return 'fi fi-tr';
2762
+ }
2763
+ }
2764
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Toolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2765
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: Toolbar, isStandalone: true, selector: "ntybase-toolbar", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleSidenav: "toggleSidenav", icon: "iconChange" }, ngImport: i0, template: "<mat-toolbar class=\"sidenav-header\">\n <div class=\"left-section\">\n <button mat-icon-button (click)=\"onToggleSidenav()\" class=\"menu-button\">\n <mat-icon>menu</mat-icon>\n </button>\n <span>Netty Admin Core</span>\n\n <button><mat-icon>person</mat-icon></button>\n\n <!-- Dil Se\u00E7me Butonu -->\n <div class=\"language-toggle\">\n <button\n *ngIf=\"icon; else text\"\n mat-icon-button\n [matMenuTriggerFor]=\"languageMenu\"\n >\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n <ng-template #text>\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n </ng-template>\n </div>\n\n <!-- Dil Men\u00FCs\u00FC -->\n <mat-menu #languageMenu=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let language of languages\"\n (click)=\"setLanguage(language)\"\n >\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n </mat-menu>\n </div>\n\n <div class=\"spacer\"></div>\n\n <div class=\"flex-stretch\"></div>\n <button\n mat-icon-button\n [mat-menu-trigger-for]=\"themeMenu\"\n class=\"theme-button\"\n >\n <mat-icon>{{ themeService.selectedTheme()?.icon }}</mat-icon>\n </button>\n <mat-menu #themeMenu=\"matMenu\">\n @for (theme of themeService.getThemes(); track theme.name) {\n <button\n [class.selected-theme]=\"themeService.selectedTheme()?.name === theme.name\"\n mat-menu-item\n (click)=\"themeService.setTheme(theme.name)\"\n >\n <mat-icon>{{ theme.icon }}</mat-icon>\n <span>{{ theme.name | titlecase }}</span>\n </button>\n }\n </mat-menu>\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"customThemeMenu\"\n class=\"custom-theme-button\"\n >\n <mat-icon>format_color_fill</mat-icon>\n </button>\n <mat-menu #customThemeMenu=\"matMenu\">\n @for (customTheme of colorPaletteService.getThemes(); track customTheme.id)\n {\n <button\n mat-menu-item\n (click)=\"colorPaletteService.setTheme(customTheme.id)\"\n >\n <div class=\"theme-menu-item\">\n <div\n class=\"color-preview\"\n [style.background-color]=\"customTheme.primary\"\n ></div>\n <span>{{ customTheme.displayName }}</span>\n </div>\n </button>\n }\n </mat-menu>\n</mat-toolbar>\n", styles: ["mat-toolbar{position:fixed;top:0;left:0;right:0;z-index:3;box-shadow:0 1px 5px #0000001a;display:flex;align-items:center}.sidenav-header{display:flex;justify-content:space-between;align-items:center;padding:16px;margin:0;font-weight:500}.sidenav-header .left-section{display:flex;align-items:center;gap:16px}.sidenav-header .spacer{flex:1 1 auto}.flex-stretch{flex:1 0 auto}.theme-menu-item{display:flex;align-items:center;gap:12px}.color-preview{width:24px;height:24px;border-radius:50%}::ng-deep .theme-button{border:none;background:none;margin-right:15px;cursor:pointer}::ng-deep .custom-theme-button{border:none;background:none;cursor:pointer}::ng-deep .menu-button{border:none;background:none;cursor:pointer}.language-button{margin:0 auto}::ng-deep button{border:none;background:none;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3$3.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$3.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i3$5.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2$2.TitleCasePipe, name: "titlecase" }] });
2766
+ }
2767
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: Toolbar, decorators: [{
2768
+ type: Component,
2769
+ args: [{ selector: 'ntybase-toolbar', imports: [
2770
+ MatIconModule,
2771
+ MatMenuModule,
2772
+ MatToolbarModule,
2773
+ CommonModule,
2774
+ TitleCasePipe,
2775
+ ], template: "<mat-toolbar class=\"sidenav-header\">\n <div class=\"left-section\">\n <button mat-icon-button (click)=\"onToggleSidenav()\" class=\"menu-button\">\n <mat-icon>menu</mat-icon>\n </button>\n <span>Netty Admin Core</span>\n\n <button><mat-icon>person</mat-icon></button>\n\n <!-- Dil Se\u00E7me Butonu -->\n <div class=\"language-toggle\">\n <button\n *ngIf=\"icon; else text\"\n mat-icon-button\n [matMenuTriggerFor]=\"languageMenu\"\n >\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n <ng-template #text>\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n </ng-template>\n </div>\n\n <!-- Dil Men\u00FCs\u00FC -->\n <mat-menu #languageMenu=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let language of languages\"\n (click)=\"setLanguage(language)\"\n >\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n </mat-menu>\n </div>\n\n <div class=\"spacer\"></div>\n\n <div class=\"flex-stretch\"></div>\n <button\n mat-icon-button\n [mat-menu-trigger-for]=\"themeMenu\"\n class=\"theme-button\"\n >\n <mat-icon>{{ themeService.selectedTheme()?.icon }}</mat-icon>\n </button>\n <mat-menu #themeMenu=\"matMenu\">\n @for (theme of themeService.getThemes(); track theme.name) {\n <button\n [class.selected-theme]=\"themeService.selectedTheme()?.name === theme.name\"\n mat-menu-item\n (click)=\"themeService.setTheme(theme.name)\"\n >\n <mat-icon>{{ theme.icon }}</mat-icon>\n <span>{{ theme.name | titlecase }}</span>\n </button>\n }\n </mat-menu>\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"customThemeMenu\"\n class=\"custom-theme-button\"\n >\n <mat-icon>format_color_fill</mat-icon>\n </button>\n <mat-menu #customThemeMenu=\"matMenu\">\n @for (customTheme of colorPaletteService.getThemes(); track customTheme.id)\n {\n <button\n mat-menu-item\n (click)=\"colorPaletteService.setTheme(customTheme.id)\"\n >\n <div class=\"theme-menu-item\">\n <div\n class=\"color-preview\"\n [style.background-color]=\"customTheme.primary\"\n ></div>\n <span>{{ customTheme.displayName }}</span>\n </div>\n </button>\n }\n </mat-menu>\n</mat-toolbar>\n", styles: ["mat-toolbar{position:fixed;top:0;left:0;right:0;z-index:3;box-shadow:0 1px 5px #0000001a;display:flex;align-items:center}.sidenav-header{display:flex;justify-content:space-between;align-items:center;padding:16px;margin:0;font-weight:500}.sidenav-header .left-section{display:flex;align-items:center;gap:16px}.sidenav-header .spacer{flex:1 1 auto}.flex-stretch{flex:1 0 auto}.theme-menu-item{display:flex;align-items:center;gap:12px}.color-preview{width:24px;height:24px;border-radius:50%}::ng-deep .theme-button{border:none;background:none;margin-right:15px;cursor:pointer}::ng-deep .custom-theme-button{border:none;background:none;cursor:pointer}::ng-deep .menu-button{border:none;background:none;cursor:pointer}.language-button{margin:0 auto}::ng-deep button{border:none;background:none;cursor:pointer}\n"] }]
2776
+ }] });
2777
+
2778
+ class Guid {
2779
+ value = this.empty;
2780
+ constructor(value) {
2781
+ if (value) {
2782
+ if (Guid.isValid(value)) {
2783
+ this.value = value;
2784
+ }
2785
+ }
2786
+ }
2787
+ static newGuid() {
2788
+ return new Guid('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
2789
+ const r = (Math.random() * 16) | 0;
2790
+ const v = c == 'x' ? r : (r & 0x3) | 0x8;
2791
+ return v.toString(16);
2792
+ }));
2793
+ }
2794
+ /**
2795
+ * return all zeros '00000000-0000-0000-0000-000000000000'
2796
+ */
2797
+ static get empty() {
2798
+ return '00000000-0000-0000-0000-000000000000';
2799
+ }
2800
+ get empty() {
2801
+ return Guid.empty;
2802
+ }
2803
+ static isValid(str) {
2804
+ const validRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2805
+ return validRegex.test(str);
2806
+ }
2807
+ toString() {
2808
+ return this.value;
2809
+ }
2810
+ toJSON() {
2811
+ return this.value;
2812
+ }
2813
+ /**
2814
+ * True is guid is empty or not valid
2815
+ * @param str
2816
+ * @returns
2817
+ */
2818
+ static isNullOrEmpty(str) {
2819
+ if (str == null || str == undefined) {
2820
+ return true;
2821
+ }
2822
+ if (!Guid.isValid(str)) {
2823
+ return true;
2824
+ }
2825
+ if (str == Guid.empty) {
2826
+ return true;
2827
+ }
2828
+ return false;
2829
+ }
2830
+ /**
2831
+ * True if the guid is valid and not all zeros (empty)
2832
+ * @param str
2833
+ * @returns
2834
+ */
2835
+ static isValidAndNotEmpty(str) {
2836
+ return !Guid.isNullOrEmpty(str);
2837
+ }
2838
+ /**
2839
+ * Return empty guid if the given guid is not valid
2840
+ * @param guid
2841
+ * @returns
2842
+ */
2843
+ static emptyWhenNull(guid) {
2844
+ if (Guid.isValidAndNotEmpty(guid)) {
2845
+ return guid;
2846
+ }
2847
+ return Guid.empty;
2848
+ }
2849
+ }
2850
+
2851
+ class AutoCompleteProxy {
2852
+ http = inject(HttpClient);
2853
+ environmentProxy = inject(EnvironmentProxy);
2854
+ getAutocompleteDataFilter(searchTable, searchData) {
2855
+ let functionUrl = this.environmentProxy.getServerLink(searchTable + '/AutoCompleteFilter');
2856
+ return this.http.post(functionUrl, searchData);
2857
+ }
2858
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoCompleteProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2859
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoCompleteProxy, providedIn: 'root' });
2860
+ }
2861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoCompleteProxy, decorators: [{
2862
+ type: Injectable,
2863
+ args: [{
2864
+ providedIn: 'root',
2865
+ }]
2866
+ }] });
2867
+
2868
+ class NtyAutoCompleteFilter {
2869
+ coid = null;
2870
+ searchString = null;
2871
+ fields = null;
2872
+ }
2873
+
2874
+ /**
2875
+ * This component can load and display any container which is provided dynamically during run time
2876
+ * The only requirement is the component that will be displayed should extend AgGridBaseComponent
2877
+ */
2878
+ class AutoCompleteLookup {
2879
+ data = inject(MAT_DIALOG_DATA);
2880
+ container;
2881
+ component = input(...(ngDevMode ? [undefined, { debugName: "component" }] : []));
2882
+ filterField = input(null, ...(ngDevMode ? [{ debugName: "filterField" }] : []));
2883
+ filterFieldValue = input(null, ...(ngDevMode ? [{ debugName: "filterFieldValue" }] : []));
2884
+ filterFieldNumeric = input(false, ...(ngDevMode ? [{ debugName: "filterFieldNumeric" }] : []));
2885
+ filterFieldEquality = input('=', ...(ngDevMode ? [{ debugName: "filterFieldEquality" }] : []));
2886
+ // Output
2887
+ selectedElement = output();
2888
+ ngOnInit() {
2889
+ // Initialize with dialog data first
2890
+ if (this.data) {
2891
+ this.loadComponent(this.data);
2892
+ }
2893
+ }
2894
+ loadComponent(config) {
2895
+ if (!config?.component)
2896
+ return;
2897
+ this.container.clear();
2898
+ const componentRef = this.container.createComponent(config.component);
2899
+ // Set all inputs using setInput
2900
+ componentRef.setInput('popupValid', true);
2901
+ componentRef.setInput('filterField', config.filterField);
2902
+ componentRef.setInput('filterFieldValue', config.filterFieldValue);
2903
+ componentRef.setInput('filterFieldEquality', config.filterFieldEquality);
2904
+ componentRef.setInput('filterFieldNumeric', config.filterFieldNumeric);
2905
+ // Handle output
2906
+ const sub = componentRef.instance.selectedElement.subscribe((data) => {
2907
+ this.selectedElement.emit(data);
2908
+ });
2909
+ // Cleanup
2910
+ componentRef.onDestroy(() => sub.unsubscribe());
2911
+ }
2912
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoCompleteLookup, deps: [], target: i0.ɵɵFactoryTarget.Component });
2913
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.7", type: AutoCompleteLookup, isStandalone: true, selector: "ntybase-auto-complete-lookup", inputs: { component: { classPropertyName: "component", publicName: "component", isSignal: true, isRequired: false, transformFunction: null }, filterField: { classPropertyName: "filterField", publicName: "filterField", isSignal: true, isRequired: false, transformFunction: null }, filterFieldValue: { classPropertyName: "filterFieldValue", publicName: "filterFieldValue", isSignal: true, isRequired: false, transformFunction: null }, filterFieldNumeric: { classPropertyName: "filterFieldNumeric", publicName: "filterFieldNumeric", isSignal: true, isRequired: false, transformFunction: null }, filterFieldEquality: { classPropertyName: "filterFieldEquality", publicName: "filterFieldEquality", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedElement: "selectedElement" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: ` <ng-template #container></ng-template> `, isInline: true });
2914
+ }
2915
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoCompleteLookup, decorators: [{
2916
+ type: Component,
2917
+ args: [{
2918
+ selector: 'ntybase-auto-complete-lookup',
2919
+ imports: [],
2920
+ template: ` <ng-template #container></ng-template> `,
2921
+ }]
2922
+ }], propDecorators: { container: [{
2923
+ type: ViewChild,
2924
+ args: ['container', { read: ViewContainerRef, static: true }]
2925
+ }] } });
2926
+
2927
+ class AutoCompletePopupMenu {
2928
+ field = '';
2929
+ recordGuid;
2930
+ fieldDisabled = false;
2931
+ componentPath = '';
2932
+ inputValue = '';
2933
+ Result = output();
2934
+ clipboard = inject(Clipboard);
2935
+ alertService = inject(AlertService);
2936
+ ngOnInit() { }
2937
+ gotoNew() {
2938
+ if (!this.componentPath)
2939
+ return;
2940
+ // Split the component path into segments
2941
+ const pathSegments = this.componentPath.split('/').filter((p) => p);
2942
+ this.GotoNewTab(pathSegments, '', 'new');
2943
+ this.Result.emit('New');
2944
+ }
2945
+ gotoMain() {
2946
+ if (!this.componentPath || !this.recordGuid)
2947
+ return;
2948
+ // Split the component path into segments
2949
+ const pathSegments = this.componentPath.split('/').filter((p) => p);
2950
+ this.GotoNewTab(pathSegments, this.recordGuid, 'edit');
2951
+ this.Result.emit('Main');
2952
+ }
2953
+ gotoLookup() {
2954
+ if (!this.fieldDisabled) {
2955
+ this.Result.emit('Lookup');
2956
+ }
2957
+ }
2958
+ GotoNewTab(target, params, type) {
2959
+ if (!target || target.length === 0)
2960
+ return;
2961
+ // Construct the URL directly in the format you want
2962
+ let url = `/${target.join('/')}`;
2963
+ // Add query parameters
2964
+ const queryParams = [];
2965
+ if (params) {
2966
+ queryParams.push(`parameters=${encodeURIComponent(JSON.stringify(params))}`);
2967
+ }
2968
+ if (type) {
2969
+ queryParams.push(`type=${type}`);
2970
+ }
2971
+ queryParams.push('isNewTab=true');
2972
+ if (queryParams.length > 0) {
2973
+ url += `?${queryParams.join('&')}`;
2974
+ }
2975
+ // Open in new tab
2976
+ window.open(url, '_blank');
2977
+ }
2978
+ copyToClipboard() {
2979
+ if (this.inputValue) {
2980
+ this.clipboard.copy(this.inputValue);
2981
+ this.alertService.showAlert('@CopiedToClipboard');
2982
+ this.Result.emit('Copy');
2983
+ }
2984
+ }
2985
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoCompletePopupMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
2986
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: AutoCompletePopupMenu, isStandalone: true, selector: "ntybase-auto-complete-popup-menu", inputs: { field: "field", recordGuid: "recordGuid", fieldDisabled: "fieldDisabled", componentPath: "componentPath", inputValue: "inputValue" }, outputs: { Result: "Result" }, ngImport: i0, template: "<div class=\"dialogbase\">\n <div class=\"menu\">\n <ul class=\"list-group\">\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"copyToClipboard()\" class=\"align-line\"\n ><mat-icon>content_copy</mat-icon>{{'@Copy'| translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"gotoMain()\" class=\"align-line\"\n ><mat-icon>edit</mat-icon>{{'@PopupGotoRecordDefinition'|\n translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"gotoNew()\" class=\"align-line\"\n ><mat-icon>add</mat-icon>{{'@PopupNewRecord'| translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center\"\n [ngClass]=\"{'menu-item':!fieldDisabled , 'menu-item-disabled':fieldDisabled }\"\n >\n <span (click)=\"gotoLookup()\" class=\"align-line\"\n ><mat-icon>search</mat-icon>{{'@PopupSelectFromList'|translate}}</span\n >\n </li>\n </ul>\n </div>\n</div>\n", styles: [".dialogbase{background:#0000001a;padding:0;border-radius:12px;overflow:hidden;box-shadow:0 4px 20px #00000026}.menu{background:var(--mat-sys-primary-container);padding:0;display:flex;border-radius:inherit}.align-line{display:flex;vertical-align:middle;align-items:center;gap:12px;padding:0 8px}.menu-item{padding:12px 16px;display:flex;vertical-align:middle;cursor:pointer;transition:all .2s ease-out;border-radius:8px;margin:4px;color:var(--mat-sys-on-primary-container)}.menu-item:hover{background:var(--mat-sys-on-primary-fixed);color:var(--mat-sys-on-primary);transform:translateY(-2px);box-shadow:0 2px 8px #0000001a}.menu-item:hover .mat-icon{color:var(--mat-sys-on-primary);transform:scale(1.05)}.menu-item-disabled,.menu-item-disabled:hover{background:var(--mat-sys-on-primary-fixed);color:var(--mat-sys-on-primary);font-style:italic;opacity:.7;cursor:not-allowed}.menu-item-disabled .mat-icon,.menu-item-disabled:hover .mat-icon{color:var(--mat-sys-on-primary)}.mat-icon{transition:all .2s ease-out;font-size:20px;width:20px;height:20px}.list-group{list-style:none;padding:8px;margin:0;width:100%}.list-group-item{background-position:bottom;background-size:100% 1px;background-repeat:no-repeat;padding-bottom:12px;margin-bottom:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3$1.TranslatePipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None });
2987
+ }
2988
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoCompletePopupMenu, decorators: [{
2989
+ type: Component,
2990
+ args: [{ selector: 'ntybase-auto-complete-popup-menu', imports: [CommonModule, ReactiveFormsModule, MatIconModule, TranslateModule], encapsulation: ViewEncapsulation.None, template: "<div class=\"dialogbase\">\n <div class=\"menu\">\n <ul class=\"list-group\">\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"copyToClipboard()\" class=\"align-line\"\n ><mat-icon>content_copy</mat-icon>{{'@Copy'| translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"gotoMain()\" class=\"align-line\"\n ><mat-icon>edit</mat-icon>{{'@PopupGotoRecordDefinition'|\n translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center menu-item\"\n >\n <span (click)=\"gotoNew()\" class=\"align-line\"\n ><mat-icon>add</mat-icon>{{'@PopupNewRecord'| translate}}</span\n >\n </li>\n <li\n class=\"list-group-item list-group-item-action d-flex justify-content-between align-items-center\"\n [ngClass]=\"{'menu-item':!fieldDisabled , 'menu-item-disabled':fieldDisabled }\"\n >\n <span (click)=\"gotoLookup()\" class=\"align-line\"\n ><mat-icon>search</mat-icon>{{'@PopupSelectFromList'|translate}}</span\n >\n </li>\n </ul>\n </div>\n</div>\n", styles: [".dialogbase{background:#0000001a;padding:0;border-radius:12px;overflow:hidden;box-shadow:0 4px 20px #00000026}.menu{background:var(--mat-sys-primary-container);padding:0;display:flex;border-radius:inherit}.align-line{display:flex;vertical-align:middle;align-items:center;gap:12px;padding:0 8px}.menu-item{padding:12px 16px;display:flex;vertical-align:middle;cursor:pointer;transition:all .2s ease-out;border-radius:8px;margin:4px;color:var(--mat-sys-on-primary-container)}.menu-item:hover{background:var(--mat-sys-on-primary-fixed);color:var(--mat-sys-on-primary);transform:translateY(-2px);box-shadow:0 2px 8px #0000001a}.menu-item:hover .mat-icon{color:var(--mat-sys-on-primary);transform:scale(1.05)}.menu-item-disabled,.menu-item-disabled:hover{background:var(--mat-sys-on-primary-fixed);color:var(--mat-sys-on-primary);font-style:italic;opacity:.7;cursor:not-allowed}.menu-item-disabled .mat-icon,.menu-item-disabled:hover .mat-icon{color:var(--mat-sys-on-primary)}.mat-icon{transition:all .2s ease-out;font-size:20px;width:20px;height:20px}.list-group{list-style:none;padding:8px;margin:0;width:100%}.list-group-item{background-position:bottom;background-size:100% 1px;background-repeat:no-repeat;padding-bottom:12px;margin-bottom:4px}\n"] }]
2991
+ }], propDecorators: { field: [{
2992
+ type: Input
2993
+ }], recordGuid: [{
2994
+ type: Input
2995
+ }], fieldDisabled: [{
2996
+ type: Input
2997
+ }], componentPath: [{
2998
+ type: Input
2999
+ }], inputValue: [{
3000
+ type: Input
3001
+ }] } });
3002
+
3003
+ class SelectionItem {
3004
+ name = null;
3005
+ value = null;
3006
+ constructor(_value = null, _name = null) {
3007
+ this.name = _name;
3008
+ this.value = _value;
3009
+ }
3010
+ }
3011
+ class AutoComplete extends UiBase {
3012
+ // Input parameters
3013
+ tableName = input('', ...(ngDevMode ? [{ debugName: "tableName" }] : []));
3014
+ searchAfter = input(3, ...(ngDevMode ? [{ debugName: "searchAfter" }] : []));
3015
+ fieldCode = input(null, ...(ngDevMode ? [{ debugName: "fieldCode" }] : []));
3016
+ fieldName = input(null, ...(ngDevMode ? [{ debugName: "fieldName" }] : []));
3017
+ lookupComponent = input(null, ...(ngDevMode ? [{ debugName: "lookupComponent" }] : []));
3018
+ lookupComponentPath = input(null, ...(ngDevMode ? [{ debugName: "lookupComponentPath" }] : []));
3019
+ filterField = input(null, ...(ngDevMode ? [{ debugName: "filterField" }] : []));
3020
+ filterFieldValue = input(null, ...(ngDevMode ? [{ debugName: "filterFieldValue" }] : []));
3021
+ filterFieldNumeric = input(false, ...(ngDevMode ? [{ debugName: "filterFieldNumeric" }] : []));
3022
+ filterFieldEquality = input('=', ...(ngDevMode ? [{ debugName: "filterFieldEquality" }] : []));
3023
+ debounceTime = input(500, ...(ngDevMode ? [{ debugName: "debounceTime" }] : []));
3024
+ // Inject dependencies
3025
+ alertService = inject(AlertService);
3026
+ autoCompleteService = inject(AutoCompleteProxy);
3027
+ dialog = inject(MatDialog);
3028
+ autoCompleteTrigger;
3029
+ inputField;
3030
+ control = new FormControl();
3031
+ matcher = new ErrorStateMatcher();
3032
+ // Signal parameters
3033
+ searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
3034
+ selectedOption = signal(null, ...(ngDevMode ? [{ debugName: "selectedOption" }] : []));
3035
+ loadedOptions = signal([], ...(ngDevMode ? [{ debugName: "loadedOptions" }] : []));
3036
+ searchSignal = signal('', ...(ngDevMode ? [{ debugName: "searchSignal" }] : []));
3037
+ debounceTimeout;
3038
+ constructor() {
3039
+ super();
3040
+ // Sync form control with selected option
3041
+ effect(() => {
3042
+ const option = this.selectedOption();
3043
+ if (option) {
3044
+ this.searchText.set(option.name || '');
3045
+ }
3046
+ else if (this.control.value && !this.selectedOption()) {
3047
+ const found = this.loadedOptions().find((opt) => opt.value === this.control.value);
3048
+ if (found) {
3049
+ this.selectedOption.set(found);
3050
+ this.searchText.set(found.name || '');
3051
+ }
3052
+ }
3053
+ });
3054
+ // Then watch the debounced signal:
3055
+ effect((onCleanup) => {
3056
+ const searchValue = this.searchSignal();
3057
+ clearTimeout(this.debounceTimeout);
3058
+ this.debounceTimeout = setTimeout(() => {
3059
+ if (this.shouldTriggerSearch(searchValue)) {
3060
+ this.loadAutoCompleteData(searchValue);
3061
+ }
3062
+ else {
3063
+ this.loadedOptions.set([]);
3064
+ }
3065
+ }, this.debounceTime());
3066
+ onCleanup(() => {
3067
+ clearTimeout(this.debounceTimeout);
3068
+ });
3069
+ });
3070
+ effect(() => {
3071
+ this.onChange(this.value());
3072
+ });
3073
+ effect(() => {
3074
+ this.updateValidators();
3075
+ });
3076
+ }
3077
+ shouldTriggerSearch(searchValue) {
3078
+ return searchValue.length >= this.searchAfter();
3079
+ }
3080
+ filteredData = computed(() => {
3081
+ return this.loadedOptions();
3082
+ }, ...(ngDevMode ? [{ debugName: "filteredData" }] : []));
3083
+ onInputChanged(event) {
3084
+ const value = event.target.value;
3085
+ this.searchText.set(value);
3086
+ this.onTouched();
3087
+ // Clear selection if text doesn't match
3088
+ if (this.selectedOption() && this.selectedOption()?.name !== value) {
3089
+ this.selectedOption.set(null);
3090
+ }
3091
+ if (value.length === 0) {
3092
+ this.clearInput();
3093
+ return;
3094
+ }
3095
+ this.searchSignal.set(value);
3096
+ this.autoCompleteTrigger.closePanel();
3097
+ }
3098
+ loadAutoCompleteData(searchValue) {
3099
+ this.loadedOptions.set([]);
3100
+ const filter = new NtyAutoCompleteFilter();
3101
+ filter.searchString =
3102
+ searchValue.trim() === '' && searchValue.length >= this.searchAfter()
3103
+ ? ''
3104
+ : searchValue;
3105
+ filter.fields = [];
3106
+ if (this.filterField() && this.filterField().trim().length > 0) {
3107
+ const fields = {
3108
+ equalitySign: this.filterFieldEquality(),
3109
+ fieldName: this.filterField(),
3110
+ fieldValue: this.filterFieldValue(),
3111
+ numeric: this.filterFieldNumeric(),
3112
+ };
3113
+ filter.fields?.push(fields);
3114
+ }
3115
+ this.autoCompleteService
3116
+ .getAutocompleteDataFilter(this.tableName(), filter)
3117
+ .subscribe({
3118
+ next: (data) => {
3119
+ if (data && Array.isArray(data)) {
3120
+ const options = data.map((item) => ({
3121
+ name: item.text,
3122
+ value: item.guidValue,
3123
+ }));
3124
+ this.loadedOptions.set(options);
3125
+ if (options.length === 1) {
3126
+ this.selectedOption.set(options[0]);
3127
+ this.control.setValue(options[0].value);
3128
+ this.searchText.set(options[0].name || '');
3129
+ }
3130
+ else if (options.length > 1) {
3131
+ this.autoCompleteTrigger.openPanel();
3132
+ }
3133
+ }
3134
+ },
3135
+ error: (err) => {
3136
+ this.alertService.showAlert(err.message);
3137
+ },
3138
+ });
3139
+ }
3140
+ clearInput() {
3141
+ this.selectedOption.set(new SelectionItem(Guid.empty, ''));
3142
+ this.control.reset();
3143
+ this.searchText.set('');
3144
+ this.loadedOptions.set([]);
3145
+ this.inputField.nativeElement.value = '';
3146
+ }
3147
+ onPaste(event) {
3148
+ const clipboardData = event.clipboardData;
3149
+ if (!clipboardData)
3150
+ return;
3151
+ const pastedText = clipboardData.getData('text');
3152
+ this.searchText.set(pastedText);
3153
+ this.searchSignal.set(pastedText);
3154
+ }
3155
+ displayFn(item) {
3156
+ return this.searchText();
3157
+ }
3158
+ optionSelected(event) {
3159
+ const selectedValue = event.option.value;
3160
+ const selected = this.loadedOptions().find((opt) => opt.value === selectedValue);
3161
+ if (selected) {
3162
+ this.selectedOption.set(selected);
3163
+ this.control.setValue(selected.value);
3164
+ this.searchText.set(selected.name || '');
3165
+ }
3166
+ }
3167
+ writeValue(value) {
3168
+ this.value.set(value);
3169
+ if (!value)
3170
+ return;
3171
+ if (this.fieldCode()) {
3172
+ this.selectedOption.set({ value, name: this.fieldCode() });
3173
+ }
3174
+ }
3175
+ onLookup() {
3176
+ if (!this.lookupComponent())
3177
+ return;
3178
+ const dialogRef = this.dialog.open(AutoCompleteLookup, {
3179
+ height: 'auto',
3180
+ width: '100%',
3181
+ maxWidth: '100%',
3182
+ maxHeight: 'auto',
3183
+ data: {
3184
+ component: this.lookupComponent(),
3185
+ ...(this.filterField() && {
3186
+ filterField: this.filterField(),
3187
+ filterFieldValue: this.filterFieldValue(),
3188
+ filterFieldEquality: this.filterFieldEquality(),
3189
+ filterFieldNumeric: this.filterFieldNumeric(),
3190
+ }),
3191
+ },
3192
+ });
3193
+ dialogRef.componentInstance.selectedElement.subscribe((data) => {
3194
+ if (data) {
3195
+ const selection = new SelectionItem(data.value, data.text);
3196
+ this.selectedOption.set(selection);
3197
+ this.control.setValue(selection.value);
3198
+ this.searchText.set(selection.name || '');
3199
+ }
3200
+ dialogRef.close();
3201
+ });
3202
+ }
3203
+ /** Popup screen to goto main table
3204
+ *
3205
+ * @param event
3206
+ * @returns
3207
+ */
3208
+ rightClick(event) {
3209
+ event.stopPropagation();
3210
+ const dialogConfig = new MatDialogConfig();
3211
+ dialogConfig.position = {
3212
+ top: event.clientY + 'px',
3213
+ left: event.clientX + 'px',
3214
+ };
3215
+ dialogConfig.height = 'auto';
3216
+ dialogConfig.width = 'auto';
3217
+ dialogConfig.panelClass = 'foo';
3218
+ const dialogRef = this.dialog.open(AutoCompletePopupMenu, dialogConfig);
3219
+ dialogRef.componentInstance.field = this.tableName();
3220
+ dialogRef.componentInstance.recordGuid = this.value();
3221
+ dialogRef.componentInstance.fieldDisabled = this.disabled();
3222
+ dialogRef.componentInstance.componentPath = this.lookupComponentPath();
3223
+ dialogRef.componentInstance.inputValue = this.searchText();
3224
+ dialogRef.componentInstance.Result.subscribe((result) => {
3225
+ switch (result) {
3226
+ case 'Lookup':
3227
+ dialogRef.close();
3228
+ this.onLookup();
3229
+ break;
3230
+ default:
3231
+ dialogRef.close();
3232
+ break;
3233
+ }
3234
+ });
3235
+ return false;
3236
+ }
3237
+ // Validation
3238
+ getErrorMessage() {
3239
+ if (this.control.hasError('required') && this.errorMessages()['required']) {
3240
+ return this.errorMessages()['required'];
3241
+ }
3242
+ return '';
3243
+ }
3244
+ updateValidators() {
3245
+ const validators = [];
3246
+ if (this.required()) {
3247
+ validators.push(Validators.required);
3248
+ }
3249
+ this.control.setValidators(validators);
3250
+ this.control.updateValueAndValidity();
3251
+ }
3252
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoComplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
3253
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.7", type: AutoComplete, isStandalone: true, selector: "ntybase-auto-complete", inputs: { tableName: { classPropertyName: "tableName", publicName: "tableName", isSignal: true, isRequired: false, transformFunction: null }, searchAfter: { classPropertyName: "searchAfter", publicName: "searchAfter", isSignal: true, isRequired: false, transformFunction: null }, fieldCode: { classPropertyName: "fieldCode", publicName: "fieldCode", isSignal: true, isRequired: false, transformFunction: null }, fieldName: { classPropertyName: "fieldName", publicName: "fieldName", isSignal: true, isRequired: false, transformFunction: null }, lookupComponent: { classPropertyName: "lookupComponent", publicName: "lookupComponent", isSignal: true, isRequired: false, transformFunction: null }, lookupComponentPath: { classPropertyName: "lookupComponentPath", publicName: "lookupComponentPath", isSignal: true, isRequired: false, transformFunction: null }, filterField: { classPropertyName: "filterField", publicName: "filterField", isSignal: true, isRequired: false, transformFunction: null }, filterFieldValue: { classPropertyName: "filterFieldValue", publicName: "filterFieldValue", isSignal: true, isRequired: false, transformFunction: null }, filterFieldNumeric: { classPropertyName: "filterFieldNumeric", publicName: "filterFieldNumeric", isSignal: true, isRequired: false, transformFunction: null }, filterFieldEquality: { classPropertyName: "filterFieldEquality", publicName: "filterFieldEquality", isSignal: true, isRequired: false, transformFunction: null }, debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
3254
+ {
3255
+ provide: NG_VALUE_ACCESSOR,
3256
+ useExisting: forwardRef(() => AutoComplete),
3257
+ multi: true,
3258
+ },
3259
+ ], viewQueries: [{ propertyName: "autoCompleteTrigger", first: true, predicate: MatAutocompleteTrigger, descendants: true }, { propertyName: "inputField", first: true, predicate: ["autoCompleteInput"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<mat-form-field\n class=\"ui-full-width\"\n [appearance]=\"appearance()\"\n [class.required-field]=\"required()\"\n [class.required-with-value]=\"required() && value()\"\n>\n <mat-label *ngIf=\"label()\">{{ label() }}</mat-label>\n\n <input\n type=\"text\"\n #autoCompleteInput\n matInput\n [(ngModel)]=\"value\"\n [matAutocomplete]=\"auto\"\n (input)=\"onInputChanged($event)\"\n (paste)=\"onPaste($event)\"\n [errorStateMatcher]=\"matcher\"\n [formControl]=\"control\"\n (contextmenu)=\"rightClick($event)\"\n />\n\n <button\n mat-icon-button\n matSuffix\n *ngIf=\"value() && !disabled()\"\n (click)=\"clearInput()\"\n aria-label=\"Clear\"\n class=\"clear-btn number-clear-btn\"\n >\n <mat-icon>cancel</mat-icon>\n </button>\n\n <button\n mat-icon-button\n matSuffix\n aria-label=\"Search\"\n (click)=\"onLookup()\"\n class=\"search-btn\"\n >\n <mat-icon>search</mat-icon>\n </button>\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [displayWith]=\"displayFn.bind(this)\"\n (optionSelected)=\"optionSelected($event)\"\n >\n <mat-option *ngFor=\"let option of filteredData()\" [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n </mat-autocomplete>\n\n <mat-error>{{ getErrorMessage() }}</mat-error>\n</mat-form-field>\n", styles: ["::ng-deep .ui-full-width{width:100%;max-width:500px}::ng-deep .mat-mdc-form-field-subscript-wrapper{width:auto;height:0}::ng-deep .clear-btn{background:none;border:none;box-shadow:none;cursor:pointer}::ng-deep .search-icon{background:none;border:none;box-shadow:none;opacity:1;cursor:pointer}::ng-deep .search-icon:hover,::ng-deep .clear-btn:hover{color:#f97a00}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1$4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i1$4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i3$6.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i3$6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i3$6.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i1$4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3260
+ }
3261
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AutoComplete, decorators: [{
3262
+ type: Component,
3263
+ args: [{ selector: 'ntybase-auto-complete', imports: [
3264
+ MatFormFieldModule,
3265
+ CommonModule,
3266
+ MatAutocompleteModule,
3267
+ FormsModule,
3268
+ MatInputModule,
3269
+ MatIconModule,
3270
+ ReactiveFormsModule,
3271
+ ], providers: [
3272
+ {
3273
+ provide: NG_VALUE_ACCESSOR,
3274
+ useExisting: forwardRef(() => AutoComplete),
3275
+ multi: true,
3276
+ },
3277
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<mat-form-field\n class=\"ui-full-width\"\n [appearance]=\"appearance()\"\n [class.required-field]=\"required()\"\n [class.required-with-value]=\"required() && value()\"\n>\n <mat-label *ngIf=\"label()\">{{ label() }}</mat-label>\n\n <input\n type=\"text\"\n #autoCompleteInput\n matInput\n [(ngModel)]=\"value\"\n [matAutocomplete]=\"auto\"\n (input)=\"onInputChanged($event)\"\n (paste)=\"onPaste($event)\"\n [errorStateMatcher]=\"matcher\"\n [formControl]=\"control\"\n (contextmenu)=\"rightClick($event)\"\n />\n\n <button\n mat-icon-button\n matSuffix\n *ngIf=\"value() && !disabled()\"\n (click)=\"clearInput()\"\n aria-label=\"Clear\"\n class=\"clear-btn number-clear-btn\"\n >\n <mat-icon>cancel</mat-icon>\n </button>\n\n <button\n mat-icon-button\n matSuffix\n aria-label=\"Search\"\n (click)=\"onLookup()\"\n class=\"search-btn\"\n >\n <mat-icon>search</mat-icon>\n </button>\n <mat-autocomplete\n #auto=\"matAutocomplete\"\n [displayWith]=\"displayFn.bind(this)\"\n (optionSelected)=\"optionSelected($event)\"\n >\n <mat-option *ngFor=\"let option of filteredData()\" [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n </mat-autocomplete>\n\n <mat-error>{{ getErrorMessage() }}</mat-error>\n</mat-form-field>\n", styles: ["::ng-deep .ui-full-width{width:100%;max-width:500px}::ng-deep .mat-mdc-form-field-subscript-wrapper{width:auto;height:0}::ng-deep .clear-btn{background:none;border:none;box-shadow:none;cursor:pointer}::ng-deep .search-icon{background:none;border:none;box-shadow:none;opacity:1;cursor:pointer}::ng-deep .search-icon:hover,::ng-deep .clear-btn:hover{color:#f97a00}\n"] }]
3278
+ }], ctorParameters: () => [], propDecorators: { autoCompleteTrigger: [{
3279
+ type: ViewChild,
3280
+ args: [MatAutocompleteTrigger]
3281
+ }], inputField: [{
3282
+ type: ViewChild,
3283
+ args: ['autoCompleteInput']
3284
+ }] } });
3285
+
3286
+ /*
3287
+ * Public API Surface of ntybase
3288
+ */
3289
+
3290
+ /**
3291
+ * Generated bundle index. Do not edit.
3292
+ */
3293
+
3294
+ export { AgGridBase, AgGridSaveBase, AlertService, AuthenticationGuard, AuthenticationInterceptor, AuthenticationService, AutoComplete, ButtonRenderer, CanDeactivateGuard, CheckboxRenderer, ColorPalette, CommonService, ConfirmDialog, CredentialsService, CurrentUserPreference, ForgotPassword, Guid, HttpError403, HttpError404, LeftSidenav, Login, LoginDto, MFACodeDto, MfaLogin, NettyAgGridService, NettyHelper, NettyMenuService, Ntybase, NtybaseModule, SelectionItem, Theme, Toolbar, UrlHelperService };
3295
+ //# sourceMappingURL=nettyapps-ntybase.mjs.map