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,287 @@
1
+ import {
2
+ Component,
3
+ ChangeDetectionStrategy,
4
+ ChangeDetectorRef,
5
+ DestroyRef,
6
+ inject,
7
+ } from '@angular/core';
8
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
9
+ import { GlobalService, Languages } from '../../services/global.service';
10
+ import { AppMessageService } from '../../services/message.service';
11
+ import { PACKAGE_VERSION } from '../../version';
12
+
13
+ const TRANSLATIONS = {
14
+ IT: {
15
+ versionLabel: 'Versione',
16
+ heroBadge: 'Piattaforma Low-Code',
17
+ desc: "Costruisci applicazioni professionali in modo rapido ed elegante. Frontend Angular 21 con backend ASP.NET Core su .NET 10 — la piattaforma pensata per chi vuole concentrarsi sul business, non sull'infrastruttura.",
18
+ cta1: 'Inizia il Viaggio',
19
+ cta2: 'Richiedi una Demo',
20
+ mailSubj1: 'Richiesta%20informazioni%20aggiuntive',
21
+ mailBody1:
22
+ 'Salve%2C%0D%0AVorrei%20ricevere%20informazioni%20aggiuntive%20sulla%20piattaforma.%0D%0A%0D%0ASaluti',
23
+ mailSubj2: 'Richiesta%20demo',
24
+ mailBody2:
25
+ 'Salve%2C%0D%0AVorrei%20richiedere%20una%20demo%20della%20piattaforma.%0D%0A%0D%0ASaluti',
26
+ mailSubjCta: 'Richiesta%20informazioni%20aggiuntive',
27
+ mailBodyCta:
28
+ 'Salve%2C%0D%0AVorrei%20ricevere%20informazioni%20aggiuntive%20sulla%20piattaforma.%0D%0A%0D%0ASaluti',
29
+ stat1: 'Velocità di sviluppo',
30
+ stat2: 'Responsive & PWA',
31
+ stat3: 'Integrazioni',
32
+ stat4: 'Cloud Ready',
33
+
34
+ /* Tech stack */
35
+ techTag: 'Tech Stack',
36
+ techTitle: "Tecnologie all'avanguardia",
37
+ techSubtitle:
38
+ "Un'architettura moderna full-stack progettata per performance, scalabilità e manutenibilità.",
39
+ techFe: 'Frontend',
40
+ techBe: 'Backend',
41
+ techData: 'Dati & Cache',
42
+ techOps: 'DevOps & CI/CD',
43
+ techFeDesc:
44
+ 'Angular 21, TypeScript 5.9, Angular Signals, OnPush Change Detection, AG Grid, FullCalendar, Chart.js, pdfmake',
45
+ techBeDesc:
46
+ 'ASP.NET Core .NET 10, API Versioning, Swagger / OpenAPI, Rate Limiting, Output Caching',
47
+ techDataDesc:
48
+ 'SQL Server & MongoDB (duale), FusionCache con Redis e backplane, Serilog su MongoDB & Grafana Loki',
49
+ techOpsDesc:
50
+ 'Docker + nginx, GitHub Actions, GitVersion SemVer, Azure Container Apps & Static Web Apps',
51
+
52
+ /* Architecture */
53
+ archTag: 'Architettura',
54
+ archTitle: 'Progettata per scalare',
55
+ archSubtitle:
56
+ 'Separazione netta tra frontend SPA e backend RESTful, con osservabilità integrata e deploy containerizzato.',
57
+ archItems: [
58
+ {
59
+ label: 'SPA Angular 21',
60
+ detail:
61
+ 'Rendering dinamico di form SVG, pipeline di validazione a stati, computed field e lookup resolution',
62
+ },
63
+ {
64
+ label: 'REST API .NET 10',
65
+ detail:
66
+ 'Versioning URL/header/query, autenticazione JWT Bearer, autorizzazione basata su ruoli',
67
+ },
68
+ {
69
+ label: 'Multi-Database',
70
+ detail:
71
+ 'SQL Server e MongoDB intercambiabili via configurazione, senza modifiche al codice applicativo',
72
+ },
73
+ {
74
+ label: 'Caching multi-livello',
75
+ detail:
76
+ 'FusionCache in-memory + Redis distribuita con backplane per invalidazione coerente',
77
+ },
78
+ {
79
+ label: 'Observability',
80
+ detail:
81
+ 'OpenTelemetry end-to-end: traces via OTLP, metriche Prometheus, log strutturati con Serilog verso Loki',
82
+ },
83
+ {
84
+ label: 'Health Checks',
85
+ detail:
86
+ 'Readiness e liveness probes per applicazione, database e Redis — pronti per Kubernetes',
87
+ },
88
+ ],
89
+
90
+ /* Features */
91
+ featTag: 'Funzionalità',
92
+ featTitle: 'Tutto ciò di cui hai bisogno',
93
+ featSubtitle:
94
+ 'Una suite completa per creare, gestire e distribuire applicazioni enterprise.',
95
+ f1Title: 'Form dinamici',
96
+ f1Desc:
97
+ 'Rendering SVG e standard con pipeline a stati: validazione, lookup, campi calcolati e 10+ tipi di campo (Testo, Numero, Data, Combo, Immagine, Video, Mappa, Grafico…).',
98
+ f2Title: 'Data Grid avanzata',
99
+ f2Desc:
100
+ 'AG Grid Community per dataset di grandi dimensioni con editing inline, master-detail e navigazione testata-righe.',
101
+ f3Title: 'Scheduling',
102
+ f3Desc:
103
+ 'Modulo di pianificazione risorse basato su FullCalendar con timeline interattiva e gestione eventi server-side.',
104
+ f4Title: 'Dashboard & Report',
105
+ f4Desc:
106
+ 'Grafici interattivi con Chart.js / ng2-charts e generazione PDF lato client con pdfmake e file-saver.',
107
+ f5Title: 'API RESTful',
108
+ f5Desc:
109
+ 'Backend ASP.NET Core con versioning (URL, header, query string), rate limiting (fixed window, token bucket, concurrency) e output caching configurabile.',
110
+ f6Title: 'Sicurezza Enterprise',
111
+ f6Desc:
112
+ 'Autenticazione JWT Bearer con refresh token, autorizzazione role-based, route guard Angular e controllo accessi granulare.',
113
+ f7Title: 'Multi-Database',
114
+ f7Desc:
115
+ 'Supporto duale SQL Server e MongoDB selezionabile via configurazione. Cache distribuita FusionCache + Redis con backplane.',
116
+ f8Title: 'Observability',
117
+ f8Desc:
118
+ 'OpenTelemetry integrato: traces OTLP (Jaeger/Tempo), metriche Prometheus, log strutturati Serilog verso Console, MongoDB e Grafana Loki.',
119
+ f9Title: 'Cloud & DevOps',
120
+ f9Desc:
121
+ 'Docker multi-stage su Alpine Linux, CI/CD con GitHub Actions e GitVersion, deploy su Azure Container Apps e Azure Static Web Apps. Tema chiaro/scuro nativo e PWA con Service Worker.',
122
+
123
+ ctaTag: 'Inizia ora',
124
+ ctaTitle: 'Pronto a navigare verso il futuro?',
125
+ ctaSubtitle:
126
+ 'Unisciti ai team che usano Newport Project per costruire applicazioni moderne in tempi record.',
127
+ ctaBtn: 'Contattaci',
128
+ rights: 'Tutti i diritti riservati',
129
+ },
130
+ EN: {
131
+ versionLabel: 'Version',
132
+ heroBadge: 'Low-Code Platform',
133
+ desc: 'Build professional applications quickly and elegantly. Angular 21 frontend with ASP.NET Core on .NET 10 — the platform designed for those who want to focus on business, not infrastructure.',
134
+ cta1: 'Start the Journey',
135
+ cta2: 'Request a Demo',
136
+ mailSubj1: 'Request%20for%20additional%20information',
137
+ mailBody1:
138
+ 'Hello%2C%0D%0AI%20would%20like%20to%20receive%20additional%20information%20about%20the%20platform.%0D%0A%0D%0ARegards',
139
+ mailSubj2: 'Demo%20request',
140
+ mailBody2:
141
+ 'Hello%2C%0D%0AI%20would%20like%20to%20request%20a%20demo%20of%20the%20platform.%0D%0A%0D%0ARegards',
142
+ mailSubjCta: 'Request%20for%20additional%20information',
143
+ mailBodyCta:
144
+ 'Hello%2C%0D%0AI%20would%20like%20to%20receive%20additional%20information%20about%20the%20platform.%0D%0A%0D%0ARegards',
145
+ stat1: 'Development Speed',
146
+ stat2: 'Responsive & PWA',
147
+ stat3: 'Integrations',
148
+ stat4: 'Cloud Ready',
149
+
150
+ /* Tech stack */
151
+ techTag: 'Tech Stack',
152
+ techTitle: 'Cutting-edge technologies',
153
+ techSubtitle:
154
+ 'A modern full-stack architecture designed for performance, scalability and maintainability.',
155
+ techFe: 'Frontend',
156
+ techBe: 'Backend',
157
+ techData: 'Data & Cache',
158
+ techOps: 'DevOps & CI/CD',
159
+ techFeDesc:
160
+ 'Angular 21, TypeScript 5.9, Angular Signals, OnPush Change Detection, AG Grid, FullCalendar, Chart.js, pdfmake',
161
+ techBeDesc:
162
+ 'ASP.NET Core .NET 10, API Versioning, Swagger / OpenAPI, Rate Limiting, Output Caching',
163
+ techDataDesc:
164
+ 'SQL Server & MongoDB (dual), FusionCache with Redis backplane, Serilog to MongoDB & Grafana Loki',
165
+ techOpsDesc:
166
+ 'Docker + nginx, GitHub Actions, GitVersion SemVer, Azure Container Apps & Static Web Apps',
167
+
168
+ /* Architecture */
169
+ archTag: 'Architecture',
170
+ archTitle: 'Designed to scale',
171
+ archSubtitle:
172
+ 'Clean separation between the SPA frontend and the RESTful backend, with built-in observability and containerized deployment.',
173
+ archItems: [
174
+ {
175
+ label: 'Angular 21 SPA',
176
+ detail:
177
+ 'Dynamic SVG form rendering, field-state validation pipeline, computed fields and lookup resolution',
178
+ },
179
+ {
180
+ label: '.NET 10 REST API',
181
+ detail:
182
+ 'URL/header/query versioning, JWT Bearer authentication, role-based authorization',
183
+ },
184
+ {
185
+ label: 'Multi-Database',
186
+ detail:
187
+ 'SQL Server and MongoDB switchable via configuration, with zero application code changes',
188
+ },
189
+ {
190
+ label: 'Multi-layer Caching',
191
+ detail:
192
+ 'FusionCache in-memory + Redis distributed cache with backplane for coherent invalidation',
193
+ },
194
+ {
195
+ label: 'Observability',
196
+ detail:
197
+ 'End-to-end OpenTelemetry: OTLP traces, Prometheus metrics, structured Serilog logs to Loki',
198
+ },
199
+ {
200
+ label: 'Health Checks',
201
+ detail:
202
+ 'Readiness and liveness probes for application, database and Redis — Kubernetes-ready',
203
+ },
204
+ ],
205
+
206
+ /* Features */
207
+ featTag: 'Features',
208
+ featTitle: 'Everything you need',
209
+ featSubtitle:
210
+ 'A complete suite for creating, managing and distributing enterprise applications.',
211
+ f1Title: 'Dynamic Forms',
212
+ f1Desc:
213
+ 'SVG and standard rendering with a field-state pipeline: validation, lookups, computed fields and 10+ field types (Text, Number, Date, Combo, Image, Video, Map, Chart…).',
214
+ f2Title: 'Advanced Data Grid',
215
+ f2Desc:
216
+ 'AG Grid Community for large datasets with inline editing, master-detail and header-rows navigation.',
217
+ f3Title: 'Scheduling',
218
+ f3Desc:
219
+ 'Resource planning module powered by FullCalendar with interactive timeline and server-side event management.',
220
+ f4Title: 'Dashboard & Reports',
221
+ f4Desc:
222
+ 'Interactive charts with Chart.js / ng2-charts and client-side PDF generation via pdfmake and file-saver.',
223
+ f5Title: 'RESTful API',
224
+ f5Desc:
225
+ 'ASP.NET Core backend with versioning (URL, header, query string), rate limiting (fixed window, token bucket, concurrency) and configurable output caching.',
226
+ f6Title: 'Enterprise Security',
227
+ f6Desc:
228
+ 'JWT Bearer authentication with refresh tokens, role-based authorization, Angular route guards and granular access control.',
229
+ f7Title: 'Multi-Database',
230
+ f7Desc:
231
+ 'Dual SQL Server and MongoDB support switchable via configuration. FusionCache + Redis distributed cache with backplane.',
232
+ f8Title: 'Observability',
233
+ f8Desc:
234
+ 'Built-in OpenTelemetry: OTLP traces (Jaeger/Tempo), Prometheus metrics, structured Serilog logs to Console, MongoDB and Grafana Loki.',
235
+ f9Title: 'Cloud & DevOps',
236
+ f9Desc:
237
+ 'Multi-stage Docker on Alpine Linux, CI/CD with GitHub Actions and GitVersion, deployed to Azure Container Apps and Azure Static Web Apps. Native light/dark theme and PWA with Service Worker.',
238
+
239
+ ctaTag: 'Get started',
240
+ ctaTitle: 'Ready to navigate towards the future?',
241
+ ctaSubtitle:
242
+ 'Join teams using Newport Project to build modern applications in record time.',
243
+ ctaBtn: 'Contact Us',
244
+ rights: 'All rights reserved',
245
+ },
246
+ };
247
+
248
+ @Component({
249
+ selector: 'app-info',
250
+ changeDetection: ChangeDetectionStrategy.OnPush,
251
+ styleUrls: ['info.component.css'],
252
+ templateUrl: 'info.component.html',
253
+ })
254
+ export class InfoComponent {
255
+ gsv = inject(GlobalService);
256
+ msg = inject(AppMessageService);
257
+ private cdr = inject(ChangeDetectorRef);
258
+ private destroyRef = inject(DestroyRef);
259
+
260
+ public version: string;
261
+ public t!: typeof TRANSLATIONS.IT;
262
+ public mail1!: string;
263
+ public mail2!: string;
264
+ public mailCta!: string;
265
+
266
+ public constructor() {
267
+ this.version = PACKAGE_VERSION;
268
+ this.applyLanguage();
269
+
270
+ this.gsv
271
+ .getStateChanged()
272
+ .pipe(takeUntilDestroyed(this.destroyRef))
273
+ .subscribe(() => {
274
+ this.applyLanguage();
275
+ this.cdr.markForCheck();
276
+ });
277
+ }
278
+
279
+ /** Selects the correct translation block and rebuilds mailto links for the current language. */
280
+ private applyLanguage(): void {
281
+ const lang = this.gsv.getLng();
282
+ this.t = lang === Languages.EN ? TRANSLATIONS.EN : TRANSLATIONS.IT;
283
+ this.mail1 = `mailto:alwacar@gmail.com?subject=${this.t.mailSubj1}&body=${this.t.mailBody1}`;
284
+ this.mail2 = `mailto:alwacar@gmail.com?subject=${this.t.mailSubj2}&body=${this.t.mailBody2}`;
285
+ this.mailCta = `mailto:alwacar@gmail.com?subject=${this.t.mailSubjCta}&body=${this.t.mailBodyCta}`;
286
+ }
287
+ }
@@ -0,0 +1,10 @@
1
+ <!-- article.component -->
2
+ <modelselector
3
+ [currentModel]="model()"
4
+ [viewList]="viewList()"
5
+ [currentCommand]="command()"
6
+ [lookupValue]="lookupValue()"
7
+ [dialogValue]="dialogValue()"
8
+ (commandsChanged)="onCommandsChanged($event)"
9
+ [scale]="scale()"></modelselector>
10
+ <!-- fine article.component -->
@@ -0,0 +1,50 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ ElementRef,
5
+ effect,
6
+ inject,
7
+ input,
8
+ output,
9
+ } from '@angular/core';
10
+ import {
11
+ ItemCommandInterface,
12
+ ItemInterface,
13
+ ViewListInterface,
14
+ } from '../../interfaces/index';
15
+ import { ModelSelectorComponent } from '../../players/model/model.selector.component';
16
+
17
+ @Component({
18
+ selector: 'article',
19
+ templateUrl: 'article.component.html',
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ imports: [ModelSelectorComponent],
22
+ })
23
+ export class ArticleComponent {
24
+ private elRef = inject(ElementRef);
25
+
26
+ model = input<ItemInterface>();
27
+ command = input<ItemCommandInterface>();
28
+ viewList = input<boolean>();
29
+ lookupValue = input<any>();
30
+ dialogValue = input<any>();
31
+ scale = input<number>();
32
+
33
+ commandsChanged = output<ItemCommandInterface[]>();
34
+ viewChanged = output<ViewListInterface>();
35
+
36
+ constructor() {
37
+ effect(() => {
38
+ if (this.model()) {
39
+ this.elRef.nativeElement.scrollTo(0, 0);
40
+ }
41
+ });
42
+ }
43
+
44
+ /** Forwards non-empty command arrays from the model selector to the parent. */
45
+ onCommandsChanged(commands: ItemCommandInterface[]) {
46
+ if (commands?.length > 0) {
47
+ this.commandsChanged.emit(commands);
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,8 @@
1
+ <canvas
2
+ baseChart
3
+ class="chart"
4
+ [data]="barChartData"
5
+ [options]="barChartOptions"
6
+ [plugins]="barChartPlugins"
7
+ [type]="barChartType">
8
+ </canvas>
@@ -0,0 +1,59 @@
1
+ import {
2
+ Component,
3
+ ChangeDetectionStrategy,
4
+ effect,
5
+ input,
6
+ viewChild,
7
+ } from '@angular/core';
8
+ import { ChartConfiguration, ChartData, ChartType, Plugin } from 'chart.js';
9
+ import {
10
+ BaseChartDirective,
11
+ provideCharts,
12
+ withDefaultRegisterables,
13
+ } from 'ng2-charts';
14
+
15
+ import DataLabelsPlugin from 'chartjs-plugin-datalabels';
16
+
17
+ @Component({
18
+ selector: 'barchart',
19
+ templateUrl: './barchart.component.html',
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ imports: [BaseChartDirective],
22
+ providers: [provideCharts(withDefaultRegisterables())],
23
+ })
24
+ export class BarChartComponent {
25
+ chart = viewChild(BaseChartDirective);
26
+ dataChart = input<string>();
27
+
28
+ barChartData!: ChartData<'bar'>;
29
+
30
+ public barChartOptions: ChartConfiguration['options'] = {
31
+ // We use these empty structures as placeholders for dynamic theming.
32
+ scales: {
33
+ x: {},
34
+ y: {
35
+ min: 10,
36
+ },
37
+ },
38
+ plugins: {
39
+ legend: {
40
+ display: true,
41
+ },
42
+ datalabels: {
43
+ anchor: 'end',
44
+ align: 'end',
45
+ },
46
+ },
47
+ };
48
+ public barChartType: ChartType = 'bar';
49
+ public barChartPlugins: Plugin[] = [DataLabelsPlugin];
50
+
51
+ constructor() {
52
+ effect(() => {
53
+ const data = this.dataChart();
54
+ if (data !== undefined) {
55
+ this.barChartData = JSON.parse(data);
56
+ }
57
+ });
58
+ }
59
+ }
@@ -0,0 +1,7 @@
1
+ export * from './model.component';
2
+ export * from './article.component';
3
+ export * from './navigation.component';
4
+ export * from './modeltoolbar.component';
5
+ export * from './itemtab.component';
6
+ export * from './itemdetail.component';
7
+ export * from './barchart.component';
@@ -0,0 +1,25 @@
1
+ <!-- itemdetail.component -->
2
+ <div [hidden]="!hasDetails" class="itab-bar">
3
+ @for (item of linkedDetails; track item; let i = $index) {
4
+ <a
5
+ class="itab-item"
6
+ [title]="item.title"
7
+ (click)="click(item)"
8
+ [hidden]="!item.visible"
9
+ [class.disabled]="!item.enabled"
10
+ [class.itab-active]="active === item">
11
+ <span
12
+ class="itab-dot"
13
+ [ngClass]="{
14
+ 'dot-success': item.state === itemState.Valid,
15
+ 'triangle-error': item.state === itemState.Error,
16
+ 'rectangle-warning': item.state === itemState.Warning,
17
+ 'dot-empty': item.state === itemState.Empty,
18
+ }"></span>
19
+ <span class="itab-label truncate">{{
20
+ msg?.getItemInfo(item, 'title')
21
+ }}</span>
22
+ </a>
23
+ }
24
+ </div>
25
+ <!-- fine itemdetail.component -->
@@ -0,0 +1,93 @@
1
+ import {
2
+ Component,
3
+ input,
4
+ output,
5
+ effect,
6
+ untracked,
7
+ ChangeDetectionStrategy,
8
+ inject,
9
+ } from '@angular/core';
10
+
11
+ import { GlobalService } from '../../services/global.service';
12
+
13
+ import { AppMessageService } from '../../services/message.service';
14
+
15
+ import { ItemInterface, ItemState, ItemType } from '../../interfaces/index';
16
+ import { NgClass } from '@angular/common';
17
+
18
+ @Component({
19
+ selector: 'itemdetail',
20
+ templateUrl: 'itemdetail.component.html',
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ imports: [NgClass],
23
+ })
24
+ export class ItemDetailComponent {
25
+ gsv = inject(GlobalService);
26
+ msg = inject(AppMessageService);
27
+
28
+ readonly model = input<ItemInterface>();
29
+ readonly insertMode = input<boolean>();
30
+ readonly disabled = input<boolean>();
31
+
32
+ readonly modelChanged = output<ItemInterface>();
33
+
34
+ public linkedDetails: ItemInterface[] = [];
35
+ public hasDetails: boolean = true;
36
+ public projectId!: string;
37
+ public itemState = ItemState;
38
+ public active!: ItemInterface;
39
+
40
+ constructor() {
41
+ effect(() => {
42
+ const m = this.model();
43
+ if (!m || m.type === ItemType.Detail) return;
44
+ const details = m.linkedDetails;
45
+ this.hasDetails = (details?.[0] ?? '') !== '';
46
+ this.projectId = this.gsv.getProject().project + '.' + this.gsv.getYear();
47
+ const projectItems = this.gsv.getProjectItems(this.projectId);
48
+ if (this.hasDetails) {
49
+ this.linkedDetails = [];
50
+ if ((m.linkedDetails?.[0] ?? '') !== '') {
51
+ m.linkedDetails!.forEach(item => {
52
+ this.linkedDetails.push(projectItems.find(e => e.name === item)!);
53
+ });
54
+ }
55
+ if (this.active?.id != m?.id) {
56
+ this.active = m;
57
+ }
58
+ const ins = untracked(() => this.insertMode());
59
+ const dis = untracked(() => this.disabled());
60
+ for (let i = 1; i < this.linkedDetails.length; i++) {
61
+ if (this.linkedDetails[i]) this.linkedDetails[i].enabled = !ins;
62
+ }
63
+ for (let i = 0; i < this.linkedDetails.length; i++) {
64
+ if (this.linkedDetails[i]) this.linkedDetails[i].enabled = !dis;
65
+ }
66
+ }
67
+ });
68
+ effect(() => {
69
+ const ins = this.insertMode();
70
+ if (ins !== undefined) {
71
+ for (let i = 1; i < this.linkedDetails.length; i++) {
72
+ if (this.linkedDetails[i]) this.linkedDetails[i].enabled = !ins;
73
+ }
74
+ }
75
+ });
76
+ effect(() => {
77
+ const dis = this.disabled();
78
+ if (dis !== undefined) {
79
+ for (let i = 0; i < this.linkedDetails.length; i++) {
80
+ if (this.linkedDetails[i]) this.linkedDetails[i].enabled = !dis;
81
+ }
82
+ }
83
+ });
84
+ }
85
+
86
+ /** Activates the selected linked-detail tab and emits modelChanged. */
87
+ public click(item: ItemInterface) {
88
+ if (this.active !== item) {
89
+ this.active = item;
90
+ this.modelChanged.emit(item);
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,25 @@
1
+ <!-- itemtab.component -->
2
+ <div [hidden]="!hasLinkeds" class="itab-bar">
3
+ @for (item of items; track item; let i = $index) {
4
+ <a
5
+ class="itab-item"
6
+ [title]="item.title"
7
+ (click)="click(item)"
8
+ [hidden]="!item.visible"
9
+ [class.disabled]="!item.enabled"
10
+ [class.itab-active]="active?.id === item?.id">
11
+ <span
12
+ class="itab-dot"
13
+ [ngClass]="{
14
+ 'dot-success': item.state === itemState.Valid,
15
+ 'triangle-error': item.state === itemState.Error,
16
+ 'rectangle-warning': item.state === itemState.Warning,
17
+ 'dot-empty': item.state === itemState.Empty,
18
+ }"></span>
19
+ <span class="itab-label truncate">{{
20
+ msg?.getItemInfo(item, 'title')
21
+ }}</span>
22
+ </a>
23
+ }
24
+ </div>
25
+ <!-- fine itemtab.component -->
@@ -0,0 +1,105 @@
1
+ import {
2
+ Component,
3
+ input,
4
+ output,
5
+ effect,
6
+ untracked,
7
+ ChangeDetectionStrategy,
8
+ ChangeDetectorRef,
9
+ DestroyRef,
10
+ inject,
11
+ } from '@angular/core';
12
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
13
+
14
+ import { GlobalService } from '../../services/global.service';
15
+
16
+ import { AppMessageService } from '../../services/message.service';
17
+
18
+ import { ItemInterface, ItemState, ItemType } from '../../interfaces/index';
19
+ import { NgClass } from '@angular/common';
20
+
21
+ @Component({
22
+ selector: 'itemtab',
23
+ templateUrl: 'itemtab.component.html',
24
+ changeDetection: ChangeDetectionStrategy.OnPush,
25
+ imports: [NgClass],
26
+ })
27
+ export class ItemTabComponent {
28
+ gsv = inject(GlobalService);
29
+ msg = inject(AppMessageService);
30
+ private cdr = inject(ChangeDetectorRef);
31
+ private destroyRef = inject(DestroyRef);
32
+
33
+ readonly model = input<ItemInterface>();
34
+ readonly insertMode = input<boolean>();
35
+ readonly disabled = input<boolean>();
36
+
37
+ readonly modelChanged = output<ItemInterface>();
38
+
39
+ public items: ItemInterface[] = [];
40
+ public hasLinkeds: boolean = true;
41
+ public projectId!: string;
42
+ public itemState = ItemState;
43
+ public active: ItemInterface | null = null;
44
+
45
+ constructor() {
46
+ // Trigger CD whenever updateState() recalculates item validation states
47
+ this.gsv
48
+ .getItemState()
49
+ .pipe(takeUntilDestroyed(this.destroyRef))
50
+ .subscribe(() => this.cdr.markForCheck());
51
+
52
+ effect(() => {
53
+ const m = this.model();
54
+ if (!m) return;
55
+ if (m.type === ItemType.Detail) {
56
+ this.active = null;
57
+ return;
58
+ }
59
+ const linkedItems =
60
+ (m.linkedItems?.[0] ?? '') !== '' ? m.linkedItems : m.parentItems;
61
+ this.hasLinkeds = (linkedItems?.[0] ?? '') !== '';
62
+ this.projectId = this.gsv.getProject().project + '.' + this.gsv.getYear();
63
+ const projectItems = this.gsv.getProjectItems(this.projectId);
64
+ if (this.hasLinkeds) {
65
+ this.items = [];
66
+ (linkedItems ?? []).forEach(item => {
67
+ this.items.push(projectItems.find(e => e.name === item)!);
68
+ });
69
+ const ins = untracked(() => this.insertMode());
70
+ const dis = untracked(() => this.disabled());
71
+ for (let i = 1; i < this.items.length; i++) {
72
+ if (this.items[i]) this.items[i].enabled = !ins;
73
+ }
74
+ for (let i = 0; i < this.items.length; i++) {
75
+ if (this.items[i]) this.items[i].enabled = !dis;
76
+ }
77
+ this.active = m;
78
+ }
79
+ });
80
+ effect(() => {
81
+ const ins = this.insertMode();
82
+ if (ins !== undefined) {
83
+ for (let i = 1; i < this.items.length; i++) {
84
+ if (this.items[i]) this.items[i].enabled = !ins;
85
+ }
86
+ }
87
+ });
88
+ effect(() => {
89
+ const dis = this.disabled();
90
+ if (dis !== undefined) {
91
+ for (let i = 0; i < this.items.length; i++) {
92
+ if (this.items[i]) this.items[i].enabled = !dis;
93
+ }
94
+ }
95
+ });
96
+ }
97
+
98
+ /** Activates the selected linked-item tab and emits modelChanged. */
99
+ public click(item: ItemInterface) {
100
+ if (this.active !== item) {
101
+ this.active = item;
102
+ this.modelChanged.emit(item);
103
+ }
104
+ }
105
+ }