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.
- package/newportsite-1.1.3.tgz +0 -0
- package/ng-package.json +7 -0
- package/obfuscate.js +70 -0
- package/package.json +15 -0
- package/src/lib/app.component.ts +47 -0
- package/src/lib/app.routing.ts +38 -0
- package/src/lib/auth/alert.component.html +5 -0
- package/src/lib/auth/alert.component.ts +24 -0
- package/src/lib/auth/auth.component.html +1 -0
- package/src/lib/auth/auth.component.ts +10 -0
- package/src/lib/auth/auth.routes.ts +16 -0
- package/src/lib/auth/index.ts +4 -0
- package/src/lib/auth/login.component.html +87 -0
- package/src/lib/auth/login.component.ts +158 -0
- package/src/lib/auth/models/index.ts +1 -0
- package/src/lib/auth/models/user.ts +25 -0
- package/src/lib/auth/register.component.html +157 -0
- package/src/lib/auth/register.component.ts +219 -0
- package/src/lib/auth/services/alert.service.ts +47 -0
- package/src/lib/auth/services/auth.service.ts +28 -0
- package/src/lib/auth/services/index.ts +3 -0
- package/src/lib/auth/services/user.service.spec.ts +112 -0
- package/src/lib/auth/services/user.service.ts +47 -0
- package/src/lib/common/card.component.html +72 -0
- package/src/lib/common/card.component.ts +102 -0
- package/src/lib/common/commands.component.html +8 -0
- package/src/lib/common/commands.component.ts +42 -0
- package/src/lib/common/context.component.html +9 -0
- package/src/lib/common/context.component.ts +38 -0
- package/src/lib/common/grid.component.html +20 -0
- package/src/lib/common/grid.component.ts +747 -0
- package/src/lib/common/index.ts +9 -0
- package/src/lib/common/loader.component.html +5 -0
- package/src/lib/common/loader.component.ts +27 -0
- package/src/lib/common/lookup.component.html +29 -0
- package/src/lib/common/lookup.component.ts +115 -0
- package/src/lib/common/messagebox.component.html +39 -0
- package/src/lib/common/messagebox.component.ts +74 -0
- package/src/lib/common/theme-toggle.component.ts +139 -0
- package/src/lib/config.ts +62 -0
- package/src/lib/containers/default-layout/default-layout.component.html +191 -0
- package/src/lib/containers/default-layout/default-layout.component.ts +158 -0
- package/src/lib/containers/default-layout/index.ts +1 -0
- package/src/lib/containers/index.ts +1 -0
- package/src/lib/directives/component.draggable.ts +80 -0
- package/src/lib/directives/index.ts +2 -0
- package/src/lib/directives/input.directive.spec.ts +158 -0
- package/src/lib/directives/input.directive.ts +210 -0
- package/src/lib/home/dashboard/dashboard.component.html +38 -0
- package/src/lib/home/dashboard/dashboard.component.ts +50 -0
- package/src/lib/home/dashboard/index.ts +1 -0
- package/src/lib/home/index.component.html +1 -0
- package/src/lib/home/index.component.ts +10 -0
- package/src/lib/home/index.routes.ts +29 -0
- package/src/lib/home/index.ts +1 -0
- package/src/lib/home/info/index.ts +1 -0
- package/src/lib/home/info/info.component.css +476 -0
- package/src/lib/home/info/info.component.html +174 -0
- package/src/lib/home/info/info.component.ts +287 -0
- package/src/lib/home/model/article.component.html +10 -0
- package/src/lib/home/model/article.component.ts +50 -0
- package/src/lib/home/model/barchart.component.html +8 -0
- package/src/lib/home/model/barchart.component.ts +59 -0
- package/src/lib/home/model/index.ts +7 -0
- package/src/lib/home/model/itemdetail.component.html +25 -0
- package/src/lib/home/model/itemdetail.component.ts +93 -0
- package/src/lib/home/model/itemtab.component.html +25 -0
- package/src/lib/home/model/itemtab.component.ts +105 -0
- package/src/lib/home/model/model.component.html +121 -0
- package/src/lib/home/model/model.component.ts +510 -0
- package/src/lib/home/model/modeltoolbar.component.html +111 -0
- package/src/lib/home/model/modeltoolbar.component.ts +157 -0
- package/src/lib/home/model/navigation.component.html +86 -0
- package/src/lib/home/model/navigation.component.ts +247 -0
- package/src/lib/home/model/services/index.ts +1 -0
- package/src/lib/home/model/services/model.service.spec.ts +423 -0
- package/src/lib/home/model/services/model.service.ts +319 -0
- package/src/lib/home/modelsearch/index.ts +1 -0
- package/src/lib/home/modelsearch/modelsearch.component.html +124 -0
- package/src/lib/home/modelsearch/modelsearch.component.ts +453 -0
- package/src/lib/interfaces/data.interface.ts +131 -0
- package/src/lib/interfaces/index.ts +2 -0
- package/src/lib/interfaces/item.interface.ts +438 -0
- package/src/lib/players/lookup/lookup.directive.ts +6 -0
- package/src/lib/players/lookup/lookup.item.component.ts +37 -0
- package/src/lib/players/lookup/lookup.item.ts +9 -0
- package/src/lib/players/lookup/lookup.player.component.ts +59 -0
- package/src/lib/players/lookup/lookup.selector.component.ts +41 -0
- package/src/lib/players/model/model.directive.ts +6 -0
- package/src/lib/players/model/model.item.component.spec.ts +311 -0
- package/src/lib/players/model/model.item.component.ts +3457 -0
- package/src/lib/players/model/model.item.ts +9 -0
- package/src/lib/players/model/model.player.component.ts +109 -0
- package/src/lib/players/model/model.selector.component.ts +59 -0
- package/src/lib/scheduler/scheduler.component.html +13 -0
- package/src/lib/scheduler/scheduler.component.scss +6 -0
- package/src/lib/scheduler/scheduler.component.ts +296 -0
- package/src/lib/scheduler/scheduler.routes.ts +15 -0
- package/src/lib/scheduler/schedulerdialog.component.html +72 -0
- package/src/lib/scheduler/schedulerdialog.component.ts +208 -0
- package/src/lib/scheduler/services/scheduler.service.ts +133 -0
- package/src/lib/services/auth-state.service.ts +129 -0
- package/src/lib/services/auth.interceptor.spec.ts +144 -0
- package/src/lib/services/auth.interceptor.ts +44 -0
- package/src/lib/services/cache.service.spec.ts +143 -0
- package/src/lib/services/cache.service.ts +71 -0
- package/src/lib/services/global-error-handler.spec.ts +39 -0
- package/src/lib/services/global-error-handler.ts +28 -0
- package/src/lib/services/global.service.spec.ts +801 -0
- package/src/lib/services/global.service.ts +724 -0
- package/src/lib/services/message.service.ts +556 -0
- package/src/lib/services/theme.service.ts +96 -0
- package/src/lib/template/authtemplate.component.html +6 -0
- package/src/lib/template/authtemplate.component.ts +13 -0
- package/src/lib/template/basetemplate.component.html +7 -0
- package/src/lib/template/basetemplate.component.ts +13 -0
- package/src/lib/template/index.ts +3 -0
- package/src/lib/template/modeltemplate.component.html +7 -0
- package/src/lib/template/modeltemplate.component.ts +21 -0
- package/src/lib/utils/piva.spec.ts +56 -0
- package/src/lib/utils/piva.ts +29 -0
- package/src/lib/validators/email.validator.spec.ts +57 -0
- package/src/lib/validators/email.validator.ts +17 -0
- package/src/lib/validators/equalPasswords.validator.spec.ts +54 -0
- package/src/lib/validators/equalPasswords.validator.ts +17 -0
- package/src/lib/validators/index.ts +2 -0
- package/src/lib/version.ts +1 -0
- package/src/public-api.ts +64 -0
- package/src/typings.d.ts +2 -0
- package/tsconfig.lib.json +18 -0
- package/tsconfig.lib.prod.json +9 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<!-- modeltoolbar.component -->
|
|
2
|
+
<div class="mtb-bar-m2" [class.pe-none]="gsv?.getLoading()">
|
|
3
|
+
<div class="mtb-group">
|
|
4
|
+
<a class="mtb-btn" (click)="click('exit')">
|
|
5
|
+
<i
|
|
6
|
+
[class.icon-disabled]="exitState || gsv?.getLoading()"
|
|
7
|
+
[class.icon-exit]="!exitState && !gsv?.getLoading()"
|
|
8
|
+
class="bi bi-box-arrow-left"
|
|
9
|
+
title="{{ msg?.get('app.exit') }}"></i>
|
|
10
|
+
</a>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="mtb-divider"></div>
|
|
14
|
+
|
|
15
|
+
<div class="mtb-group">
|
|
16
|
+
<a class="mtb-btn" (click)="click('plus')">
|
|
17
|
+
<i
|
|
18
|
+
[class.icon-disabled]="plusState || gsv?.getLoading()"
|
|
19
|
+
class="bi bi-plus-circle"
|
|
20
|
+
title="{{ msg?.get('app.plus') }}"></i>
|
|
21
|
+
</a>
|
|
22
|
+
<a class="mtb-btn" (click)="click('minus')">
|
|
23
|
+
<i
|
|
24
|
+
[class.icon-disabled]="minusState || gsv?.getLoading()"
|
|
25
|
+
class="bi bi-patch-minus"
|
|
26
|
+
title="{{ msg?.get('app.minus') }}"></i>
|
|
27
|
+
</a>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="mtb-divider"></div>
|
|
31
|
+
|
|
32
|
+
<div class="mtb-group">
|
|
33
|
+
<a class="mtb-btn" (click)="click('detail')">
|
|
34
|
+
<i
|
|
35
|
+
[class.icon-disabled]="detailState || gsv?.getLoading()"
|
|
36
|
+
class="bi bi-card-text"
|
|
37
|
+
title="{{ msg?.get('app.detail') }}"></i>
|
|
38
|
+
</a>
|
|
39
|
+
<a class="mtb-btn" (click)="click('intodetail')">
|
|
40
|
+
<i
|
|
41
|
+
[class.icon-disabled]="intodetailState || gsv?.getLoading()"
|
|
42
|
+
class="bi bi-box-arrow-up-left"
|
|
43
|
+
title="{{ msg?.get('app.intodetail') }}"></i>
|
|
44
|
+
</a>
|
|
45
|
+
<a class="mtb-btn" (click)="click('table')">
|
|
46
|
+
<i
|
|
47
|
+
[class.icon-disabled]="tableState || gsv?.getLoading()"
|
|
48
|
+
class="bi bi-list"
|
|
49
|
+
title="{{ msg?.get('app.table') }}"></i>
|
|
50
|
+
</a>
|
|
51
|
+
<a class="mtb-btn" (click)="click('print')">
|
|
52
|
+
<i
|
|
53
|
+
[class.icon-disabled]="printState || gsv?.getLoading()"
|
|
54
|
+
class="bi bi-printer"
|
|
55
|
+
title="{{ msg?.get('app.print') }}"></i>
|
|
56
|
+
</a>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div class="mtb-divider"></div>
|
|
60
|
+
|
|
61
|
+
<div class="mtb-group">
|
|
62
|
+
<a class="mtb-btn" [class.pe-none]="saveState" (click)="click('save')">
|
|
63
|
+
<i
|
|
64
|
+
[class.icon-disabled]="saveState"
|
|
65
|
+
class="bi bi-save"
|
|
66
|
+
title="{{ msg?.get('app.save') }}"></i>
|
|
67
|
+
</a>
|
|
68
|
+
<a class="mtb-btn d-none d-lg-flex" (click)="click('upload')">
|
|
69
|
+
<i
|
|
70
|
+
[class.icon-disabled]="uploadState || gsv?.getLoading()"
|
|
71
|
+
class="bi bi-upload"
|
|
72
|
+
title="{{ msg?.get('app.upload') }}"></i>
|
|
73
|
+
</a>
|
|
74
|
+
<a class="mtb-btn d-none d-lg-flex" (click)="click('download')">
|
|
75
|
+
<i
|
|
76
|
+
[class.icon-disabled]="downloadState || gsv?.getLoading()"
|
|
77
|
+
class="bi bi-download"
|
|
78
|
+
title="{{ msg?.get('app.download') }}"></i>
|
|
79
|
+
</a>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="mtb-spacer"></div>
|
|
83
|
+
|
|
84
|
+
<div class="mtb-group">
|
|
85
|
+
<a class="mtb-btn d-none d-lg-flex" (click)="click('first')">
|
|
86
|
+
<i
|
|
87
|
+
[class.icon-disabled]="firstState || gsv?.getLoading()"
|
|
88
|
+
class="bi bi-chevron-double-left"
|
|
89
|
+
title="{{ msg?.get('app.first') }}"></i>
|
|
90
|
+
</a>
|
|
91
|
+
<a class="mtb-btn" (click)="click('previous')">
|
|
92
|
+
<i
|
|
93
|
+
[class.icon-disabled]="previousState || gsv?.getLoading()"
|
|
94
|
+
class="bi bi-chevron-left"
|
|
95
|
+
title="{{ msg?.get('app.previous') }}"></i>
|
|
96
|
+
</a>
|
|
97
|
+
<a class="mtb-btn" (click)="click('next')">
|
|
98
|
+
<i
|
|
99
|
+
[class.icon-disabled]="nextState || gsv?.getLoading()"
|
|
100
|
+
class="bi bi-chevron-right"
|
|
101
|
+
title="{{ msg?.get('app.next') }}"></i>
|
|
102
|
+
</a>
|
|
103
|
+
<a class="mtb-btn d-none d-lg-flex" (click)="click('last')">
|
|
104
|
+
<i
|
|
105
|
+
[class.icon-disabled]="lastState || gsv?.getLoading()"
|
|
106
|
+
class="bi bi-chevron-double-right"
|
|
107
|
+
title="{{ msg?.get('app.last') }}"></i>
|
|
108
|
+
</a>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
<!-- fine modeltoolbar.component -->
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
ChangeDetectorRef,
|
|
5
|
+
DestroyRef,
|
|
6
|
+
OnInit,
|
|
7
|
+
effect,
|
|
8
|
+
input,
|
|
9
|
+
output,
|
|
10
|
+
inject,
|
|
11
|
+
} from '@angular/core';
|
|
12
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
13
|
+
|
|
14
|
+
import { GlobalService } from '../../services/global.service';
|
|
15
|
+
import { AppMessageService } from '../../services/message.service';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
ItemCommandInterface,
|
|
19
|
+
ViewListInterface,
|
|
20
|
+
} from '../../interfaces/index';
|
|
21
|
+
|
|
22
|
+
@Component({
|
|
23
|
+
selector: 'modeltoolbar',
|
|
24
|
+
templateUrl: 'modeltoolbar.component.html',
|
|
25
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
26
|
+
imports: [],
|
|
27
|
+
})
|
|
28
|
+
export class ModelToolbarComponent implements OnInit {
|
|
29
|
+
gsv = inject(GlobalService);
|
|
30
|
+
msg = inject(AppMessageService);
|
|
31
|
+
private destroyRef = inject(DestroyRef);
|
|
32
|
+
private cdr = inject(ChangeDetectorRef);
|
|
33
|
+
|
|
34
|
+
readonly commands = input<ItemCommandInterface[]>([]);
|
|
35
|
+
readonly disabled = input<boolean>();
|
|
36
|
+
readonly commandRaised = output<string>();
|
|
37
|
+
readonly viewChanged = output<ViewListInterface>();
|
|
38
|
+
|
|
39
|
+
private static readonly CMD_IDS = [
|
|
40
|
+
'exit',
|
|
41
|
+
'plus',
|
|
42
|
+
'minus',
|
|
43
|
+
'first',
|
|
44
|
+
'previous',
|
|
45
|
+
'next',
|
|
46
|
+
'last',
|
|
47
|
+
'upload',
|
|
48
|
+
'download',
|
|
49
|
+
'detail',
|
|
50
|
+
'intodetail',
|
|
51
|
+
'print',
|
|
52
|
+
'table',
|
|
53
|
+
'save',
|
|
54
|
+
'insertmode',
|
|
55
|
+
] as const;
|
|
56
|
+
|
|
57
|
+
/** All command states keyed by id — true = disabled. */
|
|
58
|
+
public state: Record<string, boolean> = {};
|
|
59
|
+
public hidenavigation = true;
|
|
60
|
+
|
|
61
|
+
// Template-compatible aliases (kept as getters so existing HTML bindings still work)
|
|
62
|
+
get exitState() {
|
|
63
|
+
return this.state['exit'];
|
|
64
|
+
}
|
|
65
|
+
get plusState() {
|
|
66
|
+
return this.state['plus'];
|
|
67
|
+
}
|
|
68
|
+
get minusState() {
|
|
69
|
+
return this.state['minus'];
|
|
70
|
+
}
|
|
71
|
+
get firstState() {
|
|
72
|
+
return this.state['first'];
|
|
73
|
+
}
|
|
74
|
+
get previousState() {
|
|
75
|
+
return this.state['previous'];
|
|
76
|
+
}
|
|
77
|
+
get nextState() {
|
|
78
|
+
return this.state['next'];
|
|
79
|
+
}
|
|
80
|
+
get lastState() {
|
|
81
|
+
return this.state['last'];
|
|
82
|
+
}
|
|
83
|
+
get uploadState() {
|
|
84
|
+
return this.state['upload'];
|
|
85
|
+
}
|
|
86
|
+
get downloadState() {
|
|
87
|
+
return this.state['download'];
|
|
88
|
+
}
|
|
89
|
+
get detailState() {
|
|
90
|
+
return this.state['detail'];
|
|
91
|
+
}
|
|
92
|
+
get intodetailState() {
|
|
93
|
+
return this.state['intodetail'];
|
|
94
|
+
}
|
|
95
|
+
get printState() {
|
|
96
|
+
return this.state['print'];
|
|
97
|
+
}
|
|
98
|
+
get tableState() {
|
|
99
|
+
return this.state['table'];
|
|
100
|
+
}
|
|
101
|
+
get saveState() {
|
|
102
|
+
return this.state['save'];
|
|
103
|
+
}
|
|
104
|
+
get insertModeState() {
|
|
105
|
+
return this.state['insertmode'];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
constructor() {
|
|
109
|
+
this.resetCommand();
|
|
110
|
+
effect(() => {
|
|
111
|
+
if (this.disabled()) this.resetCommand();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Lifecycle: subscribes to the GlobalService command stream and applies state updates. */
|
|
116
|
+
public ngOnInit() {
|
|
117
|
+
this.gsv
|
|
118
|
+
.getItemCommands()
|
|
119
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
120
|
+
.subscribe(commands => {
|
|
121
|
+
this.setState(commands);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Handles a toolbar button click: emits viewChanged for 'table', otherwise raises the command by id. */
|
|
126
|
+
public click(id: string) {
|
|
127
|
+
if (this.state[id]) return; // state true = disabled → skip
|
|
128
|
+
if (id === 'table') {
|
|
129
|
+
this.viewChanged.emit({ state: true });
|
|
130
|
+
this.resetCommand();
|
|
131
|
+
} else {
|
|
132
|
+
this.commandRaised.emit(id);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Sets all command states to disabled (true). */
|
|
137
|
+
private resetCommand() {
|
|
138
|
+
for (const id of ModelToolbarComponent.CMD_IDS) {
|
|
139
|
+
this.state[id] = true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Applies incoming command states and recomputes the navigation-row visibility flag. */
|
|
144
|
+
private setState(commands: ItemCommandInterface[]) {
|
|
145
|
+
for (const cmd of commands) {
|
|
146
|
+
if (cmd.id in this.state) {
|
|
147
|
+
this.state[cmd.id] = cmd.state;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
this.hidenavigation =
|
|
151
|
+
this.state['first'] &&
|
|
152
|
+
this.state['previous'] &&
|
|
153
|
+
this.state['next'] &&
|
|
154
|
+
this.state['last'];
|
|
155
|
+
this.cdr.markForCheck();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<!-- navigation.component -->
|
|
2
|
+
@if (!hide) {
|
|
3
|
+
<div
|
|
4
|
+
class="nav-panel h-100"
|
|
5
|
+
[class.nav-panel-compact]="collapsed()"
|
|
6
|
+
[class.pe-none]="gsv?.getLoading()">
|
|
7
|
+
<!-- HEADER: always visible -->
|
|
8
|
+
<div class="nav-header">
|
|
9
|
+
<div class="nav-header-top">
|
|
10
|
+
@if (!collapsed()) {
|
|
11
|
+
<span
|
|
12
|
+
class="nav-title truncate"
|
|
13
|
+
title="{{ msg?.getProjectDescription('description') }}">
|
|
14
|
+
{{ msg?.getProjectDescription('description') }}
|
|
15
|
+
</span>
|
|
16
|
+
}
|
|
17
|
+
<button
|
|
18
|
+
class="nav-toggle-btn"
|
|
19
|
+
(click)="toggleCollapse()"
|
|
20
|
+
[title]="
|
|
21
|
+
collapsed() ? msg?.get('app.expand') : msg?.get('app.collapse')
|
|
22
|
+
">
|
|
23
|
+
<i
|
|
24
|
+
class="bi"
|
|
25
|
+
[class.bi-chevron-double-right]="collapsed()"
|
|
26
|
+
[class.bi-chevron-double-left]="!collapsed()"></i>
|
|
27
|
+
</button>
|
|
28
|
+
</div>
|
|
29
|
+
@if (!collapsed()) {
|
|
30
|
+
<div class="nav-filter-wrap">
|
|
31
|
+
<i class="bi bi-search nav-filter-icon"></i>
|
|
32
|
+
<input
|
|
33
|
+
id="filter"
|
|
34
|
+
#txt1
|
|
35
|
+
[title]="msg?.get('app.filter')"
|
|
36
|
+
type="text"
|
|
37
|
+
class="nav-filter-input"
|
|
38
|
+
[placeholder]="msg?.get('app.filter')"
|
|
39
|
+
(input)="filter(txt1.value)" />
|
|
40
|
+
</div>
|
|
41
|
+
}
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<!-- ITEMS -->
|
|
45
|
+
<div class="navigation">
|
|
46
|
+
@if (collapsed()) {
|
|
47
|
+
@for (item of items; track item) {
|
|
48
|
+
<div
|
|
49
|
+
class="nav-entry nav-entry-compact"
|
|
50
|
+
[class.nav-active]="active === item">
|
|
51
|
+
<a
|
|
52
|
+
[title]="msg?.getItemInfo(item, 'title')"
|
|
53
|
+
(click)="click(item)"
|
|
54
|
+
[hidden]="!item.visible"
|
|
55
|
+
[class.disabled]="!item.enabled">
|
|
56
|
+
<span
|
|
57
|
+
class="nav-state-dot"
|
|
58
|
+
[ngClass]="getDesktopStateClass(item)"></span>
|
|
59
|
+
<span class="nav-item-id">{{ item.id }}</span>
|
|
60
|
+
</a>
|
|
61
|
+
</div>
|
|
62
|
+
}
|
|
63
|
+
} @else {
|
|
64
|
+
@for (item of items; track item) {
|
|
65
|
+
<div class="nav-entry" [class.nav-active]="active === item">
|
|
66
|
+
<a
|
|
67
|
+
[title]="msg?.getItemInfo(item, 'title')"
|
|
68
|
+
(click)="click(item)"
|
|
69
|
+
[hidden]="!item.visible"
|
|
70
|
+
[class.disabled]="!item.enabled">
|
|
71
|
+
<span
|
|
72
|
+
class="nav-state-dot"
|
|
73
|
+
[ngClass]="getDesktopStateClass(item)"></span>
|
|
74
|
+
<span
|
|
75
|
+
class="nav-item-text truncate"
|
|
76
|
+
[ngClass]="getItemTextClass(item)">
|
|
77
|
+
{{ msg?.getItemInfo(item, 'title') }}
|
|
78
|
+
</span>
|
|
79
|
+
</a>
|
|
80
|
+
</div>
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
}
|
|
86
|
+
<!-- fine navigation.component -->
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
input,
|
|
4
|
+
output,
|
|
5
|
+
effect,
|
|
6
|
+
ChangeDetectionStrategy,
|
|
7
|
+
ChangeDetectorRef,
|
|
8
|
+
OnDestroy,
|
|
9
|
+
AfterViewInit,
|
|
10
|
+
DestroyRef,
|
|
11
|
+
inject,
|
|
12
|
+
} from '@angular/core';
|
|
13
|
+
|
|
14
|
+
import { GlobalService } from '../../services/global.service';
|
|
15
|
+
|
|
16
|
+
import { AppMessageService } from '../../services/message.service';
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
ItemInterface,
|
|
20
|
+
ItemState,
|
|
21
|
+
ManagmentInterface,
|
|
22
|
+
ItemType,
|
|
23
|
+
} from '../../interfaces/index';
|
|
24
|
+
import { ManagmentService } from './services/index';
|
|
25
|
+
import { Subscription } from 'rxjs';
|
|
26
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
27
|
+
import { NgClass } from '@angular/common';
|
|
28
|
+
|
|
29
|
+
@Component({
|
|
30
|
+
selector: 'navigation',
|
|
31
|
+
templateUrl: 'navigation.component.html',
|
|
32
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
33
|
+
imports: [NgClass],
|
|
34
|
+
})
|
|
35
|
+
export class NavigationComponent implements AfterViewInit, OnDestroy {
|
|
36
|
+
gsv = inject(GlobalService);
|
|
37
|
+
msg = inject(AppMessageService);
|
|
38
|
+
msv = inject(ManagmentService);
|
|
39
|
+
private destroyRef = inject(DestroyRef);
|
|
40
|
+
private cdr = inject(ChangeDetectorRef);
|
|
41
|
+
|
|
42
|
+
readonly modelChanged = output<ItemInterface>();
|
|
43
|
+
readonly hideNavigation = output<boolean>();
|
|
44
|
+
readonly collapsedChanged = output<boolean>();
|
|
45
|
+
readonly collapsed = input<boolean>(false);
|
|
46
|
+
public items!: ItemInterface[];
|
|
47
|
+
public itemState = ItemState;
|
|
48
|
+
public active: ItemInterface | null = null;
|
|
49
|
+
public projectId!: string;
|
|
50
|
+
public projectItems: ItemInterface[] | undefined;
|
|
51
|
+
public hide = false;
|
|
52
|
+
private managment: ManagmentInterface = {
|
|
53
|
+
functionId: '',
|
|
54
|
+
appId: '',
|
|
55
|
+
entId: '',
|
|
56
|
+
year: 0,
|
|
57
|
+
keys: '',
|
|
58
|
+
fields: [],
|
|
59
|
+
states: [],
|
|
60
|
+
entIds: [],
|
|
61
|
+
keyId: '1',
|
|
62
|
+
filterId: '',
|
|
63
|
+
page: 1,
|
|
64
|
+
data: '',
|
|
65
|
+
};
|
|
66
|
+
readonly model = input<ItemInterface>();
|
|
67
|
+
private subscriptionItemState!: Subscription;
|
|
68
|
+
|
|
69
|
+
constructor() {
|
|
70
|
+
// Re-render nav state dots whenever updateState() recalculates item states.
|
|
71
|
+
this.gsv
|
|
72
|
+
.getItemState()
|
|
73
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
74
|
+
.subscribe(() => this.cdr.markForCheck());
|
|
75
|
+
|
|
76
|
+
effect(() => {
|
|
77
|
+
const m = this.model();
|
|
78
|
+
if (m && this.items?.length > 0) {
|
|
79
|
+
this.click(m);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Lifecycle: cleans up subscriptions and resets the active item state. */
|
|
85
|
+
public ngOnDestroy(): void {
|
|
86
|
+
this.subscriptionItemState?.unsubscribe();
|
|
87
|
+
this.items = [];
|
|
88
|
+
this.projectItems = [];
|
|
89
|
+
this.active = null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Lifecycle: subscribes to projectChanged and loads the initial navigation item list. */
|
|
93
|
+
public ngAfterViewInit(): void {
|
|
94
|
+
this.gsv
|
|
95
|
+
.getProjectChanged()
|
|
96
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
97
|
+
.subscribe(projectId => {
|
|
98
|
+
this.reinitializeForProject();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.initializeProject();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Fetches project items from cache or API and calls setupProject to populate the list. */
|
|
105
|
+
private initializeProject(): void {
|
|
106
|
+
this.managment.appId = this.gsv.getProject().project;
|
|
107
|
+
this.managment.year = this.gsv.getYear();
|
|
108
|
+
this.managment.keyId = this.gsv.getKeyId();
|
|
109
|
+
|
|
110
|
+
this.projectId = this.managment.appId + '.' + this.managment.year;
|
|
111
|
+
|
|
112
|
+
this.projectItems = this.gsv.getProjectItems(this.projectId);
|
|
113
|
+
|
|
114
|
+
if (this.projectItems === undefined) {
|
|
115
|
+
this.subscriptionItemState = this.msv
|
|
116
|
+
.getItemsState(this.managment)
|
|
117
|
+
.subscribe({
|
|
118
|
+
next: (data: ItemInterface[]) => {
|
|
119
|
+
this.setupProject(data);
|
|
120
|
+
this.gsv.setProjectItems(this.projectId, data);
|
|
121
|
+
},
|
|
122
|
+
error: error => {
|
|
123
|
+
console.error('Error fetching project items:', error);
|
|
124
|
+
this.gsv.navigateToDefault();
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
this.setupProject(this.gsv.getProjectItems(this.projectId));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Clears state and re-runs initializeProject when the active project changes. */
|
|
133
|
+
private reinitializeForProject(): void {
|
|
134
|
+
if (this.subscriptionItemState) {
|
|
135
|
+
this.subscriptionItemState.unsubscribe();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.items = [];
|
|
139
|
+
this.projectItems = undefined;
|
|
140
|
+
this.active = null;
|
|
141
|
+
|
|
142
|
+
this.initializeProject();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Filters the raw item array by project, resolves a `targetModel` from localStorage
|
|
147
|
+
* if present, selects the first/target item, and emits modelChanged.
|
|
148
|
+
*/
|
|
149
|
+
public setupProject(data: ItemInterface[]): void {
|
|
150
|
+
if (data.length > 0) {
|
|
151
|
+
for (let i = 0; i < data.length; i++) {
|
|
152
|
+
data[i].ivisible = data[i].visible;
|
|
153
|
+
}
|
|
154
|
+
this.items = data.filter(
|
|
155
|
+
e =>
|
|
156
|
+
((e.linkedItems ?? []).indexOf(e.name) === -1 ||
|
|
157
|
+
(e.linkedItems ?? []).indexOf(e.name) === 0) &&
|
|
158
|
+
e.enabled &&
|
|
159
|
+
e.visible &&
|
|
160
|
+
(e.groupName === '' || e.rootModel === true) &&
|
|
161
|
+
(e.type === ItemType.Multiple || e.type === ItemType.Single)
|
|
162
|
+
);
|
|
163
|
+
this.hide = this.items.length == 1;
|
|
164
|
+
this.hideNavigation.emit(this.hide);
|
|
165
|
+
|
|
166
|
+
const targetModel = localStorage.getItem('targetModel');
|
|
167
|
+
if (targetModel) {
|
|
168
|
+
const model = this.items.find(e => e.name === targetModel);
|
|
169
|
+
if (model) {
|
|
170
|
+
localStorage.removeItem('targetModel');
|
|
171
|
+
this.click(model);
|
|
172
|
+
} else {
|
|
173
|
+
localStorage.removeItem('targetModel');
|
|
174
|
+
this.click(this.items[0]);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
this.click(this.items[0]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Sets the active navigation item and emits the modelChanged event. */
|
|
183
|
+
public click(item: ItemInterface): void {
|
|
184
|
+
if (this.active !== item) {
|
|
185
|
+
this.active = item;
|
|
186
|
+
this.modelChanged.emit(item);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Shows/hides navigation items by matching the search string against title, name and groupName. */
|
|
191
|
+
public filter(value: string): void {
|
|
192
|
+
const needle = value.trim().toUpperCase();
|
|
193
|
+
this.items.forEach((model: ItemInterface) => {
|
|
194
|
+
if (
|
|
195
|
+
needle === '' ||
|
|
196
|
+
model.title.toUpperCase().includes(needle) ||
|
|
197
|
+
model.name.toUpperCase().includes(needle) ||
|
|
198
|
+
model.groupName.toUpperCase().includes(needle)
|
|
199
|
+
) {
|
|
200
|
+
model.visible = model.ivisible;
|
|
201
|
+
} else {
|
|
202
|
+
model.visible = false;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Returns true if the given item is the currently selected navigation entry. */
|
|
208
|
+
isActiveItem(item: ItemInterface): boolean {
|
|
209
|
+
return this.active === item;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Returns the CSS class object for the navigation icon (highlights when item is active). */
|
|
213
|
+
getIconClass(item: ItemInterface) {
|
|
214
|
+
return { 'has-selected': this.active === item };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Returns the CSS class object for the desktop state indicator (error/warning/valid icons). */
|
|
218
|
+
getDesktopStateClass(item: ItemInterface) {
|
|
219
|
+
return {
|
|
220
|
+
active: this.active === item,
|
|
221
|
+
'span-state': item.state === this.itemState.Valid,
|
|
222
|
+
'span-state-rect':
|
|
223
|
+
item.state === this.itemState.Error ||
|
|
224
|
+
item.state === this.itemState.Warning,
|
|
225
|
+
'dot-empty': item.state === this.itemState.Empty,
|
|
226
|
+
'dot-success': item.state === this.itemState.Valid,
|
|
227
|
+
'triangle-error': item.state === this.itemState.Error,
|
|
228
|
+
'rectangle-warning': item.state === this.itemState.Warning,
|
|
229
|
+
'font-weight-bold': this.active === item,
|
|
230
|
+
'mx-1': item.state !== this.itemState.Error,
|
|
231
|
+
'align-self-start': item.state !== this.itemState.Error,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Returns the CSS class object for the item label (bold when active). */
|
|
236
|
+
getItemTextClass(item: ItemInterface) {
|
|
237
|
+
return {
|
|
238
|
+
active: this.active === item,
|
|
239
|
+
'font-weight-bold': this.active === item,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Emits the inverse of the current collapsed state to toggle the navigation panel. */
|
|
244
|
+
toggleCollapse(): void {
|
|
245
|
+
this.collapsedChanged.emit(!this.collapsed());
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './model.service';
|