aril 1.0.39 → 1.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/http/src/serviceStateMethods.mjs +6 -2
- package/esm2022/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.mjs +211 -0
- package/esm2022/theme/layout/app/favorite-pages/favorite-pages.service.mjs +55 -0
- package/esm2022/theme/layout/app/favorite-pages/modals/add-edit-favorite-modal/add-edit-favorite-modal.component.mjs +129 -0
- package/esm2022/theme/layout/app/history/history-sidebar.component.mjs +128 -0
- package/esm2022/theme/layout/app/history/history.service.mjs +146 -0
- package/esm2022/theme/layout/app/layout/app.layout.component.mjs +9 -3
- package/esm2022/theme/layout/app/profileSidebar/app.profilesidebar.component.mjs +100 -8
- package/esm2022/theme/layout/app/profileSidebar/modals/change-password-modal/change-password-modal.component.mjs +142 -0
- package/esm2022/theme/layout/app/profileSidebar/modals/edit-profile-modal/edit-profile-modal.component.mjs +123 -0
- package/esm2022/theme/layout/app/profileSidebar/profile.service.mjs +42 -0
- package/esm2022/theme/layout/app/site-map/site-map-sidebar.component.mjs +161 -0
- package/esm2022/theme/layout/app/topbar/app.topbar.component.mjs +23 -5
- package/esm2022/theme/layout/service/app.layout.service.mjs +13 -1
- package/fesm2022/aril-http.mjs +5 -1
- package/fesm2022/aril-http.mjs.map +1 -1
- package/fesm2022/aril-theme-layout.mjs +1197 -75
- package/fesm2022/aril-theme-layout.mjs.map +1 -1
- package/package.json +84 -83
- package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.d.ts +42 -0
- package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.html +106 -0
- package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.scss +181 -0
- package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.ts +253 -0
- package/theme/layout/app/favorite-pages/favorite-pages.service.d.ts +54 -0
- package/theme/layout/app/favorite-pages/favorite-pages.service.ts +87 -0
- package/theme/layout/app/favorite-pages/modals/add-edit-favorite-modal/add-edit-favorite-modal.component.d.ts +36 -0
- package/theme/layout/app/favorite-pages/modals/add-edit-favorite-modal/add-edit-favorite-modal.component.html +27 -0
- package/theme/layout/app/favorite-pages/modals/add-edit-favorite-modal/add-edit-favorite-modal.component.ts +165 -0
- package/theme/layout/app/history/history-sidebar.component.d.ts +30 -0
- package/theme/layout/app/history/history-sidebar.component.html +88 -0
- package/theme/layout/app/history/history-sidebar.component.scss +191 -0
- package/theme/layout/app/history/history-sidebar.component.ts +139 -0
- package/theme/layout/app/history/history.service.d.ts +36 -0
- package/theme/layout/app/history/history.service.ts +182 -0
- package/theme/layout/app/layout/app.layout.component.html +3 -0
- package/theme/layout/app/layout/app.layout.component.ts +7 -1
- package/theme/layout/app/profileSidebar/app.profilesidebar.component.d.ts +17 -2
- package/theme/layout/app/profileSidebar/app.profilesidebar.component.html +107 -135
- package/theme/layout/app/profileSidebar/app.profilesidebar.component.scss +152 -0
- package/theme/layout/app/profileSidebar/app.profilesidebar.component.ts +114 -7
- package/theme/layout/app/profileSidebar/modals/change-password-modal/change-password-modal.component.d.ts +30 -0
- package/theme/layout/app/profileSidebar/modals/change-password-modal/change-password-modal.component.html +46 -0
- package/theme/layout/app/profileSidebar/modals/change-password-modal/change-password-modal.component.scss +28 -0
- package/theme/layout/app/profileSidebar/modals/change-password-modal/change-password-modal.component.ts +178 -0
- package/theme/layout/app/profileSidebar/modals/edit-profile-modal/edit-profile-modal.component.d.ts +27 -0
- package/theme/layout/app/profileSidebar/modals/edit-profile-modal/edit-profile-modal.component.html +76 -0
- package/theme/layout/app/profileSidebar/modals/edit-profile-modal/edit-profile-modal.component.ts +141 -0
- package/theme/layout/app/profileSidebar/profile.service.d.ts +67 -0
- package/theme/layout/app/profileSidebar/profile.service.ts +89 -0
- package/theme/layout/app/site-map/site-map-sidebar.component.d.ts +37 -0
- package/theme/layout/app/site-map/site-map-sidebar.component.html +118 -0
- package/theme/layout/app/site-map/site-map-sidebar.component.scss +189 -0
- package/theme/layout/app/site-map/site-map-sidebar.component.ts +189 -0
- package/theme/layout/app/topbar/app.topbar.component.d.ts +7 -1
- package/theme/layout/app/topbar/app.topbar.component.html +37 -17
- package/theme/layout/app/topbar/app.topbar.component.scss +188 -12
- package/theme/layout/app/topbar/app.topbar.component.ts +29 -7
- package/theme/layout/service/app.layout.service.d.ts +6 -0
- package/theme/layout/service/app.layout.service.ts +19 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<p-sidebar
|
|
2
|
+
[(visible)]="visible"
|
|
3
|
+
position="right"
|
|
4
|
+
[transitionOptions]="'.3s cubic-bezier(0, 0, 0.2, 1)'"
|
|
5
|
+
styleClass="layout-history-sidebar w-full sm:w-30rem"
|
|
6
|
+
*transloco="let t; read: 'history'">
|
|
7
|
+
<ng-template pTemplate="header">
|
|
8
|
+
<div class="flex align-items-center justify-content-between w-full">
|
|
9
|
+
<div class="flex align-items-center">
|
|
10
|
+
<i class="pi pi-history text-primary text-xl"></i>
|
|
11
|
+
<span class="font-bold text-lg">{{ t('pageHistory') }}</span>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</ng-template>
|
|
15
|
+
|
|
16
|
+
<div class="history-content">
|
|
17
|
+
@if (history().length > 0) {
|
|
18
|
+
<div class="search-section">
|
|
19
|
+
<div class="search-input-container">
|
|
20
|
+
<aril-text
|
|
21
|
+
class="w-full"
|
|
22
|
+
[placeholder]="t('searchHistory')"
|
|
23
|
+
[(ngModel)]="searchTerm"
|
|
24
|
+
(ngModelChange)="onSearchChange()">
|
|
25
|
+
</aril-text>
|
|
26
|
+
|
|
27
|
+
<aril-button
|
|
28
|
+
icon="TRASH"
|
|
29
|
+
color="danger"
|
|
30
|
+
size="sm"
|
|
31
|
+
class="ml-1"
|
|
32
|
+
[pTooltip]="t('confirmClearAll')"
|
|
33
|
+
tooltipPosition="left"
|
|
34
|
+
(clickEvent)="clearAllHistory($event)">
|
|
35
|
+
</aril-button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@if (filteredHistory().length > 0) {
|
|
41
|
+
<div class="history-list">
|
|
42
|
+
<p-scrollPanel [style]="{ width: '100%', height: '100%' }">
|
|
43
|
+
<div class="history-items">
|
|
44
|
+
@for (item of filteredHistory(); track trackByItemId($index, item)) {
|
|
45
|
+
<div class="history-item" (click)="navigateToItem(item)">
|
|
46
|
+
<div class="item-content">
|
|
47
|
+
<div class="item-header">
|
|
48
|
+
<div class="item-title">
|
|
49
|
+
<i class="pi pi-external-link text-primary mr-2"></i>
|
|
50
|
+
<div class="title-info">
|
|
51
|
+
<span class="title-text">{{ getLocalizedTitle(item) }}</span>
|
|
52
|
+
<span class="route-text">{{ item.url }}</span>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
}
|
|
59
|
+
</div>
|
|
60
|
+
</p-scrollPanel>
|
|
61
|
+
</div>
|
|
62
|
+
} @else {
|
|
63
|
+
<div class="empty-state">
|
|
64
|
+
<div class="empty-content">
|
|
65
|
+
<i class="pi pi-history empty-icon"></i>
|
|
66
|
+
<h3 class="empty-title">
|
|
67
|
+
{{ searchTerm() ? t('noSearchResults') : t('noHistory') }}
|
|
68
|
+
</h3>
|
|
69
|
+
<p class="empty-message">
|
|
70
|
+
{{ searchTerm() ? t('tryDifferentKeywords') : t('startBrowsing') }}
|
|
71
|
+
</p>
|
|
72
|
+
@if (searchTerm()) {
|
|
73
|
+
<aril-button
|
|
74
|
+
[label]="t('clearSearch')"
|
|
75
|
+
icon="TIMES"
|
|
76
|
+
color="secondary"
|
|
77
|
+
[outlined]="true"
|
|
78
|
+
size="sm"
|
|
79
|
+
(clickEvent)="searchTerm.set(''); onSearchChange()">
|
|
80
|
+
</aril-button>
|
|
81
|
+
}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
}
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<p-confirmDialog></p-confirmDialog>
|
|
88
|
+
</p-sidebar>
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
:host ::ng-deep {
|
|
2
|
+
.layout-history-sidebar {
|
|
3
|
+
.p-sidebar-content {
|
|
4
|
+
padding: 0;
|
|
5
|
+
background: var(--surface-ground);
|
|
6
|
+
}
|
|
7
|
+
.p-sidebar-header {
|
|
8
|
+
background: var(--primary-color);
|
|
9
|
+
color: var(--primary-color-text);
|
|
10
|
+
border-bottom: 1px solid var(--primary-600);
|
|
11
|
+
padding: 1rem 0.5rem;
|
|
12
|
+
|
|
13
|
+
i {
|
|
14
|
+
color: var(--primary-color-text);
|
|
15
|
+
font-size: 1.1rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
span {
|
|
19
|
+
font-size: 1.1rem;
|
|
20
|
+
font-weight: 600;
|
|
21
|
+
color: var(--primary-color-text);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.history-content {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
height: 92vh;
|
|
31
|
+
background: var(--surface-0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.search-section {
|
|
35
|
+
padding: 1rem;
|
|
36
|
+
border-bottom: 1px solid var(--surface-border);
|
|
37
|
+
background: var(--surface-0);
|
|
38
|
+
width: 100%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.search-input-container {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: space-between;
|
|
45
|
+
width: 100%;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// History List
|
|
49
|
+
.history-list {
|
|
50
|
+
flex: 1;
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.history-items {
|
|
55
|
+
padding: 0.5rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.history-item {
|
|
59
|
+
background: var(--surface-0);
|
|
60
|
+
border: 1px solid var(--surface-border);
|
|
61
|
+
border-radius: 8px;
|
|
62
|
+
margin-bottom: 0.5rem;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
transition: all 0.2s ease;
|
|
65
|
+
position: relative;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
|
|
68
|
+
&:hover {
|
|
69
|
+
border-color: var(--primary-200);
|
|
70
|
+
background: var(--primary-50);
|
|
71
|
+
transform: translateY(-1px);
|
|
72
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
73
|
+
.remove-btn {
|
|
74
|
+
opacity: 1;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
&:last-child {
|
|
78
|
+
margin-bottom: 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.item-content {
|
|
83
|
+
padding: 1rem 0.75rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.item-header {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: space-between;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.item-title {
|
|
93
|
+
display: flex;
|
|
94
|
+
align-items: center;
|
|
95
|
+
flex: 1;
|
|
96
|
+
min-width: 0;
|
|
97
|
+
|
|
98
|
+
.title-info {
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
flex: 1;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.title-text {
|
|
106
|
+
font-weight: 600;
|
|
107
|
+
color: var(--text-color);
|
|
108
|
+
font-size: 1rem;
|
|
109
|
+
line-height: 1.3;
|
|
110
|
+
overflow: hidden;
|
|
111
|
+
text-overflow: ellipsis;
|
|
112
|
+
white-space: nowrap;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.route-text {
|
|
116
|
+
font-size: 0.75rem;
|
|
117
|
+
color: var(--text-color-secondary);
|
|
118
|
+
font-weight: 400;
|
|
119
|
+
line-height: 1.2;
|
|
120
|
+
margin-top: 0.25rem;
|
|
121
|
+
overflow: hidden;
|
|
122
|
+
text-overflow: ellipsis;
|
|
123
|
+
white-space: nowrap;
|
|
124
|
+
opacity: 0.8;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.empty-state {
|
|
129
|
+
flex: 1;
|
|
130
|
+
display: flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
justify-content: center;
|
|
133
|
+
padding: 2rem;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.empty-content {
|
|
137
|
+
text-align: center;
|
|
138
|
+
max-width: 300px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.empty-icon {
|
|
142
|
+
font-size: 3rem;
|
|
143
|
+
color: var(--text-color-secondary);
|
|
144
|
+
margin-bottom: 1rem;
|
|
145
|
+
opacity: 0.5;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.empty-title {
|
|
149
|
+
font-size: 1.1rem;
|
|
150
|
+
font-weight: 600;
|
|
151
|
+
color: var(--text-color);
|
|
152
|
+
margin-bottom: 0.5rem;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.empty-message {
|
|
156
|
+
font-size: 0.9rem;
|
|
157
|
+
color: var(--text-color-secondary);
|
|
158
|
+
line-height: 1.4;
|
|
159
|
+
margin-bottom: 1.5rem;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@media (max-width: 768px) {
|
|
163
|
+
.history-content {
|
|
164
|
+
height: calc(100vh - 3.5rem);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.search-section {
|
|
168
|
+
padding: 0.75rem;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.item-content {
|
|
172
|
+
padding: 0.5rem;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.item-title .title-text {
|
|
176
|
+
font-size: 0.85rem;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Custom Scrollbar
|
|
181
|
+
:host ::ng-deep {
|
|
182
|
+
.p-scrollpanel-bar-y {
|
|
183
|
+
background: var(--primary-200);
|
|
184
|
+
width: 4px;
|
|
185
|
+
border-radius: 2px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.p-scrollpanel-bar-y:hover {
|
|
189
|
+
background: var(--primary-300);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component, OnDestroy, OnInit, signal } from '@angular/core';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
import { ConfirmationService, MessageService } from 'primeng/api';
|
|
6
|
+
import { BadgeModule } from 'primeng/badge';
|
|
7
|
+
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
|
8
|
+
import { InputTextModule } from 'primeng/inputtext';
|
|
9
|
+
import { ScrollPanelModule } from 'primeng/scrollpanel';
|
|
10
|
+
import { SidebarModule } from 'primeng/sidebar';
|
|
11
|
+
import { TooltipModule } from 'primeng/tooltip';
|
|
12
|
+
|
|
13
|
+
import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
|
|
14
|
+
import { Subject, takeUntil } from 'rxjs';
|
|
15
|
+
|
|
16
|
+
import { ButtonComponent } from 'aril/ui/button';
|
|
17
|
+
import { TextComponent } from 'aril/ui/text';
|
|
18
|
+
|
|
19
|
+
import { LayoutService } from '../../service/app.layout.service';
|
|
20
|
+
import { HistoryItem, HistoryService } from './history.service';
|
|
21
|
+
|
|
22
|
+
@Component({
|
|
23
|
+
standalone: true,
|
|
24
|
+
selector: 'app-history-sidebar',
|
|
25
|
+
imports: [
|
|
26
|
+
CommonModule,
|
|
27
|
+
FormsModule,
|
|
28
|
+
SidebarModule,
|
|
29
|
+
ButtonComponent,
|
|
30
|
+
InputTextModule,
|
|
31
|
+
ScrollPanelModule,
|
|
32
|
+
ConfirmDialogModule,
|
|
33
|
+
TooltipModule,
|
|
34
|
+
BadgeModule,
|
|
35
|
+
TranslocoModule,
|
|
36
|
+
TextComponent
|
|
37
|
+
],
|
|
38
|
+
templateUrl: './history-sidebar.component.html',
|
|
39
|
+
styleUrls: ['./history-sidebar.component.scss'],
|
|
40
|
+
providers: [ConfirmationService]
|
|
41
|
+
})
|
|
42
|
+
export class HistorySidebarComponent implements OnInit, OnDestroy {
|
|
43
|
+
history = signal<HistoryItem[]>([]);
|
|
44
|
+
filteredHistory = signal<HistoryItem[]>([]);
|
|
45
|
+
searchTerm = signal<string>('');
|
|
46
|
+
|
|
47
|
+
private destroy$ = new Subject<void>();
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
public layoutService: LayoutService,
|
|
51
|
+
private historyService: HistoryService,
|
|
52
|
+
private confirmationService: ConfirmationService,
|
|
53
|
+
private messageService: MessageService,
|
|
54
|
+
private translocoService: TranslocoService
|
|
55
|
+
) {}
|
|
56
|
+
|
|
57
|
+
ngOnInit(): void {
|
|
58
|
+
this.historyService.history$.pipe(takeUntil(this.destroy$)).subscribe((history) => {
|
|
59
|
+
this.history.set(history);
|
|
60
|
+
this.applyFilter();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ngOnDestroy(): void {
|
|
65
|
+
this.destroy$.next();
|
|
66
|
+
this.destroy$.complete();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get visible(): boolean {
|
|
70
|
+
return this.layoutService.state.historySidebarVisible;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
set visible(value: boolean) {
|
|
74
|
+
this.layoutService.state.historySidebarVisible = value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
onSearchChange(): void {
|
|
78
|
+
this.applyFilter();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private applyFilter(): void {
|
|
82
|
+
const searchTerm = this.searchTerm();
|
|
83
|
+
if (!searchTerm.trim()) {
|
|
84
|
+
this.filteredHistory.set(this.history());
|
|
85
|
+
} else {
|
|
86
|
+
const filtered = this.historyService.getHistoryItemsBySearchTerm(searchTerm);
|
|
87
|
+
this.filteredHistory.set(filtered);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
navigateToItem(item: HistoryItem): void {
|
|
92
|
+
this.historyService.navigateToHistoryItem(item);
|
|
93
|
+
this.visible = false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
clearAllHistory(event: Event): void {
|
|
97
|
+
this.confirmationService.confirm({
|
|
98
|
+
target: event.target as EventTarget,
|
|
99
|
+
message: this.translocoService.translate('history.confirmClearAllMessage'),
|
|
100
|
+
header: this.translocoService.translate('history.deleteConfirmation'),
|
|
101
|
+
icon: 'pi pi-info-circle',
|
|
102
|
+
acceptLabel: this.translocoService.translate('history.acceptButtonLabel'),
|
|
103
|
+
rejectLabel: this.translocoService.translate('history.rejectButtonLabel'),
|
|
104
|
+
acceptButtonStyleClass: 'p-button-success',
|
|
105
|
+
rejectButtonStyleClass: 'p-button-danger p-button-outlined',
|
|
106
|
+
accept: () => {
|
|
107
|
+
this.historyService.clearHistory();
|
|
108
|
+
this.messageService.add({
|
|
109
|
+
severity: 'success',
|
|
110
|
+
summary: this.translocoService.translate('history.acceptButtonLabel'),
|
|
111
|
+
detail: this.translocoService.translate('history.clearAll')
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
trackByItemId(index: number, item: HistoryItem) {
|
|
118
|
+
return item.id || index;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getLocalizedTitle(item: HistoryItem): string {
|
|
122
|
+
const currentLang = this.translocoService.getActiveLang() as 'tr' | 'en';
|
|
123
|
+
|
|
124
|
+
// Use multiLanguageTitle if available
|
|
125
|
+
if (item.multiLanguageTitle) {
|
|
126
|
+
const localizedTitle = item.multiLanguageTitle[currentLang];
|
|
127
|
+
if (localizedTitle) {
|
|
128
|
+
return localizedTitle;
|
|
129
|
+
}
|
|
130
|
+
// Fallback to Turkish if current language not available
|
|
131
|
+
if (item.multiLanguageTitle.tr) {
|
|
132
|
+
return item.multiLanguageTitle.tr;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Fallback to original title
|
|
137
|
+
return item.title;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export interface HistoryItem {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
multiLanguageTitle?: {
|
|
7
|
+
tr?: string;
|
|
8
|
+
en?: string;
|
|
9
|
+
};
|
|
10
|
+
url: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
export declare class HistoryService {
|
|
14
|
+
private readonly STORAGE_KEY;
|
|
15
|
+
private readonly MAX_HISTORY_SIZE;
|
|
16
|
+
private historySubject;
|
|
17
|
+
history$: Observable<HistoryItem[]>;
|
|
18
|
+
private excludedPaths;
|
|
19
|
+
private router;
|
|
20
|
+
private translocoService;
|
|
21
|
+
constructor();
|
|
22
|
+
private initializeRouterTracking;
|
|
23
|
+
private addToHistory;
|
|
24
|
+
private shouldExcludeUrl;
|
|
25
|
+
private findMenuItemByPath;
|
|
26
|
+
private findMenuItemRecursive;
|
|
27
|
+
private generateId;
|
|
28
|
+
private loadHistoryFromStorage;
|
|
29
|
+
private saveHistoryToStorage;
|
|
30
|
+
clearHistory(): void;
|
|
31
|
+
navigateToHistoryItem(item: HistoryItem): void;
|
|
32
|
+
private getHistoryFromStorage;
|
|
33
|
+
getHistoryItemsBySearchTerm(searchTerm: string): HistoryItem[];
|
|
34
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<HistoryService, never>;
|
|
35
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<HistoryService>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { Router, NavigationEnd } from '@angular/router';
|
|
3
|
+
import { BehaviorSubject, Observable } from 'rxjs';
|
|
4
|
+
import { filter, map } from 'rxjs/operators';
|
|
5
|
+
import { TranslocoService } from '@ngneat/transloco';
|
|
6
|
+
|
|
7
|
+
import { PluginMenuItem } from 'aril/boot/config/apps';
|
|
8
|
+
|
|
9
|
+
export interface HistoryItem {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
multiLanguageTitle?: {
|
|
13
|
+
tr?: string;
|
|
14
|
+
en?: string;
|
|
15
|
+
};
|
|
16
|
+
url: string;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Injectable({
|
|
21
|
+
providedIn: 'root'
|
|
22
|
+
})
|
|
23
|
+
export class HistoryService {
|
|
24
|
+
private readonly STORAGE_KEY = 'page_history';
|
|
25
|
+
private readonly MAX_HISTORY_SIZE = 20;
|
|
26
|
+
|
|
27
|
+
private historySubject = new BehaviorSubject<HistoryItem[]>([]);
|
|
28
|
+
public history$: Observable<HistoryItem[]> = this.historySubject.asObservable();
|
|
29
|
+
|
|
30
|
+
// Excluded paths that shouldn't be tracked
|
|
31
|
+
private excludedPaths = ['/login', '/logout', '/error', '/not-found', '/unauthorized'];
|
|
32
|
+
|
|
33
|
+
private router = inject(Router);
|
|
34
|
+
private translocoService = inject(TranslocoService);
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
this.loadHistoryFromStorage();
|
|
38
|
+
this.initializeRouterTracking();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private initializeRouterTracking(): void {
|
|
42
|
+
this.router.events
|
|
43
|
+
.pipe(
|
|
44
|
+
filter((event) => event instanceof NavigationEnd),
|
|
45
|
+
map((event) => event as NavigationEnd)
|
|
46
|
+
)
|
|
47
|
+
.subscribe((event: NavigationEnd) => {
|
|
48
|
+
this.addToHistory(event.urlAfterRedirects || event.url);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private addToHistory(url: string): void {
|
|
53
|
+
// Skip if URL should be excluded
|
|
54
|
+
if (this.shouldExcludeUrl(url)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const menuItem = this.findMenuItemByPath(url);
|
|
59
|
+
const multiLanguageTitle: { tr?: string; en?: string } = {};
|
|
60
|
+
|
|
61
|
+
// Get titles for both languages if menu item exists
|
|
62
|
+
if (menuItem) {
|
|
63
|
+
multiLanguageTitle.tr = menuItem.label['tr'];
|
|
64
|
+
multiLanguageTitle.en = menuItem.label['en'];
|
|
65
|
+
} else {
|
|
66
|
+
// Fallback for pages not in menu
|
|
67
|
+
multiLanguageTitle.tr = 'Bilinmeyen Sayfa';
|
|
68
|
+
multiLanguageTitle.en = 'Unknown Page';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const currentLang = this.translocoService.getActiveLang() as 'tr' | 'en';
|
|
72
|
+
const title = multiLanguageTitle[currentLang] || multiLanguageTitle.tr || 'Unknown Page';
|
|
73
|
+
const timestamp = Date.now();
|
|
74
|
+
const id = this.generateId(url, timestamp);
|
|
75
|
+
|
|
76
|
+
const newItem: HistoryItem = {
|
|
77
|
+
id,
|
|
78
|
+
title,
|
|
79
|
+
multiLanguageTitle,
|
|
80
|
+
url,
|
|
81
|
+
timestamp
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
let history = this.getHistoryFromStorage();
|
|
85
|
+
history = history.filter((item) => item.url !== url);
|
|
86
|
+
history.unshift(newItem);
|
|
87
|
+
|
|
88
|
+
// Limit history size
|
|
89
|
+
if (history.length > this.MAX_HISTORY_SIZE) {
|
|
90
|
+
history = history.slice(0, this.MAX_HISTORY_SIZE);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.saveHistoryToStorage(history);
|
|
94
|
+
this.historySubject.next(history);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private shouldExcludeUrl(url: string): boolean {
|
|
98
|
+
return this.excludedPaths.some((excludedPath) => url.startsWith(excludedPath));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private findMenuItemByPath(path: string): PluginMenuItem | null {
|
|
102
|
+
const menuItems = (<any>globalThis).hostMenuItems?.() || [];
|
|
103
|
+
|
|
104
|
+
// Special handling for home page route
|
|
105
|
+
if (path === '/' || path === '') {
|
|
106
|
+
return menuItems.find((item: PluginMenuItem) => item.routerLink === '/') || null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Remove leading slash if present for other routes
|
|
110
|
+
const cleanPath = path.startsWith('/') ? path.substring(1) : path;
|
|
111
|
+
return this.findMenuItemRecursive(menuItems, cleanPath);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private findMenuItemRecursive(items: PluginMenuItem[], path: string): PluginMenuItem | null {
|
|
115
|
+
for (const item of items) {
|
|
116
|
+
if (item.routerLink === path) {
|
|
117
|
+
return item;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (item.routerLink && path.startsWith(item.routerLink + '/')) {
|
|
121
|
+
return item;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (item.items) {
|
|
125
|
+
const found = this.findMenuItemRecursive(item.items, path);
|
|
126
|
+
if (found) {
|
|
127
|
+
return found;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private generateId(url: string, timestamp: number): string {
|
|
136
|
+
return `${url.replace(/[^a-zA-Z0-9]/g, '_')}_${timestamp}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private loadHistoryFromStorage(): void {
|
|
140
|
+
const history = this.getHistoryFromStorage();
|
|
141
|
+
this.historySubject.next(history);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private saveHistoryToStorage(history: HistoryItem[]): void {
|
|
145
|
+
try {
|
|
146
|
+
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(history));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.warn('Failed to save navigation history to localStorage:', error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public clearHistory(): void {
|
|
153
|
+
localStorage.removeItem(this.STORAGE_KEY);
|
|
154
|
+
this.historySubject.next([]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public navigateToHistoryItem(item: HistoryItem): void {
|
|
158
|
+
this.router.navigate([item.url]);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private getHistoryFromStorage(): HistoryItem[] {
|
|
162
|
+
try {
|
|
163
|
+
const stored = localStorage.getItem(this.STORAGE_KEY);
|
|
164
|
+
return stored ? JSON.parse(stored) : [];
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.warn('Failed to load navigation history from localStorage:', error);
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public getHistoryItemsBySearchTerm(searchTerm: string): HistoryItem[] {
|
|
172
|
+
const history = this.getHistoryFromStorage();
|
|
173
|
+
const lowerSearchTerm = searchTerm.toLowerCase();
|
|
174
|
+
const currentLang = this.translocoService.getActiveLang() as 'tr' | 'en';
|
|
175
|
+
|
|
176
|
+
return history.filter((item) => {
|
|
177
|
+
// Search in current language title
|
|
178
|
+
const currentTitle = item.multiLanguageTitle?.[currentLang] || item.title;
|
|
179
|
+
return currentTitle.toLowerCase().includes(lowerSearchTerm) || item.url.toLowerCase().includes(lowerSearchTerm);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -13,6 +13,9 @@ import { Subscription, filter } from 'rxjs';
|
|
|
13
13
|
import { LayoutService } from '../../service/app.layout.service';
|
|
14
14
|
import { AppMenuService } from '../../service/app.menu.service';
|
|
15
15
|
import { AppProfileSidebarComponent } from '../profileSidebar/app.profilesidebar.component';
|
|
16
|
+
import { HistorySidebarComponent } from '../history/history-sidebar.component';
|
|
17
|
+
import { SiteMapSidebarComponent } from '../site-map/site-map-sidebar.component';
|
|
18
|
+
import { FavoritePagesSidebarComponent } from '../favorite-pages/favorite-pages-sidebar.component';
|
|
16
19
|
import { AppSidebarComponent } from '../sidebar/app.sidebar.component';
|
|
17
20
|
import { AppTopbarComponent } from '../topbar/app.topbar.component';
|
|
18
21
|
import { AppChatbotComponent } from '../chatbot/app.chatbot.component';
|
|
@@ -31,6 +34,9 @@ import { ExpandableMenuComponent } from '../expandableMenu/expandable-menu.compo
|
|
|
31
34
|
ToastModule,
|
|
32
35
|
AppTopbarComponent,
|
|
33
36
|
AppProfileSidebarComponent,
|
|
37
|
+
HistorySidebarComponent,
|
|
38
|
+
SiteMapSidebarComponent,
|
|
39
|
+
FavoritePagesSidebarComponent,
|
|
34
40
|
AppChatbotComponent,
|
|
35
41
|
AppSidebarComponent,
|
|
36
42
|
ExpandableMenuComponent
|
|
@@ -174,4 +180,4 @@ export class AppLayoutComponent implements OnDestroy {
|
|
|
174
180
|
this.menuOutsideClickListener();
|
|
175
181
|
}
|
|
176
182
|
}
|
|
177
|
-
}
|
|
183
|
+
}
|
|
@@ -1,14 +1,29 @@
|
|
|
1
|
+
import { OnInit } from '@angular/core';
|
|
2
|
+
import { DialogService } from 'primeng/dynamicdialog';
|
|
3
|
+
import { TranslocoService } from '@ngneat/transloco';
|
|
1
4
|
import { KeycloakService } from 'keycloak-angular';
|
|
2
5
|
import { LayoutService } from '../../service/app.layout.service';
|
|
6
|
+
import { ProfileService, UserResponseDTO } from './profile.service';
|
|
3
7
|
import * as i0 from "@angular/core";
|
|
4
|
-
export declare class AppProfileSidebarComponent {
|
|
8
|
+
export declare class AppProfileSidebarComponent implements OnInit {
|
|
5
9
|
layoutService: LayoutService;
|
|
6
10
|
private readonly keycloak;
|
|
11
|
+
private readonly profileService;
|
|
12
|
+
private readonly dialogService;
|
|
13
|
+
private readonly translocoService;
|
|
7
14
|
username: string;
|
|
8
|
-
|
|
15
|
+
userProfile: import("@angular/core").WritableSignal<UserResponseDTO | null>;
|
|
16
|
+
private subjects;
|
|
17
|
+
getUserService: import("@angular/core").Signal<import("../../../../../../dist/http").ServiceResponse<UserResponseDTO> | undefined>;
|
|
18
|
+
constructor(layoutService: LayoutService, keycloak: KeycloakService, profileService: ProfileService, dialogService: DialogService, translocoService: TranslocoService);
|
|
19
|
+
ngOnInit(): void;
|
|
20
|
+
private initializeEffects;
|
|
21
|
+
private loadUserProfile;
|
|
9
22
|
get visible(): boolean;
|
|
10
23
|
set visible(_val: boolean);
|
|
11
24
|
logout(): void;
|
|
25
|
+
editProfile(): void;
|
|
26
|
+
changePassword(): void;
|
|
12
27
|
static ɵfac: i0.ɵɵFactoryDeclaration<AppProfileSidebarComponent, never>;
|
|
13
28
|
static ɵcmp: i0.ɵɵComponentDeclaration<AppProfileSidebarComponent, "app-profilemenu", never, {}, {}, never, never, true, never>;
|
|
14
29
|
}
|