@skysoftware-co/bayan-hr-widgets-ui 2.0.18 → 2.0.20

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 (104) hide show
  1. package/ng-package.json +9 -0
  2. package/package.json +3 -15
  3. package/src/assets/i18n/ar.json +166 -0
  4. package/src/assets/i18n/en.json +166 -0
  5. package/src/assets/i18n/fr.json +166 -0
  6. package/src/lib/my-calendar-widget/my-calendar-widget.component.html +25 -0
  7. package/src/lib/my-calendar-widget/my-calendar-widget.component.ts +220 -0
  8. package/src/lib/my-main-details-widget/my-main-details-widget.component.html +157 -0
  9. package/src/lib/my-main-details-widget/my-main-details-widget.component.ts +179 -0
  10. package/src/lib/my-next-week-vacation-insights-widget/components/next-week-vacation-insights-popup/next-week-vacation-insights-popup.component.html +131 -0
  11. package/src/lib/my-next-week-vacation-insights-widget/components/next-week-vacation-insights-popup/next-week-vacation-insights-popup.component.ts +119 -0
  12. package/src/lib/my-next-week-vacation-insights-widget/my-next-week-vacation-insights-widget.component.html +35 -0
  13. package/src/lib/my-next-week-vacation-insights-widget/my-next-week-vacation-insights-widget.component.ts +110 -0
  14. package/src/lib/my-profile/address-widget/my-profile-address-widget.component.html +57 -0
  15. package/src/lib/my-profile/address-widget/my-profile-address-widget.component.ts +70 -0
  16. package/src/lib/my-profile/bank-info-widget/my-profile-bank-info-widget.component.html +101 -0
  17. package/src/lib/my-profile/bank-info-widget/my-profile-bank-info-widget.component.ts +89 -0
  18. package/src/lib/my-profile/contact-widget/my-profile-contact-widget.component.html +89 -0
  19. package/src/lib/my-profile/contact-widget/my-profile-contact-widget.component.ts +78 -0
  20. package/src/lib/my-profile/contract-widget/my-profile-contract-widget.component.html +63 -0
  21. package/src/lib/my-profile/contract-widget/my-profile-contract-widget.component.ts +73 -0
  22. package/src/lib/my-profile/degrees-widget/my-profile-degrees-widget-component.html +86 -0
  23. package/src/lib/my-profile/degrees-widget/my-profile-degrees-widget-component.ts +98 -0
  24. package/src/lib/my-profile/dependents-documents-widget/my-profile-dependents-documents-widget.html +83 -0
  25. package/src/lib/my-profile/dependents-documents-widget/my-profile-dependents-documents-widget.ts +126 -0
  26. package/src/lib/my-profile/documents-widgets/components/document-card/document-card.component.html +53 -0
  27. package/src/lib/my-profile/documents-widgets/components/document-card/document-card.component.ts +50 -0
  28. package/src/lib/my-profile/documents-widgets/components/document-row/document-row.component.html +48 -0
  29. package/src/lib/my-profile/documents-widgets/components/document-row/document-row.component.ts +44 -0
  30. package/src/lib/my-profile/documents-widgets/components/document-tablet-card/document-tablet-card.component.html +46 -0
  31. package/src/lib/my-profile/documents-widgets/components/document-tablet-card/document-tablet-card.component.ts +47 -0
  32. package/src/lib/my-profile/employee-dependents-widget/my-profile-employee-dependents-component.html +50 -0
  33. package/src/lib/my-profile/employee-dependents-widget/my-profile-employee-dependents-component.ts +63 -0
  34. package/src/lib/my-profile/entitlements-others-widget/my-profile-entitlements-others-widget.component.html +26 -0
  35. package/src/lib/my-profile/entitlements-others-widget/my-profile-entitlements-others-widget.component.ts +66 -0
  36. package/src/lib/my-profile/experiences-widget/my-profile-experiences-widget-component.html +93 -0
  37. package/src/lib/my-profile/experiences-widget/my-profile-experiences-widget-component.ts +102 -0
  38. package/src/lib/my-profile/indemnity-widget/my-profile-indemnity-widget.component.html +49 -0
  39. package/src/lib/my-profile/indemnity-widget/my-profile-indemnity-widget.component.ts +79 -0
  40. package/src/lib/my-profile/job-info-widget/my-profile-job-info-widget.component.html +71 -0
  41. package/src/lib/my-profile/job-info-widget/my-profile-job-info-widget.component.ts +73 -0
  42. package/src/lib/my-profile/languages-widget/my-profile-languages-widget.component.html +23 -0
  43. package/src/lib/my-profile/languages-widget/my-profile-languages-widget.component.ts +70 -0
  44. package/src/lib/my-profile/medical-insurance-widget/components/medical-insurance-card/medical-insurance-card.component.html +18 -0
  45. package/src/lib/my-profile/medical-insurance-widget/components/medical-insurance-card/medical-insurance-card.component.ts +29 -0
  46. package/src/lib/my-profile/medical-insurance-widget/my-profile-medical-insurance-widget.component.html +52 -0
  47. package/src/lib/my-profile/medical-insurance-widget/my-profile-medical-insurance-widget.component.ts +103 -0
  48. package/src/lib/my-profile/official-documents-widget/my-profile-official-documents-widget.html +82 -0
  49. package/src/lib/my-profile/official-documents-widget/my-profile-official-documents-widget.ts +119 -0
  50. package/src/lib/my-profile/other-nationalities-widget/my-profile-other-nationalities-widget.component.html +23 -0
  51. package/src/lib/my-profile/other-nationalities-widget/my-profile-other-nationalities-widget.component.ts +70 -0
  52. package/src/lib/my-profile/personal-info-main-widget/my-profile-personal-info-main-widget.component.html +115 -0
  53. package/src/lib/my-profile/personal-info-main-widget/my-profile-personal-info-main-widget.component.ts +75 -0
  54. package/src/lib/my-profile/salary-widget/my-profile-salary-widget.component.html +37 -0
  55. package/src/lib/my-profile/salary-widget/my-profile-salary-widget.component.ts +85 -0
  56. package/src/lib/my-profile/service-charge-widget/my-profile-service-charge-widget.component.html +16 -0
  57. package/src/lib/my-profile/service-charge-widget/my-profile-service-charge-widget.component.ts +101 -0
  58. package/src/lib/my-profile/ticket-widget/components/ticket-card/ticket-card.component.html +23 -0
  59. package/src/lib/my-profile/ticket-widget/components/ticket-card/ticket-card.component.ts +26 -0
  60. package/src/lib/my-profile/ticket-widget/my-profile-ticket-widget.component.html +68 -0
  61. package/src/lib/my-profile/ticket-widget/my-profile-ticket-widget.component.ts +93 -0
  62. package/src/lib/my-profile/vacations-widget/my-profile-vacations-widget.component.html +34 -0
  63. package/src/lib/my-profile/vacations-widget/my-profile-vacations-widget.component.ts +66 -0
  64. package/src/lib/my-team-subordinates-popup-widget/components/employee-main-info-card/employee-information-card.component.ts +65 -0
  65. package/src/lib/my-team-subordinates-popup-widget/components/employee-main-info-card/employee-main-info-card.component.html +57 -0
  66. package/src/lib/my-team-subordinates-popup-widget/components/my-team-subordinates-popup/my-team-popup.component.html +109 -0
  67. package/src/lib/my-team-subordinates-popup-widget/components/my-team-subordinates-popup/my-team-popup.component.ts +297 -0
  68. package/src/lib/my-upcoming-events-widget/components/event-item/event-item.component.html +53 -0
  69. package/src/lib/my-upcoming-events-widget/components/event-item/event-item.component.ts +42 -0
  70. package/src/lib/my-upcoming-events-widget/components/event-tab-bar/event-tab-bar.component.html +21 -0
  71. package/src/lib/my-upcoming-events-widget/components/event-tab-bar/event-tab-bar.component.ts +44 -0
  72. package/src/lib/my-upcoming-events-widget/components/events-popup/events-popup.component.html +67 -0
  73. package/src/lib/my-upcoming-events-widget/components/events-popup/events-popup.component.ts +74 -0
  74. package/src/lib/my-upcoming-events-widget/my-upcoming-events-widget.component.html +74 -0
  75. package/src/lib/my-upcoming-events-widget/my-upcoming-events-widget.component.ts +199 -0
  76. package/src/lib/services/hr-self-widgets.service.ts +624 -0
  77. package/src/lib/shared/components/card-header/card-header.component.html +4 -0
  78. package/src/lib/shared/components/card-header/card-header.component.ts +18 -0
  79. package/src/lib/shared/components/hr-employee-calendar/hr-employee-calendar.component.html +47 -0
  80. package/src/lib/shared/components/hr-employee-calendar/hr-employee-calendar.component.ts +193 -0
  81. package/src/lib/shared/components/info-field/info-field.component.html +2 -0
  82. package/src/lib/shared/components/info-field/info-field.component.ts +20 -0
  83. package/src/lib/shared/components/widget-card/widget-card-component.html +3 -0
  84. package/src/lib/shared/components/widget-card/widget-card-component.ts +17 -0
  85. package/src/lib/shared/pipes/translate.pipe.ts +34 -0
  86. package/src/lib/shared/services/bank-information-cache.service.ts +29 -0
  87. package/src/lib/shared/services/common-methods.service.ts +33 -0
  88. package/src/lib/shared/services/constants.service.ts +12 -0
  89. package/src/lib/shared/services/entitlements-cache.service.ts +94 -0
  90. package/src/lib/shared/services/job-information-cache.service.ts +72 -0
  91. package/src/lib/shared/services/my-team-popup.service.ts +34 -0
  92. package/src/lib/shared/services/personal-information-cache.service.ts +77 -0
  93. package/src/lib/shared/services/profile-documents-cache-service.ts +44 -0
  94. package/src/lib/shared/services/profile-employee-dependents-cach.ts +37 -0
  95. package/src/lib/shared/services/qualifications-cache.service.ts +45 -0
  96. package/src/lib/shared/services/translate.service.ts +35 -0
  97. package/src/lib/shared/types/common.ts +359 -0
  98. package/src/public-api.ts +35 -0
  99. package/tsconfig.lib.json +11 -0
  100. package/tsconfig.lib.prod.json +9 -0
  101. package/tsconfig.spec.json +8 -0
  102. package/fesm2022/skysoftware-co-bayan-hr-widgets-ui.mjs +0 -4785
  103. package/fesm2022/skysoftware-co-bayan-hr-widgets-ui.mjs.map +0 -1
  104. package/types/skysoftware-co-bayan-hr-widgets-ui.d.ts +0 -1305
@@ -0,0 +1,220 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ OnInit,
5
+ ViewChild,
6
+ computed,
7
+ effect,
8
+ inject,
9
+ input,
10
+ output,
11
+ signal,
12
+ } from '@angular/core';
13
+ import { HRSelfWidgetsService } from '../services/hr-self-widgets.service';
14
+ import {
15
+ CalendarResponse,
16
+ ScheduleCalenderVacation,
17
+ ScheduleRequestsResponse,
18
+ ScheduleVacationTypes,
19
+ } from '../shared/types/common';
20
+ import { HRTranslatePipe } from '../shared/pipes/translate.pipe';
21
+ import { HRConstantsService } from '../shared/services/constants.service';
22
+ import { HREmployeeCalendarComponent } from '../shared/components/hr-employee-calendar/hr-employee-calendar.component';
23
+ import { faCalendarDays } from '@fortawesome/pro-regular-svg-icons';
24
+ import { HRCardHeaderComponent } from '../shared/components/card-header/card-header.component';
25
+ import moment from 'moment';
26
+
27
+ @Component({
28
+ standalone: true,
29
+ selector: 'hr-my-calendar-widget',
30
+ templateUrl: './my-calendar-widget.component.html',
31
+ imports: [HREmployeeCalendarComponent, HRCardHeaderComponent, HRTranslatePipe],
32
+ changeDetection: ChangeDetectionStrategy.OnPush,
33
+ })
34
+ export class MyCalendarWidgetComponent implements OnInit {
35
+ readonly baseUrl = input<string>('');
36
+ readonly headerContainerClass = input<string>('');
37
+ readonly datasource = input<ScheduleRequestsResponse | null>(null);
38
+ readonly isLoadingChanged = output<boolean>();
39
+
40
+ readonly calendarIcon = faCalendarDays;
41
+
42
+ readonly legendContainerClass = input<string>('row mb-2');
43
+ readonly legendRowClass = input<string>('d-flex');
44
+ readonly legendItemClass = input<string>('d-flex align-items-center');
45
+ readonly legendDotClass = input<string>('me-1 circle-sm');
46
+ readonly legendLabelClass = input<string>('me-1 fs-12');
47
+ readonly publicHolidayDotClass = input<string>('bg-accent');
48
+ readonly dayOffDotClass = input<string>('bg-yellow');
49
+ readonly eventsDotClass = input<string>('bg-primary');
50
+ readonly vacationsContainerClass = input<string>('d-flex mt-3');
51
+ readonly vacationsItemClass = input<string>('d-flex align-items-center');
52
+ readonly vacationsIconClass = input<string>('fs-8 me-1');
53
+ readonly calendarContainerClass = input<string>('row mt-2 col-12');
54
+ readonly calendarClass = input<string>('button-boeder-none');
55
+
56
+ private readonly selfWidgetsService = inject(HRSelfWidgetsService);
57
+ private readonly constants = inject(HRConstantsService);
58
+ private readonly translatePipe = inject(HRTranslatePipe);
59
+
60
+ readonly publicHolidayColor = input<string>(this.constants.defaultPublicHolidayColor);
61
+ readonly dayOffColor = input<string>(this.constants.defaultDayOffColor);
62
+ readonly eventsColor = input<string>(this.constants.defaultEventsColor);
63
+
64
+ @ViewChild(HREmployeeCalendarComponent) hrCalendar!: HREmployeeCalendarComponent;
65
+
66
+ readonly minDate: Date;
67
+ readonly maxDate: Date;
68
+
69
+ private readonly baseCalendarData = signal<ScheduleCalenderVacation[]>([]);
70
+ private readonly hrVacations = signal<ScheduleCalenderVacation[]>([]);
71
+ private readonly calendarLoaded = signal(false);
72
+
73
+ readonly scheduleCalenderVacation = computed<ScheduleCalenderVacation[]>(() => {
74
+ if (!this.calendarLoaded()) {
75
+ return [];
76
+ }
77
+
78
+ const pendingVacations: ScheduleCalenderVacation[] =
79
+ this.datasource()?.PendingVacationRequests?.map(v => ({
80
+ VacationType: ScheduleVacationTypes.Vacations,
81
+ Description: v.VacationTypeName,
82
+ Color: v.Color,
83
+ VacationDays: { StartDate: v.StartDate, EndDate: v.EndDate },
84
+ })) ?? [];
85
+
86
+ const mergedVacations = this.mergeConsecutiveVacations(
87
+ [...this.hrVacations(), ...pendingVacations].sort(
88
+ (a, b) => new Date(a.VacationDays.StartDate).getTime() - new Date(b.VacationDays.StartDate).getTime(),
89
+ ),
90
+ );
91
+
92
+ const events: ScheduleCalenderVacation[] =
93
+ this.datasource()?.PendingEventRequests?.map(ev => ({
94
+ VacationType: ScheduleVacationTypes.Events,
95
+ Description: ev.Description,
96
+ Color: this.eventsColor(),
97
+ VacationDays: { StartDate: ev.StartDate, EndDate: ev.EndDate },
98
+ })) ?? [];
99
+
100
+ return [...mergedVacations, ...this.baseCalendarData(), ...events];
101
+ });
102
+
103
+ private readonly refreshCalendarEffect = effect(() => {
104
+ if (!this.calendarLoaded()) {
105
+ return;
106
+ }
107
+
108
+ this.scheduleCalenderVacation();
109
+ this.refreshCalendar();
110
+ });
111
+
112
+ constructor() {
113
+ const currentDate = moment().date(1).subtract(1, 'month');
114
+ this.minDate = new Date(currentDate.year(), currentDate.month(), currentDate.date());
115
+ this.maxDate = new Date(this.minDate.getFullYear() + 1, this.minDate.getMonth() + 1, 0);
116
+ }
117
+
118
+ ngOnInit(): void {
119
+ this.loadCalendar();
120
+ }
121
+
122
+ private getCalendar(month: number, year: number, monthsCount: number) {
123
+ return this.selfWidgetsService.getCalendar(this.baseUrl(), month, year, monthsCount);
124
+ }
125
+
126
+ private loadCalendar(): void {
127
+ const currentDate = moment().date(1).subtract(1, 'month');
128
+ const month = currentDate.month() === 0 ? 1 : currentDate.month() + 1;
129
+ const year = currentDate.year();
130
+
131
+ this.isLoadingChanged.emit(true);
132
+ this.getCalendar(month, year, 12).subscribe({
133
+ next: (response: any) => {
134
+ this.isLoadingChanged.emit(false);
135
+ this.mapCalendarData(response.ResponseData as CalendarResponse);
136
+ },
137
+ error: () => {
138
+ this.isLoadingChanged.emit(false);
139
+ },
140
+ });
141
+ }
142
+
143
+ private mapCalendarData(data: CalendarResponse): void {
144
+ this.baseCalendarData.set([
145
+ ...data.PublicHolidays.map(ph => ({
146
+ VacationType: ScheduleVacationTypes.PublicHoliday,
147
+ Description: ph.Description,
148
+ Color: this.publicHolidayColor(),
149
+ VacationDays: { StartDate: ph.StartDate, EndDate: ph.EndDate },
150
+ })),
151
+ ...data.OffDays.map(od => ({
152
+ VacationType: ScheduleVacationTypes.DaysOff,
153
+ Description: this.translatePipe.transform('DayOff'),
154
+ Color: this.dayOffColor(),
155
+ VacationDays: { StartDate: od, EndDate: od },
156
+ })),
157
+ ]);
158
+
159
+ this.hrVacations.set(
160
+ (data.Vacations ?? []).map(v => ({
161
+ VacationType: ScheduleVacationTypes.Vacations,
162
+ Description: v.VacationTypeName,
163
+ Color: v.Color,
164
+ VacationDays: { StartDate: v.StartDate, EndDate: v.EndDate },
165
+ })),
166
+ );
167
+
168
+ this.calendarLoaded.set(true);
169
+ }
170
+
171
+ private refreshCalendar(): void {
172
+ setTimeout(() => {
173
+ if (this.hrCalendar?.calendar) {
174
+ this.hrCalendar.calendar.instance._refresh();
175
+ this.hrCalendar.resetCalenderCurrentDate();
176
+ }
177
+ }, 100);
178
+ }
179
+
180
+ private mergeConsecutiveVacations(vacations: ScheduleCalenderVacation[]): ScheduleCalenderVacation[] {
181
+ if (vacations.length === 0) return [];
182
+
183
+ const grouped = new Map<string, ScheduleCalenderVacation[]>();
184
+ for (const v of vacations) {
185
+ const key = `${v.Description}|${v.Color ?? ''}`;
186
+ if (!grouped.has(key)) grouped.set(key, []);
187
+ grouped.get(key)!.push(v);
188
+ }
189
+
190
+ const result: ScheduleCalenderVacation[] = [];
191
+
192
+ grouped.forEach(items => {
193
+ items.sort((a, b) => new Date(a.VacationDays.StartDate).getTime() - new Date(b.VacationDays.StartDate).getTime());
194
+
195
+ let merged = { ...items[0], VacationDays: { ...items[0].VacationDays } };
196
+
197
+ for (let i = 1; i < items.length; i++) {
198
+ const current = items[i];
199
+ const lastEnd = new Date(merged.VacationDays.EndDate);
200
+ const currentStart = new Date(current.VacationDays.StartDate);
201
+ const diffDays = (currentStart.getTime() - lastEnd.getTime()) / (1000 * 60 * 60 * 24);
202
+
203
+ if (diffDays <= 1) {
204
+ const currentEnd = new Date(current.VacationDays.EndDate);
205
+ if (currentEnd.getTime() > lastEnd.getTime()) {
206
+ merged.VacationDays.EndDate = current.VacationDays.EndDate;
207
+ }
208
+ } else {
209
+ result.push(merged);
210
+ merged = { ...current, VacationDays: { ...current.VacationDays } };
211
+ }
212
+ }
213
+ result.push(merged);
214
+ });
215
+
216
+ return result.sort(
217
+ (a, b) => new Date(a.VacationDays.StartDate).getTime() - new Date(b.VacationDays.StartDate).getTime(),
218
+ );
219
+ }
220
+ }
@@ -0,0 +1,157 @@
1
+ <div [class]="containerClass()">
2
+ <div [class]="cardBodyClass()">
3
+ <div [class]="headerRowClass()">
4
+ <div>
5
+ @if (employeeMainInfo() != null) {
6
+ <div [class]="employeeInfoContainerClass()">
7
+ <bayan-employee-badge
8
+ [badge]="employeeBadge()"
9
+ [badgeClass]="badgeClass()"
10
+ [imageClass]="badgeImageClass()"
11
+ [width]="badgeWidth()"
12
+ [height]="badgeHeight()"
13
+ (employeePhotoError)="handleEmployeePhotoError()"
14
+ ></bayan-employee-badge>
15
+
16
+ <div [class]="employeeDetailsClass()">
17
+ <div [class]="employeeNameContainerClass()">
18
+ <div
19
+ [class]="employeeNameClass()"
20
+ data-bs-toggle="tooltip"
21
+ [title]="employeeMainInfo()!.EmployeeNumber + ' | ' + employeeMainInfo()!.EmployeeName"
22
+ >
23
+ {{ employeeMainInfo()!.EmployeeNumber + ' | ' + employeeMainInfo()!.EmployeeName }}
24
+ </div>
25
+ </div>
26
+ @if (employeeMainInfo()!.PositionName != null && employeeMainInfo()!.PositionName != '') {
27
+ <div [class]="employeePositionClass()" data-bs-toggle="tooltip" [title]="employeeMainInfo()!.PositionName">
28
+ {{ employeeMainInfo()!.PositionName }}
29
+ </div>
30
+ }
31
+ @if (employeeMainInfo()!.GradeName != null) {
32
+ <div [class]="employeeGradeClass()" [title]="employeeMainInfo()!.GradeName">
33
+ {{ employeeMainInfo()!.GradeName }}
34
+ </div>
35
+ }
36
+ </div>
37
+ </div>
38
+ }
39
+ </div>
40
+ <div [class]="shortcutsContainerClass()">
41
+ @for (Icon of shortcutsWithUrls(); track Icon; let i = $index) {
42
+ <sky-shortcut-icon
43
+ [shortcut]="Icon"
44
+ [itemClass]="shortcutItemClass()"
45
+ [iconContainerClass]="shortcutIconContainerClass()"
46
+ [iconClass]="shortcutIconClass()"
47
+ [textClass]="shortcutTextClass()"
48
+ (onClicked)="shortcutClicked($event)"
49
+ ></sky-shortcut-icon>
50
+ }
51
+ </div>
52
+ </div>
53
+ <div [class]="detailsRowClass()" [ngClass]="{ 'flex-wrap': isMobile() || isTablet() }">
54
+ <div [class]="reportingBlockClass()" [ngClass]="{ 'mb-3': isMobile() }">
55
+ <div [class]="captionLabelClasses() + ' me-5'">
56
+ <div>
57
+ {{ 'ReportingTo' | translate }}
58
+ </div>
59
+ <div [class]="captionLabelValueClass()">
60
+ @if (employeeMainInfo()?.ReportingTo) {
61
+ {{ employeeMainInfo()?.ReportingTo }}
62
+ } @else {
63
+ {{ 'NotAnknown' | translate }}
64
+ }
65
+ </div>
66
+ </div>
67
+ <div [class]="captionLabelClasses()" [ngClass]="{ 'border-end border-1 pe-4': !isMobile() }">
68
+ <div>
69
+ {{ 'ServiceYears' | translate }}
70
+ </div>
71
+ <div [class]="captionLabelValueClass()">
72
+ {{ employeeMainInfo()?.ServiceYears }}
73
+ </div>
74
+ </div>
75
+ </div>
76
+
77
+ <div class="flex-grow-1" [ngClass]="{ ' ms-4': !isMobile() && !isTablet(), 'w-100': isTablet() }">
78
+ <div [class]="vacationBalancesContainerClass()" [ngClass]="{ 'flex-wrap': isMobile(), 'w-100': isTablet() }">
79
+ @for (vacationBalance of employeeMainInfo()?.VacationBalances; track vacationBalance; let i = $index) {
80
+ @if (i <= 2 || employeeMainInfo()?.VacationBalances?.length == 4) {
81
+ <div [class]="vacationBalanceItemClass()" [ngClass]="{ 'mb-2 w-40': isMobile() }">
82
+ <div [class]="vacationBalanceHeaderClass()">
83
+ <div [class]="vacationColorDotClass()">
84
+ <div
85
+ class="rounded rounded-circle"
86
+ style="height: 11px; width: 11px"
87
+ [style.background-color]="vacationBalance.VacationTypeColor"
88
+ ></div>
89
+ </div>
90
+ <div [class]="vacationTypeNameClass()">
91
+ {{ vacationBalance.VacationTypeName }}
92
+ </div>
93
+ </div>
94
+ <div [class]="vacationBalanceValueRowClass()">
95
+ <div [class]="vacationColorDotClass()">
96
+ <div class="rounded rounded-circle invisible" style="height: 11px; width: 11px"></div>
97
+ </div>
98
+ <div [class]="vacationBalanceValueClass()">
99
+ {{ vacationBalance.CurrentBalance | number: '1.2-2' }}
100
+ {{ (vacationBalance.CurrentBalance! == 1 ? 'day' : 'days') | translate }}
101
+ </div>
102
+ </div>
103
+ </div>
104
+ }
105
+ }
106
+
107
+ @if (employeeMainInfo()?.VacationBalances && employeeMainInfo()!.VacationBalances!.length > 4) {
108
+ <div [class]="vacationBalanceItemClass()">
109
+ <div [class]="tooltipTriggerLinkClass()" id="otherVacationBalances">
110
+ <div [class]="vacationColorDotClass()">
111
+ <div
112
+ class="rounded rounded-circle"
113
+ style="height: 11px; width: 11px"
114
+ [style.background-color]="defaultVacationBalanceColor()"
115
+ ></div>
116
+ </div>
117
+ <div [class]="tooltipTriggerTextClass()">
118
+ +{{ employeeMainInfo()!.VacationBalances!.length - 3 }} {{ 'More' | translate }}
119
+ </div>
120
+ </div>
121
+ <div [class]="vacationBalanceValueRowClass()" style="visibility: hidden">
122
+ <div [class]="vacationColorDotClass()">
123
+ <div class="rounded rounded-circle" style="height: 11px; width: 11px"></div>
124
+ </div>
125
+ <div class="mx-1 fw-medium fs-14 text-dark-gray">{{ 'More' | translate }}</div>
126
+ </div>
127
+ <dx-tooltip [target]="'#otherVacationBalances'" [showEvent]="'mouseenter'" [hideEvent]="'mouseleave'">
128
+ @for (vacationBalance of employeeMainInfo()!.VacationBalances!; track vacationBalance; let i = $index) {
129
+ @if (i > 2) {
130
+ <div [class]="tooltipVacationRowClass()">
131
+ <div [class]="tooltipVacationNameContainerClass()">
132
+ <div [class]="vacationColorDotClass()">
133
+ <div
134
+ class="rounded rounded-circle"
135
+ style="height: 11px; width: 11px"
136
+ [style.background-color]="vacationBalance.VacationTypeColor"
137
+ ></div>
138
+ </div>
139
+ <div [class]="tooltipVacationNameClass()">
140
+ {{ vacationBalance.VacationTypeName }}
141
+ </div>
142
+ </div>
143
+ <div>
144
+ {{ vacationBalance.CurrentBalance | number: '1.2-2' }}
145
+ {{ (vacationBalance.CurrentBalance! == 1 ? 'day' : 'days') | translate }}
146
+ </div>
147
+ </div>
148
+ }
149
+ }
150
+ </dx-tooltip>
151
+ </div>
152
+ }
153
+ </div>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
@@ -0,0 +1,179 @@
1
+ import { ChangeDetectionStrategy, Component, OnInit, computed, inject, input, output, signal } from '@angular/core';
2
+ import { DxTooltipModule } from 'devextreme-angular';
3
+ import { DecimalPipe, NgClass } from '@angular/common';
4
+ import { HRSelfWidgetsService } from '../services/hr-self-widgets.service';
5
+ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
6
+ import { BayanEmployeeBadgeComponent, BayanEmployeeBadgeDTO } from '@skysoftware-co/bayan-components-ui';
7
+ import { SkyShortcutIconComponent, ShortcutIconDTO } from '@skysoftware-co/sky-components-ui';
8
+ import { MyMainDetailsWidgetDetails, MainWidgetShortcut } from '../shared/types/common';
9
+ import { HRConstantsService } from '../shared/services/constants.service';
10
+ import { HRTranslatePipe } from '../shared/pipes/translate.pipe';
11
+
12
+ @Component({
13
+ selector: 'hr-my-main-details-widget',
14
+ standalone: true,
15
+ imports: [
16
+ DxTooltipModule,
17
+ NgClass,
18
+ FontAwesomeModule,
19
+ DecimalPipe,
20
+ BayanEmployeeBadgeComponent,
21
+ SkyShortcutIconComponent,
22
+ HRTranslatePipe,
23
+ ],
24
+ providers: [HRTranslatePipe],
25
+ templateUrl: './my-main-details-widget.component.html',
26
+ changeDetection: ChangeDetectionStrategy.OnPush,
27
+ })
28
+ export class MyMainDetailsWidgetComponent implements OnInit {
29
+ private readonly selfWidgetsService = inject(HRSelfWidgetsService);
30
+ private readonly constants = inject(HRConstantsService);
31
+ private readonly translatePipe = inject(HRTranslatePipe);
32
+
33
+ readonly employeeMainInfo = signal<MyMainDetailsWidgetDetails | null>(null);
34
+
35
+ readonly onShortcutClicked = output<ShortcutIconDTO>();
36
+ readonly isLoadingChanged = output<boolean>();
37
+
38
+ readonly badgeClass = input<string>('badge employee-rounded-badge-xl fs-5');
39
+ readonly badgeImageClass = input<string>('rounded-circle shadow-lg');
40
+ readonly badgeWidth = input<string>('50px');
41
+ readonly badgeHeight = input<string>('50px');
42
+ readonly employeeInfoContainerClass = input<string>('d-flex align-content-center');
43
+ readonly employeeDetailsClass = input<string>('ms-3');
44
+ readonly employeeNameContainerClass = input<string>('d-flex align-items-center');
45
+ readonly employeeNameClass = input<string>('fs-14 text-dark-gray fw-medium text-ellipsis-one-line');
46
+ readonly employeePositionClass = input<string>('fs-12 fw-normal text-dark-gray text-ellipsis-one-line mb-1');
47
+ readonly employeeGradeClass = input<string>('fs-12 text-dark-gray');
48
+
49
+ readonly containerClass = input<string>('card rounded rounded-4 card-shadow p-16 border-top-0 mb-4');
50
+ readonly cardBodyClass = input<string>('card-body p-4');
51
+ readonly headerRowClass = input<string>('d-md-flex justify-content-between align-items-center mt-2 mb-3');
52
+
53
+ readonly shortcutsContainerClass = input<string>(
54
+ 'd-flex justify-content-md-end justify-content-sm-start fs-12 fw-normal text-secondary mt-3 mt-md-0',
55
+ );
56
+ readonly shortcutItemClass = input<string>('d-flex flex-column align-items-center mx-2 wp-60');
57
+ readonly shortcutIconContainerClass = input<string>(
58
+ 'd-flex justify-content-center align-items-center rounded-circle-40 cursor-pointer',
59
+ );
60
+ readonly shortcutIconClass = input<string>('fs-4');
61
+ readonly shortcutTextClass = input<string>('text-center mt-1');
62
+
63
+ readonly detailsRowClass = input<string>('d-flex mt-4');
64
+
65
+ readonly reportingBlockClass = input<string>('d-flex text-secondary align-items-center mb-2 mx-2');
66
+ readonly captionLabelClasses = input<string>('d-flex flex-column align-self-start fs-14 text-dark-gray');
67
+ readonly captionLabelValueClass = input<string>('fw-medium mt-2');
68
+
69
+ readonly vacationBalancesContainerClass = input<string>('d-flex text-secondary align-items-center mb-2');
70
+ readonly vacationBalanceItemClass = input<string>('d-flex flex-column me-4');
71
+ readonly vacationBalanceHeaderClass = input<string>('d-flex align-items-center');
72
+ readonly vacationColorDotClass = input<string>('vacation-color-container-sm');
73
+ readonly vacationTypeNameClass = input<string>('mx-2 fs-14 fw-normal text-dark-gray');
74
+ readonly vacationBalanceValueRowClass = input<string>('d-flex mt-2');
75
+ readonly vacationBalanceValueClass = input<string>('mx-2 fw-medium fs-14 text-dark-gray');
76
+
77
+ readonly tooltipTriggerLinkClass = input<string>('d-flex align-items-center cursor-pointer');
78
+ readonly tooltipTriggerTextClass = input<string>('mx-2 fs-14 text-dark-gray');
79
+
80
+ readonly tooltipVacationRowClass = input<string>('d-flex align-items-center justify-content-around fs-12 mb-2');
81
+ readonly tooltipVacationNameContainerClass = input<string>('d-flex flex-grow-1 align-items-center me-5');
82
+ readonly tooltipVacationNameClass = input<string>('mx-2');
83
+
84
+ readonly defaultVacationBalanceColor = input<string>(this.constants.defaultVacationBalanceColor);
85
+
86
+ readonly defaultVacationColor = input<string>(this.constants.defaultVacationColor);
87
+
88
+ readonly baseUrl = input<string>('');
89
+
90
+ readonly employeeBadge = signal<BayanEmployeeBadgeDTO>({
91
+ EmployeePhotoKey: null,
92
+ EmployeePhotoFailed: false,
93
+ EmployeeNameInitials: '',
94
+ });
95
+
96
+ readonly isMobile = input<boolean>(false);
97
+ readonly isTablet = input<boolean>(false);
98
+ readonly shortcuts = input<ShortcutIconDTO[]>([]);
99
+
100
+ readonly shortcutsWithUrls = computed<ShortcutIconDTO[]>(() => {
101
+ const employeeMainInfo = this.employeeMainInfo();
102
+ return this.shortcuts().map(item => {
103
+ const translated: ShortcutIconDTO = {
104
+ ...item,
105
+ title: this.translatePipe.transform(item.title),
106
+ };
107
+
108
+ if (translated.shortcutId === MainWidgetShortcut.Attendance && employeeMainInfo?.AttendanceUrl != null) {
109
+ return { ...translated, navigateUrl: [employeeMainInfo.AttendanceUrl] };
110
+ }
111
+
112
+ if (
113
+ translated.shortcutId === MainWidgetShortcut.VacationStatement &&
114
+ employeeMainInfo?.VacationStatementUrl != null
115
+ ) {
116
+ return { ...translated, navigateUrl: [employeeMainInfo.VacationStatementUrl] };
117
+ }
118
+
119
+ return translated;
120
+ });
121
+ });
122
+
123
+ ngOnInit(): void {
124
+ this.loadEmployeeMainInfo();
125
+ }
126
+
127
+ loadEmployeeMainInfo() {
128
+ this.isLoadingChanged.emit(true);
129
+
130
+ this.getEmployeeMainDetails().subscribe({
131
+ next: (data: any) => {
132
+ const responses = data.ResponseData;
133
+ const vacationBalances = responses.VacationBalances.map((v: any) => ({
134
+ ...v,
135
+ VacationTypeColor: v.VacationTypeColor ?? this.defaultVacationColor(),
136
+ }));
137
+
138
+ const employeeMainInfo: MyMainDetailsWidgetDetails = {
139
+ EmployeeNumber: responses.EmployeeNumber,
140
+ EmployeeName: responses.EmployeeName,
141
+ EmployeeNameInitials: responses.EmployeeNameInitials,
142
+ PositionName: responses.PositionName,
143
+ GradeName: responses.GradeName,
144
+ PhotoPath: responses.PhotoPath,
145
+ ReportingTo: responses.ReportingToEmployeeName,
146
+ ServiceYears: `${responses.ServicePeriod.Years} ${this.translatePipe.transform('Years')} ${responses.ServicePeriod.Months} ${this.translatePipe.transform('Months')}`,
147
+ VacationBalances: vacationBalances,
148
+ AttendanceUrl: responses.AttendanceReportUrl,
149
+ VacationStatementUrl: responses.VacationStatementReportUrl,
150
+ };
151
+ this.employeeMainInfo.set(employeeMainInfo);
152
+
153
+ this.employeeBadge.set({
154
+ EmployeePhotoKey: employeeMainInfo.PhotoPath || null,
155
+ EmployeeNameInitials: employeeMainInfo.EmployeeNameInitials || '',
156
+ EmployeePhotoFailed: false,
157
+ });
158
+
159
+ this.isLoadingChanged.emit(false);
160
+ },
161
+ error: (error: any) => {
162
+ this.isLoadingChanged.emit(false);
163
+ if (error.status == 401) return;
164
+ },
165
+ });
166
+ }
167
+
168
+ shortcutClicked(icon: ShortcutIconDTO) {
169
+ this.onShortcutClicked.emit(icon);
170
+ }
171
+
172
+ handleEmployeePhotoError() {
173
+ this.employeeBadge.update(badge => ({ ...badge, EmployeePhotoFailed: true }));
174
+ }
175
+
176
+ getEmployeeMainDetails() {
177
+ return this.selfWidgetsService.getMainDetails(this.baseUrl());
178
+ }
179
+ }
@@ -0,0 +1,131 @@
1
+ <dx-popup
2
+ [visible]="popupVisible()"
3
+ [height]="height()"
4
+ [minHeight]="'40%'"
5
+ [maxHeight]="'70%'"
6
+ [width]="width()"
7
+ contentTemplate="popup-content"
8
+ [showCloseButton]="false"
9
+ shadingColor="rgba(0,0,0,0.4)"
10
+ titleTemplate="title"
11
+ [showTitle]="true"
12
+ (onHidden)="onHidden()"
13
+ >
14
+ <div *dxTemplate="let data of 'title'">
15
+ <div class="d-flex justify-content-between align-items-center my-0">
16
+ <p class="fs-16 fw-meduim text-ellipsis-one-line">
17
+ <span class="text-dark-gray">{{ popupTitle ? (popupTitle | translate) : ('NextWeekVacationInsights' | translate) }}</span>
18
+ <span class="mx-2 text-primary">({{ getPopupTitle() | translate }})</span>
19
+ </p>
20
+ <div class="py-2" (click)="onPopupClose()">
21
+ <fa-icon [icon]="xMarkIcon" class="fs-18 text-dark-gray cursor-pointer"></fa-icon>
22
+ </div>
23
+ </div>
24
+ </div>
25
+
26
+ <div *dxTemplate="let d of 'popup-content'">
27
+ <div class="mb-2 d-flex justify-content-end">
28
+ <dx-text-box
29
+ [width]="'250px'"
30
+ mode="search"
31
+ [value]="searchValue()"
32
+ [placeholder]="'Search...' | translate"
33
+ (onValueChanged)="onSearchValueChanged($event)"
34
+ ></dx-text-box>
35
+ </div>
36
+ @if (dataSource()) {
37
+ <dx-data-grid
38
+ class="popup-data-grid"
39
+ [dataSource]="dataSource()"
40
+ [showBorders]="false"
41
+ [showRowLines]="false"
42
+ [showColumnLines]="false"
43
+ [columnAutoWidth]="true"
44
+ [wordWrapEnabled]="true"
45
+ [height]="gridHeight()"
46
+ [allowColumnReordering]="false"
47
+ [remoteOperations]="{ paging: true }"
48
+ >
49
+ <dxo-data-grid-sorting mode="none"></dxo-data-grid-sorting>
50
+
51
+ <dxo-data-grid-scrolling
52
+ mode="infinite"
53
+ [useNative]="false"
54
+ [scrollByContent]="true"
55
+ [scrollByThumb]="true"
56
+ showScrollbar="always"
57
+ ></dxo-data-grid-scrolling>
58
+
59
+ <dxo-data-grid-paging [enabled]="true" [pageSize]="pageSize()"></dxo-data-grid-paging>
60
+
61
+ <dxi-data-grid-column
62
+ caption="{{ 'Employee' | translate }}"
63
+ dataField="EmployeeName"
64
+ cellTemplate="employeeCellTemplate"
65
+ [minWidth]="250"
66
+ ></dxi-data-grid-column>
67
+
68
+ @if (popupType() === VacationPopupType.Upcoming) {
69
+ <dxi-data-grid-column
70
+ caption="{{ 'StartDate' | translate }}"
71
+ dataField="VacationStartDate"
72
+ dataType="date"
73
+ format="dd/MM/yyyy"
74
+ [width]="120"
75
+ alignment="left"
76
+ ></dxi-data-grid-column>
77
+ <dxi-data-grid-column
78
+ caption="{{ 'EndDate' | translate }}"
79
+ dataField="VacationEndDate"
80
+ dataType="date"
81
+ format="dd/MM/yyyy"
82
+ [width]="120"
83
+ alignment="left"
84
+ ></dxi-data-grid-column>
85
+ } @else {
86
+ <dxi-data-grid-column
87
+ caption="{{ 'ReturnDate' | translate }}"
88
+ dataField="ReturnDate"
89
+ dataType="date"
90
+ format="dd/MM/yyyy"
91
+ [width]="120"
92
+ alignment="left"
93
+ ></dxi-data-grid-column>
94
+ }
95
+
96
+ <div *dxTemplate="let cell of 'employeeCellTemplate'">
97
+ <div class="d-flex align-items-center">
98
+ <bayan-employee-badge
99
+ [badge]="getEmployeeBadge(cell.data)"
100
+ badgeClass="badge employee-rounded-badge-xl fs-6"
101
+ imageClass="rounded-circle"
102
+ [width]="'40px'"
103
+ [height]="'40px'"
104
+ (employeePhotoError)="handleEmployeePhotoError(cell.data)"
105
+ ></bayan-employee-badge>
106
+ <div class="d-flex flex-column ms-1">
107
+ <div class="d-flex flex-row ms-2">
108
+ @if (showProperty()) {
109
+ <span
110
+ class="property-badge cursor-pointer me-1"
111
+ [title]="cell.data.PropertyName"
112
+ [innerHTML]="cell.data.PropertySymbol"
113
+ ></span>
114
+ }
115
+ <span
116
+ class="text-ellipsis-one-line fs-14 text-dark-gray fw-normal"
117
+ [title]="cell.data.EmployeeNumber + ' | ' + cell.data.EmployeeName"
118
+ [innerHTML]="cell.data.EmployeeNumber + ' | ' + cell.data.EmployeeName | highlight: searchValue()"
119
+ ></span>
120
+ </div>
121
+ <p
122
+ class="ms-2 mt-1 text-ellipsis-one-line fs-12 text-dark-gray fw-normal mb-0"
123
+ [innerHTML]="cell.data.EmployeeAdminUnitName | highlight: searchValue()"
124
+ ></p>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ </dx-data-grid>
129
+ }
130
+ </div>
131
+ </dx-popup>