newportsite 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/newportsite-1.1.3.tgz +0 -0
  2. package/ng-package.json +7 -0
  3. package/obfuscate.js +70 -0
  4. package/package.json +15 -0
  5. package/src/lib/app.component.ts +47 -0
  6. package/src/lib/app.routing.ts +38 -0
  7. package/src/lib/auth/alert.component.html +5 -0
  8. package/src/lib/auth/alert.component.ts +24 -0
  9. package/src/lib/auth/auth.component.html +1 -0
  10. package/src/lib/auth/auth.component.ts +10 -0
  11. package/src/lib/auth/auth.routes.ts +16 -0
  12. package/src/lib/auth/index.ts +4 -0
  13. package/src/lib/auth/login.component.html +87 -0
  14. package/src/lib/auth/login.component.ts +158 -0
  15. package/src/lib/auth/models/index.ts +1 -0
  16. package/src/lib/auth/models/user.ts +25 -0
  17. package/src/lib/auth/register.component.html +157 -0
  18. package/src/lib/auth/register.component.ts +219 -0
  19. package/src/lib/auth/services/alert.service.ts +47 -0
  20. package/src/lib/auth/services/auth.service.ts +28 -0
  21. package/src/lib/auth/services/index.ts +3 -0
  22. package/src/lib/auth/services/user.service.spec.ts +112 -0
  23. package/src/lib/auth/services/user.service.ts +47 -0
  24. package/src/lib/common/card.component.html +72 -0
  25. package/src/lib/common/card.component.ts +102 -0
  26. package/src/lib/common/commands.component.html +8 -0
  27. package/src/lib/common/commands.component.ts +42 -0
  28. package/src/lib/common/context.component.html +9 -0
  29. package/src/lib/common/context.component.ts +38 -0
  30. package/src/lib/common/grid.component.html +20 -0
  31. package/src/lib/common/grid.component.ts +747 -0
  32. package/src/lib/common/index.ts +9 -0
  33. package/src/lib/common/loader.component.html +5 -0
  34. package/src/lib/common/loader.component.ts +27 -0
  35. package/src/lib/common/lookup.component.html +29 -0
  36. package/src/lib/common/lookup.component.ts +115 -0
  37. package/src/lib/common/messagebox.component.html +39 -0
  38. package/src/lib/common/messagebox.component.ts +74 -0
  39. package/src/lib/common/theme-toggle.component.ts +139 -0
  40. package/src/lib/config.ts +62 -0
  41. package/src/lib/containers/default-layout/default-layout.component.html +191 -0
  42. package/src/lib/containers/default-layout/default-layout.component.ts +158 -0
  43. package/src/lib/containers/default-layout/index.ts +1 -0
  44. package/src/lib/containers/index.ts +1 -0
  45. package/src/lib/directives/component.draggable.ts +80 -0
  46. package/src/lib/directives/index.ts +2 -0
  47. package/src/lib/directives/input.directive.spec.ts +158 -0
  48. package/src/lib/directives/input.directive.ts +210 -0
  49. package/src/lib/home/dashboard/dashboard.component.html +38 -0
  50. package/src/lib/home/dashboard/dashboard.component.ts +50 -0
  51. package/src/lib/home/dashboard/index.ts +1 -0
  52. package/src/lib/home/index.component.html +1 -0
  53. package/src/lib/home/index.component.ts +10 -0
  54. package/src/lib/home/index.routes.ts +29 -0
  55. package/src/lib/home/index.ts +1 -0
  56. package/src/lib/home/info/index.ts +1 -0
  57. package/src/lib/home/info/info.component.css +476 -0
  58. package/src/lib/home/info/info.component.html +174 -0
  59. package/src/lib/home/info/info.component.ts +287 -0
  60. package/src/lib/home/model/article.component.html +10 -0
  61. package/src/lib/home/model/article.component.ts +50 -0
  62. package/src/lib/home/model/barchart.component.html +8 -0
  63. package/src/lib/home/model/barchart.component.ts +59 -0
  64. package/src/lib/home/model/index.ts +7 -0
  65. package/src/lib/home/model/itemdetail.component.html +25 -0
  66. package/src/lib/home/model/itemdetail.component.ts +93 -0
  67. package/src/lib/home/model/itemtab.component.html +25 -0
  68. package/src/lib/home/model/itemtab.component.ts +105 -0
  69. package/src/lib/home/model/model.component.html +121 -0
  70. package/src/lib/home/model/model.component.ts +510 -0
  71. package/src/lib/home/model/modeltoolbar.component.html +111 -0
  72. package/src/lib/home/model/modeltoolbar.component.ts +157 -0
  73. package/src/lib/home/model/navigation.component.html +86 -0
  74. package/src/lib/home/model/navigation.component.ts +247 -0
  75. package/src/lib/home/model/services/index.ts +1 -0
  76. package/src/lib/home/model/services/model.service.spec.ts +423 -0
  77. package/src/lib/home/model/services/model.service.ts +319 -0
  78. package/src/lib/home/modelsearch/index.ts +1 -0
  79. package/src/lib/home/modelsearch/modelsearch.component.html +124 -0
  80. package/src/lib/home/modelsearch/modelsearch.component.ts +453 -0
  81. package/src/lib/interfaces/data.interface.ts +131 -0
  82. package/src/lib/interfaces/index.ts +2 -0
  83. package/src/lib/interfaces/item.interface.ts +438 -0
  84. package/src/lib/players/lookup/lookup.directive.ts +6 -0
  85. package/src/lib/players/lookup/lookup.item.component.ts +37 -0
  86. package/src/lib/players/lookup/lookup.item.ts +9 -0
  87. package/src/lib/players/lookup/lookup.player.component.ts +59 -0
  88. package/src/lib/players/lookup/lookup.selector.component.ts +41 -0
  89. package/src/lib/players/model/model.directive.ts +6 -0
  90. package/src/lib/players/model/model.item.component.spec.ts +311 -0
  91. package/src/lib/players/model/model.item.component.ts +3457 -0
  92. package/src/lib/players/model/model.item.ts +9 -0
  93. package/src/lib/players/model/model.player.component.ts +109 -0
  94. package/src/lib/players/model/model.selector.component.ts +59 -0
  95. package/src/lib/scheduler/scheduler.component.html +13 -0
  96. package/src/lib/scheduler/scheduler.component.scss +6 -0
  97. package/src/lib/scheduler/scheduler.component.ts +296 -0
  98. package/src/lib/scheduler/scheduler.routes.ts +15 -0
  99. package/src/lib/scheduler/schedulerdialog.component.html +72 -0
  100. package/src/lib/scheduler/schedulerdialog.component.ts +208 -0
  101. package/src/lib/scheduler/services/scheduler.service.ts +133 -0
  102. package/src/lib/services/auth-state.service.ts +129 -0
  103. package/src/lib/services/auth.interceptor.spec.ts +144 -0
  104. package/src/lib/services/auth.interceptor.ts +44 -0
  105. package/src/lib/services/cache.service.spec.ts +143 -0
  106. package/src/lib/services/cache.service.ts +71 -0
  107. package/src/lib/services/global-error-handler.spec.ts +39 -0
  108. package/src/lib/services/global-error-handler.ts +28 -0
  109. package/src/lib/services/global.service.spec.ts +801 -0
  110. package/src/lib/services/global.service.ts +724 -0
  111. package/src/lib/services/message.service.ts +556 -0
  112. package/src/lib/services/theme.service.ts +96 -0
  113. package/src/lib/template/authtemplate.component.html +6 -0
  114. package/src/lib/template/authtemplate.component.ts +13 -0
  115. package/src/lib/template/basetemplate.component.html +7 -0
  116. package/src/lib/template/basetemplate.component.ts +13 -0
  117. package/src/lib/template/index.ts +3 -0
  118. package/src/lib/template/modeltemplate.component.html +7 -0
  119. package/src/lib/template/modeltemplate.component.ts +21 -0
  120. package/src/lib/utils/piva.spec.ts +56 -0
  121. package/src/lib/utils/piva.ts +29 -0
  122. package/src/lib/validators/email.validator.spec.ts +57 -0
  123. package/src/lib/validators/email.validator.ts +17 -0
  124. package/src/lib/validators/equalPasswords.validator.spec.ts +54 -0
  125. package/src/lib/validators/equalPasswords.validator.ts +17 -0
  126. package/src/lib/validators/index.ts +2 -0
  127. package/src/lib/version.ts +1 -0
  128. package/src/public-api.ts +64 -0
  129. package/src/typings.d.ts +2 -0
  130. package/tsconfig.lib.json +18 -0
  131. package/tsconfig.lib.prod.json +9 -0
@@ -0,0 +1,724 @@
1
+ import { Injectable, inject, signal } from '@angular/core';
2
+ import { Location } from '@angular/common';
3
+ import { Router } from '@angular/router';
4
+ import { HttpHeaders } from '@angular/common/http';
5
+ import { User } from '../auth/models/index';
6
+ import { Subject, Observable } from 'rxjs';
7
+ import { NEWPORT_CONFIG } from '../config';
8
+ import { AuthStateService } from './auth-state.service';
9
+ import { CacheService } from './cache.service';
10
+
11
+ import {
12
+ ItemInterface,
13
+ ItemFieldChangedInterface,
14
+ ItemLinkInterface,
15
+ UserLicenses,
16
+ ItemCommandInterface,
17
+ ItemDialogInterface,
18
+ ItemCurrentFocusInterface,
19
+ ViewListInterface,
20
+ ManagmentInterface,
21
+ LookupFieldInterface,
22
+ DialogFieldInterface,
23
+ EntitiesData,
24
+ Entities,
25
+ Member,
26
+ SchedulerInterface,
27
+ } from '../interfaces/index';
28
+
29
+ export enum Languages {
30
+ IT = 'IT',
31
+ EN = 'EN',
32
+ }
33
+
34
+ @Injectable({ providedIn: 'root' })
35
+ export class GlobalService {
36
+ private router = inject(Router);
37
+ private location = inject(Location);
38
+ private authState = inject(AuthStateService);
39
+ private cache = inject(CacheService);
40
+ private config = inject(NEWPORT_CONFIG);
41
+
42
+ private loaderSubject = new Subject<boolean>();
43
+ private loadedSubject = new Subject<boolean>();
44
+ private modelSubject = new Subject<ItemInterface>();
45
+ private commandsSubject = new Subject<ItemCommandInterface[]>();
46
+ private currentFieldSubject = new Subject<Member>();
47
+ private fieldChangedSubject = new Subject<ItemFieldChangedInterface>();
48
+ private lookupSubject = new Subject<ViewListInterface>();
49
+ private projectChangedSubject = new Subject<string>(); // NEW: Project changed
50
+ private stateChangedSubject = new Subject<void>(); // language or year changed
51
+ private lookupFieldSubject = new Subject<LookupFieldInterface>();
52
+ private dialogFieldSubject = new Subject<DialogFieldInterface>();
53
+ private gridRefreshSubject = new Subject<void>();
54
+ private itemStateSubject = new Subject<void>();
55
+ private readonly _isLoading = signal(false);
56
+ private readonly _isModalActive = signal(false);
57
+ private modalActiveSubject = new Subject<boolean>();
58
+ private isDialog: ItemDialogInterface | null = null;
59
+ private currentFocus: ItemCurrentFocusInterface = { id: '', value: '' };
60
+ private currentKeys: string = '';
61
+ private active = '/auth/login';
62
+ private lng = '';
63
+ private years!: number[];
64
+ private year!: number;
65
+ private projects!: UserLicenses[][];
66
+ private project!: UserLicenses;
67
+ private decimalChar = ',';
68
+ private thousandChar = '.';
69
+ private keyId = '';
70
+
71
+ private inactivityTime: number = 0;
72
+ private sessionTime: number = 0;
73
+
74
+ private currentLinkItem!: ItemLinkInterface;
75
+
76
+ private extendedDescription = '';
77
+ private scale: number = 1;
78
+
79
+ private view: string = 'Dashboard';
80
+
81
+ constructor() {
82
+ this.lng = Languages.IT;
83
+ this.sessionTime = new Date().getTime();
84
+ this.inactivityTime = new Date().getTime();
85
+ this.logOff();
86
+ }
87
+
88
+ /** Normalizes a URL using Angular Location, ensuring correct hash routing. */
89
+ public sanitizeurl(url: string) {
90
+ return this.location.normalize(url);
91
+ }
92
+
93
+ /** Navigates to the given URL and updates the active link tracker. */
94
+ public navigate(url: string) {
95
+ this.setActiveLink(url);
96
+ return this.router.navigateByUrl(this.location.normalize(url));
97
+ }
98
+
99
+ /** Clears user session, resets dialog/focus state and redirects to login. */
100
+ public logOff(): void {
101
+ this.setCurrentUser(null);
102
+ this.isDialog = { target: '', state: false };
103
+ this.currentFocus = { id: '', value: '' };
104
+ this.view = 'Dashboard';
105
+ this.setView(this.view);
106
+ this.navigate('auth/login');
107
+ }
108
+
109
+ /** Redirects to the default login page. */
110
+ public navigateToDefault() {
111
+ this.navigate('/auth/login');
112
+ }
113
+
114
+ /** Returns true if a valid, non-expired user session is present in localStorage. */
115
+ public isLogged(): boolean {
116
+ return this.authState.isLogged();
117
+ }
118
+
119
+ /** Sets the currently active route URL and returns it. */
120
+ public setActiveLink(url: string) {
121
+ this.active = url;
122
+ return this.active;
123
+ }
124
+
125
+ /** Returns the currently active route URL. */
126
+ public getActiveLink(): string {
127
+ return this.active;
128
+ }
129
+
130
+ /** Reads and parses the current user from localStorage. Returns null on failure. */
131
+ public getCurrentUser(): User | null {
132
+ return this.authState.getCurrentUser();
133
+ }
134
+
135
+ /** Persists the current user to localStorage, or removes it if null. */
136
+ public setCurrentUser(currentUser: User | null): void {
137
+ this.authState.setCurrentUser(currentUser);
138
+ }
139
+
140
+ /** Builds the HTTP options object containing the JWT Authorization header. */
141
+ public jwt(): { headers: HttpHeaders };
142
+ public jwt(fileType: string): { headers: HttpHeaders; reportProgress: boolean; responseType: 'blob' };
143
+ public jwt(fileType?: string): { headers: HttpHeaders; reportProgress?: boolean; responseType?: 'blob' } {
144
+ return fileType ? this.authState.jwt(fileType) : this.authState.jwt();
145
+ }
146
+
147
+ /** Constructs HttpHeaders with Content-Type and the Bearer token (if logged in). */
148
+ public getHeaders(user: User | null): HttpHeaders {
149
+ return this.authState.getHeaders(user);
150
+ }
151
+
152
+ /** Returns the display name of the currently logged-in user, or an empty string. */
153
+ public loggedUser(): string {
154
+ return this.authState.loggedUser();
155
+ }
156
+
157
+ /** Returns the base API URL from the current environment configuration. */
158
+ public restService(): string {
159
+ return this.config.apiUrl;
160
+ }
161
+
162
+ /** Navigates to the view associated with the given view name (Dashboard, Calendar, etc.). */
163
+ public setView(view: string): void {
164
+ this.view = view;
165
+ if (view === 'Calendario' || view === 'Calendar') {
166
+ this.navigate('/scheduler');
167
+ } else if (view === 'Dashboard') {
168
+ this.navigate('/home');
169
+ } else if (view === 'Informazioni' || view === 'Information') {
170
+ this.navigate('/home/info');
171
+ }
172
+ }
173
+
174
+ /** Returns the current view name. */
175
+ public getView() {
176
+ return this.view;
177
+ }
178
+
179
+ /** Sets the application language, updates decimal/thousand separators and notifies subscribers. */
180
+ public setLng(lng: string) {
181
+ if (lng === Languages.IT) {
182
+ this.setDecimalChar(',');
183
+ this.setThousandChar('.');
184
+ } else if (lng === Languages.EN) {
185
+ this.setDecimalChar('.');
186
+ this.setThousandChar(',');
187
+ }
188
+ this.lng = lng;
189
+ // Clear cached project items so navigation reloads with new language
190
+ this.cache.clearProjectItems();
191
+ this.stateChangedSubject.next();
192
+ // Fire projectChanged so NavigationComponent reinitializes
193
+ if (this.project) {
194
+ this.projectChangedSubject.next(this.project.project + '.' + this.year);
195
+ }
196
+ }
197
+
198
+ /** Returns the active locale code ('IT' or 'EN'). */
199
+ public getLng() {
200
+ return this.lng;
201
+ }
202
+
203
+ /** Sets the active project; fires projectChangedSubject when the project changes. */
204
+ public setProject(project: UserLicenses | null): void {
205
+ const previousProject = this.project?.project;
206
+ this.project = project!;
207
+ const newProject = project?.project + '.' + project?.year;
208
+ if (previousProject !== project?.project) {
209
+ this.projectChangedSubject.next(newProject);
210
+ }
211
+ }
212
+
213
+ /** Returns the currently active project license. */
214
+ public getProject(): UserLicenses {
215
+ return this.project;
216
+ }
217
+
218
+ /** Stores the full matrix of available project licenses. */
219
+ public setProjects(projects: UserLicenses[][]) {
220
+ this.projects = projects;
221
+ }
222
+
223
+ /** Returns the full matrix of available project licenses. */
224
+ public getProjects(): UserLicenses[][] {
225
+ return this.projects;
226
+ }
227
+
228
+ /** Stores the list of available fiscal years. */
229
+ public setYears(years: number[]) {
230
+ this.years = years;
231
+ }
232
+
233
+ /** Returns the list of available fiscal years. */
234
+ public getYears(): number[] {
235
+ return this.years;
236
+ }
237
+
238
+ /** Sets the active fiscal year, clears item cache and notifies subscribers. */
239
+ public setYear(year: number) {
240
+ this.year = year;
241
+ // Clear cached items for new year; fire projectChanged to reload navigation
242
+ this.cache.clearProjectItems();
243
+ this.stateChangedSubject.next();
244
+ if (this.project) {
245
+ this.projectChangedSubject.next(this.project.project + '.' + year);
246
+ }
247
+ }
248
+
249
+ /** Returns the active fiscal year. */
250
+ public getYear(): number {
251
+ return this.year;
252
+ }
253
+
254
+ /** Sets the primary key identifier used for record navigation. */
255
+ public setKeyId(keyId: string) {
256
+ this.keyId = keyId;
257
+ }
258
+
259
+ /** Returns the primary key identifier used for record navigation. */
260
+ public getKeyId(): string {
261
+ return this.keyId;
262
+ }
263
+
264
+ /** Emits a field-focus change event to all CurrentField subscribers. */
265
+ public setCurrentField(field: Member) {
266
+ this.currentFieldSubject.next(field);
267
+ }
268
+
269
+ /** Returns an Observable that emits whenever the focused field changes. */
270
+ public getCurrentField(): Observable<Member> {
271
+ return this.currentFieldSubject.asObservable();
272
+ }
273
+
274
+ /** Completes the CurrentField subject (called on teardown). */
275
+ public completeCurrentField() {
276
+ this.currentFieldSubject.complete();
277
+ }
278
+
279
+ /** Emits a field-value/state change event to all FieldChanged subscribers. */
280
+ public setFieldChanged(field: ItemFieldChangedInterface) {
281
+ this.fieldChangedSubject.next(field);
282
+ }
283
+
284
+ /** Returns an Observable that emits on every field value or state change. */
285
+ public getFieldChanged(): Observable<ItemFieldChangedInterface> {
286
+ return this.fieldChangedSubject.asObservable();
287
+ }
288
+
289
+ /** Completes the FieldChanged subject (called on teardown). */
290
+ public completeFieldChanged() {
291
+ this.fieldChangedSubject.complete();
292
+ }
293
+
294
+ /** Emits the newly selected model item, or signals deselection when undefined. */
295
+ public setCurrentItem(model: ItemInterface | undefined): void {
296
+ if (model) {
297
+ this.modelSubject.next(model);
298
+ }
299
+ }
300
+
301
+ /** Returns an Observable that emits the currently selected model item. */
302
+ public getCurrentItem(): Observable<ItemInterface> {
303
+ return this.modelSubject.asObservable();
304
+ }
305
+
306
+ /** Completes the CurrentItem subject (called on teardown). */
307
+ public completeCurrentItem() {
308
+ this.modelSubject.complete();
309
+ }
310
+
311
+ /** Signals the grid to refresh its data (e.g. after a record mutation). */
312
+ public emitGridRefresh(): void {
313
+ this.gridRefreshSubject.next();
314
+ }
315
+
316
+ /** Returns an Observable that emits a void signal on each grid refresh request. */
317
+ public getGridRefresh(): Observable<void> {
318
+ return this.gridRefreshSubject.asObservable();
319
+ }
320
+
321
+ /** Signals that item validation states (ItemState) have been updated by updateState(). */
322
+ public emitItemState(): void {
323
+ this.itemStateSubject.next();
324
+ }
325
+
326
+ /** Returns an Observable that emits whenever item states are recalculated. */
327
+ public getItemState(): Observable<void> {
328
+ return this.itemStateSubject.asObservable();
329
+ }
330
+
331
+ /** Returns an Observable that emits the new project key ('APP.YEAR') on project change. */
332
+ public getProjectChanged(): Observable<string> {
333
+ return this.projectChangedSubject.asObservable();
334
+ }
335
+
336
+ /** Returns an Observable that emits when the locale or fiscal year changes. */
337
+ public getStateChanged(): Observable<void> {
338
+ return this.stateChangedSubject.asObservable();
339
+ }
340
+
341
+ /** Shows or hides the global loader overlay; also updates the loading signal. */
342
+ public setLoaderState(state: boolean) {
343
+ this.loaderSubject.next(state);
344
+ this.setLoading(state);
345
+ }
346
+
347
+ /** Returns an Observable that emits whenever the loader visibility changes. */
348
+ public getLoaderState(): Observable<boolean> {
349
+ return this.loaderSubject.asObservable();
350
+ }
351
+
352
+ /** Updates the reactive loading signal directly (without emitting the subject). */
353
+ public setLoading(loading: boolean) {
354
+ this._isLoading.set(loading);
355
+ }
356
+
357
+ /** Returns the current loading state synchronously via the signal. */
358
+ public getLoading(): boolean {
359
+ return this._isLoading();
360
+ }
361
+
362
+ /** Marks a modal (dialog or lookup) as active/inactive; updates signal and emits event. */
363
+ public setModalActive(active: boolean) {
364
+ this._isModalActive.set(active);
365
+ this.modalActiveSubject.next(active);
366
+ }
367
+
368
+ /** Returns whether a modal overlay is currently active (synchronous signal). */
369
+ public getModalActive(): boolean {
370
+ return this._isModalActive();
371
+ }
372
+
373
+ /** Returns an Observable that emits on every modal active/inactive transition. */
374
+ public getModalActiveChanges(): Observable<boolean> {
375
+ return this.modalActiveSubject.asObservable();
376
+ }
377
+
378
+ /** Emits a loaded-state event (used by components that need to know data is ready). */
379
+ public setLoadedState(state: boolean) {
380
+ this.loadedSubject.next(state);
381
+ }
382
+
383
+ /** Returns an Observable that emits when the loaded state changes. */
384
+ public getLoadedState(): Observable<boolean> {
385
+ return this.loadedSubject.asObservable();
386
+ }
387
+
388
+ /** Completes the loader subject (called on teardown). */
389
+ public completeLoaderState() {
390
+ this.loaderSubject.complete();
391
+ }
392
+
393
+ /** Emits the updated toolbar command-state array to all toolbar subscribers. */
394
+ public setItemCommands(commands: ItemCommandInterface[]) {
395
+ this.commandsSubject.next(commands);
396
+ }
397
+
398
+ /** Returns an Observable that emits the command-state array on each toolbar update. */
399
+ public getItemCommands(): Observable<ItemCommandInterface[]> {
400
+ return this.commandsSubject.asObservable();
401
+ }
402
+
403
+ /** Completes the commands subject (called on teardown). */
404
+ public completeItemCommands() {
405
+ this.commandsSubject.complete();
406
+ }
407
+
408
+ /** Emits a lookup-result event (selected row from lookup popup). */
409
+ public setLookup(lookup: ViewListInterface) {
410
+ this.lookupSubject.next(lookup);
411
+ }
412
+
413
+ /** Returns an Observable that emits when a lookup row is selected. */
414
+ public getLookup(): Observable<ViewListInterface> {
415
+ return this.lookupSubject.asObservable();
416
+ }
417
+
418
+ /** Completes the lookup subject (called on teardown). */
419
+ public completeLookup() {
420
+ this.lookupSubject.complete();
421
+ }
422
+
423
+ /** Broadcasts lookup-panel display state (visibility, size, position, data) to listeners. */
424
+ public setLookupField(lookup: LookupFieldInterface) {
425
+ this.lookupFieldSubject.next(lookup);
426
+ }
427
+
428
+ /** Returns an Observable that emits whenever the lookup panel state changes. */
429
+ public getLookupField(): Observable<LookupFieldInterface> {
430
+ return this.lookupFieldSubject.asObservable();
431
+ }
432
+
433
+ /** Broadcasts dialog-panel display state (visibility, size, position, message) to listeners. */
434
+ public setDialogField(dialog: DialogFieldInterface) {
435
+ this.dialogFieldSubject.next(dialog);
436
+ }
437
+
438
+ /** Returns an Observable that emits whenever the dialog panel state changes. */
439
+ public getDialogField(): Observable<DialogFieldInterface> {
440
+ return this.dialogFieldSubject.asObservable();
441
+ }
442
+
443
+ /** Completes the lookupField subject (called on teardown). */
444
+ public completeLookupField() {
445
+ this.lookupFieldSubject.complete();
446
+ }
447
+
448
+ /** Sets the decimal separator character for the active locale. */
449
+ public setDecimalChar(char: string) {
450
+ this.decimalChar = char;
451
+ }
452
+
453
+ /** Returns the decimal separator character for the active locale. */
454
+ public getDecimalChar() {
455
+ return this.decimalChar;
456
+ }
457
+
458
+ /** Sets the thousands-separator character for the active locale. */
459
+ public setThousandChar(char: string) {
460
+ this.thousandChar = char;
461
+ }
462
+
463
+ /** Returns the thousands-separator character for the active locale. */
464
+ public getThousandChar() {
465
+ return this.thousandChar;
466
+ }
467
+
468
+ /** Sets the dialog interaction state (target field, open/closed). */
469
+ public setDialog(dialog: ItemDialogInterface | null) {
470
+ this.isDialog = dialog;
471
+ }
472
+
473
+ /** Returns the current dialog interaction state, or null if no dialog is open. */
474
+ public getDialog(): ItemDialogInterface | null {
475
+ return this.isDialog;
476
+ }
477
+
478
+ /** Stores the field that should receive focus after the current interaction completes. */
479
+ public setCurrentFocus(currentFocus: ItemCurrentFocusInterface) {
480
+ this.currentFocus = currentFocus;
481
+ }
482
+
483
+ /** Returns the field scheduled to receive focus next. */
484
+ public getCurrentFocus() {
485
+ return this.currentFocus;
486
+ }
487
+
488
+ /** Stores the composite key string used to restore record position. */
489
+ public setCurrentKeys(currentKeys: string) {
490
+ this.currentKeys = currentKeys;
491
+ }
492
+
493
+ /** Returns the composite key string used to restore record position. */
494
+ public getCurrentKeys() {
495
+ return this.currentKeys;
496
+ }
497
+
498
+ /** Opens an external URL in the same window without leaving a history entry in the SPA router. */
499
+ public externalnavigate(url: string) {
500
+ window.location.href = url;
501
+ window.history.pushState('', '', '');
502
+ }
503
+
504
+ /** Retrieves the cached entity data for the given composite key (APP.ENT.YEAR). */
505
+ public getData(id: string): EntitiesData | undefined {
506
+ return this.cache.getData(id);
507
+ }
508
+
509
+ /** Stores entity data in the in-memory cache under the given composite key. */
510
+ public setData(id: string, data: EntitiesData) {
511
+ this.cache.setData(id, data);
512
+ }
513
+
514
+ /** Removes the cached entity data for the given composite key. */
515
+ public removeData(id: string) {
516
+ this.cache.removeData(id);
517
+ }
518
+
519
+ /** Returns the cached schema field definitions for the given entity key. */
520
+ public getFields(id: string): Member[] {
521
+ return this.cache.getFields(id);
522
+ }
523
+
524
+ /** Stores schema field definitions in the cache under the given entity key. */
525
+ public setFields(id: string, fields: Member[]) {
526
+ this.cache.setFields(id, fields);
527
+ }
528
+
529
+ /** Returns the cached navigation item list for the given project key (APP.YEAR). */
530
+ public getProjectItems(id: string) {
531
+ return this.cache.getProjectItems(id);
532
+ }
533
+
534
+ /** Stores the navigation item list in the cache for the given project key. */
535
+ public setProjectItems(id: string, projectItems: ItemInterface[]) {
536
+ this.cache.setProjectItems(id, projectItems);
537
+ }
538
+
539
+ /** Stores the link context of the currently navigated record for cross-entity navigation. */
540
+ public setCurrentLinkItem(item: ItemLinkInterface) {
541
+ this.currentLinkItem = item;
542
+ }
543
+
544
+ /** Returns the link context of the currently navigated record. */
545
+ public getCurrentLinkItem(): ItemLinkInterface {
546
+ return this.currentLinkItem;
547
+ }
548
+
549
+ /** Sorts an array by a dot-notation property path (e.g. 'address.city'). */
550
+ public sortArray<T>(p: string, array: T[]) {
551
+ const property: string[] = p.split('.');
552
+ const l: number = property.length;
553
+ return array.sort((a: T, b: T) => {
554
+ let va: any = a;
555
+ let vb: any = b;
556
+ let i = 0;
557
+ while (i < l) {
558
+ va = va[property[i]];
559
+ vb = vb[property[i]];
560
+ i++;
561
+ }
562
+ return va < vb ? -1 : 1;
563
+ });
564
+ }
565
+
566
+ /** Left-pads a number with zeros to reach the given total length. */
567
+ public padNumber(num: number, size: number): string {
568
+ let s = num + '';
569
+ while (s.length < size) {
570
+ s = '0' + s;
571
+ }
572
+ return s;
573
+ }
574
+
575
+ /** Normalizes a [dd, mm, yyyy] array into a zero-padded 'dd/mm/yyyy' string. */
576
+ public normalizeDate(dt: string[]): string {
577
+ const dd: string = dt[0];
578
+ const mm: string = dt[1];
579
+ const yyyy: string = dt[2];
580
+ return (
581
+ this.padNumber(parseInt(dd, 10), 2) +
582
+ '/' +
583
+ this.padNumber(parseInt(mm, 10), 2) +
584
+ '/' +
585
+ yyyy
586
+ );
587
+ }
588
+
589
+ /** Builds a sortable composite key string by zero-padding each key segment to 7 digits. */
590
+ public getKey(keys: string[]): string {
591
+ let retValue = '';
592
+ for (const key of keys) {
593
+ retValue += this.padNumber(parseInt(key, 10), 7);
594
+ }
595
+ return retValue;
596
+ }
597
+
598
+ /** Sets the extended description shown in the model title bar. */
599
+ public setExtendedDescription(description: string) {
600
+ this.extendedDescription = description;
601
+ }
602
+
603
+ /** Returns the extended description currently shown in the model title bar. */
604
+ public getExtendedDescription(): string {
605
+ return this.extendedDescription;
606
+ }
607
+
608
+ /** Sets the total render scale factor (SVG + form combined). */
609
+ public setScale(scale: number) {
610
+ this.scale = scale;
611
+ }
612
+
613
+ /** Returns the current total render scale factor. */
614
+ public getScale(): number {
615
+ return this.scale;
616
+ }
617
+
618
+ /** Returns the cached data JSON (runtime entity data) for the given project key. */
619
+ public getDataJson(id: string): EntitiesData {
620
+ return this.cache.getDataJson(id);
621
+ }
622
+
623
+ /** Stores the data JSON for the given project key in the session cache. */
624
+ public setDataJson(id: string, dataJson: EntitiesData) {
625
+ this.cache.setDataJson(id, dataJson);
626
+ }
627
+
628
+ /** Returns the cached schema JSON (entity structure) for the given project key. */
629
+ public getSchemaJson(id: string): Entities {
630
+ return this.cache.getSchemaJson(id);
631
+ }
632
+
633
+ /** Stores the schema JSON for the given project key in the session cache. */
634
+ public setSchemaJson(id: string, schemaJson: Entities) {
635
+ this.cache.setSchemaJson(id, schemaJson);
636
+ }
637
+
638
+ /** Returns a fresh, zeroed-out ManagmentInterface instance ready for an API call. */
639
+ public getManagmentInterface(): ManagmentInterface {
640
+ const managment: ManagmentInterface = {
641
+ functionId: '',
642
+ appId: '',
643
+ entId: '',
644
+ year: 0,
645
+ keys: '',
646
+ fields: [],
647
+ states: [],
648
+ entIds: [],
649
+ keyId: '1',
650
+ filterId: '',
651
+ page: 1,
652
+ data: '',
653
+ };
654
+ return managment;
655
+ }
656
+
657
+ /** Returns a fresh, zeroed-out SchedulerInterface instance ready for a scheduler API call. */
658
+ public getSchedulerInterface(): SchedulerInterface {
659
+ const scheduler: SchedulerInterface = {
660
+ year: 0,
661
+ id: '',
662
+ start: null,
663
+ end: null,
664
+ assigned: 0,
665
+ description: '',
666
+ allday: false,
667
+ };
668
+ return scheduler;
669
+ }
670
+
671
+ /**
672
+ * Activates a project: sets project/key-id, lazy-loads its data/schema JSON and
673
+ * navigates to the appropriate route (Calendar, Search list, or Model form).
674
+ */
675
+ public activateProject(prj: UserLicenses) {
676
+ this.setProject(prj);
677
+ this.setCurrentItem(undefined);
678
+ this.setKeyId('1');
679
+ if (prj.type === 'C') {
680
+ if (this.getLng() === Languages.IT) {
681
+ this.setView('Calendario');
682
+ } else if (this.getLng() === Languages.EN) {
683
+ this.setView('Calendar');
684
+ }
685
+ return;
686
+ }
687
+ this.config.projectsInfoLoader(prj.year, prj.project).then(prj_Info => {
688
+ const id = prj.project + '.' + prj.year;
689
+ if (!this.getDataJson(id)) {
690
+ this.setDataJson(id, prj_Info.getData());
691
+ }
692
+ if (!this.getSchemaJson(id)) {
693
+ this.setSchemaJson(id, prj_Info.getSchema());
694
+ }
695
+ if (prj.type === 'D' || prj.type === 'G') {
696
+ this.setActiveLink('/home/modelsearch');
697
+ this.navigate('/home/modelsearch');
698
+ } else {
699
+ this.setActiveLink('/home/model');
700
+ this.navigate('/home/model');
701
+ }
702
+ });
703
+ }
704
+
705
+ /** Parses a pixel-value string (e.g. '120px') and returns the integer part. */
706
+ public getInt(value: string): number {
707
+ return parseInt(value);
708
+ }
709
+
710
+ /** Parses a string and returns the floating-point number. */
711
+ public getFloat(value: string): number {
712
+ return parseFloat(value);
713
+ }
714
+
715
+ public makeId(length: number): string {
716
+ let result = '';
717
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
718
+ const charactersLength = characters.length;
719
+ for (let i = 0; i < length; i++) {
720
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
721
+ }
722
+ return result;
723
+ }
724
+ }