@nettyapps/ntybase 21.1.39 → 21.1.41

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.
@@ -12,12 +12,12 @@ import * as i1$2 from '@angular/material/snack-bar';
12
12
  import { MatSnackBarModule } from '@angular/material/snack-bar';
13
13
  import * as i1$1 from '@ngx-translate/core';
14
14
  import { TranslateModule, TranslateService } from '@ngx-translate/core';
15
+ import * as i1$4 from '@angular/common';
15
16
  import { CommonModule, DatePipe, Location, DecimalPipe } from '@angular/common';
16
17
  import { Title } from '@angular/platform-browser';
17
18
  import * as i2$2 from '@nettyapps/ntycontract';
18
19
  import { injectNettyStandardFilterProxy, EnvironmentProxy, injectNettyStandardProxy, injectNettyStandardLogProxy } from '@nettyapps/ntycontract';
19
20
  import { I18nService } from '@nettyapps/ntyi18n';
20
- import { ExcelLogViewer } from '@nettyapps/ntyux';
21
21
  import { Router, ActivatedRoute } from '@angular/router';
22
22
  import { ModuleRegistry, AllCommunityModule, ClientSideRowModelModule, HighlightChangesModule, TextFilterModule, NumberFilterModule, DateFilterModule } from 'ag-grid-community';
23
23
  import { themeQuartz, StatusBarModule, ClipboardModule, ExcelExportModule, ColumnMenuModule, ContextMenuModule, CellSelectionModule, RowSelectionModule, SetFilterModule, MultiFilterModule } from 'ag-grid-enterprise';
@@ -29,9 +29,11 @@ import * as i1$3 from '@angular/common/http';
29
29
  import { HttpClient, HttpErrorResponse, HttpResponse, HTTP_INTERCEPTORS, HttpHeaders } from '@angular/common/http';
30
30
  import { Mutex } from 'async-mutex';
31
31
  import { toSignal } from '@angular/core/rxjs-interop';
32
+ import * as i1$5 from '@angular/forms';
33
+ import { FormsModule, FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
34
+ import { AgGridAngular } from 'ag-grid-angular';
35
+ import { NettyUIButton, NettyUIFilterButton } from '@nettyapps/ntyui';
32
36
  import { catchError as catchError$1, map as map$1, take, finalize as finalize$1 } from 'rxjs/operators';
33
- import * as i1$4 from '@angular/forms';
34
- import { FormBuilder, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
35
37
  import * as i3$1 from '@angular/material/input';
36
38
  import { MatInputModule } from '@angular/material/input';
37
39
  import { MatFormFieldModule } from '@angular/material/form-field';
@@ -2149,1397 +2151,1409 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
2149
2151
  args: [{ selector: 'ntybase-ag-grid-list-base', imports: [], template: ``, host: { 'ntybase-id': 'NettyAgGridListBase' } }]
2150
2152
  }], ctorParameters: () => [] });
2151
2153
 
2152
- class ExcelImportBase extends NettyAgGridListBase {
2153
- isFileSelectionHidden = signal(false, ...(ngDevMode ? [{ debugName: "isFileSelectionHidden" }] : []));
2154
- isFileValid = signal(false, ...(ngDevMode ? [{ debugName: "isFileValid" }] : []));
2155
- logs = signal([], ...(ngDevMode ? [{ debugName: "logs" }] : []));
2156
- hasLogs = computed(() => this.logs().length > 0, ...(ngDevMode ? [{ debugName: "hasLogs" }] : []));
2157
- logDialog = inject(MatDialog);
2158
- parser = new ExcelParser([]);
2159
- // override methods
2160
- onBtnClick(e) { }
2161
- loadData() { }
2162
- // Methods
2163
- onFilesSelected(evt) {
2164
- const files = Array.isArray(evt) ? evt : evt.target.files;
2165
- if (!files || files.length !== 1) {
2166
- return;
2167
- }
2168
- const file = files[0];
2169
- if (file) {
2170
- this.recordList.set([]);
2171
- this.logs.set([]);
2172
- this.parser.parse(file).then(result => {
2173
- this.recordList.set(result.data);
2174
- this.logs.set(result.logs);
2175
- this.isFileSelectionHidden.set(true);
2176
- if (result.logs.length > 0)
2177
- this.showLogs();
2178
- });
2179
- }
2180
- }
2181
- downloadSampleExcel() {
2182
- this.parser.generateSampleExcel();
2183
- }
2184
- showLogs() {
2185
- const dialogRef = this.logDialog.open(ExcelLogViewer, {
2186
- height: '70%',
2187
- width: '90%',
2188
- maxWidth: '100vw',
2189
- maxHeight: '100vh'
2190
- });
2191
- dialogRef.componentInstance.setParseData(this.logs());
2192
- dialogRef.componentInstance.selectedElement.subscribe((element) => {
2193
- dialogRef.close();
2194
- });
2195
- }
2196
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ExcelImportBase, deps: null, target: i0.ɵɵFactoryTarget.Component });
2197
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: ExcelImportBase, isStandalone: true, selector: "ntybase-excel-import-base", usesInheritance: true, ngImport: i0, template: ``, isInline: true });
2198
- }
2199
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ExcelImportBase, decorators: [{
2200
- type: Component,
2201
- args: [{ selector: 'ntybase-excel-import-base', imports: [], template: `` }]
2202
- }] });
2203
-
2204
- class Ntybase {
2205
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: Ntybase, deps: [], target: i0.ɵɵFactoryTarget.Component });
2206
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: Ntybase, isStandalone: false, selector: "lib-ntybase", ngImport: i0, template: `
2207
- <p>
2208
- ntybase works!
2209
- </p>
2210
- `, isInline: true, styles: [""] });
2211
- }
2212
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: Ntybase, decorators: [{
2213
- type: Component,
2214
- args: [{ selector: 'lib-ntybase', standalone: false, template: `
2215
- <p>
2216
- ntybase works!
2217
- </p>
2218
- ` }]
2219
- }] });
2220
-
2221
- const credentialsKey = 'credentials';
2222
- class CredentialsService {
2223
- _credentials = null;
2224
- constructor() {
2225
- const savedCredentials = sessionStorage.getItem(credentialsKey) ||
2226
- localStorage.getItem(credentialsKey);
2227
- if (savedCredentials) {
2228
- this._credentials = JSON.parse(savedCredentials);
2229
- }
2230
- }
2231
- /**
2232
- * Checks is the user is authenticated.
2233
- * @return True if the user is authenticated.
2234
- */
2235
- isAuthenticated() {
2236
- return !!this.credentials;
2237
- }
2238
- /**
2239
- * Gets the user credentials.
2240
- * @return The user credentials or null if the user is not authenticated.
2241
- */
2242
- get credentials() {
2243
- return this._credentials;
2244
- }
2245
- get token() {
2246
- return this._credentials?.token ?? null;
2247
- }
2248
- /**
2249
- * Sets the user credentials.
2250
- * The credentials may be persisted across sessions by setting the `remember` parameter to true.
2251
- * Otherwise, the credentials are only persisted for the current session.
2252
- * @param credentials The user credentials.
2253
- * @param remember True to remember credentials across sessions.
2254
- */
2255
- setCredentials(credentials, remember) {
2256
- this._credentials = credentials || null;
2257
- if (credentials) {
2258
- const storage = remember ? localStorage : sessionStorage;
2259
- storage.setItem(credentialsKey, JSON.stringify(credentials));
2260
- }
2261
- else {
2262
- sessionStorage.removeItem(credentialsKey);
2263
- localStorage.removeItem(credentialsKey);
2264
- }
2265
- }
2266
- /** Get Credentials
2267
- *
2268
- * @returns
2269
- */
2270
- getCredentials() {
2271
- let _credentialsString = sessionStorage.getItem(credentialsKey);
2272
- if (_credentialsString == null || _credentialsString == '') {
2273
- _credentialsString = localStorage.getItem(credentialsKey);
2274
- }
2275
- return JSON.parse(_credentialsString ?? '');
2276
- }
2277
- /** Get the token if available otherwise return empty string
2278
- *
2279
- * @returns
2280
- */
2281
- getToken() {
2282
- try {
2283
- let _credentials = this.getCredentials();
2284
- return _credentials.token;
2285
- }
2286
- catch (error) {
2287
- return '';
2288
- }
2154
+ class RangeNumberFilter {
2155
+ params;
2156
+ filterText = '';
2157
+ conditions = [];
2158
+ agInit(params) {
2159
+ this.params = params;
2289
2160
  }
2290
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CredentialsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2291
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CredentialsService, providedIn: 'root' });
2292
- }
2293
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CredentialsService, decorators: [{
2294
- type: Injectable,
2295
- args: [{
2296
- providedIn: 'root',
2297
- }]
2298
- }], ctorParameters: () => [] });
2299
-
2300
- class UrlHelperService {
2301
- router = inject(Router);
2302
- cleanUrl(url) {
2303
- let result = url
2304
- .replace('/mfalogin?redirect=', '')
2305
- .replace('/mfalogin?redirect=', '')
2306
- .replace('/mfalogin?redirect=', '')
2307
- .replace('/login?redirect=', '')
2308
- .replace('/login?redirect=', '')
2309
- .replace('/login?redirect=', '')
2310
- .replace(new RegExp('%25', 'g'), '%')
2311
- .replace(new RegExp('%25', 'g'), '%')
2312
- .replace(new RegExp('%25', 'g'), '%')
2313
- .replace(new RegExp('%25', 'g'), '%')
2314
- .replace(new RegExp('%25', 'g'), '%')
2315
- .replace(new RegExp('%25', 'g'), '%')
2316
- .replace(new RegExp('%22', 'g'), '"')
2317
- .replace(new RegExp('%3F', 'g'), '?')
2318
- .replace(new RegExp('%3D', 'g'), '=')
2319
- .replace(new RegExp('%2F', 'g'), '/')
2320
- .replace(new RegExp('%26', 'g'), '&')
2321
- .replace(new RegExp('/mfalogin?redirect=', 'g'), '')
2322
- .replace(new RegExp('/login?redirect=', 'g'), '');
2323
- console.log('url:', url);
2324
- console.log('result:', result);
2325
- // return url;
2326
- return result;
2161
+ onFilterTextChanged(event) {
2162
+ this.filterText = event.target.value;
2163
+ this.parseFilterText();
2164
+ this.params.filterChangedCallback();
2327
2165
  }
2328
- navigate(url) {
2329
- let urlParts = url.split('?');
2330
- if (urlParts.length == 1) {
2331
- return this.router.navigate(urlParts, { replaceUrl: true });
2166
+ parseFilterText() {
2167
+ this.conditions = [];
2168
+ if (!this.filterText.trim()) {
2169
+ return;
2332
2170
  }
2333
- let parameters = urlParts[1].split('&');
2334
- return this.router.navigate([urlParts[0]], {
2335
- queryParams: {
2336
- parameters: parameters[0]?.replace('parameters=', '') ?? '',
2337
- type: parameters[1]?.replace('type=', '') ?? '',
2338
- },
2339
- replaceUrl: true,
2340
- });
2341
- }
2342
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: UrlHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2343
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: UrlHelperService, providedIn: 'root' });
2344
- }
2345
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: UrlHelperService, decorators: [{
2346
- type: Injectable,
2347
- args: [{
2348
- providedIn: 'root',
2349
- }]
2350
- }] });
2351
-
2352
- class AuthenticationInterceptor {
2353
- router = inject(Router);
2354
- credentialsService = inject(CredentialsService);
2355
- environmentProxy = inject(EnvironmentProxy);
2356
- urlHelperService = inject(UrlHelperService);
2357
- intercept(req, next) {
2358
- if (req.headers.get('No-Auth') == 'True')
2359
- return next.handle(req.clone());
2360
- let token = this.credentialsService.token;
2361
- if (token != null) {
2362
- let appName = this.environmentProxy.getApplicationName();
2363
- const clonedreq = req.clone({
2364
- headers: req.headers.set('Authorization', 'Bearer ' + token),
2365
- // .set("NettyAppName",appName)
2366
- });
2367
- return next.handle(clonedreq).pipe(catchError$1((error) => {
2368
- if (error instanceof HttpErrorResponse && error.status === 401) {
2369
- // Handle 401 error, e.g., navigate to the login page
2370
- this.router.navigate(['/login'], {
2371
- queryParams: {
2372
- redirect: this.urlHelperService.cleanUrl(this.router.url),
2373
- },
2374
- replaceUrl: true,
2375
- });
2376
- // Return an observable with a successful response
2377
- return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
2378
- }
2379
- if (error instanceof HttpErrorResponse && error.status === 403) {
2380
- this.router.navigate(['/forbidden'], {
2381
- state: { attemptedUrl: this.router.url }, // Orijinal URL'i state olarak geçme
2382
- });
2383
- return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
2384
- }
2385
- if (error instanceof HttpErrorResponse && error.status === 428) {
2386
- // Handle 428 error, e.g., navigate to the login page
2387
- this.router.navigate(['/mfalogin'], {
2388
- queryParams: {
2389
- redirect: this.urlHelperService.cleanUrl(this.router.url),
2390
- },
2391
- replaceUrl: true,
2392
- });
2393
- // Return an observable with a successful response
2394
- return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
2171
+ const parts = this.filterText
2172
+ .split(',')
2173
+ .map((part) => part.trim())
2174
+ .filter((part) => part !== '');
2175
+ for (const part of parts) {
2176
+ // Hariç tutma kontrolü (! ile başlıyor mu)
2177
+ if (part.startsWith('!')) {
2178
+ const excludePart = part.substring(1).trim();
2179
+ // Hariç tutma için aralık kontrolü
2180
+ if (excludePart.includes('..')) {
2181
+ const rangeParts = excludePart.split('..');
2182
+ if (rangeParts.length === 2) {
2183
+ const minStr = rangeParts[0].trim();
2184
+ const maxStr = rangeParts[1].trim();
2185
+ // !..10 formatı (max hariç)
2186
+ if (minStr === '' && maxStr !== '') {
2187
+ const max = Number(maxStr);
2188
+ if (!isNaN(max)) {
2189
+ this.conditions.push({
2190
+ type: 'exclude',
2191
+ min: -Infinity,
2192
+ max: max,
2193
+ });
2194
+ }
2195
+ }
2196
+ // !10.. formatı (min hariç)
2197
+ else if (minStr !== '' && maxStr === '') {
2198
+ const min = Number(minStr);
2199
+ if (!isNaN(min)) {
2200
+ this.conditions.push({
2201
+ type: 'exclude',
2202
+ min: min,
2203
+ max: Infinity,
2204
+ });
2205
+ }
2206
+ }
2207
+ // !min..max formatı (aralık hariç)
2208
+ else if (minStr !== '' && maxStr !== '') {
2209
+ const min = Number(minStr);
2210
+ const max = Number(maxStr);
2211
+ if (!isNaN(min) && !isNaN(max)) {
2212
+ this.conditions.push({
2213
+ type: 'exclude',
2214
+ min: Math.min(min, max),
2215
+ max: Math.max(min, max),
2216
+ });
2217
+ }
2218
+ }
2219
+ }
2395
2220
  }
2396
- // For other errors, re-throw the error to propagate it further
2397
- return throwError(() => error);
2398
- }));
2399
- }
2400
- else {
2401
- return next.handle(req.clone()).pipe(catchError$1((error) => {
2402
- if (error instanceof HttpErrorResponse && error.status === 401) {
2403
- // Handle 401 error, e.g., navigate to the login page
2404
- this.router.navigate(['/login'], {
2405
- queryParams: {
2406
- redirect: this.urlHelperService.cleanUrl(this.router.url),
2407
- },
2408
- replaceUrl: true,
2409
- });
2410
- // Return an observable with a successful response
2411
- return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
2221
+ else {
2222
+ // Tek değer hariç tutma
2223
+ const value = Number(excludePart);
2224
+ if (!isNaN(value)) {
2225
+ this.conditions.push({
2226
+ type: 'exclude',
2227
+ value: value,
2228
+ });
2229
+ }
2412
2230
  }
2413
- if (error instanceof HttpErrorResponse && error.status === 403) {
2414
- this.router.navigate(['/forbidden'], {
2415
- state: { attemptedUrl: this.router.url }, // Orijinal URL'i state olarak geçme
2416
- });
2417
- return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
2231
+ }
2232
+ // Aralık kontrolü (.. içeriyor mu)
2233
+ else if (part.includes('..')) {
2234
+ const rangeParts = part.split('..');
2235
+ if (rangeParts.length === 2) {
2236
+ const minStr = rangeParts[0].trim();
2237
+ const maxStr = rangeParts[1].trim();
2238
+ // ..10 formatı (max belirtilmiş)
2239
+ if (minStr === '' && maxStr !== '') {
2240
+ const max = Number(maxStr);
2241
+ if (!isNaN(max)) {
2242
+ this.conditions.push({
2243
+ type: 'range',
2244
+ min: -Infinity,
2245
+ max: max,
2246
+ });
2247
+ }
2248
+ }
2249
+ // 10.. formatı (min belirtilmiş)
2250
+ else if (minStr !== '' && maxStr === '') {
2251
+ const min = Number(minStr);
2252
+ if (!isNaN(min)) {
2253
+ this.conditions.push({
2254
+ type: 'range',
2255
+ min: min,
2256
+ max: Infinity,
2257
+ });
2258
+ }
2259
+ }
2260
+ // min..max formatı
2261
+ else if (minStr !== '' && maxStr !== '') {
2262
+ const min = Number(minStr);
2263
+ const max = Number(maxStr);
2264
+ if (!isNaN(min) && !isNaN(max)) {
2265
+ this.conditions.push({
2266
+ type: 'range',
2267
+ min: Math.min(min, max),
2268
+ max: Math.max(min, max),
2269
+ });
2270
+ }
2271
+ }
2418
2272
  }
2419
- if (error instanceof HttpErrorResponse && error.status === 428) {
2420
- // Handle 428 error, e.g., navigate to the login page
2421
- this.router.navigate(['/mfalogin'], {
2422
- queryParams: {
2423
- redirect: this.urlHelperService.cleanUrl(this.router.url),
2424
- },
2425
- replaceUrl: true,
2273
+ }
2274
+ else {
2275
+ // Tek değer kontrolü
2276
+ const value = Number(part);
2277
+ if (!isNaN(value)) {
2278
+ this.conditions.push({
2279
+ type: 'value',
2280
+ value: value,
2426
2281
  });
2427
- // Return an observable with a successful response
2428
- return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
2429
2282
  }
2430
- // For other errors, re-throw the error to propagate it further
2431
- return throwError(() => error);
2432
- }));
2433
- }
2434
- }
2435
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AuthenticationInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2436
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AuthenticationInterceptor });
2437
- }
2438
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AuthenticationInterceptor, decorators: [{
2439
- type: Injectable
2440
- }] });
2441
-
2442
- class CanDeactivateGuard {
2443
- canDeactivate(component) {
2444
- return component.canDeactivate ? component.canDeactivate() : true;
2445
- }
2446
- }
2447
-
2448
- class NtybaseModule {
2449
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NtybaseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2450
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.2", ngImport: i0, type: NtybaseModule, declarations: [Ntybase], exports: [Ntybase] });
2451
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NtybaseModule, providers: [
2452
- {
2453
- provide: HTTP_INTERCEPTORS,
2454
- useClass: AuthenticationInterceptor,
2455
- multi: true,
2456
- },
2457
- [CanDeactivateGuard],
2458
- DatePipe,
2459
- ] });
2460
- }
2461
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NtybaseModule, decorators: [{
2462
- type: NgModule,
2463
- args: [{
2464
- declarations: [Ntybase],
2465
- imports: [],
2466
- exports: [Ntybase],
2467
- providers: [
2468
- {
2469
- provide: HTTP_INTERCEPTORS,
2470
- useClass: AuthenticationInterceptor,
2471
- multi: true,
2472
- },
2473
- [CanDeactivateGuard],
2474
- DatePipe,
2475
- ],
2476
- }]
2477
- }] });
2478
-
2479
- class Guid {
2480
- value = this.empty;
2481
- constructor(value) {
2482
- if (value) {
2483
- if (Guid.isValid(value)) {
2484
- this.value = value;
2485
2283
  }
2486
2284
  }
2487
2285
  }
2488
- static newGuid() {
2489
- return new Guid('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
2490
- const r = (Math.random() * 16) | 0;
2491
- const v = c == 'x' ? r : (r & 0x3) | 0x8;
2492
- return v.toString(16);
2493
- }));
2494
- }
2495
- /**
2496
- * return all zeros '00000000-0000-0000-0000-000000000000'
2497
- */
2498
- static get empty() {
2499
- return '00000000-0000-0000-0000-000000000000';
2500
- }
2501
- get empty() {
2502
- return Guid.empty;
2503
- }
2504
- static isValid(str) {
2505
- const validRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i;
2506
- return validRegex.test(str);
2507
- }
2508
- toString() {
2509
- return this.value;
2510
- }
2511
- toJSON() {
2512
- return this.value;
2513
- }
2514
- /**
2515
- * True is guid is empty or not valid
2516
- * @param str
2517
- * @returns
2518
- */
2519
- static isNullOrEmpty(str) {
2520
- if (str == null || str == undefined || str.trim() == '' || str == Guid.empty) {
2521
- return true;
2522
- }
2523
- if (!Guid.isValid(str)) {
2286
+ doesFilterPass(params) {
2287
+ const cellValue = this.params.getValue(params.node);
2288
+ // Eğer filtre yoksa tüm satırları göster
2289
+ if (this.conditions.length === 0) {
2524
2290
  return true;
2525
2291
  }
2526
- return false;
2527
- }
2528
- /**
2529
- * True if the guid is valid and not all zeros (empty)
2530
- * @param str
2531
- * @returns
2532
- */
2533
- static isValidAndNotEmpty(str) {
2534
- return !Guid.isNullOrEmpty(str);
2535
- }
2536
- /**
2537
- * Return empty guid if the given guid is not valid
2538
- * @param guid
2539
- * @returns
2540
- */
2541
- static emptyWhenNull(guid) {
2542
- if (Guid.isValidAndNotEmpty(guid)) {
2543
- return guid;
2292
+ // Hücre değeri null/undefined ise veya sayı değilse filtrele
2293
+ if (cellValue == null || typeof cellValue !== 'number') {
2294
+ return false;
2544
2295
  }
2545
- return Guid.empty;
2546
- }
2547
- }
2548
-
2549
- ModuleRegistry.registerModules([AllCommunityModule, StatusBarModule, ClientSideRowModelModule, ClipboardModule, ExcelExportModule, ColumnMenuModule,
2550
- ContextMenuModule, CellSelectionModule, HighlightChangesModule, RowSelectionModule,]);
2551
- // AgGrid Dark Mode Row Style
2552
- class NettyAgGridListFilterBase extends NettyAgGridListBase {
2553
- // ********************************************
2554
- // *** INPUTS ***
2555
- // ********************************************
2556
- // Filter section
2557
- displayFilter = input(true, ...(ngDevMode ? [{ debugName: "displayFilter" }] : [])); // Does the component display it's filter section when embadded
2558
- _displayFilter = computed(() => this.displayFilter() ?? true, ...(ngDevMode ? [{ debugName: "_displayFilter" }] : [])); // Computed version of displayFilter to prevent undefined
2559
- isFilterValid = signal(true, ...(ngDevMode ? [{ debugName: "isFilterValid" }] : [])); // Can the filter be used
2560
- isFilterExpanded = linkedSignal({ ...(ngDevMode ? { debugName: "isFilterExpanded" } : {}), // Is the filter component expanded
2561
- source: () => ({
2562
- embedded: this._isEmbedded(),
2563
- displayFilter: this._displayFilter(),
2564
- valid: this.isFilterValid(),
2565
- }),
2566
- computation: (s) => {
2567
- if (s.embedded || !s.displayFilter)
2568
- return false;
2569
- return s.valid;
2570
- } });
2571
- filterRefreshTrigger = signal(0, ...(ngDevMode ? [{ debugName: "filterRefreshTrigger" }] : []));
2572
- constructor() {
2573
- super();
2574
- effect(() => {
2575
- if (this.hasValidValue(this.parameterGUID())) {
2576
- this.isFilterExpanded.set(false);
2577
- this.setFilter();
2578
- this.loadData();
2296
+ // VİRGÜL OR MANTIĞI: Koşullardan herhangi biri sağlanıyorsa true döndür
2297
+ const passesAnyCondition = this.conditions.some((condition) => {
2298
+ if (condition.type === 'range' &&
2299
+ condition.min !== undefined &&
2300
+ condition.max !== undefined) {
2301
+ // Aralık koşulu: değer aralık içinde olmalı
2302
+ return cellValue >= condition.min && cellValue <= condition.max;
2579
2303
  }
2580
- });
2581
- }
2582
- async ngOnInit() {
2583
- await super.ngOnInit();
2584
- }
2585
- // *********************************************************
2586
- // *** Data Management Functions ***
2587
- // *********************************************************
2588
- loadData() {
2589
- if (Guid.isNullOrEmpty(this.parameterGUID())) {
2590
- this.setData([], true);
2591
- return;
2592
- }
2593
- this.nettyAppsProxy.select(this.record).subscribe({
2594
- next: (data) => {
2595
- this.setData(data, false);
2596
- },
2597
- error: (err) => this.alertService.showError('@dataLoadFailed', err),
2598
- });
2599
- }
2600
- async refreshData() {
2601
- try {
2602
- this.refreshFilterData();
2603
- }
2604
- catch (err) {
2605
- this.alertService.showError(err);
2606
- }
2607
- }
2608
- /**
2609
- * Triggers the filter component to refresh its data
2610
- */
2611
- refreshFilterData() {
2612
- this.filterRefreshTrigger.update(val => val > 10000 ? 1 : val + 1);
2304
+ else if (condition.type === 'value' && condition.value !== undefined) {
2305
+ // Değer koşulu: değer eşit olmalı
2306
+ return cellValue === condition.value;
2307
+ }
2308
+ else if (condition.type === 'exclude') {
2309
+ if (condition.value !== undefined) {
2310
+ // Tek değer hariç tutma: değer eşit olmamalı
2311
+ return cellValue !== condition.value;
2312
+ }
2313
+ else if (condition.min !== undefined && condition.max !== undefined) {
2314
+ // Aralık hariç tutma: değer aralık dışında olmalı
2315
+ return !(cellValue >= condition.min && cellValue <= condition.max);
2316
+ }
2317
+ }
2318
+ return false;
2319
+ });
2320
+ return passesAnyCondition;
2613
2321
  }
2614
- onReverseIsFilterValid() {
2615
- this.isFilterValid.update((a) => !a);
2322
+ isFilterActive() {
2323
+ return this.conditions.length > 0;
2616
2324
  }
2617
- // *****************************************
2618
- // *** Logging Functions ***
2619
- // *****************************************
2620
- logInputs(message) {
2621
- if (!message || message.length < 1) {
2622
- message = 'AgGridListFilterBase - Inputs log';
2325
+ getModel() {
2326
+ if (this.isFilterActive()) {
2327
+ return {
2328
+ filterType: 'custom',
2329
+ filterText: this.filterText,
2330
+ conditions: this.conditions,
2331
+ };
2623
2332
  }
2624
- const inputs = {
2625
- "popupFilterValid": this.popupFilterValid(),
2626
- "_isPopupFilterValid": this._isPopupFilterValid(),
2627
- "popupValid": this.popupValid(),
2628
- "_isPopupValid": this._isPopupValid(),
2629
- "componantParameterGUID": this.componantParameterGUID(),
2630
- "componantParameterType": this.componantParameterType(),
2631
- "embedded": this.embedded(),
2632
- "_isEmbedded": this._isEmbedded(),
2633
- "displayFilter": this.displayFilter(),
2634
- "_displayFilter": this._displayFilter(),
2635
- "isFilterValid": this.isFilterValid(),
2636
- "isFilterExpanded": this.isFilterExpanded(),
2637
- };
2638
- console.log(message, inputs);
2333
+ return null;
2639
2334
  }
2640
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridListFilterBase, deps: [], target: i0.ɵɵFactoryTarget.Component });
2641
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: NettyAgGridListFilterBase, isStandalone: true, selector: "ntybase-ag-grid-list-filter-base", inputs: { displayFilter: { classPropertyName: "displayFilter", publicName: "displayFilter", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "ntybase-id": "NettyAgGridListFilterBase" } }, usesInheritance: true, ngImport: i0, template: ``, isInline: true });
2642
- }
2643
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridListFilterBase, decorators: [{
2644
- type: Component,
2645
- args: [{ selector: 'ntybase-ag-grid-list-filter-base', imports: [], template: ``, host: { 'ntybase-id': 'NettyAgGridListFilterBase' } }]
2646
- }], ctorParameters: () => [], propDecorators: { displayFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayFilter", required: false }] }] } });
2647
-
2648
- class NettyAgGridLogBase extends NettyAgGridBase {
2649
- // ---------------------------------------------------
2650
- // --- RECORD LIST ---
2651
- // ---------------------------------------------------
2652
- nettyAppsProxy = injectNettyStandardLogProxy(this.componentName());
2653
- /**
2654
- * Component initialization lifecycle hook
2655
- */
2656
- async ngOnInit() {
2657
- this.nettyAppsProxy.setURLPath(this.componentName());
2658
- await this.setAccessRights(true);
2659
- const savedSearchValue = sessionStorage.getItem(this.searchValueName());
2660
- if (savedSearchValue) {
2661
- this.searchValue.set(savedSearchValue);
2335
+ setModel(model) {
2336
+ if (model && model.filterText) {
2337
+ this.filterText = model.filterText;
2338
+ this.conditions = model.conditions || [];
2339
+ }
2340
+ else {
2341
+ this.filterText = '';
2342
+ this.conditions = [];
2662
2343
  }
2663
- this.loadData();
2664
- // Load user grid preferences
2665
- await this.nettyAgGridService.copyGridUserPereferenceToLocal(this.preferenceType());
2666
- await this.AfterOnInit();
2667
- }
2668
- loadData() {
2669
- this.nettyAppsProxy.selectLog(this.record).subscribe({
2670
- next: (data) => {
2671
- this.setData(data, false);
2672
- },
2673
- error: (err) => this.alertService.showError('@dataLoadFailed', err),
2674
- });
2675
2344
  }
2676
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridLogBase, deps: null, target: i0.ɵɵFactoryTarget.Component });
2677
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: NettyAgGridLogBase, isStandalone: true, selector: "ntybase-ag-grid-log-base", host: { attributes: { "ntybase-id": "NettyAgGridLogBase" } }, usesInheritance: true, ngImport: i0, template: ``, isInline: true });
2345
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeNumberFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
2346
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: RangeNumberFilter, isStandalone: true, selector: "ntybase-range-number-filter", host: { attributes: { "ntybase-id": "RangeNumberFilter" } }, ngImport: i0, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@numberFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@numberFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\"\n >{{ '@formats' | translate }}:</strong\n >\n\n <div class=\"custom-filter__format\">\n {{ '@closedRange' | translate }}:\n <code>min..max</code>\n <span class=\"custom-filter__example\">(0..5)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@equalOrGreater' | translate }}:\n <code>min..</code>\n <span class=\"custom-filter__example\">(5..)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@equalOrLess' | translate }}:\n <code>..max</code>\n <span class=\"custom-filter__example\">(..10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@singleValue' | translate }}:\n <code>number</code>\n <span class=\"custom-filter__example\">(10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@exclusion' | translate }}:\n <code>!number</code>\n <span class=\"custom-filter__example\">(!5)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@rangeExclusion' | translate }}:\n <code>!min..max</code>\n <span class=\"custom-filter__example\">(!5..10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>value1,value2,range</code>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
2678
2347
  }
2679
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridLogBase, decorators: [{
2348
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeNumberFilter, decorators: [{
2680
2349
  type: Component,
2681
- args: [{ selector: 'ntybase-ag-grid-log-base', imports: [], template: ``, host: { 'ntybase-id': 'NettyAgGridLogBase' } }]
2350
+ args: [{ selector: 'ntybase-range-number-filter', imports: [TranslateModule], host: { 'ntybase-id': 'RangeNumberFilter' }, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@numberFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@numberFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\"\n >{{ '@formats' | translate }}:</strong\n >\n\n <div class=\"custom-filter__format\">\n {{ '@closedRange' | translate }}:\n <code>min..max</code>\n <span class=\"custom-filter__example\">(0..5)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@equalOrGreater' | translate }}:\n <code>min..</code>\n <span class=\"custom-filter__example\">(5..)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@equalOrLess' | translate }}:\n <code>..max</code>\n <span class=\"custom-filter__example\">(..10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@singleValue' | translate }}:\n <code>number</code>\n <span class=\"custom-filter__example\">(10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@exclusion' | translate }}:\n <code>!number</code>\n <span class=\"custom-filter__example\">(!5)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@rangeExclusion' | translate }}:\n <code>!min..max</code>\n <span class=\"custom-filter__example\">(!5..10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>value1,value2,range</code>\n </div>\n </div>\n</div>\n" }]
2682
2351
  }] });
2683
2352
 
2684
- class NettyAgGridSaveBase extends NettyAppsBase {
2685
- // Services
2686
- router = inject(Router);
2687
- route = inject(ActivatedRoute);
2688
- commonService = inject(CommonService);
2689
- environment = inject(EnvironmentProxy);
2690
- dialog = inject(MatDialog);
2691
- viewMode = signal('sidenav', ...(ngDevMode ? [{ debugName: "viewMode" }] : []));
2692
- // Input signals
2693
- parameters = input('', ...(ngDevMode ? [{ debugName: "parameters" }] : []));
2694
- // ---------------------------------------------------
2695
- // --- RECORD LIST ---
2696
- // ---------------------------------------------------
2697
- nettyAppsProxy = injectNettyStandardProxy('');
2698
- recordType = signal('', ...(ngDevMode ? [{ debugName: "recordType" }] : []));
2699
- currentItem = signal({}, ...(ngDevMode ? [{ debugName: "currentItem" }] : []));
2700
- initialItem = {};
2701
- // Form tracking
2702
- formChanged = false;
2703
- saveForm;
2704
- // Dialog related properties
2705
- dialogRef = inject((MatDialogRef), { optional: true });
2706
- dialogData = inject(MAT_DIALOG_DATA, { optional: true });
2707
- constructor() {
2708
- super();
2709
- effect(() => {
2710
- this.parameters();
2711
- this.loadDetailData();
2712
- });
2713
- }
2714
- // Controls the visibility of additional editable fields (e.g., password, extra settings)
2715
- // When true, certain input fields become visible for editing
2716
- updateValid = signal(false, ...(ngDevMode ? [{ debugName: "updateValid" }] : []));
2717
- setUpdateValid(value) {
2718
- this.updateValid.set(value);
2353
+ class RangeStringFilter {
2354
+ params;
2355
+ filterText = '';
2356
+ conditions = [];
2357
+ agInit(params) {
2358
+ this.params = params;
2719
2359
  }
2720
- /**
2721
- * Initialize parameters. This method is called in ngOnInit or constructor
2722
- */
2723
- initParameters(urlPath) {
2724
- this.nettyAppsProxy.setURLPath(urlPath);
2725
- this.recordType.set(urlPath);
2360
+ onFilterTextChanged(event) {
2361
+ this.filterText = event.target.value;
2362
+ this.parseFilterText();
2363
+ this.params.filterChangedCallback();
2726
2364
  }
2727
- /**
2728
- * Determine view mode based on current route
2729
- */
2730
- determineViewMode() {
2731
- if (this.embedded() || this.dialogData?.embedded) {
2732
- this.viewMode.set('dialog');
2733
- }
2734
- else if (this.router.url.includes('(rightSidenav:')) {
2735
- this.viewMode.set('sidenav');
2365
+ parseFilterText() {
2366
+ this.conditions = [];
2367
+ if (!this.filterText.trim()) {
2368
+ return;
2736
2369
  }
2737
- else {
2738
- this.viewMode.set('fullscreen');
2370
+ const parts = this.filterText
2371
+ .split(',')
2372
+ .map((part) => part.trim())
2373
+ .filter((part) => part !== '');
2374
+ for (const part of parts) {
2375
+ // "!" ile başlayan olumsuz koşul kontrolü
2376
+ if (part.startsWith('!')) {
2377
+ const positiveCondition = part.slice(1);
2378
+ const positiveParsed = this.parseSingleCondition(positiveCondition);
2379
+ if (positiveParsed) {
2380
+ this.conditions.push({
2381
+ type: 'not',
2382
+ condition: positiveParsed,
2383
+ });
2384
+ }
2385
+ continue;
2386
+ }
2387
+ const parsedCondition = this.parseSingleCondition(part);
2388
+ if (parsedCondition) {
2389
+ this.conditions.push(parsedCondition);
2390
+ }
2739
2391
  }
2740
2392
  }
2741
- /**
2742
- * Set data to the form while preserving any unsaved changes
2743
- * @param item - Data object of type T to populate the form
2744
- */
2745
- initializeFormData(item) {
2746
- // Merge incoming data with current form state to preserve unsaved changes
2747
- const currentItem = this.currentItem();
2748
- const updatedItem = { ...currentItem, ...item };
2749
- // Create a new instance using the factory method
2750
- const newItem = this.createItemInstance(updatedItem);
2751
- this.currentItem.set(newItem);
2752
- // Update initial item reference for change detection
2753
- this.initialItem = this.createItemInstance(updatedItem);
2754
- // Reset form changed flag
2755
- this.formChanged = false;
2756
- }
2757
- /**
2758
- * @param data - Data to populate the new instance
2759
- */
2760
- createItemInstance(data) {
2761
- const instance = this.createNewRecord();
2762
- Object.assign(instance, data);
2763
- return instance;
2764
- }
2765
- /**
2766
- * Check for form changes
2767
- */
2768
- ngDoCheck() {
2769
- this.formChanged = !this.isEqual(this.currentItem(), this.initialItem);
2770
- }
2771
- isEqual(item1, item2) {
2772
- return JSON.stringify(item1) === JSON.stringify(item2);
2773
- }
2774
- /**
2775
- * Check if can deactivate component
2776
- */
2777
- async canDeactivate() {
2778
- if (this.formChanged) {
2779
- const confirmed = await this.alertService.showConfirm('@unsavedChangesConfirm');
2780
- if (!confirmed) {
2781
- return false;
2393
+ parseSingleCondition(condition) {
2394
+ // Regex kontrolü ($$ ile başlıyorsa)
2395
+ if (condition.startsWith('$$')) {
2396
+ const regexPattern = condition.slice(2);
2397
+ if (regexPattern) {
2398
+ return {
2399
+ type: 'regex',
2400
+ pattern: regexPattern,
2401
+ };
2782
2402
  }
2783
- return true;
2784
2403
  }
2785
- return true;
2786
- }
2787
- getGuidFromParameters() {
2788
- if (this.viewMode() === 'dialog' && this.dialogData) {
2789
- return this.dialogData.parameters || '';
2404
+ // Aralık kontrolü (.. içeriyor mu) - ama $$.. şeklinde değilse
2405
+ if (condition.includes('..') && !condition.startsWith('$$')) {
2406
+ const rangeParts = condition.split('..');
2407
+ if (rangeParts.length === 2) {
2408
+ const start = rangeParts[0].trim();
2409
+ const end = rangeParts[1].trim();
2410
+ if (start && end) {
2411
+ return {
2412
+ type: 'range',
2413
+ start: start.toLowerCase(),
2414
+ end: end.toLowerCase(),
2415
+ };
2416
+ }
2417
+ }
2790
2418
  }
2791
- if (this.parameters()) {
2792
- try {
2793
- return JSON.parse(this.parameters());
2419
+ // Tek karakter kontrolü (? ile)
2420
+ else if (condition.includes('?') && !condition.startsWith('$$')) {
2421
+ // Eğer sadece normal karakterler varsa (özel karakter yoksa) exact match
2422
+ const hasSpecialChars = condition.includes('*') ||
2423
+ condition.includes('?') ||
2424
+ condition.includes('..');
2425
+ if (!hasSpecialChars) {
2426
+ return {
2427
+ type: 'exact',
2428
+ value: condition.toLowerCase(),
2429
+ };
2794
2430
  }
2795
- catch (e) {
2796
- return this.parameters();
2431
+ // ? karakteri içeriyorsa singleChar kontrolü
2432
+ const value = condition.toLowerCase();
2433
+ if (value) {
2434
+ return {
2435
+ type: 'singleChar',
2436
+ value: value,
2437
+ };
2797
2438
  }
2798
2439
  }
2799
- return '';
2800
- }
2801
- /**
2802
- * Close sidenav or navigate back
2803
- */
2804
- async closeSidenav() {
2805
- const canDeactivate = await this.canDeactivate();
2806
- if (canDeactivate) {
2807
- if (this.viewMode() === 'dialog' && this.dialogRef) {
2808
- this.dialogRef.close('saved');
2440
+ // Başlangıç kontrolü (* sonunda)
2441
+ else if (condition.endsWith('*') &&
2442
+ !condition.startsWith('*') &&
2443
+ !condition.startsWith('$$')) {
2444
+ const value = condition.slice(0, -1).toLowerCase();
2445
+ if (value) {
2446
+ return {
2447
+ type: 'startsWith',
2448
+ value: value,
2449
+ };
2809
2450
  }
2810
- else if (this.viewMode() === 'sidenav') {
2811
- this.formChanged = false;
2812
- this.commonService.clearOutlet();
2451
+ }
2452
+ // Bitiş kontrolü (* başında)
2453
+ else if (condition.startsWith('*') &&
2454
+ !condition.endsWith('*') &&
2455
+ !condition.startsWith('$$')) {
2456
+ const value = condition.slice(1).toLowerCase();
2457
+ if (value) {
2458
+ return {
2459
+ type: 'endsWith',
2460
+ value: value,
2461
+ };
2813
2462
  }
2814
- else {
2815
- const cleanPath = this.commonService.getCleanUrlPath();
2816
- this.router.navigate([cleanPath]);
2463
+ }
2464
+ // İçeren kontrolü (* ile başlayıp * ile bitiyorsa)
2465
+ else if (condition.startsWith('*') &&
2466
+ condition.endsWith('*') &&
2467
+ !condition.startsWith('$$')) {
2468
+ const value = condition.slice(1, -1).toLowerCase();
2469
+ if (value) {
2470
+ return {
2471
+ type: 'contains',
2472
+ value: value,
2473
+ };
2817
2474
  }
2818
2475
  }
2819
- }
2820
- /**
2821
- * Back button clicked
2822
- */
2823
- async backClicked() {
2824
- const canDeactivate = await this.canDeactivate();
2825
- if (canDeactivate) {
2826
- if (this.viewMode() === 'dialog' && this.dialogRef) {
2827
- this.dialogRef.close('back');
2476
+ // Normal exact match (hiçbir özel karakter yoksa)
2477
+ else if (!condition.startsWith('$$') &&
2478
+ !condition.includes('*') &&
2479
+ !condition.includes('?') &&
2480
+ !condition.includes('..')) {
2481
+ const value = condition.toLowerCase();
2482
+ if (value) {
2483
+ return {
2484
+ type: 'exact',
2485
+ value: value,
2486
+ };
2828
2487
  }
2829
- else {
2830
- this.commonService.goBack();
2488
+ }
2489
+ // Normal içeren kontrolü (varsayılan) - regex değilse
2490
+ else if (!condition.startsWith('$$')) {
2491
+ const value = condition.toLowerCase();
2492
+ if (value) {
2493
+ return {
2494
+ type: 'contains',
2495
+ value: value,
2496
+ };
2831
2497
  }
2832
2498
  }
2499
+ return null;
2833
2500
  }
2834
- validateSaveRecord(recordGUID) {
2835
- if (this.saveForm && this.saveForm.invalid) {
2836
- Object.keys(this.saveForm.controls).forEach((key) => {
2837
- const control = this.saveForm.controls[key];
2838
- control.markAsTouched();
2839
- control.markAsDirty();
2840
- });
2841
- this.alertService.showError('@pleaseFillRequiredFields');
2842
- return false;
2501
+ doesFilterPass(params) {
2502
+ const field = this.params.colDef.field;
2503
+ if (!field)
2504
+ return true;
2505
+ const cellValue = params.data
2506
+ ? String(params.data[field] || '').toLowerCase()
2507
+ : '';
2508
+ // Eğer filtre yoksa tüm satırları göster
2509
+ if (this.conditions.length === 0) {
2510
+ return true;
2843
2511
  }
2844
- // First check if there are any visual form changes
2845
- if (!this.formChanged && recordGUID) {
2846
- this.alertService.showAlert('@noChangesDetected');
2847
- // if (this.closeAfterSave()) {
2848
- // this.closeSidenav();
2849
- // }
2512
+ // Hücre değeri boşsa filtrele
2513
+ if (!cellValue) {
2850
2514
  return false;
2851
2515
  }
2852
- return true;
2516
+ // VİRGÜL OR MANTIĞI: Koşullardan herhangi biri sağlanıyorsa true döndür
2517
+ return this.conditions.some((condition) => {
2518
+ const conditionResult = this.checkSingleCondition(cellValue, condition);
2519
+ return condition.type === 'not' ? !conditionResult : conditionResult;
2520
+ });
2853
2521
  }
2854
- /**
2855
- * Save data to API
2856
- * Handles both create and update operations based on record existence
2857
- */
2858
- saveRecord() {
2859
- if (!this.validateSaveRecord(this.currentItem().getPK())) {
2860
- return;
2522
+ checkSingleCondition(cellValue, condition) {
2523
+ switch (condition.type) {
2524
+ case 'contains':
2525
+ return condition.value ? cellValue.includes(condition.value) : false;
2526
+ case 'startsWith':
2527
+ return condition.value ? cellValue.startsWith(condition.value) : false;
2528
+ case 'endsWith':
2529
+ return condition.value ? cellValue.endsWith(condition.value) : false;
2530
+ case 'exact':
2531
+ return condition.value ? cellValue === condition.value : false;
2532
+ case 'range':
2533
+ if (condition.start && condition.end) {
2534
+ return cellValue >= condition.start && cellValue <= condition.end;
2535
+ }
2536
+ return false;
2537
+ case 'singleChar':
2538
+ if (condition.value) {
2539
+ // ? karakterini herhangi bir karakter olarak değerlendir
2540
+ const pattern = condition.value;
2541
+ if (cellValue.length !== pattern.length) {
2542
+ return false;
2543
+ }
2544
+ // Her karakteri tek tek kontrol et
2545
+ for (let i = 0; i < pattern.length; i++) {
2546
+ const patternChar = pattern[i];
2547
+ const cellChar = cellValue[i];
2548
+ // Eğer pattern'de ? değilse ve karakterler eşleşmiyorsa false
2549
+ if (patternChar !== '?' && patternChar !== cellChar) {
2550
+ return false;
2551
+ }
2552
+ }
2553
+ return true;
2554
+ }
2555
+ return false;
2556
+ case 'regex':
2557
+ if (condition.pattern) {
2558
+ try {
2559
+ const regex = new RegExp(condition.pattern, 'i'); // case-insensitive
2560
+ return regex.test(cellValue);
2561
+ }
2562
+ catch (e) {
2563
+ // Geçersiz regex patterni durumunda false döndür
2564
+ console.warn('Geçersiz regex patterni:', condition.pattern);
2565
+ return false;
2566
+ }
2567
+ }
2568
+ return false;
2569
+ case 'not':
2570
+ return this.checkSingleCondition(cellValue, condition.condition);
2571
+ default:
2572
+ return false;
2861
2573
  }
2862
- if (this.currentItem().getPK()) {
2863
- this.updateRecord();
2574
+ }
2575
+ isFilterActive() {
2576
+ return this.conditions.length > 0;
2577
+ }
2578
+ getModel() {
2579
+ if (this.isFilterActive()) {
2580
+ return {
2581
+ filterType: 'custom',
2582
+ filterText: this.filterText,
2583
+ conditions: this.conditions,
2584
+ };
2585
+ }
2586
+ return null;
2587
+ }
2588
+ setModel(model) {
2589
+ if (model && model.filterText) {
2590
+ this.filterText = model.filterText;
2591
+ this.conditions = model.conditions || [];
2592
+ }
2593
+ else {
2594
+ this.filterText = '';
2595
+ this.conditions = [];
2596
+ }
2597
+ }
2598
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeStringFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
2599
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: RangeStringFilter, isStandalone: true, selector: "ntybase-range-string-filter", host: { attributes: { "ntybase-id": "RangeStringFilter" } }, ngImport: i0, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@stringFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@stringFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\">\n {{ '@formats' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@exactMatch' | translate }}:\n <code>text</code>\n <span class=\"custom-filter__example\">(ahmet)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@startsWith' | translate }}:\n <code>text*</code>\n <span class=\"custom-filter__example\">(ahmet*)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@endsWith' | translate }}:\n <code>*text</code>\n <span class=\"custom-filter__example\">(*metin)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@contains' | translate }}:\n <code>*text*</code>\n <span class=\"custom-filter__example\">(*ahmet*)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@range' | translate }}:\n <code>start..end</code>\n <span class=\"custom-filter__example\">(ali..veli)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n <span class=\"custom-filter__description\">\n {{ '@singleCharacter' | translate }}:\n </span>\n <code>?em</code>\n <span class=\"custom-filter__example\">\n (?em = 3 characters, 2nd and 3rd letters \"em\")\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n <span class=\"custom-filter__description\">\n {{ '@multipleCharacters' | translate }}:\n </span>\n <code>???in</code>\n <span class=\"custom-filter__example\">\n (5 characters, last 2 letters \"in\")\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@not' | translate }}:\n <code>!condition</code>\n <span class=\"custom-filter__example\">(!*ahmet*, !?hmet)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@regex' | translate }}:\n <code>$$pattern</code>\n <span class=\"custom-filter__example\">($$^[A-Z].*, $$a.*b)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>value1,value2,*end,$$pattern</code>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
2600
+ }
2601
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeStringFilter, decorators: [{
2602
+ type: Component,
2603
+ args: [{ selector: 'ntybase-range-string-filter', imports: [TranslateModule], host: { 'ntybase-id': 'RangeStringFilter' }, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@stringFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@stringFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\">\n {{ '@formats' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@exactMatch' | translate }}:\n <code>text</code>\n <span class=\"custom-filter__example\">(ahmet)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@startsWith' | translate }}:\n <code>text*</code>\n <span class=\"custom-filter__example\">(ahmet*)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@endsWith' | translate }}:\n <code>*text</code>\n <span class=\"custom-filter__example\">(*metin)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@contains' | translate }}:\n <code>*text*</code>\n <span class=\"custom-filter__example\">(*ahmet*)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@range' | translate }}:\n <code>start..end</code>\n <span class=\"custom-filter__example\">(ali..veli)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n <span class=\"custom-filter__description\">\n {{ '@singleCharacter' | translate }}:\n </span>\n <code>?em</code>\n <span class=\"custom-filter__example\">\n (?em = 3 characters, 2nd and 3rd letters \"em\")\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n <span class=\"custom-filter__description\">\n {{ '@multipleCharacters' | translate }}:\n </span>\n <code>???in</code>\n <span class=\"custom-filter__example\">\n (5 characters, last 2 letters \"in\")\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@not' | translate }}:\n <code>!condition</code>\n <span class=\"custom-filter__example\">(!*ahmet*, !?hmet)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@regex' | translate }}:\n <code>$$pattern</code>\n <span class=\"custom-filter__example\">($$^[A-Z].*, $$a.*b)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>value1,value2,*end,$$pattern</code>\n </div>\n </div>\n</div>\n" }]
2604
+ }] });
2605
+
2606
+ //-------------------------------
2607
+ //| Generated by NettyWizard V2 |
2608
+ //-------------------------------
2609
+ class ExcelLogViewer extends NettyAgGridListBase {
2610
+ /** Component Constructor */
2611
+ constructor() {
2612
+ super();
2613
+ }
2614
+ /** Get the rowId */
2615
+ getRowId = (params) => params.data.rowIndex.toString();
2616
+ /** Get the entity type for the component */
2617
+ getEntityType = () => 'ParseLog';
2618
+ pkFieldName() { return 'rowIndex'; }
2619
+ loadData() { }
2620
+ setParseData(data) {
2621
+ data.forEach((item) => {
2622
+ if (item.messageKey) {
2623
+ item.message = this.translateService.instant(item.messageKey, item.messageParams);
2624
+ }
2625
+ });
2626
+ super.setData(data);
2627
+ }
2628
+ /** Initialize Ag-Grid specific settings */
2629
+ initAgGrid() {
2630
+ this.excelStyles = this.nettyAgGridService.getExcelStyles();
2631
+ this.columnDefs.set([
2632
+ { headerName: this.translateService.instant('@EXCEL_PARSER_LOG.row'), field: 'rowIndex', cellClass: 'numberType', filter: this.customFilters() ? RangeNumberFilter : 'agNumberColumnFilter', type: "numericColumn", cellStyle: { textAlign: 'right' } },
2633
+ { headerName: this.translateService.instant('@EXCEL_PARSER_LOG.message'), field: 'message', cellClass: 'text-format', filter: this.customFilters() ? RangeStringFilter : 'agSetColumnFilter', },
2634
+ { headerName: this.translateService.instant('@EXCEL_PARSER_LOG.level'), field: 'level', cellClass: 'text-format', filter: this.customFilters() ? RangeStringFilter : 'agSetColumnFilter', },
2635
+ ]);
2636
+ this.initAgGrid_extension();
2637
+ }
2638
+ popupClose() {
2639
+ super.popupClose();
2640
+ }
2641
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ExcelLogViewer, deps: [], target: i0.ɵɵFactoryTarget.Component });
2642
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: ExcelLogViewer, isStandalone: true, selector: "app-excel-log-viewer", usesInheritance: true, ngImport: i0, template: "<div class='list-container'>\n <div class='list-menu-button-bar'>\n <ntyui-button type='menu' icon='settings_ethernet' [disableOnClick]='false' (clicked)='expandCollapse()' />\n <ntyui-button type='menu' icon='regular_expression'\n [toolTip]=\"(customFilters() ? '@customFilterActive' : '@defaultFilterActive') | translate\"\n [disableOnClick]='true' [disableDuration]='0'\n [ngClass]=\"{'custom-filter-mode': !customFilters(),'default-filter-mode': customFilters()}\"\n (clicked)='toggleFilterMode()' />\n <ntyui-filter-button icon='filter_alt' (isSearchOpen)=\"onFilterTextBoxVisibilityChange($event)\"\n [searchValue]='searchValue()' (onSearch)='quickSearch($event)' />\n <ntyui-button type='close' class='list-close-button' [toolTip]=\"'@close'|translate\" (clicked)='popupClose()' />\n </div>\n <div class='list-grid'>\n <ag-grid-angular #agGrid class='ag-theme-balham grid-form-grid-full' [rowData]='recordList()'\n [columnDefs]='columnDefs()' [gridOptions]='gridOptions' [rowSelection]='gridOptions.rowSelection'\n [components]='frameworkComponents' [suppressAggFuncInHeader]='true' [enableCellTextSelection]='true'\n (firstDataRendered)='onFirstDataRendered($event)' (columnMoved)='saveGrid($event)'\n (columnVisible)='saveGrid($event)' (columnResized)='saveGrid($event)' (gridReady)='onGridReady($event)'\n [statusBar]='statusBar'></ag-grid-angular>\n </div>\n</div>", styles: [""], dependencies: [{ kind: "component", type: AgGridAngular, selector: "ag-grid-angular", inputs: ["gridOptions", "modules", "statusBar", "sideBar", "suppressContextMenu", "preventDefaultOnContextMenu", "allowContextMenuWithControlKey", "columnMenu", "suppressMenuHide", "enableBrowserTooltips", "tooltipTrigger", "tooltipShowDelay", "tooltipSwitchShowDelay", "tooltipHideDelay", "tooltipMouseTrack", "tooltipShowMode", "tooltipInteraction", "popupParent", "copyHeadersToClipboard", "copyGroupHeadersToClipboard", "clipboardDelimiter", "suppressCopyRowsToClipboard", "suppressCopySingleCellRanges", "suppressLastEmptyLineOnPaste", "suppressClipboardPaste", "suppressClipboardApi", "suppressCutToClipboard", "columnDefs", "defaultColDef", "defaultColGroupDef", "columnTypes", "dataTypeDefinitions", "maintainColumnOrder", "enableStrictPivotColumnOrder", "suppressFieldDotNotation", "headerHeight", "groupHeaderHeight", "floatingFiltersHeight", "pivotHeaderHeight", "pivotGroupHeaderHeight", "hidePaddedHeaderRows", "allowDragFromColumnsToolPanel", "suppressMovableColumns", "suppressColumnMoveAnimation", "suppressMoveWhenColumnDragging", "suppressDragLeaveHidesColumns", "suppressGroupChangesColumnVisibility", "suppressMakeColumnVisibleAfterUnGroup", "suppressRowGroupHidesColumns", "colResizeDefault", "suppressAutoSize", "autoSizePadding", "skipHeaderOnAutoSize", "autoSizeStrategy", "animateColumnResizing", "components", "editType", "suppressStartEditOnTab", "getFullRowEditValidationErrors", "invalidEditValueMode", "singleClickEdit", "suppressClickEdit", "readOnlyEdit", "stopEditingWhenCellsLoseFocus", "enterNavigatesVertically", "enterNavigatesVerticallyAfterEdit", "enableCellEditingOnBackspace", "undoRedoCellEditing", "undoRedoCellEditingLimit", "defaultCsvExportParams", "suppressCsvExport", "defaultExcelExportParams", "suppressExcelExport", "excelStyles", "findSearchValue", "findOptions", "quickFilterText", "cacheQuickFilter", "includeHiddenColumnsInQuickFilter", "quickFilterParser", "quickFilterMatcher", "applyQuickFilterBeforePivotOrAgg", "excludeChildrenWhenTreeDataFiltering", "enableAdvancedFilter", "alwaysPassFilter", "includeHiddenColumnsInAdvancedFilter", "advancedFilterParent", "advancedFilterBuilderParams", "advancedFilterParams", "suppressAdvancedFilterEval", "suppressSetFilterByDefault", "enableFilterHandlers", "filterHandlers", "enableCharts", "chartThemes", "customChartThemes", "chartThemeOverrides", "chartToolPanelsDef", "chartMenuItems", "loadingCellRenderer", "loadingCellRendererParams", "loadingCellRendererSelector", "localeText", "masterDetail", "keepDetailRows", "keepDetailRowsCount", "detailCellRenderer", "detailCellRendererParams", "detailRowHeight", "detailRowAutoHeight", "context", "alignedGrids", "tabIndex", "rowBuffer", "valueCache", "valueCacheNeverExpires", "enableCellExpressions", "suppressTouch", "suppressFocusAfterRefresh", "suppressBrowserResizeObserver", "suppressPropertyNamesCheck", "suppressChangeDetection", "debug", "loading", "overlayLoadingTemplate", "loadingOverlayComponent", "loadingOverlayComponentParams", "suppressLoadingOverlay", "overlayNoRowsTemplate", "noRowsOverlayComponent", "noRowsOverlayComponentParams", "suppressNoRowsOverlay", "suppressOverlays", "overlayComponent", "overlayComponentParams", "overlayComponentSelector", "activeOverlay", "activeOverlayParams", "pagination", "paginationPageSize", "paginationPageSizeSelector", "paginationAutoPageSize", "paginateChildRows", "suppressPaginationPanel", "pivotMode", "pivotPanelShow", "pivotMaxGeneratedColumns", "pivotDefaultExpanded", "pivotColumnGroupTotals", "pivotRowTotals", "pivotSuppressAutoColumn", "suppressExpandablePivotGroups", "functionsReadOnly", "aggFuncs", "formulaDataSource", "formulaFuncs", "suppressAggFuncInHeader", "alwaysAggregateAtRootLevel", "aggregateOnlyChangedColumns", "suppressAggFilteredOnly", "removePivotHeaderRowWhenSingleValueColumn", "animateRows", "cellFlashDuration", "cellFadeDuration", "allowShowChangeAfterFilter", "domLayout", "ensureDomOrder", "enableCellSpan", "enableRtl", "suppressColumnVirtualisation", "suppressMaxRenderedRowRestriction", "suppressRowVirtualisation", "rowDragManaged", "refreshAfterGroupEdit", "rowDragInsertDelay", "suppressRowDrag", "suppressMoveWhenRowDragging", "rowDragEntireRow", "rowDragMultiRow", "rowDragText", "dragAndDropImageComponent", "dragAndDropImageComponentParams", "fullWidthCellRenderer", "fullWidthCellRendererParams", "embedFullWidthRows", "groupDisplayType", "groupDefaultExpanded", "autoGroupColumnDef", "groupMaintainOrder", "groupSelectsChildren", "groupLockGroupColumns", "groupAggFiltering", "groupTotalRow", "grandTotalRow", "suppressStickyTotalRow", "groupSuppressBlankHeader", "groupSelectsFiltered", "showOpenedGroup", "groupHideParentOfSingleChild", "groupRemoveSingleChildren", "groupRemoveLowestSingleChildren", "groupHideOpenParents", "groupAllowUnbalanced", "rowGroupPanelShow", "groupRowRenderer", "groupRowRendererParams", "treeData", "treeDataChildrenField", "treeDataParentIdField", "rowGroupPanelSuppressSort", "suppressGroupRowsSticky", "groupHierarchyConfig", "pinnedTopRowData", "pinnedBottomRowData", "enableRowPinning", "isRowPinnable", "isRowPinned", "rowModelType", "rowData", "asyncTransactionWaitMillis", "suppressModelUpdateAfterUpdateTransaction", "datasource", "cacheOverflowSize", "infiniteInitialRowCount", "serverSideInitialRowCount", "suppressServerSideFullWidthLoadingRow", "cacheBlockSize", "maxBlocksInCache", "maxConcurrentDatasourceRequests", "blockLoadDebounceMillis", "purgeClosedRowNodes", "serverSideDatasource", "serverSideSortAllLevels", "serverSideEnableClientSideSort", "serverSideOnlyRefreshFilteredGroups", "serverSidePivotResultFieldSeparator", "viewportDatasource", "viewportRowModelPageSize", "viewportRowModelBufferSize", "alwaysShowHorizontalScroll", "alwaysShowVerticalScroll", "debounceVerticalScrollbar", "suppressHorizontalScroll", "suppressScrollOnNewData", "suppressScrollWhenPopupsAreOpen", "suppressAnimationFrame", "suppressMiddleClickScrolls", "suppressPreventDefaultOnMouseWheel", "scrollbarWidth", "rowSelection", "cellSelection", "rowMultiSelectWithClick", "suppressRowDeselection", "suppressRowClickSelection", "suppressCellFocus", "suppressHeaderFocus", "selectionColumnDef", "rowNumbers", "suppressMultiRangeSelection", "enableCellTextSelection", "enableRangeSelection", "enableRangeHandle", "enableFillHandle", "fillHandleDirection", "suppressClearOnFillReduction", "sortingOrder", "accentedSort", "unSortIcon", "suppressMultiSort", "alwaysMultiSort", "multiSortKey", "suppressMaintainUnsortedOrder", "icons", "rowHeight", "rowStyle", "rowClass", "rowClassRules", "suppressRowHoverHighlight", "suppressRowTransform", "columnHoverHighlight", "gridId", "deltaSort", "treeDataDisplayType", "enableGroupEdit", "initialState", "theme", "loadThemeGoogleFonts", "themeCssLayer", "styleNonce", "themeStyleContainer", "getContextMenuItems", "getMainMenuItems", "postProcessPopup", "processUnpinnedColumns", "processCellForClipboard", "processHeaderForClipboard", "processGroupHeaderForClipboard", "processCellFromClipboard", "sendToClipboard", "processDataFromClipboard", "isExternalFilterPresent", "doesExternalFilterPass", "getChartToolbarItems", "createChartContainer", "focusGridInnerElement", "navigateToNextHeader", "tabToNextHeader", "navigateToNextCell", "tabToNextCell", "getLocaleText", "getDocument", "paginationNumberFormatter", "getGroupRowAgg", "isGroupOpenByDefault", "ssrmExpandAllAffectsAllRows", "initialGroupOrderComparator", "processPivotResultColDef", "processPivotResultColGroupDef", "getDataPath", "getChildCount", "getServerSideGroupLevelParams", "isServerSideGroupOpenByDefault", "isApplyServerSideTransaction", "isServerSideGroup", "getServerSideGroupKey", "getBusinessKeyForNode", "getRowId", "resetRowDataOnUpdate", "processRowPostCreate", "isRowSelectable", "isRowMaster", "fillOperation", "postSortRows", "getRowStyle", "getRowClass", "getRowHeight", "isFullWidthRow", "isRowValidDropPosition"], outputs: ["toolPanelVisibleChanged", "toolPanelSizeChanged", "columnMenuVisibleChanged", "contextMenuVisibleChanged", "cutStart", "cutEnd", "pasteStart", "pasteEnd", "columnVisible", "columnPinned", "columnResized", "columnMoved", "columnValueChanged", "columnPivotModeChanged", "columnPivotChanged", "columnGroupOpened", "newColumnsLoaded", "gridColumnsChanged", "displayedColumnsChanged", "virtualColumnsChanged", "columnEverythingChanged", "columnsReset", "columnHeaderMouseOver", "columnHeaderMouseLeave", "columnHeaderClicked", "columnHeaderContextMenu", "componentStateChanged", "cellValueChanged", "cellEditRequest", "rowValueChanged", "cellEditingStarted", "cellEditingStopped", "rowEditingStarted", "rowEditingStopped", "bulkEditingStarted", "bulkEditingStopped", "batchEditingStarted", "batchEditingStopped", "undoStarted", "undoEnded", "redoStarted", "redoEnded", "cellSelectionDeleteStart", "cellSelectionDeleteEnd", "rangeDeleteStart", "rangeDeleteEnd", "fillStart", "fillEnd", "filterOpened", "filterChanged", "filterModified", "filterUiChanged", "floatingFilterUiChanged", "advancedFilterBuilderVisibleChanged", "findChanged", "chartCreated", "chartRangeSelectionChanged", "chartOptionsChanged", "chartDestroyed", "cellKeyDown", "gridReady", "firstDataRendered", "gridSizeChanged", "modelUpdated", "virtualRowRemoved", "viewportChanged", "bodyScroll", "bodyScrollEnd", "dragStarted", "dragStopped", "dragCancelled", "stateUpdated", "paginationChanged", "rowDragEnter", "rowDragMove", "rowDragLeave", "rowDragEnd", "rowDragCancel", "rowResizeStarted", "rowResizeEnded", "columnRowGroupChanged", "rowGroupOpened", "expandOrCollapseAll", "pivotMaxColumnsExceeded", "pinnedRowDataChanged", "pinnedRowsChanged", "rowDataUpdated", "asyncTransactionsFlushed", "storeRefreshed", "headerFocused", "cellClicked", "cellDoubleClicked", "cellFocused", "cellMouseOver", "cellMouseOut", "cellMouseDown", "rowClicked", "rowDoubleClicked", "rowSelected", "selectionChanged", "cellContextMenu", "rangeSelectionChanged", "cellSelectionChanged", "tooltipShow", "tooltipHide", "sortChanged"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: NettyUIButton, selector: "ntyui-button", inputs: ["icon", "isFilled", "menuReference", "disableOnClick", "disableDuration", "waitingText", "type", "toolTip"], outputs: ["clicked"] }, { kind: "component", type: NettyUIFilterButton, selector: "ntyui-filter-button", inputs: ["icon", "waitingText", "searchValue"], outputs: ["clicked", "onSearch", "isSearchOpen"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
2643
+ }
2644
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ExcelLogViewer, decorators: [{
2645
+ type: Component,
2646
+ args: [{ selector: 'app-excel-log-viewer', imports: [AgGridAngular, CommonModule, FormsModule, NettyUIButton, NettyUIFilterButton,
2647
+ FormsModule, TranslateModule], template: "<div class='list-container'>\n <div class='list-menu-button-bar'>\n <ntyui-button type='menu' icon='settings_ethernet' [disableOnClick]='false' (clicked)='expandCollapse()' />\n <ntyui-button type='menu' icon='regular_expression'\n [toolTip]=\"(customFilters() ? '@customFilterActive' : '@defaultFilterActive') | translate\"\n [disableOnClick]='true' [disableDuration]='0'\n [ngClass]=\"{'custom-filter-mode': !customFilters(),'default-filter-mode': customFilters()}\"\n (clicked)='toggleFilterMode()' />\n <ntyui-filter-button icon='filter_alt' (isSearchOpen)=\"onFilterTextBoxVisibilityChange($event)\"\n [searchValue]='searchValue()' (onSearch)='quickSearch($event)' />\n <ntyui-button type='close' class='list-close-button' [toolTip]=\"'@close'|translate\" (clicked)='popupClose()' />\n </div>\n <div class='list-grid'>\n <ag-grid-angular #agGrid class='ag-theme-balham grid-form-grid-full' [rowData]='recordList()'\n [columnDefs]='columnDefs()' [gridOptions]='gridOptions' [rowSelection]='gridOptions.rowSelection'\n [components]='frameworkComponents' [suppressAggFuncInHeader]='true' [enableCellTextSelection]='true'\n (firstDataRendered)='onFirstDataRendered($event)' (columnMoved)='saveGrid($event)'\n (columnVisible)='saveGrid($event)' (columnResized)='saveGrid($event)' (gridReady)='onGridReady($event)'\n [statusBar]='statusBar'></ag-grid-angular>\n </div>\n</div>" }]
2648
+ }], ctorParameters: () => [] });
2649
+
2650
+ class ExcelImportBase extends NettyAgGridListBase {
2651
+ isFileSelectionHidden = signal(false, ...(ngDevMode ? [{ debugName: "isFileSelectionHidden" }] : []));
2652
+ isFileValid = signal(false, ...(ngDevMode ? [{ debugName: "isFileValid" }] : []));
2653
+ logs = signal([], ...(ngDevMode ? [{ debugName: "logs" }] : []));
2654
+ hasLogs = computed(() => this.logs().length > 0, ...(ngDevMode ? [{ debugName: "hasLogs" }] : []));
2655
+ logDialog = inject(MatDialog);
2656
+ parser = new ExcelParser([]);
2657
+ // override methods
2658
+ onBtnClick(e) { }
2659
+ loadData() { }
2660
+ // Methods
2661
+ onFilesSelected(evt) {
2662
+ const files = Array.isArray(evt) ? evt : evt.target.files;
2663
+ if (!files || files.length !== 1) {
2864
2664
  return;
2865
2665
  }
2866
- this.insertRecord();
2666
+ const file = files[0];
2667
+ if (file) {
2668
+ this.recordList.set([]);
2669
+ this.logs.set([]);
2670
+ this.parser.parse(file).then(result => {
2671
+ this.recordList.set(result.data);
2672
+ this.logs.set(result.logs);
2673
+ this.isFileSelectionHidden.set(true);
2674
+ if (result.logs.length > 0)
2675
+ this.showLogs();
2676
+ });
2677
+ }
2678
+ }
2679
+ downloadSampleExcel() {
2680
+ this.parser.generateSampleExcel();
2867
2681
  }
2868
- /**
2869
- * Create new record
2870
- */
2871
- insertRecord() {
2872
- const createData = {};
2873
- const currentItem = this.currentItem();
2874
- Object.keys(currentItem).forEach((key) => {
2875
- if (currentItem[key] !== undefined && currentItem[key] !== null && currentItem[key] !== '') {
2876
- createData[key] = currentItem[key];
2877
- }
2682
+ showLogs() {
2683
+ const dialogRef = this.logDialog.open(ExcelLogViewer, {
2684
+ height: '70%',
2685
+ width: '90%',
2686
+ maxWidth: '100vw',
2687
+ maxHeight: '100vh'
2878
2688
  });
2879
- this.nettyAppsProxy.insert(createData).subscribe({
2880
- next: (newRecord) => {
2881
- this.initializeFormData(newRecord);
2882
- this.commonService.notifyUpdate(this.recordType(), 'add', newRecord);
2883
- this.closeSidenav();
2884
- this.alertService.showSuccess('@recordCreatedSuccessfully');
2885
- },
2886
- error: (err) => this.alertService.showError('@creationFailed', err),
2689
+ dialogRef.componentInstance.setParseData(this.logs());
2690
+ dialogRef.componentInstance.selectedElement.subscribe((element) => {
2691
+ dialogRef.close();
2887
2692
  });
2888
2693
  }
2694
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ExcelImportBase, deps: null, target: i0.ɵɵFactoryTarget.Component });
2695
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: ExcelImportBase, isStandalone: true, selector: "ntybase-excel-import-base", usesInheritance: true, ngImport: i0, template: ``, isInline: true });
2696
+ }
2697
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ExcelImportBase, decorators: [{
2698
+ type: Component,
2699
+ args: [{ selector: 'ntybase-excel-import-base', imports: [], template: `` }]
2700
+ }] });
2701
+
2702
+ class Ntybase {
2703
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: Ntybase, deps: [], target: i0.ɵɵFactoryTarget.Component });
2704
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: Ntybase, isStandalone: false, selector: "lib-ntybase", ngImport: i0, template: `
2705
+ <p>
2706
+ ntybase works!
2707
+ </p>
2708
+ `, isInline: true, styles: [""] });
2709
+ }
2710
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: Ntybase, decorators: [{
2711
+ type: Component,
2712
+ args: [{ selector: 'lib-ntybase', standalone: false, template: `
2713
+ <p>
2714
+ ntybase works!
2715
+ </p>
2716
+ ` }]
2717
+ }] });
2718
+
2719
+ const credentialsKey = 'credentials';
2720
+ class CredentialsService {
2721
+ _credentials = null;
2722
+ constructor() {
2723
+ const savedCredentials = sessionStorage.getItem(credentialsKey) ||
2724
+ localStorage.getItem(credentialsKey);
2725
+ if (savedCredentials) {
2726
+ this._credentials = JSON.parse(savedCredentials);
2727
+ }
2728
+ }
2889
2729
  /**
2890
- * Update record
2730
+ * Checks is the user is authenticated.
2731
+ * @return True if the user is authenticated.
2891
2732
  */
2892
- updateRecord() {
2893
- const changes = this.initialItem.compare(this.currentItem());
2894
- const updateData = this.createNewRecord();
2895
- Object.assign(updateData, changes);
2896
- updateData.setPK(this.currentItem().getPK());
2897
- this.nettyAppsProxy.update(updateData).subscribe({
2898
- next: (updatedRecord) => {
2899
- this.initializeFormData(updatedRecord);
2900
- this.commonService.notifyUpdate(this.recordType(), 'update', updatedRecord);
2901
- this.closeSidenav();
2902
- this.alertService.showSuccess('@recordUpdatedSuccessfully');
2903
- },
2904
- error: (err) => this.alertService.showError('@updateFailed', err),
2905
- });
2733
+ isAuthenticated() {
2734
+ return !!this.credentials;
2906
2735
  }
2907
2736
  /**
2908
- * Create new record
2737
+ * Gets the user credentials.
2738
+ * @return The user credentials or null if the user is not authenticated.
2909
2739
  */
2910
- async createNewData() {
2911
- // If no GUID provided, initialize a new record
2912
- let newRecord = this.createNewRecord();
2913
- newRecord = await lastValueFrom(this.nettyAppsProxy.initRecord(newRecord))
2914
- .catch((er) => {
2915
- this.alertService.showError('@initRecordFailed:', er);
2916
- return this.createNewRecord();
2917
- });
2918
- this.initializeFormData(newRecord);
2919
- this.updateValid.set(true);
2740
+ get credentials() {
2741
+ return this._credentials;
2742
+ }
2743
+ get token() {
2744
+ return this._credentials?.token ?? null;
2920
2745
  }
2921
2746
  /**
2922
- * Load data from API based on GUID
2923
- * Handles both new record creation and existing record editing
2924
- * guid - Unique identifier of the record
2747
+ * Sets the user credentials.
2748
+ * The credentials may be persisted across sessions by setting the `remember` parameter to true.
2749
+ * Otherwise, the credentials are only persisted for the current session.
2750
+ * @param credentials The user credentials.
2751
+ * @param remember True to remember credentials across sessions.
2925
2752
  */
2926
- async loadDetailData() {
2927
- const guid = this.getGuidFromParameters();
2928
- if (!guid) {
2929
- await this.createNewData();
2930
- return;
2753
+ setCredentials(credentials, remember) {
2754
+ this._credentials = credentials || null;
2755
+ if (credentials) {
2756
+ const storage = remember ? localStorage : sessionStorage;
2757
+ storage.setItem(credentialsKey, JSON.stringify(credentials));
2758
+ }
2759
+ else {
2760
+ sessionStorage.removeItem(credentialsKey);
2761
+ localStorage.removeItem(credentialsKey);
2931
2762
  }
2932
- // Fetch existing record from API
2933
- this.nettyAppsProxy.selectGUID(guid).subscribe({
2934
- next: (data) => {
2935
- const record = this.createItemInstance(data);
2936
- if (!record || !record.getPK()) {
2937
- this.alertService.showError('@recordNotFound');
2938
- const cleanPath = this.commonService.getCleanUrlPath();
2939
- this.router.navigate([cleanPath]);
2940
- return;
2941
- }
2942
- this.initializeFormData(record);
2943
- this.alertService.showSuccess('@dataLoadSuccess');
2944
- // Ensure sidenav is visible when in sidenav mode
2945
- if (this.viewMode() === 'sidenav') {
2946
- this.commonService.toggleRightSidenav(true);
2947
- }
2948
- },
2949
- error: (err) => {
2950
- this.alertService.showError('@dataLoadFailed');
2951
- this.commonService.toggleRightSidenav(false);
2952
- // Redirect to user list on error in fullscreen mode
2953
- if (this.viewMode() === 'fullscreen') {
2954
- const cleanPath = this.commonService.getCleanUrlPath();
2955
- this.router.navigate([cleanPath]);
2956
- }
2957
- },
2958
- });
2959
2763
  }
2960
- // ***************************************************
2961
- // *** gotoURL Methods ***
2962
- // ***************************************************
2963
- gotoURL(routePrefix, rightSidenav = [], parameters, type, dialogComponent = null, isNewTab = false, isPopup = this._isEmbedded()) {
2964
- const baseHref = this.environment.getBaseHref().endsWith('/')
2965
- ? this.environment.getBaseHref().slice(0, -1) // Sondaki / işaretini kaldır
2966
- : this.environment.getBaseHref();
2967
- const navigationExtras = {
2968
- queryParams: {
2969
- parameters: JSON.stringify(parameters),
2970
- ...(type && { type }),
2971
- isNewTab: isNewTab || undefined,
2972
- },
2973
- queryParamsHandling: 'merge',
2974
- };
2975
- if (isNewTab) {
2976
- const fullUrl = this.router
2977
- .createUrlTree([baseHref, ...routePrefix, ...rightSidenav], navigationExtras)
2978
- .toString();
2979
- window.open(fullUrl, '_blank');
2980
- return;
2764
+ /** Get Credentials
2765
+ *
2766
+ * @returns
2767
+ */
2768
+ getCredentials() {
2769
+ let _credentialsString = sessionStorage.getItem(credentialsKey);
2770
+ if (_credentialsString == null || _credentialsString == '') {
2771
+ _credentialsString = localStorage.getItem(credentialsKey);
2981
2772
  }
2982
- if (isPopup && dialogComponent) {
2983
- this.dialog
2984
- .open(dialogComponent, {
2985
- data: {
2986
- parameters: parameters,
2987
- mode: type || 'edit',
2988
- embedded: true,
2989
- },
2990
- maxWidth: '100vw',
2991
- disableClose: true,
2992
- hasBackdrop: false,
2993
- })
2994
- .afterClosed();
2995
- return;
2773
+ return JSON.parse(_credentialsString ?? '');
2774
+ }
2775
+ /** Get the token if available otherwise return empty string
2776
+ *
2777
+ * @returns
2778
+ */
2779
+ getToken() {
2780
+ try {
2781
+ let _credentials = this.getCredentials();
2782
+ return _credentials.token;
2996
2783
  }
2997
- // Log control
2998
- if (rightSidenav.length == 0 || rightSidenav[0] === 'log') {
2999
- this.router.navigate([baseHref, ...routePrefix, ...rightSidenav], navigationExtras);
3000
- return;
2784
+ catch (error) {
2785
+ return '';
3001
2786
  }
3002
- // In all other cases, open the side menu
3003
- this.router
3004
- .navigate([
3005
- {
3006
- outlets: {
3007
- primary: routePrefix,
3008
- rightSidenav: [...routePrefix, ...rightSidenav],
3009
- },
3010
- },
3011
- ], navigationExtras)
3012
- .then(() => {
3013
- // Ensure sidenav is opened after navigation
3014
- this.commonService.toggleRightSidenav(true);
3015
- });
3016
2787
  }
3017
- popupGotoURL(urlSegments) {
3018
- const baseHref = this.environment.getBaseHref().endsWith('/')
3019
- ? this.environment.getBaseHref().slice(0, -1) // Sondaki / işaretini kaldır
3020
- : this.environment.getBaseHref();
3021
- this.router.navigate([
3022
- {
3023
- outlets: {
3024
- primary: [baseHref, ...urlSegments],
3025
- rightSidenav: null,
3026
- },
2788
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CredentialsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2789
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CredentialsService, providedIn: 'root' });
2790
+ }
2791
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CredentialsService, decorators: [{
2792
+ type: Injectable,
2793
+ args: [{
2794
+ providedIn: 'root',
2795
+ }]
2796
+ }], ctorParameters: () => [] });
2797
+
2798
+ class UrlHelperService {
2799
+ router = inject(Router);
2800
+ cleanUrl(url) {
2801
+ let result = url
2802
+ .replace('/mfalogin?redirect=', '')
2803
+ .replace('/mfalogin?redirect=', '')
2804
+ .replace('/mfalogin?redirect=', '')
2805
+ .replace('/login?redirect=', '')
2806
+ .replace('/login?redirect=', '')
2807
+ .replace('/login?redirect=', '')
2808
+ .replace(new RegExp('%25', 'g'), '%')
2809
+ .replace(new RegExp('%25', 'g'), '%')
2810
+ .replace(new RegExp('%25', 'g'), '%')
2811
+ .replace(new RegExp('%25', 'g'), '%')
2812
+ .replace(new RegExp('%25', 'g'), '%')
2813
+ .replace(new RegExp('%25', 'g'), '%')
2814
+ .replace(new RegExp('%22', 'g'), '"')
2815
+ .replace(new RegExp('%3F', 'g'), '?')
2816
+ .replace(new RegExp('%3D', 'g'), '=')
2817
+ .replace(new RegExp('%2F', 'g'), '/')
2818
+ .replace(new RegExp('%26', 'g'), '&')
2819
+ .replace(new RegExp('/mfalogin?redirect=', 'g'), '')
2820
+ .replace(new RegExp('/login?redirect=', 'g'), '');
2821
+ console.log('url:', url);
2822
+ console.log('result:', result);
2823
+ // return url;
2824
+ return result;
2825
+ }
2826
+ navigate(url) {
2827
+ let urlParts = url.split('?');
2828
+ if (urlParts.length == 1) {
2829
+ return this.router.navigate(urlParts, { replaceUrl: true });
2830
+ }
2831
+ let parameters = urlParts[1].split('&');
2832
+ return this.router.navigate([urlParts[0]], {
2833
+ queryParams: {
2834
+ parameters: parameters[0]?.replace('parameters=', '') ?? '',
2835
+ type: parameters[1]?.replace('type=', '') ?? '',
3027
2836
  },
3028
- ]);
2837
+ replaceUrl: true,
2838
+ });
3029
2839
  }
3030
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridSaveBase, deps: [], target: i0.ɵɵFactoryTarget.Component });
3031
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: NettyAgGridSaveBase, isStandalone: true, selector: "ntybase-ag-grid-save-base", inputs: { parameters: { classPropertyName: "parameters", publicName: "parameters", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "saveForm", first: true, predicate: ["saveForm"], descendants: true }], usesInheritance: true, ngImport: i0, template: ``, isInline: true });
2840
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: UrlHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2841
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: UrlHelperService, providedIn: 'root' });
3032
2842
  }
3033
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridSaveBase, decorators: [{
3034
- type: Component,
3035
- args: [{ selector: 'ntybase-ag-grid-save-base', imports: [], template: `` }]
3036
- }], ctorParameters: () => [], propDecorators: { parameters: [{ type: i0.Input, args: [{ isSignal: true, alias: "parameters", required: false }] }], saveForm: [{
3037
- type: ViewChild,
3038
- args: ['saveForm']
3039
- }] } });
2843
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: UrlHelperService, decorators: [{
2844
+ type: Injectable,
2845
+ args: [{
2846
+ providedIn: 'root',
2847
+ }]
2848
+ }] });
3040
2849
 
3041
- class RangeDateTimeFilter {
3042
- params;
3043
- filterText = '';
3044
- conditions = [];
3045
- // Dil servisini inject edin
3046
- i18nService = inject(I18nService);
3047
- translateService = inject(TranslateService);
3048
- // Türkçe ve İngilizce özel anahtar kelimeler
3049
- specialKeywords = {
3050
- today: 'today',
3051
- bugün: 'today',
3052
- yesterday: 'yesterday',
3053
- dün: 'yesterday',
3054
- thisweek: 'thisWeek',
3055
- 'bu hafta': 'thisWeek',
3056
- thismonth: 'thisMonth',
3057
- 'bu ay': 'thisMonth',
3058
- };
3059
- agInit(params) {
3060
- this.params = params;
3061
- }
3062
- onFilterTextChanged(event) {
3063
- this.filterText = event.target.value;
3064
- this.parseFilterText();
3065
- this.params.filterChangedCallback();
3066
- }
3067
- parseFilterText() {
3068
- this.conditions = [];
3069
- if (!this.filterText.trim()) {
3070
- return;
3071
- }
3072
- // Virgülle ayrılmış parçaları al ve temizle
3073
- const parts = this.filterText
3074
- .split(',')
3075
- .map((part) => part.trim())
3076
- .filter((part) => part !== '');
3077
- for (const part of parts) {
3078
- // Aralık kontrolü (.. içeriyor mu) - ÖNCE aralık kontrolü yap
3079
- if (part.includes('..')) {
3080
- const rangeParts = part.split('..');
3081
- if (rangeParts.length === 2) {
3082
- const startStr = rangeParts[0].trim();
3083
- const endStr = rangeParts[1].trim();
3084
- const startDate = this.parseDateForRange(startStr, false);
3085
- const endDate = this.parseDateForRange(endStr, true);
3086
- if (startDate && endDate) {
3087
- // Tarihleri doğru sırala (küçükten büyüğe)
3088
- const sortedStart = startDate < endDate ? startDate : endDate;
3089
- const sortedEnd = startDate < endDate ? endDate : startDate;
3090
- this.conditions.push({
3091
- type: 'range',
3092
- start: sortedStart,
3093
- end: sortedEnd,
3094
- });
3095
- }
2850
+ class AuthenticationInterceptor {
2851
+ router = inject(Router);
2852
+ credentialsService = inject(CredentialsService);
2853
+ environmentProxy = inject(EnvironmentProxy);
2854
+ urlHelperService = inject(UrlHelperService);
2855
+ intercept(req, next) {
2856
+ if (req.headers.get('No-Auth') == 'True')
2857
+ return next.handle(req.clone());
2858
+ let token = this.credentialsService.token;
2859
+ if (token != null) {
2860
+ let appName = this.environmentProxy.getApplicationName();
2861
+ const clonedreq = req.clone({
2862
+ headers: req.headers.set('Authorization', 'Bearer ' + token),
2863
+ // .set("NettyAppName",appName)
2864
+ });
2865
+ return next.handle(clonedreq).pipe(catchError$1((error) => {
2866
+ if (error instanceof HttpErrorResponse && error.status === 401) {
2867
+ // Handle 401 error, e.g., navigate to the login page
2868
+ this.router.navigate(['/login'], {
2869
+ queryParams: {
2870
+ redirect: this.urlHelperService.cleanUrl(this.router.url),
2871
+ },
2872
+ replaceUrl: true,
2873
+ });
2874
+ // Return an observable with a successful response
2875
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
3096
2876
  }
3097
- }
3098
- else {
3099
- // Özel anahtar kelime kontrolü (Türkçe ve İngilizce)
3100
- const normalizedPart = part.toLowerCase();
3101
- if (this.specialKeywords[normalizedPart]) {
3102
- this.conditions.push({
3103
- type: 'special',
3104
- special: this.specialKeywords[normalizedPart],
2877
+ if (error instanceof HttpErrorResponse && error.status === 403) {
2878
+ this.router.navigate(['/forbidden'], {
2879
+ state: { attemptedUrl: this.router.url }, // Orijinal URL'i state olarak geçme
3105
2880
  });
3106
- continue;
2881
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
3107
2882
  }
3108
- // Tek tarih kontrolü
3109
- const date = this.parseDate(part);
3110
- if (date) {
3111
- this.conditions.push({
3112
- type: 'date',
3113
- date: date,
2883
+ if (error instanceof HttpErrorResponse && error.status === 428) {
2884
+ // Handle 428 error, e.g., navigate to the login page
2885
+ this.router.navigate(['/mfalogin'], {
2886
+ queryParams: {
2887
+ redirect: this.urlHelperService.cleanUrl(this.router.url),
2888
+ },
2889
+ replaceUrl: true,
3114
2890
  });
2891
+ // Return an observable with a successful response
2892
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
3115
2893
  }
3116
- }
3117
- }
3118
- }
3119
- parseDateForRange(dateString, isEndDate) {
3120
- if (!dateString)
3121
- return null;
3122
- // Özel anahtar kelimeler için tarih dönüşümü
3123
- const normalizedString = dateString.toLowerCase();
3124
- if (this.specialKeywords[normalizedString]) {
3125
- const specialType = this.specialKeywords[normalizedString];
3126
- const range = this.getSpecialDateRange(specialType);
3127
- return isEndDate ? range.end : range.start;
2894
+ // For other errors, re-throw the error to propagate it further
2895
+ return throwError(() => error);
2896
+ }));
3128
2897
  }
3129
- return this.parseDate(dateString);
3130
- }
3131
- // Mevcut dili kontrol eden yardımcı metod
3132
- isTurkish() {
3133
- const currentLang = this.i18nService.language.toLowerCase();
3134
- return (currentLang === 'tr' ||
3135
- currentLang === 'türkçe' ||
3136
- currentLang === 'tr-tr');
3137
- }
3138
- parseDate(dateString) {
3139
- if (!dateString)
3140
- return null;
3141
- // Mevcut dile göre formatları belirle
3142
- const isTurkish = this.isTurkish();
3143
- // Tarih formatları - ÖNCE saat içeren formatlar
3144
- const formats = [
3145
- // ISO format: YYYY-AA-GG SS:DD (her dilde aynı)
3146
- /^(\d{4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{2})$/,
3147
- // Dile göre formatlar
3148
- ...(isTurkish
3149
- ? [
3150
- // Türkçe format: GG.AA.YYYY SS:DD (., /, - ile)
3151
- /^(\d{1,2})[\.\/-](\d{1,2})[\.\/-](\d{4}) (\d{1,2}):(\d{2})$/,
3152
- // Türkçe format: GG.AA.YYYY (., /, - ile)
3153
- /^(\d{1,2})[\.\/-](\d{1,2})[\.\/-](\d{4})$/,
3154
- ]
3155
- : [
3156
- // İngilizce/USA format: AA/GG/YYYY SS:DD (/, ., - ile)
3157
- /^(\d{1,2})[\/\.-](\d{1,2})[\/\.-](\d{4}) (\d{1,2}):(\d{2})$/,
3158
- // İngilizce/USA format: AA/GG/YYYY (/, ., - ile)
3159
- /^(\d{1,2})[\/\.-](\d{1,2})[\/\.-](\d{4})$/,
3160
- ]),
3161
- // Sadece tarih formatları (saat yok)
3162
- /^(\d{4})-(\d{1,2})-(\d{1,2})$/, // YYYY-AA-GG (ISO)
3163
- ];
3164
- for (const format of formats) {
3165
- const match = dateString.match(format);
3166
- if (match) {
3167
- let year, month, day, hours = 0, minutes = 0;
3168
- if (format === formats[0]) {
3169
- // ISO: YYYY-AA-GG SS:DD
3170
- year = parseInt(match[1], 10);
3171
- month = parseInt(match[2], 10) - 1;
3172
- day = parseInt(match[3], 10);
3173
- hours = parseInt(match[4], 10);
3174
- minutes = parseInt(match[5], 10);
3175
- }
3176
- else if (isTurkish && format === formats[1]) {
3177
- // Türkçe: GG.AA.YYYY SS:DD
3178
- day = parseInt(match[1], 10);
3179
- month = parseInt(match[2], 10) - 1;
3180
- year = parseInt(match[3], 10);
3181
- hours = parseInt(match[4], 10);
3182
- minutes = parseInt(match[5], 10);
3183
- }
3184
- else if (!isTurkish && format === formats[1]) {
3185
- // İngilizce: AA/GG/YYYY SS:DD
3186
- month = parseInt(match[1], 10) - 1;
3187
- day = parseInt(match[2], 10);
3188
- year = parseInt(match[3], 10);
3189
- hours = parseInt(match[4], 10);
3190
- minutes = parseInt(match[5], 10);
3191
- }
3192
- else if (isTurkish && format === formats[2]) {
3193
- // Türkçe: GG.AA.YYYY
3194
- day = parseInt(match[1], 10);
3195
- month = parseInt(match[2], 10) - 1;
3196
- year = parseInt(match[3], 10);
3197
- }
3198
- else if (!isTurkish && format === formats[2]) {
3199
- // İngilizce: AA/GG/YYYY
3200
- month = parseInt(match[1], 10) - 1;
3201
- day = parseInt(match[2], 10);
3202
- year = parseInt(match[3], 10);
3203
- }
3204
- else if (format === formats[3]) {
3205
- // ISO: YYYY-AA-GG
3206
- year = parseInt(match[1], 10);
3207
- month = parseInt(match[2], 10) - 1;
3208
- day = parseInt(match[3], 10);
2898
+ else {
2899
+ return next.handle(req.clone()).pipe(catchError$1((error) => {
2900
+ if (error instanceof HttpErrorResponse && error.status === 401) {
2901
+ // Handle 401 error, e.g., navigate to the login page
2902
+ this.router.navigate(['/login'], {
2903
+ queryParams: {
2904
+ redirect: this.urlHelperService.cleanUrl(this.router.url),
2905
+ },
2906
+ replaceUrl: true,
2907
+ });
2908
+ // Return an observable with a successful response
2909
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
3209
2910
  }
3210
- else {
3211
- continue;
2911
+ if (error instanceof HttpErrorResponse && error.status === 403) {
2912
+ this.router.navigate(['/forbidden'], {
2913
+ state: { attemptedUrl: this.router.url }, // Orijinal URL'i state olarak geçme
2914
+ });
2915
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
3212
2916
  }
3213
- // Tarih geçerlilik kontrolü
3214
- const date = new Date(year, month, day, hours, minutes);
3215
- if (date.getFullYear() === year &&
3216
- date.getMonth() === month &&
3217
- date.getDate() === day &&
3218
- date.getHours() === hours &&
3219
- date.getMinutes() === minutes) {
3220
- return date;
2917
+ if (error instanceof HttpErrorResponse && error.status === 428) {
2918
+ // Handle 428 error, e.g., navigate to the login page
2919
+ this.router.navigate(['/mfalogin'], {
2920
+ queryParams: {
2921
+ redirect: this.urlHelperService.cleanUrl(this.router.url),
2922
+ },
2923
+ replaceUrl: true,
2924
+ });
2925
+ // Return an observable with a successful response
2926
+ return of(new HttpResponse({ status: 200, body: { message: 'Success' } }));
3221
2927
  }
3222
- }
2928
+ // For other errors, re-throw the error to propagate it further
2929
+ return throwError(() => error);
2930
+ }));
3223
2931
  }
3224
- return null;
3225
2932
  }
3226
- getSpecialDateRange(special) {
3227
- const now = new Date();
3228
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
3229
- switch (special) {
3230
- case 'today':
3231
- const tomorrow = new Date(today);
3232
- tomorrow.setDate(tomorrow.getDate() + 1);
3233
- return { start: today, end: tomorrow };
3234
- case 'yesterday':
3235
- const yesterday = new Date(today);
3236
- yesterday.setDate(yesterday.getDate() - 1);
3237
- const yesterdayEnd = new Date(yesterday);
3238
- yesterdayEnd.setDate(yesterdayEnd.getDate() + 1);
3239
- return { start: yesterday, end: yesterdayEnd };
3240
- case 'thisWeek':
3241
- const startOfWeek = new Date(today);
3242
- startOfWeek.setDate(today.getDate() - today.getDay());
3243
- const endOfWeek = new Date(startOfWeek);
3244
- endOfWeek.setDate(startOfWeek.getDate() + 7);
3245
- return { start: startOfWeek, end: endOfWeek };
3246
- case 'thisMonth':
3247
- const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
3248
- const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);
3249
- return { start: startOfMonth, end: endOfMonth };
3250
- default:
3251
- return { start: today, end: today };
2933
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AuthenticationInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2934
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AuthenticationInterceptor });
2935
+ }
2936
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AuthenticationInterceptor, decorators: [{
2937
+ type: Injectable
2938
+ }] });
2939
+
2940
+ class CanDeactivateGuard {
2941
+ canDeactivate(component) {
2942
+ return component.canDeactivate ? component.canDeactivate() : true;
2943
+ }
2944
+ }
2945
+
2946
+ class NtybaseModule {
2947
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NtybaseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2948
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.2", ngImport: i0, type: NtybaseModule, declarations: [Ntybase], exports: [Ntybase] });
2949
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NtybaseModule, providers: [
2950
+ {
2951
+ provide: HTTP_INTERCEPTORS,
2952
+ useClass: AuthenticationInterceptor,
2953
+ multi: true,
2954
+ },
2955
+ [CanDeactivateGuard],
2956
+ DatePipe,
2957
+ ] });
2958
+ }
2959
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NtybaseModule, decorators: [{
2960
+ type: NgModule,
2961
+ args: [{
2962
+ declarations: [Ntybase],
2963
+ imports: [],
2964
+ exports: [Ntybase],
2965
+ providers: [
2966
+ {
2967
+ provide: HTTP_INTERCEPTORS,
2968
+ useClass: AuthenticationInterceptor,
2969
+ multi: true,
2970
+ },
2971
+ [CanDeactivateGuard],
2972
+ DatePipe,
2973
+ ],
2974
+ }]
2975
+ }] });
2976
+
2977
+ class Guid {
2978
+ value = this.empty;
2979
+ constructor(value) {
2980
+ if (value) {
2981
+ if (Guid.isValid(value)) {
2982
+ this.value = value;
2983
+ }
3252
2984
  }
3253
2985
  }
3254
- doesFilterPass(params) {
3255
- const field = this.params.colDef.field;
3256
- if (!field)
3257
- return true;
3258
- const cellValue = params.data ? params.data[field] : null;
3259
- // Eğer filtre yoksa tüm satırları göster
3260
- if (this.conditions.length === 0) {
2986
+ static newGuid() {
2987
+ return new Guid('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
2988
+ const r = (Math.random() * 16) | 0;
2989
+ const v = c == 'x' ? r : (r & 0x3) | 0x8;
2990
+ return v.toString(16);
2991
+ }));
2992
+ }
2993
+ /**
2994
+ * return all zeros '00000000-0000-0000-0000-000000000000'
2995
+ */
2996
+ static get empty() {
2997
+ return '00000000-0000-0000-0000-000000000000';
2998
+ }
2999
+ get empty() {
3000
+ return Guid.empty;
3001
+ }
3002
+ static isValid(str) {
3003
+ const validRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i;
3004
+ return validRegex.test(str);
3005
+ }
3006
+ toString() {
3007
+ return this.value;
3008
+ }
3009
+ toJSON() {
3010
+ return this.value;
3011
+ }
3012
+ /**
3013
+ * True is guid is empty or not valid
3014
+ * @param str
3015
+ * @returns
3016
+ */
3017
+ static isNullOrEmpty(str) {
3018
+ if (str == null || str == undefined || str.trim() == '' || str == Guid.empty) {
3261
3019
  return true;
3262
3020
  }
3263
- // Hücre değeri null/undefined ise filtrele
3264
- if (!cellValue) {
3265
- return false;
3266
- }
3267
- // Hücre değerini Date'e dönüştür
3268
- let cellDate;
3269
- if (cellValue instanceof Date) {
3270
- cellDate = cellValue;
3271
- }
3272
- else if (typeof cellValue === 'string') {
3273
- // String ise Date'e parse et
3274
- cellDate = new Date(cellValue);
3275
- if (isNaN(cellDate.getTime())) {
3276
- return false; // Geçersiz tarih
3277
- }
3021
+ if (!Guid.isValid(str)) {
3022
+ return true;
3278
3023
  }
3279
- else {
3280
- return false; // Desteklenmeyen tip
3024
+ return false;
3025
+ }
3026
+ /**
3027
+ * True if the guid is valid and not all zeros (empty)
3028
+ * @param str
3029
+ * @returns
3030
+ */
3031
+ static isValidAndNotEmpty(str) {
3032
+ return !Guid.isNullOrEmpty(str);
3033
+ }
3034
+ /**
3035
+ * Return empty guid if the given guid is not valid
3036
+ * @param guid
3037
+ * @returns
3038
+ */
3039
+ static emptyWhenNull(guid) {
3040
+ if (Guid.isValidAndNotEmpty(guid)) {
3041
+ return guid;
3281
3042
  }
3282
- const cellTime = cellDate.getTime();
3283
- // Koşullardan herhangi birini sağlıyor mu kontrol et
3284
- return this.conditions.some((condition) => {
3285
- switch (condition.type) {
3286
- case 'date':
3287
- if (!condition.date)
3288
- return false;
3289
- const conditionTime = condition.date.getTime();
3290
- const nextDay = new Date(condition.date);
3291
- nextDay.setDate(nextDay.getDate() + 1);
3292
- return cellTime >= conditionTime && cellTime < nextDay.getTime();
3293
- case 'range':
3294
- if (!condition.start || !condition.end)
3295
- return false;
3296
- const startTime = condition.start.getTime();
3297
- const endTime = condition.end.getTime();
3298
- return cellTime >= startTime && cellTime < endTime;
3299
- case 'special':
3300
- if (!condition.special)
3301
- return false;
3302
- const range = this.getSpecialDateRange(condition.special);
3303
- return (cellTime >= range.start.getTime() && cellTime < range.end.getTime());
3304
- default:
3305
- return false;
3043
+ return Guid.empty;
3044
+ }
3045
+ }
3046
+
3047
+ ModuleRegistry.registerModules([AllCommunityModule, StatusBarModule, ClientSideRowModelModule, ClipboardModule, ExcelExportModule, ColumnMenuModule,
3048
+ ContextMenuModule, CellSelectionModule, HighlightChangesModule, RowSelectionModule,]);
3049
+ // AgGrid Dark Mode Row Style
3050
+ class NettyAgGridListFilterBase extends NettyAgGridListBase {
3051
+ // ********************************************
3052
+ // *** INPUTS ***
3053
+ // ********************************************
3054
+ // Filter section
3055
+ displayFilter = input(true, ...(ngDevMode ? [{ debugName: "displayFilter" }] : [])); // Does the component display it's filter section when embadded
3056
+ _displayFilter = computed(() => this.displayFilter() ?? true, ...(ngDevMode ? [{ debugName: "_displayFilter" }] : [])); // Computed version of displayFilter to prevent undefined
3057
+ isFilterValid = signal(true, ...(ngDevMode ? [{ debugName: "isFilterValid" }] : [])); // Can the filter be used
3058
+ isFilterExpanded = linkedSignal({ ...(ngDevMode ? { debugName: "isFilterExpanded" } : {}), // Is the filter component expanded
3059
+ source: () => ({
3060
+ embedded: this._isEmbedded(),
3061
+ displayFilter: this._displayFilter(),
3062
+ valid: this.isFilterValid(),
3063
+ }),
3064
+ computation: (s) => {
3065
+ if (s.embedded || !s.displayFilter)
3066
+ return false;
3067
+ return s.valid;
3068
+ } });
3069
+ filterRefreshTrigger = signal(0, ...(ngDevMode ? [{ debugName: "filterRefreshTrigger" }] : []));
3070
+ constructor() {
3071
+ super();
3072
+ effect(() => {
3073
+ if (this.hasValidValue(this.parameterGUID())) {
3074
+ this.isFilterExpanded.set(false);
3075
+ this.setFilter();
3076
+ this.loadData();
3306
3077
  }
3307
3078
  });
3308
3079
  }
3309
- isFilterActive() {
3310
- return this.conditions.length > 0;
3311
- }
3312
- getModel() {
3313
- if (this.isFilterActive()) {
3314
- return {
3315
- filterType: 'custom',
3316
- filterText: this.filterText,
3317
- conditions: this.conditions,
3318
- };
3080
+ async ngOnInit() {
3081
+ await super.ngOnInit();
3082
+ }
3083
+ // *********************************************************
3084
+ // *** Data Management Functions ***
3085
+ // *********************************************************
3086
+ loadData() {
3087
+ if (Guid.isNullOrEmpty(this.parameterGUID())) {
3088
+ this.setData([], true);
3089
+ return;
3090
+ }
3091
+ this.nettyAppsProxy.select(this.record).subscribe({
3092
+ next: (data) => {
3093
+ this.setData(data, false);
3094
+ },
3095
+ error: (err) => this.alertService.showError('@dataLoadFailed', err),
3096
+ });
3097
+ }
3098
+ async refreshData() {
3099
+ try {
3100
+ this.refreshFilterData();
3101
+ }
3102
+ catch (err) {
3103
+ this.alertService.showError(err);
3319
3104
  }
3320
- return null;
3321
3105
  }
3322
- setModel(model) {
3323
- if (model && model.filterText) {
3324
- this.filterText = model.filterText;
3325
- this.parseFilterText(); // Modelden set ederken koşulları tekrar parse et
3106
+ /**
3107
+ * Triggers the filter component to refresh its data
3108
+ */
3109
+ refreshFilterData() {
3110
+ this.filterRefreshTrigger.update(val => val > 10000 ? 1 : val + 1);
3111
+ }
3112
+ onReverseIsFilterValid() {
3113
+ this.isFilterValid.update((a) => !a);
3114
+ }
3115
+ // *****************************************
3116
+ // *** Logging Functions ***
3117
+ // *****************************************
3118
+ logInputs(message) {
3119
+ if (!message || message.length < 1) {
3120
+ message = 'AgGridListFilterBase - Inputs log';
3326
3121
  }
3327
- else {
3328
- this.filterText = '';
3329
- this.conditions = [];
3122
+ const inputs = {
3123
+ "popupFilterValid": this.popupFilterValid(),
3124
+ "_isPopupFilterValid": this._isPopupFilterValid(),
3125
+ "popupValid": this.popupValid(),
3126
+ "_isPopupValid": this._isPopupValid(),
3127
+ "componantParameterGUID": this.componantParameterGUID(),
3128
+ "componantParameterType": this.componantParameterType(),
3129
+ "embedded": this.embedded(),
3130
+ "_isEmbedded": this._isEmbedded(),
3131
+ "displayFilter": this.displayFilter(),
3132
+ "_displayFilter": this._displayFilter(),
3133
+ "isFilterValid": this.isFilterValid(),
3134
+ "isFilterExpanded": this.isFilterExpanded(),
3135
+ };
3136
+ console.log(message, inputs);
3137
+ }
3138
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridListFilterBase, deps: [], target: i0.ɵɵFactoryTarget.Component });
3139
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: NettyAgGridListFilterBase, isStandalone: true, selector: "ntybase-ag-grid-list-filter-base", inputs: { displayFilter: { classPropertyName: "displayFilter", publicName: "displayFilter", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "ntybase-id": "NettyAgGridListFilterBase" } }, usesInheritance: true, ngImport: i0, template: ``, isInline: true });
3140
+ }
3141
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridListFilterBase, decorators: [{
3142
+ type: Component,
3143
+ args: [{ selector: 'ntybase-ag-grid-list-filter-base', imports: [], template: ``, host: { 'ntybase-id': 'NettyAgGridListFilterBase' } }]
3144
+ }], ctorParameters: () => [], propDecorators: { displayFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayFilter", required: false }] }] } });
3145
+
3146
+ class NettyAgGridLogBase extends NettyAgGridBase {
3147
+ // ---------------------------------------------------
3148
+ // --- RECORD LIST ---
3149
+ // ---------------------------------------------------
3150
+ nettyAppsProxy = injectNettyStandardLogProxy(this.componentName());
3151
+ /**
3152
+ * Component initialization lifecycle hook
3153
+ */
3154
+ async ngOnInit() {
3155
+ this.nettyAppsProxy.setURLPath(this.componentName());
3156
+ await this.setAccessRights(true);
3157
+ const savedSearchValue = sessionStorage.getItem(this.searchValueName());
3158
+ if (savedSearchValue) {
3159
+ this.searchValue.set(savedSearchValue);
3330
3160
  }
3161
+ this.loadData();
3162
+ // Load user grid preferences
3163
+ await this.nettyAgGridService.copyGridUserPereferenceToLocal(this.preferenceType());
3164
+ await this.AfterOnInit();
3331
3165
  }
3332
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeDateTimeFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
3333
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: RangeDateTimeFilter, isStandalone: true, selector: "ntybase-range-date-time-filter", host: { attributes: { "ntybase-id": "RangeDateTimeFilter" } }, ngImport: i0, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@dateTimeFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@dateTimeFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\">\n {{ '@formats' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@range' | translate }}:\n <code>start..end</code>\n <span class=\"custom-filter__example\">(today..yesterday)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@singleDate' | translate }}:\n <code>YYYY-MM-DD</code>\n <span class=\"custom-filter__example\">(2024-05-15)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@dateTime' | translate }}:\n <code>YYYY-MM-DD HH:MM</code>\n <span class=\"custom-filter__example\">(2024-05-15 14:30)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>date1,date2,start..end</code>\n </div>\n\n <strong class=\"custom-filter__subtitle\">\n {{ '@specialKeywords' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@turkish' | translate }}:\n <span class=\"custom-filter__keywords\">\n <code class=\"custom-filter__keyword\">bug\u00FCn</code>\n <code class=\"custom-filter__keyword\">d\u00FCn</code>\n <code class=\"custom-filter__keyword\">bu hafta</code>\n <code class=\"custom-filter__keyword\">bu ay</code>\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@english' | translate }}:\n <span class=\"custom-filter__keywords\">\n <code class=\"custom-filter__keyword\">today</code>\n <code class=\"custom-filter__keyword\">yesterday</code>\n <code class=\"custom-filter__keyword\">thisWeek</code>\n <code class=\"custom-filter__keyword\">thisMonth</code>\n </span>\n </div>\n\n <div class=\"custom-filter__note\">{{ '@filterExample' | translate }}</div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
3166
+ loadData() {
3167
+ this.nettyAppsProxy.selectLog(this.record).subscribe({
3168
+ next: (data) => {
3169
+ this.setData(data, false);
3170
+ },
3171
+ error: (err) => this.alertService.showError('@dataLoadFailed', err),
3172
+ });
3173
+ }
3174
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridLogBase, deps: null, target: i0.ɵɵFactoryTarget.Component });
3175
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: NettyAgGridLogBase, isStandalone: true, selector: "ntybase-ag-grid-log-base", host: { attributes: { "ntybase-id": "NettyAgGridLogBase" } }, usesInheritance: true, ngImport: i0, template: ``, isInline: true });
3334
3176
  }
3335
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeDateTimeFilter, decorators: [{
3177
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridLogBase, decorators: [{
3336
3178
  type: Component,
3337
- args: [{ selector: 'ntybase-range-date-time-filter', imports: [TranslateModule], host: { 'ntybase-id': 'RangeDateTimeFilter' }, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@dateTimeFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@dateTimeFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\">\n {{ '@formats' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@range' | translate }}:\n <code>start..end</code>\n <span class=\"custom-filter__example\">(today..yesterday)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@singleDate' | translate }}:\n <code>YYYY-MM-DD</code>\n <span class=\"custom-filter__example\">(2024-05-15)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@dateTime' | translate }}:\n <code>YYYY-MM-DD HH:MM</code>\n <span class=\"custom-filter__example\">(2024-05-15 14:30)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>date1,date2,start..end</code>\n </div>\n\n <strong class=\"custom-filter__subtitle\">\n {{ '@specialKeywords' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@turkish' | translate }}:\n <span class=\"custom-filter__keywords\">\n <code class=\"custom-filter__keyword\">bug\u00FCn</code>\n <code class=\"custom-filter__keyword\">d\u00FCn</code>\n <code class=\"custom-filter__keyword\">bu hafta</code>\n <code class=\"custom-filter__keyword\">bu ay</code>\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@english' | translate }}:\n <span class=\"custom-filter__keywords\">\n <code class=\"custom-filter__keyword\">today</code>\n <code class=\"custom-filter__keyword\">yesterday</code>\n <code class=\"custom-filter__keyword\">thisWeek</code>\n <code class=\"custom-filter__keyword\">thisMonth</code>\n </span>\n </div>\n\n <div class=\"custom-filter__note\">{{ '@filterExample' | translate }}</div>\n </div>\n</div>\n" }]
3179
+ args: [{ selector: 'ntybase-ag-grid-log-base', imports: [], template: ``, host: { 'ntybase-id': 'NettyAgGridLogBase' } }]
3338
3180
  }] });
3339
3181
 
3340
- class RangeNumberFilter {
3341
- params;
3342
- filterText = '';
3343
- conditions = [];
3344
- agInit(params) {
3345
- this.params = params;
3182
+ class NettyAgGridSaveBase extends NettyAppsBase {
3183
+ // Services
3184
+ router = inject(Router);
3185
+ route = inject(ActivatedRoute);
3186
+ commonService = inject(CommonService);
3187
+ environment = inject(EnvironmentProxy);
3188
+ dialog = inject(MatDialog);
3189
+ viewMode = signal('sidenav', ...(ngDevMode ? [{ debugName: "viewMode" }] : []));
3190
+ // Input signals
3191
+ parameters = input('', ...(ngDevMode ? [{ debugName: "parameters" }] : []));
3192
+ // ---------------------------------------------------
3193
+ // --- RECORD LIST ---
3194
+ // ---------------------------------------------------
3195
+ nettyAppsProxy = injectNettyStandardProxy('');
3196
+ recordType = signal('', ...(ngDevMode ? [{ debugName: "recordType" }] : []));
3197
+ currentItem = signal({}, ...(ngDevMode ? [{ debugName: "currentItem" }] : []));
3198
+ initialItem = {};
3199
+ // Form tracking
3200
+ formChanged = false;
3201
+ saveForm;
3202
+ // Dialog related properties
3203
+ dialogRef = inject((MatDialogRef), { optional: true });
3204
+ dialogData = inject(MAT_DIALOG_DATA, { optional: true });
3205
+ constructor() {
3206
+ super();
3207
+ effect(() => {
3208
+ this.parameters();
3209
+ this.loadDetailData();
3210
+ });
3346
3211
  }
3347
- onFilterTextChanged(event) {
3348
- this.filterText = event.target.value;
3349
- this.parseFilterText();
3350
- this.params.filterChangedCallback();
3212
+ // Controls the visibility of additional editable fields (e.g., password, extra settings)
3213
+ // When true, certain input fields become visible for editing
3214
+ updateValid = signal(false, ...(ngDevMode ? [{ debugName: "updateValid" }] : []));
3215
+ setUpdateValid(value) {
3216
+ this.updateValid.set(value);
3351
3217
  }
3352
- parseFilterText() {
3353
- this.conditions = [];
3354
- if (!this.filterText.trim()) {
3355
- return;
3218
+ /**
3219
+ * Initialize parameters. This method is called in ngOnInit or constructor
3220
+ */
3221
+ initParameters(urlPath) {
3222
+ this.nettyAppsProxy.setURLPath(urlPath);
3223
+ this.recordType.set(urlPath);
3224
+ }
3225
+ /**
3226
+ * Determine view mode based on current route
3227
+ */
3228
+ determineViewMode() {
3229
+ if (this.embedded() || this.dialogData?.embedded) {
3230
+ this.viewMode.set('dialog');
3356
3231
  }
3357
- const parts = this.filterText
3358
- .split(',')
3359
- .map((part) => part.trim())
3360
- .filter((part) => part !== '');
3361
- for (const part of parts) {
3362
- // Hariç tutma kontrolü (! ile başlıyor mu)
3363
- if (part.startsWith('!')) {
3364
- const excludePart = part.substring(1).trim();
3365
- // Hariç tutma için aralık kontrolü
3366
- if (excludePart.includes('..')) {
3367
- const rangeParts = excludePart.split('..');
3368
- if (rangeParts.length === 2) {
3369
- const minStr = rangeParts[0].trim();
3370
- const maxStr = rangeParts[1].trim();
3371
- // !..10 formatı (max hariç)
3372
- if (minStr === '' && maxStr !== '') {
3373
- const max = Number(maxStr);
3374
- if (!isNaN(max)) {
3375
- this.conditions.push({
3376
- type: 'exclude',
3377
- min: -Infinity,
3378
- max: max,
3379
- });
3380
- }
3381
- }
3382
- // !10.. formatı (min hariç)
3383
- else if (minStr !== '' && maxStr === '') {
3384
- const min = Number(minStr);
3385
- if (!isNaN(min)) {
3386
- this.conditions.push({
3387
- type: 'exclude',
3388
- min: min,
3389
- max: Infinity,
3390
- });
3391
- }
3392
- }
3393
- // !min..max formatı (aralık hariç)
3394
- else if (minStr !== '' && maxStr !== '') {
3395
- const min = Number(minStr);
3396
- const max = Number(maxStr);
3397
- if (!isNaN(min) && !isNaN(max)) {
3398
- this.conditions.push({
3399
- type: 'exclude',
3400
- min: Math.min(min, max),
3401
- max: Math.max(min, max),
3402
- });
3403
- }
3404
- }
3405
- }
3406
- }
3407
- else {
3408
- // Tek değer hariç tutma
3409
- const value = Number(excludePart);
3410
- if (!isNaN(value)) {
3411
- this.conditions.push({
3412
- type: 'exclude',
3413
- value: value,
3414
- });
3415
- }
3416
- }
3232
+ else if (this.router.url.includes('(rightSidenav:')) {
3233
+ this.viewMode.set('sidenav');
3234
+ }
3235
+ else {
3236
+ this.viewMode.set('fullscreen');
3237
+ }
3238
+ }
3239
+ /**
3240
+ * Set data to the form while preserving any unsaved changes
3241
+ * @param item - Data object of type T to populate the form
3242
+ */
3243
+ initializeFormData(item) {
3244
+ // Merge incoming data with current form state to preserve unsaved changes
3245
+ const currentItem = this.currentItem();
3246
+ const updatedItem = { ...currentItem, ...item };
3247
+ // Create a new instance using the factory method
3248
+ const newItem = this.createItemInstance(updatedItem);
3249
+ this.currentItem.set(newItem);
3250
+ // Update initial item reference for change detection
3251
+ this.initialItem = this.createItemInstance(updatedItem);
3252
+ // Reset form changed flag
3253
+ this.formChanged = false;
3254
+ }
3255
+ /**
3256
+ * @param data - Data to populate the new instance
3257
+ */
3258
+ createItemInstance(data) {
3259
+ const instance = this.createNewRecord();
3260
+ Object.assign(instance, data);
3261
+ return instance;
3262
+ }
3263
+ /**
3264
+ * Check for form changes
3265
+ */
3266
+ ngDoCheck() {
3267
+ this.formChanged = !this.isEqual(this.currentItem(), this.initialItem);
3268
+ }
3269
+ isEqual(item1, item2) {
3270
+ return JSON.stringify(item1) === JSON.stringify(item2);
3271
+ }
3272
+ /**
3273
+ * Check if can deactivate component
3274
+ */
3275
+ async canDeactivate() {
3276
+ if (this.formChanged) {
3277
+ const confirmed = await this.alertService.showConfirm('@unsavedChangesConfirm');
3278
+ if (!confirmed) {
3279
+ return false;
3417
3280
  }
3418
- // Aralık kontrolü (.. içeriyor mu)
3419
- else if (part.includes('..')) {
3420
- const rangeParts = part.split('..');
3421
- if (rangeParts.length === 2) {
3422
- const minStr = rangeParts[0].trim();
3423
- const maxStr = rangeParts[1].trim();
3424
- // ..10 formatı (max belirtilmiş)
3425
- if (minStr === '' && maxStr !== '') {
3426
- const max = Number(maxStr);
3427
- if (!isNaN(max)) {
3428
- this.conditions.push({
3429
- type: 'range',
3430
- min: -Infinity,
3431
- max: max,
3432
- });
3433
- }
3434
- }
3435
- // 10.. formatı (min belirtilmiş)
3436
- else if (minStr !== '' && maxStr === '') {
3437
- const min = Number(minStr);
3438
- if (!isNaN(min)) {
3439
- this.conditions.push({
3440
- type: 'range',
3441
- min: min,
3442
- max: Infinity,
3443
- });
3444
- }
3445
- }
3446
- // min..max formatı
3447
- else if (minStr !== '' && maxStr !== '') {
3448
- const min = Number(minStr);
3449
- const max = Number(maxStr);
3450
- if (!isNaN(min) && !isNaN(max)) {
3451
- this.conditions.push({
3452
- type: 'range',
3453
- min: Math.min(min, max),
3454
- max: Math.max(min, max),
3455
- });
3456
- }
3457
- }
3458
- }
3281
+ return true;
3282
+ }
3283
+ return true;
3284
+ }
3285
+ getGuidFromParameters() {
3286
+ if (this.viewMode() === 'dialog' && this.dialogData) {
3287
+ return this.dialogData.parameters || '';
3288
+ }
3289
+ if (this.parameters()) {
3290
+ try {
3291
+ return JSON.parse(this.parameters());
3292
+ }
3293
+ catch (e) {
3294
+ return this.parameters();
3295
+ }
3296
+ }
3297
+ return '';
3298
+ }
3299
+ /**
3300
+ * Close sidenav or navigate back
3301
+ */
3302
+ async closeSidenav() {
3303
+ const canDeactivate = await this.canDeactivate();
3304
+ if (canDeactivate) {
3305
+ if (this.viewMode() === 'dialog' && this.dialogRef) {
3306
+ this.dialogRef.close('saved');
3307
+ }
3308
+ else if (this.viewMode() === 'sidenav') {
3309
+ this.formChanged = false;
3310
+ this.commonService.clearOutlet();
3459
3311
  }
3460
3312
  else {
3461
- // Tek değer kontrolü
3462
- const value = Number(part);
3463
- if (!isNaN(value)) {
3464
- this.conditions.push({
3465
- type: 'value',
3466
- value: value,
3467
- });
3468
- }
3313
+ const cleanPath = this.commonService.getCleanUrlPath();
3314
+ this.router.navigate([cleanPath]);
3469
3315
  }
3470
3316
  }
3471
3317
  }
3472
- doesFilterPass(params) {
3473
- const cellValue = this.params.getValue(params.node);
3474
- // Eğer filtre yoksa tüm satırları göster
3475
- if (this.conditions.length === 0) {
3476
- return true;
3318
+ /**
3319
+ * Back button clicked
3320
+ */
3321
+ async backClicked() {
3322
+ const canDeactivate = await this.canDeactivate();
3323
+ if (canDeactivate) {
3324
+ if (this.viewMode() === 'dialog' && this.dialogRef) {
3325
+ this.dialogRef.close('back');
3326
+ }
3327
+ else {
3328
+ this.commonService.goBack();
3329
+ }
3477
3330
  }
3478
- // Hücre değeri null/undefined ise veya sayı değilse filtrele
3479
- if (cellValue == null || typeof cellValue !== 'number') {
3331
+ }
3332
+ validateSaveRecord(recordGUID) {
3333
+ if (this.saveForm && this.saveForm.invalid) {
3334
+ Object.keys(this.saveForm.controls).forEach((key) => {
3335
+ const control = this.saveForm.controls[key];
3336
+ control.markAsTouched();
3337
+ control.markAsDirty();
3338
+ });
3339
+ this.alertService.showError('@pleaseFillRequiredFields');
3480
3340
  return false;
3481
3341
  }
3482
- // VİRGÜL OR MANTIĞI: Koşullardan herhangi biri sağlanıyorsa true döndür
3483
- const passesAnyCondition = this.conditions.some((condition) => {
3484
- if (condition.type === 'range' &&
3485
- condition.min !== undefined &&
3486
- condition.max !== undefined) {
3487
- // Aralık koşulu: değer aralık içinde olmalı
3488
- return cellValue >= condition.min && cellValue <= condition.max;
3489
- }
3490
- else if (condition.type === 'value' && condition.value !== undefined) {
3491
- // Değer koşulu: değer eşit olmalı
3492
- return cellValue === condition.value;
3342
+ // First check if there are any visual form changes
3343
+ if (!this.formChanged && recordGUID) {
3344
+ this.alertService.showAlert('@noChangesDetected');
3345
+ // if (this.closeAfterSave()) {
3346
+ // this.closeSidenav();
3347
+ // }
3348
+ return false;
3349
+ }
3350
+ return true;
3351
+ }
3352
+ /**
3353
+ * Save data to API
3354
+ * Handles both create and update operations based on record existence
3355
+ */
3356
+ saveRecord() {
3357
+ if (!this.validateSaveRecord(this.currentItem().getPK())) {
3358
+ return;
3359
+ }
3360
+ if (this.currentItem().getPK()) {
3361
+ this.updateRecord();
3362
+ return;
3363
+ }
3364
+ this.insertRecord();
3365
+ }
3366
+ /**
3367
+ * Create new record
3368
+ */
3369
+ insertRecord() {
3370
+ const createData = {};
3371
+ const currentItem = this.currentItem();
3372
+ Object.keys(currentItem).forEach((key) => {
3373
+ if (currentItem[key] !== undefined && currentItem[key] !== null && currentItem[key] !== '') {
3374
+ createData[key] = currentItem[key];
3493
3375
  }
3494
- else if (condition.type === 'exclude') {
3495
- if (condition.value !== undefined) {
3496
- // Tek değer hariç tutma: değer eşit olmamalı
3497
- return cellValue !== condition.value;
3376
+ });
3377
+ this.nettyAppsProxy.insert(createData).subscribe({
3378
+ next: (newRecord) => {
3379
+ this.initializeFormData(newRecord);
3380
+ this.commonService.notifyUpdate(this.recordType(), 'add', newRecord);
3381
+ this.closeSidenav();
3382
+ this.alertService.showSuccess('@recordCreatedSuccessfully');
3383
+ },
3384
+ error: (err) => this.alertService.showError('@creationFailed', err),
3385
+ });
3386
+ }
3387
+ /**
3388
+ * Update record
3389
+ */
3390
+ updateRecord() {
3391
+ const changes = this.initialItem.compare(this.currentItem());
3392
+ const updateData = this.createNewRecord();
3393
+ Object.assign(updateData, changes);
3394
+ updateData.setPK(this.currentItem().getPK());
3395
+ this.nettyAppsProxy.update(updateData).subscribe({
3396
+ next: (updatedRecord) => {
3397
+ this.initializeFormData(updatedRecord);
3398
+ this.commonService.notifyUpdate(this.recordType(), 'update', updatedRecord);
3399
+ this.closeSidenav();
3400
+ this.alertService.showSuccess('@recordUpdatedSuccessfully');
3401
+ },
3402
+ error: (err) => this.alertService.showError('@updateFailed', err),
3403
+ });
3404
+ }
3405
+ /**
3406
+ * Create new record
3407
+ */
3408
+ async createNewData() {
3409
+ // If no GUID provided, initialize a new record
3410
+ let newRecord = this.createNewRecord();
3411
+ newRecord = await lastValueFrom(this.nettyAppsProxy.initRecord(newRecord))
3412
+ .catch((er) => {
3413
+ this.alertService.showError('@initRecordFailed:', er);
3414
+ return this.createNewRecord();
3415
+ });
3416
+ this.initializeFormData(newRecord);
3417
+ this.updateValid.set(true);
3418
+ }
3419
+ /**
3420
+ * Load data from API based on GUID
3421
+ * Handles both new record creation and existing record editing
3422
+ * guid - Unique identifier of the record
3423
+ */
3424
+ async loadDetailData() {
3425
+ const guid = this.getGuidFromParameters();
3426
+ if (!guid) {
3427
+ await this.createNewData();
3428
+ return;
3429
+ }
3430
+ // Fetch existing record from API
3431
+ this.nettyAppsProxy.selectGUID(guid).subscribe({
3432
+ next: (data) => {
3433
+ const record = this.createItemInstance(data);
3434
+ if (!record || !record.getPK()) {
3435
+ this.alertService.showError('@recordNotFound');
3436
+ const cleanPath = this.commonService.getCleanUrlPath();
3437
+ this.router.navigate([cleanPath]);
3438
+ return;
3498
3439
  }
3499
- else if (condition.min !== undefined && condition.max !== undefined) {
3500
- // Aralık hariç tutma: değer aralık dışında olmalı
3501
- return !(cellValue >= condition.min && cellValue <= condition.max);
3440
+ this.initializeFormData(record);
3441
+ this.alertService.showSuccess('@dataLoadSuccess');
3442
+ // Ensure sidenav is visible when in sidenav mode
3443
+ if (this.viewMode() === 'sidenav') {
3444
+ this.commonService.toggleRightSidenav(true);
3445
+ }
3446
+ },
3447
+ error: (err) => {
3448
+ this.alertService.showError('@dataLoadFailed');
3449
+ this.commonService.toggleRightSidenav(false);
3450
+ // Redirect to user list on error in fullscreen mode
3451
+ if (this.viewMode() === 'fullscreen') {
3452
+ const cleanPath = this.commonService.getCleanUrlPath();
3453
+ this.router.navigate([cleanPath]);
3502
3454
  }
3503
- }
3504
- return false;
3455
+ },
3505
3456
  });
3506
- return passesAnyCondition;
3507
3457
  }
3508
- isFilterActive() {
3509
- return this.conditions.length > 0;
3510
- }
3511
- getModel() {
3512
- if (this.isFilterActive()) {
3513
- return {
3514
- filterType: 'custom',
3515
- filterText: this.filterText,
3516
- conditions: this.conditions,
3517
- };
3458
+ // ***************************************************
3459
+ // *** gotoURL Methods ***
3460
+ // ***************************************************
3461
+ gotoURL(routePrefix, rightSidenav = [], parameters, type, dialogComponent = null, isNewTab = false, isPopup = this._isEmbedded()) {
3462
+ const baseHref = this.environment.getBaseHref().endsWith('/')
3463
+ ? this.environment.getBaseHref().slice(0, -1) // Sondaki / işaretini kaldır
3464
+ : this.environment.getBaseHref();
3465
+ const navigationExtras = {
3466
+ queryParams: {
3467
+ parameters: JSON.stringify(parameters),
3468
+ ...(type && { type }),
3469
+ isNewTab: isNewTab || undefined,
3470
+ },
3471
+ queryParamsHandling: 'merge',
3472
+ };
3473
+ if (isNewTab) {
3474
+ const fullUrl = this.router
3475
+ .createUrlTree([baseHref, ...routePrefix, ...rightSidenav], navigationExtras)
3476
+ .toString();
3477
+ window.open(fullUrl, '_blank');
3478
+ return;
3518
3479
  }
3519
- return null;
3520
- }
3521
- setModel(model) {
3522
- if (model && model.filterText) {
3523
- this.filterText = model.filterText;
3524
- this.conditions = model.conditions || [];
3480
+ if (isPopup && dialogComponent) {
3481
+ this.dialog
3482
+ .open(dialogComponent, {
3483
+ data: {
3484
+ parameters: parameters,
3485
+ mode: type || 'edit',
3486
+ embedded: true,
3487
+ },
3488
+ maxWidth: '100vw',
3489
+ disableClose: true,
3490
+ hasBackdrop: false,
3491
+ })
3492
+ .afterClosed();
3493
+ return;
3525
3494
  }
3526
- else {
3527
- this.filterText = '';
3528
- this.conditions = [];
3495
+ // Log control
3496
+ if (rightSidenav.length == 0 || rightSidenav[0] === 'log') {
3497
+ this.router.navigate([baseHref, ...routePrefix, ...rightSidenav], navigationExtras);
3498
+ return;
3529
3499
  }
3500
+ // In all other cases, open the side menu
3501
+ this.router
3502
+ .navigate([
3503
+ {
3504
+ outlets: {
3505
+ primary: routePrefix,
3506
+ rightSidenav: [...routePrefix, ...rightSidenav],
3507
+ },
3508
+ },
3509
+ ], navigationExtras)
3510
+ .then(() => {
3511
+ // Ensure sidenav is opened after navigation
3512
+ this.commonService.toggleRightSidenav(true);
3513
+ });
3530
3514
  }
3531
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeNumberFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
3532
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: RangeNumberFilter, isStandalone: true, selector: "ntybase-range-number-filter", host: { attributes: { "ntybase-id": "RangeNumberFilter" } }, ngImport: i0, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@numberFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@numberFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\"\n >{{ '@formats' | translate }}:</strong\n >\n\n <div class=\"custom-filter__format\">\n {{ '@closedRange' | translate }}:\n <code>min..max</code>\n <span class=\"custom-filter__example\">(0..5)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@equalOrGreater' | translate }}:\n <code>min..</code>\n <span class=\"custom-filter__example\">(5..)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@equalOrLess' | translate }}:\n <code>..max</code>\n <span class=\"custom-filter__example\">(..10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@singleValue' | translate }}:\n <code>number</code>\n <span class=\"custom-filter__example\">(10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@exclusion' | translate }}:\n <code>!number</code>\n <span class=\"custom-filter__example\">(!5)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@rangeExclusion' | translate }}:\n <code>!min..max</code>\n <span class=\"custom-filter__example\">(!5..10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>value1,value2,range</code>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
3515
+ popupGotoURL(urlSegments) {
3516
+ const baseHref = this.environment.getBaseHref().endsWith('/')
3517
+ ? this.environment.getBaseHref().slice(0, -1) // Sondaki / işaretini kaldır
3518
+ : this.environment.getBaseHref();
3519
+ this.router.navigate([
3520
+ {
3521
+ outlets: {
3522
+ primary: [baseHref, ...urlSegments],
3523
+ rightSidenav: null,
3524
+ },
3525
+ },
3526
+ ]);
3527
+ }
3528
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridSaveBase, deps: [], target: i0.ɵɵFactoryTarget.Component });
3529
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: NettyAgGridSaveBase, isStandalone: true, selector: "ntybase-ag-grid-save-base", inputs: { parameters: { classPropertyName: "parameters", publicName: "parameters", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "saveForm", first: true, predicate: ["saveForm"], descendants: true }], usesInheritance: true, ngImport: i0, template: ``, isInline: true });
3533
3530
  }
3534
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeNumberFilter, decorators: [{
3531
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: NettyAgGridSaveBase, decorators: [{
3535
3532
  type: Component,
3536
- args: [{ selector: 'ntybase-range-number-filter', imports: [TranslateModule], host: { 'ntybase-id': 'RangeNumberFilter' }, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@numberFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@numberFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\"\n >{{ '@formats' | translate }}:</strong\n >\n\n <div class=\"custom-filter__format\">\n {{ '@closedRange' | translate }}:\n <code>min..max</code>\n <span class=\"custom-filter__example\">(0..5)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@equalOrGreater' | translate }}:\n <code>min..</code>\n <span class=\"custom-filter__example\">(5..)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@equalOrLess' | translate }}:\n <code>..max</code>\n <span class=\"custom-filter__example\">(..10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@singleValue' | translate }}:\n <code>number</code>\n <span class=\"custom-filter__example\">(10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@exclusion' | translate }}:\n <code>!number</code>\n <span class=\"custom-filter__example\">(!5)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@rangeExclusion' | translate }}:\n <code>!min..max</code>\n <span class=\"custom-filter__example\">(!5..10)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>value1,value2,range</code>\n </div>\n </div>\n</div>\n" }]
3537
- }] });
3533
+ args: [{ selector: 'ntybase-ag-grid-save-base', imports: [], template: `` }]
3534
+ }], ctorParameters: () => [], propDecorators: { parameters: [{ type: i0.Input, args: [{ isSignal: true, alias: "parameters", required: false }] }], saveForm: [{
3535
+ type: ViewChild,
3536
+ args: ['saveForm']
3537
+ }] } });
3538
3538
 
3539
- class RangeStringFilter {
3539
+ class RangeDateTimeFilter {
3540
3540
  params;
3541
3541
  filterText = '';
3542
3542
  conditions = [];
3543
+ // Dil servisini inject edin
3544
+ i18nService = inject(I18nService);
3545
+ translateService = inject(TranslateService);
3546
+ // Türkçe ve İngilizce özel anahtar kelimeler
3547
+ specialKeywords = {
3548
+ today: 'today',
3549
+ bugün: 'today',
3550
+ yesterday: 'yesterday',
3551
+ dün: 'yesterday',
3552
+ thisweek: 'thisWeek',
3553
+ 'bu hafta': 'thisWeek',
3554
+ thismonth: 'thisMonth',
3555
+ 'bu ay': 'thisMonth',
3556
+ };
3543
3557
  agInit(params) {
3544
3558
  this.params = params;
3545
3559
  }
@@ -3553,210 +3567,242 @@ class RangeStringFilter {
3553
3567
  if (!this.filterText.trim()) {
3554
3568
  return;
3555
3569
  }
3570
+ // Virgülle ayrılmış parçaları al ve temizle
3556
3571
  const parts = this.filterText
3557
3572
  .split(',')
3558
3573
  .map((part) => part.trim())
3559
3574
  .filter((part) => part !== '');
3560
3575
  for (const part of parts) {
3561
- // "!" ile başlayan olumsuz koşul kontrolü
3562
- if (part.startsWith('!')) {
3563
- const positiveCondition = part.slice(1);
3564
- const positiveParsed = this.parseSingleCondition(positiveCondition);
3565
- if (positiveParsed) {
3566
- this.conditions.push({
3567
- type: 'not',
3568
- condition: positiveParsed,
3569
- });
3576
+ // Aralık kontrolü (.. içeriyor mu) - ÖNCE aralık kontrolü yap
3577
+ if (part.includes('..')) {
3578
+ const rangeParts = part.split('..');
3579
+ if (rangeParts.length === 2) {
3580
+ const startStr = rangeParts[0].trim();
3581
+ const endStr = rangeParts[1].trim();
3582
+ const startDate = this.parseDateForRange(startStr, false);
3583
+ const endDate = this.parseDateForRange(endStr, true);
3584
+ if (startDate && endDate) {
3585
+ // Tarihleri doğru sırala (küçükten büyüğe)
3586
+ const sortedStart = startDate < endDate ? startDate : endDate;
3587
+ const sortedEnd = startDate < endDate ? endDate : startDate;
3588
+ this.conditions.push({
3589
+ type: 'range',
3590
+ start: sortedStart,
3591
+ end: sortedEnd,
3592
+ });
3593
+ }
3570
3594
  }
3571
- continue;
3572
- }
3573
- const parsedCondition = this.parseSingleCondition(part);
3574
- if (parsedCondition) {
3575
- this.conditions.push(parsedCondition);
3576
- }
3577
- }
3578
- }
3579
- parseSingleCondition(condition) {
3580
- // Regex kontrolü ($$ ile başlıyorsa)
3581
- if (condition.startsWith('$$')) {
3582
- const regexPattern = condition.slice(2);
3583
- if (regexPattern) {
3584
- return {
3585
- type: 'regex',
3586
- pattern: regexPattern,
3587
- };
3588
3595
  }
3589
- }
3590
- // Aralık kontrolü (.. içeriyor mu) - ama $$.. şeklinde değilse
3591
- if (condition.includes('..') && !condition.startsWith('$$')) {
3592
- const rangeParts = condition.split('..');
3593
- if (rangeParts.length === 2) {
3594
- const start = rangeParts[0].trim();
3595
- const end = rangeParts[1].trim();
3596
- if (start && end) {
3597
- return {
3598
- type: 'range',
3599
- start: start.toLowerCase(),
3600
- end: end.toLowerCase(),
3601
- };
3596
+ else {
3597
+ // Özel anahtar kelime kontrolü (Türkçe ve İngilizce)
3598
+ const normalizedPart = part.toLowerCase();
3599
+ if (this.specialKeywords[normalizedPart]) {
3600
+ this.conditions.push({
3601
+ type: 'special',
3602
+ special: this.specialKeywords[normalizedPart],
3603
+ });
3604
+ continue;
3605
+ }
3606
+ // Tek tarih kontrolü
3607
+ const date = this.parseDate(part);
3608
+ if (date) {
3609
+ this.conditions.push({
3610
+ type: 'date',
3611
+ date: date,
3612
+ });
3602
3613
  }
3603
3614
  }
3604
3615
  }
3605
- // Tek karakter kontrolü (? ile)
3606
- else if (condition.includes('?') && !condition.startsWith('$$')) {
3607
- // Eğer sadece normal karakterler varsa (özel karakter yoksa) exact match
3608
- const hasSpecialChars = condition.includes('*') ||
3609
- condition.includes('?') ||
3610
- condition.includes('..');
3611
- if (!hasSpecialChars) {
3612
- return {
3613
- type: 'exact',
3614
- value: condition.toLowerCase(),
3615
- };
3616
- }
3617
- // ? karakteri içeriyorsa singleChar kontrolü
3618
- const value = condition.toLowerCase();
3619
- if (value) {
3620
- return {
3621
- type: 'singleChar',
3622
- value: value,
3623
- };
3624
- }
3625
- }
3626
- // Başlangıç kontrolü (* sonunda)
3627
- else if (condition.endsWith('*') &&
3628
- !condition.startsWith('*') &&
3629
- !condition.startsWith('$$')) {
3630
- const value = condition.slice(0, -1).toLowerCase();
3631
- if (value) {
3632
- return {
3633
- type: 'startsWith',
3634
- value: value,
3635
- };
3636
- }
3637
- }
3638
- // Bitiş kontrolü (* başında)
3639
- else if (condition.startsWith('*') &&
3640
- !condition.endsWith('*') &&
3641
- !condition.startsWith('$$')) {
3642
- const value = condition.slice(1).toLowerCase();
3643
- if (value) {
3644
- return {
3645
- type: 'endsWith',
3646
- value: value,
3647
- };
3648
- }
3649
- }
3650
- // İçeren kontrolü (* ile başlayıp * ile bitiyorsa)
3651
- else if (condition.startsWith('*') &&
3652
- condition.endsWith('*') &&
3653
- !condition.startsWith('$$')) {
3654
- const value = condition.slice(1, -1).toLowerCase();
3655
- if (value) {
3656
- return {
3657
- type: 'contains',
3658
- value: value,
3659
- };
3660
- }
3661
- }
3662
- // Normal exact match (hiçbir özel karakter yoksa)
3663
- else if (!condition.startsWith('$$') &&
3664
- !condition.includes('*') &&
3665
- !condition.includes('?') &&
3666
- !condition.includes('..')) {
3667
- const value = condition.toLowerCase();
3668
- if (value) {
3669
- return {
3670
- type: 'exact',
3671
- value: value,
3672
- };
3673
- }
3674
- }
3675
- // Normal içeren kontrolü (varsayılan) - regex değilse
3676
- else if (!condition.startsWith('$$')) {
3677
- const value = condition.toLowerCase();
3678
- if (value) {
3679
- return {
3680
- type: 'contains',
3681
- value: value,
3682
- };
3616
+ }
3617
+ parseDateForRange(dateString, isEndDate) {
3618
+ if (!dateString)
3619
+ return null;
3620
+ // Özel anahtar kelimeler için tarih dönüşümü
3621
+ const normalizedString = dateString.toLowerCase();
3622
+ if (this.specialKeywords[normalizedString]) {
3623
+ const specialType = this.specialKeywords[normalizedString];
3624
+ const range = this.getSpecialDateRange(specialType);
3625
+ return isEndDate ? range.end : range.start;
3626
+ }
3627
+ return this.parseDate(dateString);
3628
+ }
3629
+ // Mevcut dili kontrol eden yardımcı metod
3630
+ isTurkish() {
3631
+ const currentLang = this.i18nService.language.toLowerCase();
3632
+ return (currentLang === 'tr' ||
3633
+ currentLang === 'türkçe' ||
3634
+ currentLang === 'tr-tr');
3635
+ }
3636
+ parseDate(dateString) {
3637
+ if (!dateString)
3638
+ return null;
3639
+ // Mevcut dile göre formatları belirle
3640
+ const isTurkish = this.isTurkish();
3641
+ // Tarih formatları - ÖNCE saat içeren formatlar
3642
+ const formats = [
3643
+ // ISO format: YYYY-AA-GG SS:DD (her dilde aynı)
3644
+ /^(\d{4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{2})$/,
3645
+ // Dile göre formatlar
3646
+ ...(isTurkish
3647
+ ? [
3648
+ // Türkçe format: GG.AA.YYYY SS:DD (., /, - ile)
3649
+ /^(\d{1,2})[\.\/-](\d{1,2})[\.\/-](\d{4}) (\d{1,2}):(\d{2})$/,
3650
+ // Türkçe format: GG.AA.YYYY (., /, - ile)
3651
+ /^(\d{1,2})[\.\/-](\d{1,2})[\.\/-](\d{4})$/,
3652
+ ]
3653
+ : [
3654
+ // İngilizce/USA format: AA/GG/YYYY SS:DD (/, ., - ile)
3655
+ /^(\d{1,2})[\/\.-](\d{1,2})[\/\.-](\d{4}) (\d{1,2}):(\d{2})$/,
3656
+ // İngilizce/USA format: AA/GG/YYYY (/, ., - ile)
3657
+ /^(\d{1,2})[\/\.-](\d{1,2})[\/\.-](\d{4})$/,
3658
+ ]),
3659
+ // Sadece tarih formatları (saat yok)
3660
+ /^(\d{4})-(\d{1,2})-(\d{1,2})$/, // YYYY-AA-GG (ISO)
3661
+ ];
3662
+ for (const format of formats) {
3663
+ const match = dateString.match(format);
3664
+ if (match) {
3665
+ let year, month, day, hours = 0, minutes = 0;
3666
+ if (format === formats[0]) {
3667
+ // ISO: YYYY-AA-GG SS:DD
3668
+ year = parseInt(match[1], 10);
3669
+ month = parseInt(match[2], 10) - 1;
3670
+ day = parseInt(match[3], 10);
3671
+ hours = parseInt(match[4], 10);
3672
+ minutes = parseInt(match[5], 10);
3673
+ }
3674
+ else if (isTurkish && format === formats[1]) {
3675
+ // Türkçe: GG.AA.YYYY SS:DD
3676
+ day = parseInt(match[1], 10);
3677
+ month = parseInt(match[2], 10) - 1;
3678
+ year = parseInt(match[3], 10);
3679
+ hours = parseInt(match[4], 10);
3680
+ minutes = parseInt(match[5], 10);
3681
+ }
3682
+ else if (!isTurkish && format === formats[1]) {
3683
+ // İngilizce: AA/GG/YYYY SS:DD
3684
+ month = parseInt(match[1], 10) - 1;
3685
+ day = parseInt(match[2], 10);
3686
+ year = parseInt(match[3], 10);
3687
+ hours = parseInt(match[4], 10);
3688
+ minutes = parseInt(match[5], 10);
3689
+ }
3690
+ else if (isTurkish && format === formats[2]) {
3691
+ // Türkçe: GG.AA.YYYY
3692
+ day = parseInt(match[1], 10);
3693
+ month = parseInt(match[2], 10) - 1;
3694
+ year = parseInt(match[3], 10);
3695
+ }
3696
+ else if (!isTurkish && format === formats[2]) {
3697
+ // İngilizce: AA/GG/YYYY
3698
+ month = parseInt(match[1], 10) - 1;
3699
+ day = parseInt(match[2], 10);
3700
+ year = parseInt(match[3], 10);
3701
+ }
3702
+ else if (format === formats[3]) {
3703
+ // ISO: YYYY-AA-GG
3704
+ year = parseInt(match[1], 10);
3705
+ month = parseInt(match[2], 10) - 1;
3706
+ day = parseInt(match[3], 10);
3707
+ }
3708
+ else {
3709
+ continue;
3710
+ }
3711
+ // Tarih geçerlilik kontrolü
3712
+ const date = new Date(year, month, day, hours, minutes);
3713
+ if (date.getFullYear() === year &&
3714
+ date.getMonth() === month &&
3715
+ date.getDate() === day &&
3716
+ date.getHours() === hours &&
3717
+ date.getMinutes() === minutes) {
3718
+ return date;
3719
+ }
3683
3720
  }
3684
3721
  }
3685
3722
  return null;
3686
3723
  }
3724
+ getSpecialDateRange(special) {
3725
+ const now = new Date();
3726
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
3727
+ switch (special) {
3728
+ case 'today':
3729
+ const tomorrow = new Date(today);
3730
+ tomorrow.setDate(tomorrow.getDate() + 1);
3731
+ return { start: today, end: tomorrow };
3732
+ case 'yesterday':
3733
+ const yesterday = new Date(today);
3734
+ yesterday.setDate(yesterday.getDate() - 1);
3735
+ const yesterdayEnd = new Date(yesterday);
3736
+ yesterdayEnd.setDate(yesterdayEnd.getDate() + 1);
3737
+ return { start: yesterday, end: yesterdayEnd };
3738
+ case 'thisWeek':
3739
+ const startOfWeek = new Date(today);
3740
+ startOfWeek.setDate(today.getDate() - today.getDay());
3741
+ const endOfWeek = new Date(startOfWeek);
3742
+ endOfWeek.setDate(startOfWeek.getDate() + 7);
3743
+ return { start: startOfWeek, end: endOfWeek };
3744
+ case 'thisMonth':
3745
+ const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
3746
+ const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);
3747
+ return { start: startOfMonth, end: endOfMonth };
3748
+ default:
3749
+ return { start: today, end: today };
3750
+ }
3751
+ }
3687
3752
  doesFilterPass(params) {
3688
3753
  const field = this.params.colDef.field;
3689
3754
  if (!field)
3690
3755
  return true;
3691
- const cellValue = params.data
3692
- ? String(params.data[field] || '').toLowerCase()
3693
- : '';
3756
+ const cellValue = params.data ? params.data[field] : null;
3694
3757
  // Eğer filtre yoksa tüm satırları göster
3695
3758
  if (this.conditions.length === 0) {
3696
3759
  return true;
3697
3760
  }
3698
- // Hücre değeri boşsa filtrele
3761
+ // Hücre değeri null/undefined ise filtrele
3699
3762
  if (!cellValue) {
3700
3763
  return false;
3701
3764
  }
3702
- // VİRGÜL OR MANTIĞI: Koşullardan herhangi biri sağlanıyorsa true döndür
3765
+ // Hücre değerini Date'e dönüştür
3766
+ let cellDate;
3767
+ if (cellValue instanceof Date) {
3768
+ cellDate = cellValue;
3769
+ }
3770
+ else if (typeof cellValue === 'string') {
3771
+ // String ise Date'e parse et
3772
+ cellDate = new Date(cellValue);
3773
+ if (isNaN(cellDate.getTime())) {
3774
+ return false; // Geçersiz tarih
3775
+ }
3776
+ }
3777
+ else {
3778
+ return false; // Desteklenmeyen tip
3779
+ }
3780
+ const cellTime = cellDate.getTime();
3781
+ // Koşullardan herhangi birini sağlıyor mu kontrol et
3703
3782
  return this.conditions.some((condition) => {
3704
- const conditionResult = this.checkSingleCondition(cellValue, condition);
3705
- return condition.type === 'not' ? !conditionResult : conditionResult;
3706
- });
3707
- }
3708
- checkSingleCondition(cellValue, condition) {
3709
- switch (condition.type) {
3710
- case 'contains':
3711
- return condition.value ? cellValue.includes(condition.value) : false;
3712
- case 'startsWith':
3713
- return condition.value ? cellValue.startsWith(condition.value) : false;
3714
- case 'endsWith':
3715
- return condition.value ? cellValue.endsWith(condition.value) : false;
3716
- case 'exact':
3717
- return condition.value ? cellValue === condition.value : false;
3718
- case 'range':
3719
- if (condition.start && condition.end) {
3720
- return cellValue >= condition.start && cellValue <= condition.end;
3721
- }
3722
- return false;
3723
- case 'singleChar':
3724
- if (condition.value) {
3725
- // ? karakterini herhangi bir karakter olarak değerlendir
3726
- const pattern = condition.value;
3727
- if (cellValue.length !== pattern.length) {
3783
+ switch (condition.type) {
3784
+ case 'date':
3785
+ if (!condition.date)
3728
3786
  return false;
3729
- }
3730
- // Her karakteri tek tek kontrol et
3731
- for (let i = 0; i < pattern.length; i++) {
3732
- const patternChar = pattern[i];
3733
- const cellChar = cellValue[i];
3734
- // Eğer pattern'de ? değilse ve karakterler eşleşmiyorsa false
3735
- if (patternChar !== '?' && patternChar !== cellChar) {
3736
- return false;
3737
- }
3738
- }
3739
- return true;
3740
- }
3741
- return false;
3742
- case 'regex':
3743
- if (condition.pattern) {
3744
- try {
3745
- const regex = new RegExp(condition.pattern, 'i'); // case-insensitive
3746
- return regex.test(cellValue);
3747
- }
3748
- catch (e) {
3749
- // Geçersiz regex patterni durumunda false döndür
3750
- console.warn('Geçersiz regex patterni:', condition.pattern);
3787
+ const conditionTime = condition.date.getTime();
3788
+ const nextDay = new Date(condition.date);
3789
+ nextDay.setDate(nextDay.getDate() + 1);
3790
+ return cellTime >= conditionTime && cellTime < nextDay.getTime();
3791
+ case 'range':
3792
+ if (!condition.start || !condition.end)
3751
3793
  return false;
3752
- }
3753
- }
3754
- return false;
3755
- case 'not':
3756
- return this.checkSingleCondition(cellValue, condition.condition);
3757
- default:
3758
- return false;
3759
- }
3794
+ const startTime = condition.start.getTime();
3795
+ const endTime = condition.end.getTime();
3796
+ return cellTime >= startTime && cellTime < endTime;
3797
+ case 'special':
3798
+ if (!condition.special)
3799
+ return false;
3800
+ const range = this.getSpecialDateRange(condition.special);
3801
+ return (cellTime >= range.start.getTime() && cellTime < range.end.getTime());
3802
+ default:
3803
+ return false;
3804
+ }
3805
+ });
3760
3806
  }
3761
3807
  isFilterActive() {
3762
3808
  return this.conditions.length > 0;
@@ -3774,19 +3820,19 @@ class RangeStringFilter {
3774
3820
  setModel(model) {
3775
3821
  if (model && model.filterText) {
3776
3822
  this.filterText = model.filterText;
3777
- this.conditions = model.conditions || [];
3823
+ this.parseFilterText(); // Modelden set ederken koşulları tekrar parse et
3778
3824
  }
3779
3825
  else {
3780
3826
  this.filterText = '';
3781
3827
  this.conditions = [];
3782
3828
  }
3783
3829
  }
3784
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeStringFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
3785
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: RangeStringFilter, isStandalone: true, selector: "ntybase-range-string-filter", host: { attributes: { "ntybase-id": "RangeStringFilter" } }, ngImport: i0, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@stringFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@stringFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\">\n {{ '@formats' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@exactMatch' | translate }}:\n <code>text</code>\n <span class=\"custom-filter__example\">(ahmet)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@startsWith' | translate }}:\n <code>text*</code>\n <span class=\"custom-filter__example\">(ahmet*)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@endsWith' | translate }}:\n <code>*text</code>\n <span class=\"custom-filter__example\">(*metin)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@contains' | translate }}:\n <code>*text*</code>\n <span class=\"custom-filter__example\">(*ahmet*)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@range' | translate }}:\n <code>start..end</code>\n <span class=\"custom-filter__example\">(ali..veli)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n <span class=\"custom-filter__description\">\n {{ '@singleCharacter' | translate }}:\n </span>\n <code>?em</code>\n <span class=\"custom-filter__example\">\n (?em = 3 characters, 2nd and 3rd letters \"em\")\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n <span class=\"custom-filter__description\">\n {{ '@multipleCharacters' | translate }}:\n </span>\n <code>???in</code>\n <span class=\"custom-filter__example\">\n (5 characters, last 2 letters \"in\")\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@not' | translate }}:\n <code>!condition</code>\n <span class=\"custom-filter__example\">(!*ahmet*, !?hmet)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@regex' | translate }}:\n <code>$$pattern</code>\n <span class=\"custom-filter__example\">($$^[A-Z].*, $$a.*b)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>value1,value2,*end,$$pattern</code>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
3830
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeDateTimeFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
3831
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: RangeDateTimeFilter, isStandalone: true, selector: "ntybase-range-date-time-filter", host: { attributes: { "ntybase-id": "RangeDateTimeFilter" } }, ngImport: i0, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@dateTimeFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@dateTimeFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\">\n {{ '@formats' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@range' | translate }}:\n <code>start..end</code>\n <span class=\"custom-filter__example\">(today..yesterday)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@singleDate' | translate }}:\n <code>YYYY-MM-DD</code>\n <span class=\"custom-filter__example\">(2024-05-15)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@dateTime' | translate }}:\n <code>YYYY-MM-DD HH:MM</code>\n <span class=\"custom-filter__example\">(2024-05-15 14:30)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>date1,date2,start..end</code>\n </div>\n\n <strong class=\"custom-filter__subtitle\">\n {{ '@specialKeywords' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@turkish' | translate }}:\n <span class=\"custom-filter__keywords\">\n <code class=\"custom-filter__keyword\">bug\u00FCn</code>\n <code class=\"custom-filter__keyword\">d\u00FCn</code>\n <code class=\"custom-filter__keyword\">bu hafta</code>\n <code class=\"custom-filter__keyword\">bu ay</code>\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@english' | translate }}:\n <span class=\"custom-filter__keywords\">\n <code class=\"custom-filter__keyword\">today</code>\n <code class=\"custom-filter__keyword\">yesterday</code>\n <code class=\"custom-filter__keyword\">thisWeek</code>\n <code class=\"custom-filter__keyword\">thisMonth</code>\n </span>\n </div>\n\n <div class=\"custom-filter__note\">{{ '@filterExample' | translate }}</div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
3786
3832
  }
3787
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeStringFilter, decorators: [{
3833
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RangeDateTimeFilter, decorators: [{
3788
3834
  type: Component,
3789
- args: [{ selector: 'ntybase-range-string-filter', imports: [TranslateModule], host: { 'ntybase-id': 'RangeStringFilter' }, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@stringFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@stringFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\">\n {{ '@formats' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@exactMatch' | translate }}:\n <code>text</code>\n <span class=\"custom-filter__example\">(ahmet)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@startsWith' | translate }}:\n <code>text*</code>\n <span class=\"custom-filter__example\">(ahmet*)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@endsWith' | translate }}:\n <code>*text</code>\n <span class=\"custom-filter__example\">(*metin)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@contains' | translate }}:\n <code>*text*</code>\n <span class=\"custom-filter__example\">(*ahmet*)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@range' | translate }}:\n <code>start..end</code>\n <span class=\"custom-filter__example\">(ali..veli)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n <span class=\"custom-filter__description\">\n {{ '@singleCharacter' | translate }}:\n </span>\n <code>?em</code>\n <span class=\"custom-filter__example\">\n (?em = 3 characters, 2nd and 3rd letters \"em\")\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n <span class=\"custom-filter__description\">\n {{ '@multipleCharacters' | translate }}:\n </span>\n <code>???in</code>\n <span class=\"custom-filter__example\">\n (5 characters, last 2 letters \"in\")\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@not' | translate }}:\n <code>!condition</code>\n <span class=\"custom-filter__example\">(!*ahmet*, !?hmet)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@regex' | translate }}:\n <code>$$pattern</code>\n <span class=\"custom-filter__example\">($$^[A-Z].*, $$a.*b)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>value1,value2,*end,$$pattern</code>\n </div>\n </div>\n</div>\n" }]
3835
+ args: [{ selector: 'ntybase-range-date-time-filter', imports: [TranslateModule], host: { 'ntybase-id': 'RangeDateTimeFilter' }, template: "<div class=\"custom-filter\">\n <div class=\"custom-filter__header\">\n <label class=\"custom-filter__title\">\n {{ '@dateTimeFilter' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"custom-filter__input\"\n [placeholder]=\"'@dateTimeFilterPlaceholder' | translate\"\n [value]=\"filterText\"\n (input)=\"onFilterTextChanged($event)\"\n />\n </div>\n\n <div class=\"custom-filter__help\">\n <strong class=\"custom-filter__help-title\">\n {{ '@formats' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@range' | translate }}:\n <code>start..end</code>\n <span class=\"custom-filter__example\">(today..yesterday)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@singleDate' | translate }}:\n <code>YYYY-MM-DD</code>\n <span class=\"custom-filter__example\">(2024-05-15)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@dateTime' | translate }}:\n <code>YYYY-MM-DD HH:MM</code>\n <span class=\"custom-filter__example\">(2024-05-15 14:30)</span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@multiple' | translate }}:\n <code>date1,date2,start..end</code>\n </div>\n\n <strong class=\"custom-filter__subtitle\">\n {{ '@specialKeywords' | translate }}:\n </strong>\n\n <div class=\"custom-filter__format\">\n {{ '@turkish' | translate }}:\n <span class=\"custom-filter__keywords\">\n <code class=\"custom-filter__keyword\">bug\u00FCn</code>\n <code class=\"custom-filter__keyword\">d\u00FCn</code>\n <code class=\"custom-filter__keyword\">bu hafta</code>\n <code class=\"custom-filter__keyword\">bu ay</code>\n </span>\n </div>\n\n <div class=\"custom-filter__format\">\n {{ '@english' | translate }}:\n <span class=\"custom-filter__keywords\">\n <code class=\"custom-filter__keyword\">today</code>\n <code class=\"custom-filter__keyword\">yesterday</code>\n <code class=\"custom-filter__keyword\">thisWeek</code>\n <code class=\"custom-filter__keyword\">thisMonth</code>\n </span>\n </div>\n\n <div class=\"custom-filter__note\">{{ '@filterExample' | translate }}</div>\n </div>\n</div>\n" }]
3790
3836
  }] });
3791
3837
 
3792
3838
  class AuthenticationGuard {
@@ -4297,7 +4343,7 @@ class Login extends AuthBase {
4297
4343
  });
4298
4344
  }
4299
4345
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: Login, deps: null, target: i0.ɵɵFactoryTarget.Component });
4300
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", 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 @if (icon()) {\n <button mat-icon-button [matMenuTriggerFor]=\"languageMenu\">\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n } @else {\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n }\n </div>\n\n <h2>{{'app_name' | translate }}</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n @for (language of languages; track language) {\n <button mat-menu-item (click)=\"setLanguage(language)\">\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n }\n </mat-menu>\n\n @if (!isLoading()) {\n <form (ngSubmit)=\"login()\" [formGroup]=\"loginForm\" novalidate>\n @if (error()) {\n <div class=\"error-message\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n }\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 @if (loginForm.controls['username'].invalid &&\n loginForm.controls['username'].touched) {\n <mat-error class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'username is required' | translate }}\n </mat-error>\n }\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 @if (loginForm.controls['password'].invalid &&\n loginForm.controls['password'].touched) {\n <div class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'password is required' | translate }}\n </div>\n }\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 } @else {\n <div class=\"loading-spinner\">\n <span>{{ '@loggingIn' | translate }}</span>\n </div>\n }\n\n <div class=\"footer\">\n @if(version() && version() !== '?.?'){\n <div class=\"version\">{{ version() }}</div>\n }\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$4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$4.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$4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$4.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$4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$4.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$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: i2$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$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: i3$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
4346
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", 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 @if (icon()) {\n <button mat-icon-button [matMenuTriggerFor]=\"languageMenu\">\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n } @else {\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n }\n </div>\n\n <h2>{{'app_name' | translate }}</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n @for (language of languages; track language) {\n <button mat-menu-item (click)=\"setLanguage(language)\">\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n }\n </mat-menu>\n\n @if (!isLoading()) {\n <form (ngSubmit)=\"login()\" [formGroup]=\"loginForm\" novalidate>\n @if (error()) {\n <div class=\"error-message\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n }\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 @if (loginForm.controls['username'].invalid &&\n loginForm.controls['username'].touched) {\n <mat-error class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'username is required' | translate }}\n </mat-error>\n }\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 @if (loginForm.controls['password'].invalid &&\n loginForm.controls['password'].touched) {\n <div class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'password is required' | translate }}\n </div>\n }\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 } @else {\n <div class=\"loading-spinner\">\n <span>{{ '@loggingIn' | translate }}</span>\n </div>\n }\n\n <div class=\"footer\">\n @if(version() && version() !== '?.?'){\n <div class=\"version\">{{ version() }}</div>\n }\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$5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$5.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$5.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$5.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$5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$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: i2$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$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: i3$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
4301
4347
  }
4302
4348
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: Login, decorators: [{
4303
4349
  type: Component,
@@ -4401,7 +4447,7 @@ class MfaLogin extends AuthBase {
4401
4447
  clearInterval(this.countdownInterval);
4402
4448
  }
4403
4449
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: MfaLogin, deps: null, target: i0.ɵɵFactoryTarget.Component });
4404
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", 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 @if (icon()) {\n <button mat-icon-button [matMenuTriggerFor]=\"languageMenu\">\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n } @else {\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n }\n </div>\n\n <h2>{{'app_name' | translate }}</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n @for (language of languages; track language) {\n <button mat-menu-item (click)=\"setLanguage(language)\">\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n }\n </mat-menu>\n\n @if (!isLoading()) {\n <form (ngSubmit)=\"login()\" [formGroup]=\"loginForm\" novalidate>\n @if (error()) {\n <div class=\"error-message\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n }\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 @if (loginForm.controls['mfaCode'].invalid &&\n loginForm.controls['mfaCode'].touched) {\n <div class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'mfaCode is required' | translate }}\n </div>\n }\n\n <!-- Time -->\n @if (!resendable()) {\n <div class=\"time-remaining\">\n {{ '@tokenCount' | translate }}: {{ getFormattedTime() }}\n </div>\n }\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 @if (resendable()) {\n <div class=\"resend-container\">\n <span (click)=\"onResendMFACode()\" class=\"resend-link\">\n {{ 'resend MFACode' | translate }}\n </span>\n </div>\n }\n </form>\n } @else {\n <div class=\"loading-spinner\">\n <span>{{ '@loggingIn' | translate }}</span>\n </div>\n }\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$4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$4.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$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$4.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$4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$4.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$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: i2$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$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: i1$1.TranslatePipe, name: "translate" }] });
4450
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", 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 @if (icon()) {\n <button mat-icon-button [matMenuTriggerFor]=\"languageMenu\">\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n } @else {\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n }\n </div>\n\n <h2>{{'app_name' | translate }}</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n @for (language of languages; track language) {\n <button mat-menu-item (click)=\"setLanguage(language)\">\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n }\n </mat-menu>\n\n @if (!isLoading()) {\n <form (ngSubmit)=\"login()\" [formGroup]=\"loginForm\" novalidate>\n @if (error()) {\n <div class=\"error-message\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n }\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 @if (loginForm.controls['mfaCode'].invalid &&\n loginForm.controls['mfaCode'].touched) {\n <div class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'mfaCode is required' | translate }}\n </div>\n }\n\n <!-- Time -->\n @if (!resendable()) {\n <div class=\"time-remaining\">\n {{ '@tokenCount' | translate }}: {{ getFormattedTime() }}\n </div>\n }\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 @if (resendable()) {\n <div class=\"resend-container\">\n <span (click)=\"onResendMFACode()\" class=\"resend-link\">\n {{ 'resend MFACode' | translate }}\n </span>\n </div>\n }\n </form>\n } @else {\n <div class=\"loading-spinner\">\n <span>{{ '@loggingIn' | translate }}</span>\n </div>\n }\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$5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$5.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$5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$5.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$5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$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: i2$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$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: i1$1.TranslatePipe, name: "translate" }] });
4405
4451
  }
4406
4452
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: MfaLogin, decorators: [{
4407
4453
  type: Component,
@@ -4575,7 +4621,7 @@ class ForgotPassword extends AuthBase {
4575
4621
  });
4576
4622
  }
4577
4623
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ForgotPassword, deps: [], target: i0.ɵɵFactoryTarget.Component });
4578
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", 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 @if (icon()) {\n <button mat-icon-button [matMenuTriggerFor]=\"languageMenu\">\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n } @else {\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n }\n </div>\n\n <h2>{{'app_name' | translate }}</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n @for (language of languages; track language) {\n <button mat-menu-item (click)=\"setLanguage(language)\">\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n }\n </mat-menu>\n\n @if (!isLoading()) {\n <form\n (ngSubmit)=\"updatePassword()\"\n [formGroup]=\"updatePasswordForm\"\n novalidate\n >\n @if (error()) {\n <div class=\"error-message\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n }\n\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 @if (updatePasswordForm.controls['newPassword'].invalid &&\n updatePasswordForm.controls['newPassword'].touched) {\n <mat-error class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'password is required' | translate }}\n </mat-error>\n }\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 @if (updatePasswordForm.controls['newPasswordCheck'].invalid &&\n updatePasswordForm.controls['newPasswordCheck'].touched) {\n <mat-error class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'password is required' | translate }}\n </mat-error>\n }\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 } @else {\n <!-- Burada eski #loading template'inin i\u00E7eri\u011Fi direkt olarak kullan\u0131l\u0131yor -->\n <div class=\"loading-spinner\">\n <span>{{ '@updatingPassword' | translate }}</span>\n </div>\n }\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$4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$4.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$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$4.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$4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$4.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$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: i2$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$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: i3$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
4624
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", 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 @if (icon()) {\n <button mat-icon-button [matMenuTriggerFor]=\"languageMenu\">\n <span class=\"{{ getCurrentLanguageIcon() }}\"></span>\n </button>\n } @else {\n <button\n mat-raised-button\n color=\"primary\"\n [matMenuTriggerFor]=\"languageMenu\"\n >\n {{ currentLanguage }}\n </button>\n }\n </div>\n\n <h2>{{'app_name' | translate }}</h2>\n\n <!-- Language Menu -->\n <mat-menu #languageMenu=\"matMenu\">\n @for (language of languages; track language) {\n <button mat-menu-item (click)=\"setLanguage(language)\">\n <span class=\"{{ getLanguageIcon(language) }}\"></span>\n {{ language }}\n </button>\n }\n </mat-menu>\n\n @if (!isLoading()) {\n <form\n (ngSubmit)=\"updatePassword()\"\n [formGroup]=\"updatePasswordForm\"\n novalidate\n >\n @if (error()) {\n <div class=\"error-message\">\n <span class=\"error-icon\">!</span>\n {{ error() }}\n </div>\n }\n\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 @if (updatePasswordForm.controls['newPassword'].invalid &&\n updatePasswordForm.controls['newPassword'].touched) {\n <mat-error class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'password is required' | translate }}\n </mat-error>\n }\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 @if (updatePasswordForm.controls['newPasswordCheck'].invalid &&\n updatePasswordForm.controls['newPasswordCheck'].touched) {\n <mat-error class=\"validation-error\">\n <span class=\"error-icon\">!</span>\n {{ 'password is required' | translate }}\n </mat-error>\n }\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 } @else {\n <!-- Burada eski #loading template'inin i\u00E7eri\u011Fi direkt olarak kullan\u0131l\u0131yor -->\n <div class=\"loading-spinner\">\n <span>{{ '@updatingPassword' | translate }}</span>\n </div>\n }\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$5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$5.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$5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$5.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$5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$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: i2$3.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$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: i3$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
4579
4625
  }
4580
4626
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ForgotPassword, decorators: [{
4581
4627
  type: Component,
@@ -4851,5 +4897,5 @@ const toEnum = (v, enumObj) => {
4851
4897
  * Generated bundle index. Do not edit.
4852
4898
  */
4853
4899
 
4854
- export { AlertService, AuthenticationGuard, AuthenticationInterceptor, AuthenticationService, ButtonRenderer, CanDeactivateGuard, CheckboxRenderer, CommonService, ConfirmDialog, CredentialsService, CurrentUserPreference, ENVIRONMENT_CONFIG, EnvironmentInfo, EnvironmentInfoService, ErrorAlert, ExcelImportBase, ExcelParser, ExcelParserError, ForgotPassword, Guid, Login, LoginDto, MFACodeDto, MfaLogin, NettyAgGridBase, NettyAgGridListBase, NettyAgGridListFilterBase, NettyAgGridLogBase, NettyAgGridSaveBase, NettyAgGridService, NettyAppsBase, NettyAppsFilterBase, NettyBaseApp, NettyHelper, NettyImageService, NettyMenuService, NtyLoadingComponent, NtyLoadingInterceptor, NtyLoadingService, Ntybase, NtybaseModule, PageTitle, ParseLog, RangeDateTimeFilter, RangeNumberFilter, RangeStringFilter, UrlHelperService, ntyAuthenticationInterceptor, toBoolean, toDate, toEnum, toNumber, toUpperString };
4900
+ export { AlertService, AuthenticationGuard, AuthenticationInterceptor, AuthenticationService, ButtonRenderer, CanDeactivateGuard, CheckboxRenderer, CommonService, ConfirmDialog, CredentialsService, CurrentUserPreference, ENVIRONMENT_CONFIG, EnvironmentInfo, EnvironmentInfoService, ErrorAlert, ExcelImportBase, ExcelLogViewer, ExcelParser, ExcelParserError, ForgotPassword, Guid, Login, LoginDto, MFACodeDto, MfaLogin, NettyAgGridBase, NettyAgGridListBase, NettyAgGridListFilterBase, NettyAgGridLogBase, NettyAgGridSaveBase, NettyAgGridService, NettyAppsBase, NettyAppsFilterBase, NettyBaseApp, NettyHelper, NettyImageService, NettyMenuService, NtyLoadingComponent, NtyLoadingInterceptor, NtyLoadingService, Ntybase, NtybaseModule, PageTitle, ParseLog, RangeDateTimeFilter, RangeNumberFilter, RangeStringFilter, UrlHelperService, ntyAuthenticationInterceptor, toBoolean, toDate, toEnum, toNumber, toUpperString };
4855
4901
  //# sourceMappingURL=nettyapps-ntybase.mjs.map