@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.
- package/README.md +166 -0
- package/fesm2022/nettyapps-ntybase.mjs +3295 -0
- package/fesm2022/nettyapps-ntybase.mjs.map +1 -0
- package/index.d.ts +882 -0
- package/nettyapps-ntybase-0.0.1.tgz +0 -0
- package/package.json +23 -0
|
@@ -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>© 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>© 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
|