tango-app-ui-manage-tickets 3.7.0-beta.60 → 3.7.0-beta.62

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 (177) hide show
  1. package/esm2022/lib/components/add-csm-modal/add-csm-modal.component.mjs +98 -0
  2. package/esm2022/lib/components/audit-log/audit-log.component.mjs +11 -0
  3. package/esm2022/lib/components/audit-mapping-list/audit-mapping-list.component.mjs +486 -0
  4. package/esm2022/lib/components/audit-metrics/audit-metrics.component.mjs +298 -0
  5. package/esm2022/lib/components/audit-report-popup/audit-report-popup.component.mjs +389 -0
  6. package/esm2022/lib/components/audit-retag/audit-retag.component.mjs +472 -0
  7. package/esm2022/lib/components/comment-model/comment-model.component.mjs +58 -0
  8. package/esm2022/lib/components/count/count.component.mjs +89 -0
  9. package/esm2022/lib/components/custom-select/custom-select.component.mjs +187 -0
  10. package/esm2022/lib/components/filter-options/filter-options.component.mjs +41 -0
  11. package/esm2022/lib/components/footfall-dic/footfall-dic.component.mjs +1061 -0
  12. package/esm2022/lib/components/footfall-dicview/footfall-dicview.component.mjs +1014 -0
  13. package/esm2022/lib/components/footfall-popup/footfall-popup.component.mjs +15 -0
  14. package/esm2022/lib/components/group-select/group-select.component.mjs +155 -0
  15. package/esm2022/lib/components/re-trigger/re-trigger.component.mjs +96 -0
  16. package/esm2022/lib/components/reactive-select/reactive-select.component.mjs +108 -0
  17. package/esm2022/lib/components/remove-audit/remove-audit.component.mjs +81 -0
  18. package/esm2022/lib/components/start-audit/start-audit.component.mjs +761 -0
  19. package/esm2022/lib/components/tango-manage-tickets/tango-manage-tickets.component.mjs +131 -0
  20. package/esm2022/lib/components/ticket-filter-panel/ticket-filter-panel.component.mjs +435 -0
  21. package/esm2022/lib/components/ticket-footfall-new/ticket-footfall-new.component.mjs +2748 -0
  22. package/esm2022/lib/components/ticketclosepopup/ticketclosepopup.component.mjs +43 -0
  23. package/esm2022/lib/components/tickets/tickets.component.mjs +847 -0
  24. package/esm2022/lib/components/viewcategory/viewcategory.component.mjs +89 -0
  25. package/esm2022/lib/services/audit.service.mjs +88 -0
  26. package/esm2022/lib/services/excel.service.mjs +45 -0
  27. package/esm2022/lib/services/ticket.service.mjs +319 -0
  28. package/esm2022/lib/services/timer.service.mjs +84 -0
  29. package/esm2022/lib/tango-manage-tickets-routing.module.mjs +44 -0
  30. package/esm2022/lib/tango-manage-tickets.module.mjs +109 -0
  31. package/esm2022/public-api.mjs +6 -0
  32. package/esm2022/tango-app-ui-manage-tickets.mjs +5 -0
  33. package/fesm2022/tango-app-ui-manage-tickets.mjs +10165 -0
  34. package/fesm2022/tango-app-ui-manage-tickets.mjs.map +1 -0
  35. package/index.d.ts +5 -0
  36. package/lib/components/add-csm-modal/add-csm-modal.component.d.ts +30 -0
  37. package/lib/components/audit-log/audit-log.component.d.ts +5 -0
  38. package/lib/components/audit-mapping-list/audit-mapping-list.component.d.ts +73 -0
  39. package/lib/components/audit-metrics/audit-metrics.component.d.ts +59 -0
  40. package/lib/components/audit-report-popup/audit-report-popup.component.d.ts +52 -0
  41. package/lib/components/audit-retag/audit-retag.component.d.ts +59 -0
  42. package/lib/components/comment-model/comment-model.component.d.ts +17 -0
  43. package/lib/components/count/count.component.d.ts +23 -0
  44. package/lib/components/custom-select/custom-select.component.d.ts +35 -0
  45. package/lib/components/filter-options/filter-options.component.d.ts +15 -0
  46. package/lib/components/footfall-dic/footfall-dic.component.d.ts +143 -0
  47. package/lib/components/footfall-dicview/footfall-dicview.component.d.ts +132 -0
  48. package/lib/components/footfall-popup/footfall-popup.component.d.ts +8 -0
  49. package/lib/components/group-select/group-select.component.d.ts +33 -0
  50. package/lib/components/re-trigger/re-trigger.component.d.ts +32 -0
  51. package/lib/components/reactive-select/reactive-select.component.d.ts +32 -0
  52. package/lib/components/remove-audit/remove-audit.component.d.ts +16 -0
  53. package/lib/components/start-audit/start-audit.component.d.ts +86 -0
  54. package/lib/components/tango-manage-tickets/tango-manage-tickets.component.d.ts +28 -0
  55. package/lib/components/ticket-filter-panel/ticket-filter-panel.component.d.ts +79 -0
  56. package/lib/components/ticket-footfall-new/ticket-footfall-new.component.d.ts +287 -0
  57. package/lib/components/ticketclosepopup/ticketclosepopup.component.d.ts +15 -0
  58. package/lib/components/tickets/tickets.component.d.ts +88 -0
  59. package/lib/components/viewcategory/viewcategory.component.d.ts +16 -0
  60. package/lib/services/audit.service.d.ts +36 -0
  61. package/lib/services/excel.service.d.ts +10 -0
  62. package/lib/services/ticket.service.d.ts +87 -0
  63. package/lib/services/timer.service.d.ts +22 -0
  64. package/lib/tango-manage-tickets-routing.module.d.ts +7 -0
  65. package/lib/tango-manage-tickets.module.d.ts +38 -0
  66. package/package.json +25 -12
  67. package/{src/public-api.ts → public-api.d.ts} +2 -8
  68. package/.eslintrc.json +0 -37
  69. package/ng-package.json +0 -7
  70. package/src/lib/components/add-csm-modal/add-csm-modal.component.html +0 -32
  71. package/src/lib/components/add-csm-modal/add-csm-modal.component.scss +0 -14
  72. package/src/lib/components/add-csm-modal/add-csm-modal.component.spec.ts +0 -23
  73. package/src/lib/components/add-csm-modal/add-csm-modal.component.ts +0 -94
  74. package/src/lib/components/audit-log/audit-log.component.html +0 -1
  75. package/src/lib/components/audit-log/audit-log.component.scss +0 -0
  76. package/src/lib/components/audit-log/audit-log.component.spec.ts +0 -23
  77. package/src/lib/components/audit-log/audit-log.component.ts +0 -10
  78. package/src/lib/components/audit-mapping-list/audit-mapping-list.component.html +0 -234
  79. package/src/lib/components/audit-mapping-list/audit-mapping-list.component.scss +0 -186
  80. package/src/lib/components/audit-mapping-list/audit-mapping-list.component.spec.ts +0 -23
  81. package/src/lib/components/audit-mapping-list/audit-mapping-list.component.ts +0 -520
  82. package/src/lib/components/audit-metrics/audit-metrics.component.html +0 -345
  83. package/src/lib/components/audit-metrics/audit-metrics.component.scss +0 -34
  84. package/src/lib/components/audit-metrics/audit-metrics.component.spec.ts +0 -23
  85. package/src/lib/components/audit-metrics/audit-metrics.component.ts +0 -292
  86. package/src/lib/components/audit-report-popup/audit-report-popup.component.html +0 -111
  87. package/src/lib/components/audit-report-popup/audit-report-popup.component.scss +0 -101
  88. package/src/lib/components/audit-report-popup/audit-report-popup.component.spec.ts +0 -23
  89. package/src/lib/components/audit-report-popup/audit-report-popup.component.ts +0 -397
  90. package/src/lib/components/audit-retag/audit-retag.component.html +0 -129
  91. package/src/lib/components/audit-retag/audit-retag.component.scss +0 -146
  92. package/src/lib/components/audit-retag/audit-retag.component.spec.ts +0 -23
  93. package/src/lib/components/audit-retag/audit-retag.component.ts +0 -489
  94. package/src/lib/components/comment-model/comment-model.component.html +0 -24
  95. package/src/lib/components/comment-model/comment-model.component.scss +0 -20
  96. package/src/lib/components/comment-model/comment-model.component.spec.ts +0 -23
  97. package/src/lib/components/comment-model/comment-model.component.ts +0 -53
  98. package/src/lib/components/count/count.component.html +0 -54
  99. package/src/lib/components/count/count.component.scss +0 -14
  100. package/src/lib/components/count/count.component.spec.ts +0 -23
  101. package/src/lib/components/count/count.component.ts +0 -82
  102. package/src/lib/components/custom-select/custom-select.component.html +0 -134
  103. package/src/lib/components/custom-select/custom-select.component.scss +0 -204
  104. package/src/lib/components/custom-select/custom-select.component.spec.ts +0 -23
  105. package/src/lib/components/custom-select/custom-select.component.ts +0 -189
  106. package/src/lib/components/filter-options/filter-options.component.html +0 -51
  107. package/src/lib/components/filter-options/filter-options.component.scss +0 -102
  108. package/src/lib/components/filter-options/filter-options.component.spec.ts +0 -23
  109. package/src/lib/components/filter-options/filter-options.component.ts +0 -38
  110. package/src/lib/components/footfall-dic/footfall-dic.component.html +0 -1275
  111. package/src/lib/components/footfall-dic/footfall-dic.component.scss +0 -273
  112. package/src/lib/components/footfall-dic/footfall-dic.component.spec.ts +0 -23
  113. package/src/lib/components/footfall-dic/footfall-dic.component.ts +0 -1206
  114. package/src/lib/components/footfall-dicview/footfall-dicview.component.html +0 -1136
  115. package/src/lib/components/footfall-dicview/footfall-dicview.component.scss +0 -416
  116. package/src/lib/components/footfall-dicview/footfall-dicview.component.spec.ts +0 -23
  117. package/src/lib/components/footfall-dicview/footfall-dicview.component.ts +0 -1168
  118. package/src/lib/components/footfall-popup/footfall-popup.component.html +0 -61
  119. package/src/lib/components/footfall-popup/footfall-popup.component.scss +0 -20
  120. package/src/lib/components/footfall-popup/footfall-popup.component.spec.ts +0 -23
  121. package/src/lib/components/footfall-popup/footfall-popup.component.ts +0 -12
  122. package/src/lib/components/group-select/group-select.component.html +0 -44
  123. package/src/lib/components/group-select/group-select.component.scss +0 -144
  124. package/src/lib/components/group-select/group-select.component.spec.ts +0 -23
  125. package/src/lib/components/group-select/group-select.component.ts +0 -145
  126. package/src/lib/components/re-trigger/re-trigger.component.html +0 -53
  127. package/src/lib/components/re-trigger/re-trigger.component.scss +0 -16
  128. package/src/lib/components/re-trigger/re-trigger.component.spec.ts +0 -23
  129. package/src/lib/components/re-trigger/re-trigger.component.ts +0 -96
  130. package/src/lib/components/reactive-select/reactive-select.component.html +0 -18
  131. package/src/lib/components/reactive-select/reactive-select.component.scss +0 -52
  132. package/src/lib/components/reactive-select/reactive-select.component.spec.ts +0 -23
  133. package/src/lib/components/reactive-select/reactive-select.component.ts +0 -104
  134. package/src/lib/components/remove-audit/remove-audit.component.html +0 -38
  135. package/src/lib/components/remove-audit/remove-audit.component.scss +0 -27
  136. package/src/lib/components/remove-audit/remove-audit.component.spec.ts +0 -23
  137. package/src/lib/components/remove-audit/remove-audit.component.ts +0 -81
  138. package/src/lib/components/start-audit/start-audit.component.html +0 -174
  139. package/src/lib/components/start-audit/start-audit.component.scss +0 -185
  140. package/src/lib/components/start-audit/start-audit.component.spec.ts +0 -23
  141. package/src/lib/components/start-audit/start-audit.component.ts +0 -761
  142. package/src/lib/components/tango-manage-tickets/tango-manage-tickets.component.html +0 -43
  143. package/src/lib/components/tango-manage-tickets/tango-manage-tickets.component.scss +0 -35
  144. package/src/lib/components/tango-manage-tickets/tango-manage-tickets.component.spec.ts +0 -23
  145. package/src/lib/components/tango-manage-tickets/tango-manage-tickets.component.ts +0 -118
  146. package/src/lib/components/ticket-filter-panel/ticket-filter-panel.component.html +0 -386
  147. package/src/lib/components/ticket-filter-panel/ticket-filter-panel.component.scss +0 -87
  148. package/src/lib/components/ticket-filter-panel/ticket-filter-panel.component.spec.ts +0 -23
  149. package/src/lib/components/ticket-filter-panel/ticket-filter-panel.component.ts +0 -494
  150. package/src/lib/components/ticket-footfall-new/ticket-footfall-new.component.html +0 -4743
  151. package/src/lib/components/ticket-footfall-new/ticket-footfall-new.component.scss +0 -1208
  152. package/src/lib/components/ticket-footfall-new/ticket-footfall-new.component.spec.ts +0 -23
  153. package/src/lib/components/ticket-footfall-new/ticket-footfall-new.component.ts +0 -3351
  154. package/src/lib/components/ticketclosepopup/ticketclosepopup.component.html +0 -100
  155. package/src/lib/components/ticketclosepopup/ticketclosepopup.component.scss +0 -34
  156. package/src/lib/components/ticketclosepopup/ticketclosepopup.component.spec.ts +0 -23
  157. package/src/lib/components/ticketclosepopup/ticketclosepopup.component.ts +0 -48
  158. package/src/lib/components/tickets/tickets.component.html +0 -451
  159. package/src/lib/components/tickets/tickets.component.scss +0 -131
  160. package/src/lib/components/tickets/tickets.component.spec.ts +0 -23
  161. package/src/lib/components/tickets/tickets.component.ts +0 -809
  162. package/src/lib/components/viewcategory/viewcategory.component.html +0 -38
  163. package/src/lib/components/viewcategory/viewcategory.component.scss +0 -29
  164. package/src/lib/components/viewcategory/viewcategory.component.spec.ts +0 -23
  165. package/src/lib/components/viewcategory/viewcategory.component.ts +0 -79
  166. package/src/lib/services/audit.service.spec.ts +0 -16
  167. package/src/lib/services/audit.service.ts +0 -98
  168. package/src/lib/services/excel.service.ts +0 -48
  169. package/src/lib/services/ticket.service.spec.ts +0 -16
  170. package/src/lib/services/ticket.service.ts +0 -501
  171. package/src/lib/services/timer.service.spec.ts +0 -16
  172. package/src/lib/services/timer.service.ts +0 -92
  173. package/src/lib/tango-manage-tickets-routing.module.ts +0 -37
  174. package/src/lib/tango-manage-tickets.module.ts +0 -68
  175. package/tsconfig.lib.json +0 -14
  176. package/tsconfig.lib.prod.json +0 -10
  177. package/tsconfig.spec.json +0 -14
@@ -1,3351 +0,0 @@
1
- import {
2
- Component,
3
- AfterViewInit,
4
- OnDestroy,
5
- OnInit,
6
- ChangeDetectorRef,
7
- EventEmitter,
8
- Output,
9
- HostListener,
10
- ElementRef,
11
- ViewChild,
12
- } from "@angular/core";
13
- import { FootfallPopupComponent } from "../footfall-popup/footfall-popup.component";
14
- import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
15
- import { GlobalStateService } from "tango-app-ui-global";
16
- import { Subject, takeUntil } from "rxjs";
17
- import { TicketService } from "../../services/ticket.service";
18
- import { ExcelService } from "../../services/excel.service";
19
- import { ToastService } from "tango-app-ui-shared";
20
- import { FormBuilder, FormGroup } from "@angular/forms";
21
- import { Router } from "@angular/router";
22
- import 'dayjs/locale/en';
23
- import utc from 'dayjs/plugin/utc';
24
- import timezone from 'dayjs/plugin/timezone';
25
- dayjs.extend(utc);
26
- dayjs.extend(timezone)
27
- import { MatTooltip } from '@angular/material/tooltip';
28
- import dayjs from 'dayjs';
29
- import customParseFormat from 'dayjs/plugin/customParseFormat';
30
- import { TicketclosepopupComponent } from "../ticketclosepopup/ticketclosepopup.component";
31
- import { AuditService } from "../../services/audit.service";
32
- dayjs.extend(customParseFormat)
33
-
34
- @Component({
35
- selector: "lib-ticket-footfall-new",
36
- templateUrl: "./ticket-footfall-new.component.html",
37
- styleUrl: "./ticket-footfall-new.component.scss",
38
- })
39
- export class TicketFootfallNewComponent implements OnInit, OnDestroy {
40
- searchValue: any = "";
41
- sortedColumn: string = "";
42
- sortDirection: number = 1;
43
- private readonly destroy$ = new Subject();
44
- constructor(
45
- private modalService: NgbModal,
46
- public gs: GlobalStateService,
47
- private service: TicketService,
48
- private cd: ChangeDetectorRef,
49
- private excelService: ExcelService,
50
- private ts: ToastService,
51
- private fb: FormBuilder,
52
- private router: Router,
53
- private auditservice: AuditService,
54
- ) { }
55
- headerFilters: any;
56
- footfallList_req: any;
57
- usersDetails: any;
58
- selectedStore: any;
59
- selectedReviewer: any;
60
- storeList: any = []
61
- dayjs = dayjs
62
- selectedDateRange: any = {};
63
- arrowshow = true;
64
- accuracyList: any = []
65
- selectedIssue: any = ''
66
- selectedsubIssue: any = ''
67
- isCustomDate = (m: dayjs.Dayjs) => {
68
- const isValidDate = m > this.dayjs();
69
- return isValidDate ? 'invalid-date' : false;
70
- }
71
- @ViewChild('tooltip') tooltip: MatTooltip;
72
- footfallcount: any = 0
73
- @ViewChild('internalticket') internalticket: any;
74
- @ViewChild('closeacuuracyissue') closeacuuracyissue: any;
75
- clientData: any;
76
- ngOnInit(): void {
77
- let tickettype = sessionStorage.getItem('ticketType')
78
- this.tangoType = tickettype
79
- sessionStorage.clear();
80
- this.imageUrl = this.service?.footfallCDN;
81
- let clientdata: any = localStorage.getItem("footfall-config");
82
- this.clientData = JSON.parse(clientdata)
83
- let userData: any = localStorage.getItem("user-info");
84
- this.usersDetails = JSON.parse(userData);
85
-
86
- this.gs.dataRangeValue?.pipe(takeUntil(this.destroy$))?.subscribe({
87
- next: (data: any) => {
88
- if (data) {
89
- this.headerFilters = data;
90
- this.footfallList_req = {
91
- client: this.headerFilters.clients.toString(),
92
- fromDate: this.headerFilters?.date?.startDate,
93
- toDate: this.headerFilters?.date?.endDate,
94
- };
95
- if (!this.selectedRole) {
96
- if (this.hasApproverAccess) {
97
- this.selectedRole = 'approver';
98
- } else if (this.hasReviewerAccess) {
99
- this.selectedRole = 'reviewer';
100
- }
101
- }
102
- this.viewTicket('store');
103
- let payload = {
104
- clientId: this.headerFilters.clients
105
- }
106
- const yesterday = this.dayjs().subtract(1, "days");
107
- const formattedDate = {
108
- startDate: yesterday.format("YYYY-MM-DD"),
109
- endDate: yesterday.format("YYYY-MM-DD"),
110
- };
111
- this.selectedDateRange = {
112
- startDate: yesterday.format("DD-MM-YYYY"),
113
- endDate: yesterday.format("DD-MM-YYYY"),
114
- };
115
- this.service.getstoreList(payload).subscribe({
116
- next: (e: any) => {
117
- if (e) {
118
- this.storeList = e.data.result;
119
- }
120
- }
121
- })
122
- }
123
- },
124
- });
125
- }
126
- viewTicket(type: any) {
127
-
128
- this.offset = 1;
129
- this.limit = 10;
130
- this.tangoType = type;
131
- this.getTicketSummary(type);
132
- this.select = "ticketList";
133
- if (this.usersDetails?.userType !== 'tango') {
134
- if (this.hasApproverAccess && this.selectedRole === 'approver') {
135
- this.SelectedRole('approver');
136
- return;
137
- }
138
-
139
- if (this.hasReviewerAccess && this.selectedRole === 'reviewer') {
140
- this.SelectedRole('reviewer');
141
- return;
142
- }
143
- }
144
- this.getTicketList(type);
145
-
146
-
147
- }
148
-
149
- private areAllItemsReviewed(mapping: any): boolean {
150
- const revised = mapping?.revisedDetail || [];
151
- if (!revised.length) return false;
152
-
153
- const itemsToCheck: any[] = [];
154
-
155
- revised.forEach((detail: any) => {
156
- // If duplicate parent → check its duplicateImage children
157
- if (
158
- detail?.revopsType === 'duplicate' &&
159
- detail?.isParent &&
160
- Array.isArray(detail?.duplicateImage) &&
161
- detail.duplicateImage.length
162
- ) {
163
- itemsToCheck.push(...detail.duplicateImage);
164
- } else {
165
- // Normal item or non-parent duplicate
166
- itemsToCheck.push(detail);
167
- }
168
- });
169
-
170
- // All must have review approved/rejected
171
- return itemsToCheck.length > 0 && itemsToCheck.every(item => this.hasReviewDecision(item));
172
- }
173
- private hasReviewDecision(item: any): boolean {
174
- const actions = item?.actions || [];
175
- return actions.some(
176
- (a: any) =>
177
- a.actionType === 'review' &&
178
- (a.action === 'approved' || a.action === 'rejected')
179
- );
180
- }
181
-
182
- selectedRole: 'approver' | 'reviewer' | '' = '';
183
- statusVal: any;
184
- dueDate: any;
185
- getHeaderStatus(): any {
186
- // Default: ticket-level status
187
- let headerStatus = this.ticketData?.status || '--';
188
-
189
- if (!this.footfallTicketsData || !this.footfallTicketsData.length) {
190
- return headerStatus;
191
- }
192
-
193
- // 🔹 1) Collect latest mapping by type (only those with a status)
194
- let tangoMapping: any = null;
195
- let approverMapping: any = null;
196
- let reviewerMapping: any = null;
197
-
198
- for (const ticket of this.footfallTicketsData) {
199
- const source = ticket?._source;
200
- const mappingList = source?.mappingInfo || [];
201
-
202
- for (const m of mappingList) {
203
- this.dueDate = m.dueDate ? m.dueDate : ''
204
- if (!m?.status) continue; // need status to show in header
205
-
206
- if (m.type === 'tangoreview') {
207
- tangoMapping = m; // last tango wins
208
- }
209
-
210
- if (m.type === 'approve') {
211
- approverMapping = m; // last approve wins (Open / In-Progress / Closed)
212
- }
213
-
214
- if (m.type === 'review') {
215
- reviewerMapping = m; // last review wins
216
-
217
- }
218
- }
219
- }
220
-
221
- // 🔹 2) Compute permissionType same as your API
222
- let permissionType: 'approve' | 'review' | null = null;
223
-
224
- if (this.usersDetails?.userType !== 'tango') {
225
- if (this.hasApproverAccess && this.selectedRole === 'approver') {
226
- permissionType = 'approve';
227
- } else if (this.hasReviewerAccess && this.selectedRole === 'reviewer') {
228
- permissionType = 'review';
229
- }
230
- }
231
-
232
- let roleMapping: any = null;
233
-
234
- // 🔹 3) Decide which mapping to show in header
235
- if (!permissionType) {
236
- // tango or no special role → prefer tango > approve > review
237
- roleMapping = tangoMapping || approverMapping || reviewerMapping;
238
- } else if (permissionType === 'approve') {
239
- // approver view → try approve first, else fallbacks
240
- roleMapping = approverMapping || tangoMapping || reviewerMapping;
241
- } else if (permissionType === 'review') {
242
-
243
- // reviewer view → try review first, else fallbacks
244
- roleMapping = reviewerMapping
245
- }
246
-
247
- // Save for other places if needed
248
- this.statusVal = roleMapping;
249
- if (roleMapping?.status) {
250
- this.areAllItemsReviewed(roleMapping); // keep your side-effect
251
- headerStatus = roleMapping.status; // "Open", "In-Progress", "Closed", etc.
252
- }
253
- return headerStatus;
254
- }
255
-
256
-
257
-
258
- approverClosed: any = false;
259
- getCurrentRoleMapping(): any {
260
- if (!this.footfallTicketsData || !this.footfallTicketsData.length) {
261
- return null;
262
- }
263
-
264
- let approverMapping: any = null;
265
- let reviewerMapping: any = null;
266
- let tangoMapping: any = null;
267
- let hasClosedApprove = false; // 👈 track overall condition
268
-
269
- for (const ticket of this.footfallTicketsData) {
270
- const source = ticket?._source;
271
- const mappingList = source?.mappingInfo || [];
272
-
273
- for (const m of mappingList) {
274
-
275
- // 👇 overall check for arrowshow (approve + Closed)
276
- if (
277
- m?.type === 'approve' &&
278
- m?.status === 'Closed'
279
- ) {
280
- this.approverClosed = hasClosedApprove = true;
281
- this.arrowshow = !hasClosedApprove;
282
- // console.log(this.approverClosed)
283
- }
284
-
285
- if (m?.type === 'tangoreview' && m?.revisedDetail?.length) {
286
- tangoMapping = m; // last tango wins
287
- }
288
-
289
- if (m?.type === 'approve' && m?.revisedDetail?.length) {
290
- approverMapping = m;
291
- // last approve wins
292
- }
293
-
294
- if (m?.type === 'review' && m?.revisedDetail?.length) {
295
- reviewerMapping = m; // last review wins
296
-
297
- }
298
- }
299
- }
300
- if (tangoMapping) {
301
- return tangoMapping;
302
- }
303
-
304
- // 🔹 2) Then approver / reviewer based on access
305
- if (this.hasApproverAccess && approverMapping) {
306
- return approverMapping;
307
- }
308
-
309
- if (this.hasReviewerAccess && reviewerMapping) {
310
- return reviewerMapping;
311
- }
312
-
313
- // 🔹 3) Fallback if nothing found
314
- return null;
315
- }
316
-
317
-
318
- disableToday = (date: any): boolean => {
319
- const today = dayjs().format("YYYY-MM-DD");
320
- return dayjs(date).format("YYYY-MM-DD") === today;
321
- };
322
-
323
-
324
- datechange(event: any) {
325
- if (event && event.startDate && event.endDate) {
326
- if (
327
- this.dayjs(event.startDate).isValid() &&
328
- this.dayjs(event.endDate).isValid()
329
- ) {
330
- this.selectedDateRange.startDate = event.startDate
331
- this.selectedDateRange.endDate = event.endDate
332
- var datetime = {
333
- startDate: this.dayjs(event.startDate, "DD-MM-YYYY").format("YYYY-MM-DD"),
334
- endDate: this.dayjs(event.endDate, "DD-MM-YYYY").format("YYYY-MM-DD"),
335
- };
336
- if (this.selectedStore?.storeId) {
337
-
338
- this.service
339
- .getfootfallcount({
340
- "storeId": this.selectedStore?.storeId,
341
- "Date": this.dayjs(this.selectedDateRange.startDate, "DD-MM-YYYY").format("YYYY-MM-DD"),
342
- })
343
- .pipe(takeUntil(this.destroy$))
344
- .subscribe({
345
- next: (res: any) => {
346
- if (res && res.code === 200) {
347
- // console.log(res.data)
348
- this.footfallcount = res?.data[0]?.footfallCount
349
-
350
-
351
- }
352
- }
353
- })
354
- }
355
- }
356
- }
357
- }
358
-
359
- getStatusBadgeClass(status: string): string {
360
- const map: any = {
361
- 'Open': 'badge-light-warning',
362
- 'open': 'badge-light-warning',
363
- 'In-Progress': 'badge-light-primary',
364
- 'tangoreview': 'badge-light-primary',
365
- 'Closed': 'badge-secondary',
366
- 'Pending': 'badge-light-warning',
367
- 'Reviewer-Closed': 'badge-light-warning',
368
- 'Rejected': 'badge-light-danger',
369
- 'Open - Accuracy Issue': 'badge-light-danger',
370
- 'Raised': 'badge-light-info'
371
- };
372
-
373
- return map[status] || 'badge-light-dark'; // fallback
374
- }
375
-
376
- getFootfallSummaryData: any;
377
- getTicketSummary(type: any) {
378
- this.setPermissionType();
379
- this.service
380
- .getTicketSummaryApi(
381
- this.footfallList_req.client,
382
- this.footfallList_req.fromDate,
383
- this.footfallList_req.toDate,
384
- type,
385
- this.permissionType,
386
- )
387
- .pipe(takeUntil(this.destroy$))
388
- .subscribe({
389
- next: (res: any) => {
390
- if (res && res?.data && res?.data?.result) {
391
- this.getFootfallSummaryData = res?.data?.result;
392
- } else {
393
- this.getFootfallSummaryData = [];
394
- }
395
- },
396
- error: (err: any) => {
397
- this.getFootfallSummaryData = [];
398
- },
399
- complete: () => {
400
- this.getFootfallSummaryData.length === 0;
401
- },
402
- });
403
- this.cd.detectChanges();
404
- }
405
- offset = 1;
406
- limit = 10;
407
- isExport: any = false;
408
- footfallListData: any;
409
- totalItems: any;
410
- paginationSizes = [10, 20, 30];
411
- loading = true;
412
- noData = false;
413
- tangoType: any = "store";
414
- filterPayload: any = null;
415
- permissionType: string | null = null;
416
- setPermissionType() {
417
- if (this.usersDetails?.userType !== 'tango') {
418
- this.permissionType =
419
- this.hasApproverAccess && this.selectedRole === 'approver'
420
- ? 'approve'
421
- : this.hasReviewerAccess && this.selectedRole === 'reviewer'
422
- ? 'review'
423
- : null;
424
- } else {
425
- // tango -> can see all flows
426
- this.permissionType = '';
427
- }
428
- }
429
- private buildFiltersForApi(): any {
430
- const p = this.filterPayload || {};
431
-
432
- // Always send all keys
433
- const filters: any = {
434
- filterByStatus: [],
435
- filterByTango: null,
436
- filterByReviewer: null,
437
- filterByApprover: null,
438
- filterByReviewedBy: [],
439
- fileterByApprovedBy: []
440
- };
441
-
442
- // Status
443
- if (p.filterByStatus?.length) {
444
- filters.filterByStatus = p.filterByStatus;
445
- }
446
-
447
- // Role-based Accuracy Blocks
448
- if (this.usersDetails?.userType === 'tango') {
449
- filters.filterByTango = p.filterByTango?.length ? p.filterByTango : null;
450
- filters.filterByReviewer = p.filterByReviewer?.length ? p.filterByReviewer : null;
451
- filters.filterByApprover = p.filterByApprover?.length ? p.filterByApprover : null;
452
-
453
- } else if (this.permissionType === 'review') {
454
- filters.filterByReviewer = p.filterByReviewer?.length ? p.filterByReviewer : null;
455
-
456
- } else if (this.permissionType === 'approve') {
457
- filters.filterByApprover = p.filterByApprover?.length ? p.filterByApprover : null;
458
- }
459
-
460
- // ReviewedBy
461
- if (p.filterByReviewedBy?.length) {
462
- filters.filterByReviewedBy = p.filterByReviewedBy;
463
- }
464
-
465
- // ApprovedBy
466
- if (p.fileterByApprovedBy?.length) {
467
- filters.fileterByApprovedBy = p.fileterByApprovedBy; // keep spelling
468
- }
469
-
470
- return filters;
471
- }
472
-
473
-
474
-
475
- getTicketList(type: any) {
476
- this.currentPage = 1;
477
- this.offset = 1;
478
- this.limit = 10
479
- this.loading = true;
480
- this.noData = false
481
- this.searchValue = this.searchValue?.trim() || "";
482
- this.isExport = false;
483
- this.setPermissionType(); // make sure it’s up-to-date
484
-
485
- const filters = this.buildFiltersForApi();
486
-
487
- this.service
488
- .getTicketListApi(
489
- this.footfallList_req?.client,
490
- this.footfallList_req?.fromDate,
491
- this.footfallList_req?.toDate,
492
- this.searchValue,
493
- 10,
494
- 1,
495
- this.isExport,
496
- this.sortedColumn,
497
- this.sortDirection,
498
- type,
499
- this.permissionType,
500
- filters
501
- ).pipe(takeUntil(this.destroy$))
502
- .subscribe({
503
- next: (res: any) => {
504
- if (res && res.code === 200) {
505
- this.noData = false;
506
- this.loading = false;
507
- this.footfallListData = res?.data?.result;
508
-
509
- if (this.footfallListData.length > 0) {
510
- this.generateColumns(this.footfallListData[0]);
511
- }
512
- this.totalItems = res?.data?.count;
513
- if (this.totalItems < 10) {
514
- this.paginationSizes = [this.totalItems];
515
- } else {
516
- this.paginationSizes = [10, 20, 30];
517
- }
518
- } else {
519
- this.totalItems = 0
520
- this.noData = true;
521
- this.loading = false;
522
- this.footfallListData = [];
523
- }
524
- this.cd.detectChanges();
525
- },
526
- error: (err: any) => {
527
- this.noData = true;
528
- this.loading = false;
529
- },
530
- complete: () => {
531
- this.loading = false;
532
- },
533
- });
534
- }
535
- tableColumns: any[] = [];
536
- labelOverrides: Record<string, string> = {
537
- ticketId: 'Ticket ID',
538
- storeName: 'Store Name',
539
- storeId: 'Store ID', // or just 'Store'
540
- storeRevisedAccuracy: 'Store (Accuracy%)',
541
- reviewerRevisedAccuracy: 'Reviewer (Accuracy%)',
542
- approverRevisedAccuracy: 'Approver (Accuracy%)',
543
- tangoRevisedAccuracy: 'Tango (Accuracy%)',
544
- ticketRaised: 'Ticket Raised On',
545
- issueDate: 'Issue Date',
546
- footfall: 'Actual FF',
547
- };
548
- hiddenColumns: string[] = [
549
- 'storeName', // uncomment if you want to REMOVE the extra "Store Name" column
550
- 'type',
551
- ];
552
- sortableColumns: string[] = ['issueDate', 'storeId', 'footfall'];
553
-
554
- generateColumns(firstRow: any) {
555
- const keys = Object.keys(firstRow)
556
- .filter(key => !this.hiddenColumns.includes(key)); // remove unwanted columns
557
-
558
- this.tableColumns = keys.map(key => ({
559
- key,
560
- label: this.labelOverrides[key] ?? this.formatColumnName(key),
561
- sortable: this.sortableColumns.includes(key), // ✅ only 3 columns sortable
562
- type: this.detectColumnType ? this.detectColumnType(key) : null
563
- }));
564
- }
565
-
566
- formatColumnName(key: string): string {
567
- return key
568
- .replace(/([A-Z])/g, ' $1')
569
- .replace(/_/g, ' ')
570
- .replace(/\b\w/g, c => c.toUpperCase());
571
- }
572
-
573
- detectColumnType(key: string) {
574
- if (key === 'storeName' || key === 'ticketId') return 'store';
575
- if (key.toLowerCase().includes('date') || key === 'ticketRaised') return 'date';
576
- if (key.toLowerCase().includes('status')) return 'status';
577
- return 'text';
578
- }
579
- currentPage: any = 1;
580
- pageSize: any = 10;
581
- onPageChange(pageOffset: number) {
582
- this.currentPage = Number(pageOffset);
583
- this.offset = Number(pageOffset);
584
- // this.limit = 10;
585
- this.loading = true;
586
- this.noData = false
587
- this.searchValue = this.searchValue?.trim() || "";
588
- this.isExport = false;
589
- this.setPermissionType(); // make sure it’s up-to-date
590
-
591
- const filters = this.buildFiltersForApi();
592
-
593
- this.service
594
- .getTicketListApi(
595
- this.footfallList_req?.client,
596
- this.footfallList_req?.fromDate,
597
- this.footfallList_req?.toDate,
598
- this.searchValue,
599
- this.limit,
600
- this.offset,
601
- this.isExport,
602
- this.sortedColumn,
603
- this.sortDirection,
604
- this.tangoType,
605
- this.permissionType,
606
- filters
607
- ).pipe(takeUntil(this.destroy$))
608
- .subscribe({
609
- next: (res: any) => {
610
- if (res && res.code === 200) {
611
- this.noData = false;
612
- this.loading = false;
613
- this.footfallListData = res?.data?.result;
614
- if (this.footfallListData.length > 0) {
615
- this.generateColumns(this.footfallListData[0]);
616
- }
617
- this.totalItems = res?.data?.count;
618
- if (this.totalItems < 10) {
619
- this.paginationSizes = [this.totalItems];
620
- } else {
621
- this.paginationSizes = [10, 20, 30];
622
- }
623
- } else {
624
- this.totalItems = 0
625
- this.noData = true;
626
- this.loading = false;
627
- this.footfallListData = [];
628
- }
629
- this.cd.detectChanges();
630
- },
631
- error: (err: any) => {
632
- this.noData = true;
633
- this.loading = false;
634
- },
635
- complete: () => {
636
- this.loading = false;
637
- },
638
- });
639
- }
640
-
641
- onPageSizeChange(pageSize: number) {
642
- this.pageSize = Number(pageSize);
643
- this.limit = Number(pageSize);
644
- this.currentPage = 1;
645
- this.offset = 1;
646
- this.loading = true;
647
- this.noData = false
648
- this.searchValue = this.searchValue?.trim() || "";
649
- this.isExport = false;
650
- this.setPermissionType(); // make sure it’s up-to-date
651
-
652
- const filters = this.buildFiltersForApi();
653
-
654
- this.service
655
- .getTicketListApi(
656
- this.footfallList_req?.client,
657
- this.footfallList_req?.fromDate,
658
- this.footfallList_req?.toDate,
659
- this.searchValue,
660
- this.limit,
661
- this.offset,
662
- this.isExport,
663
- this.sortedColumn,
664
- this.sortDirection,
665
- this.tangoType,
666
- this.permissionType,
667
- filters
668
- ).pipe(takeUntil(this.destroy$))
669
- .subscribe({
670
- next: (res: any) => {
671
- if (res && res.code === 200) {
672
- this.noData = false;
673
- this.loading = false;
674
- this.footfallListData = res?.data?.result;
675
- if (this.footfallListData.length > 0) {
676
- this.generateColumns(this.footfallListData[0]);
677
- }
678
- this.totalItems = res?.data?.count;
679
- if (this.totalItems < 10) {
680
- this.paginationSizes = [this.totalItems];
681
- } else {
682
- this.paginationSizes = [10, 20, 30];
683
- }
684
- } else {
685
- this.totalItems = 0
686
- this.noData = true;
687
- this.loading = false;
688
- this.footfallListData = [];
689
- }
690
- this.cd.detectChanges();
691
- },
692
- error: (err: any) => {
693
- this.noData = true;
694
- this.loading = false;
695
- },
696
- complete: () => {
697
- this.loading = false;
698
- },
699
- });
700
- }
701
-
702
- setpageSize() {
703
- if (this.totalItems < 10) {
704
- return this.totalItems;
705
- } else {
706
- return this.pageSize;
707
- }
708
- }
709
- searchData() {
710
- this.currentPage = 1;
711
- this.offset = 1;
712
- this.limit = 10;
713
- this.getTicketList(this.tangoType);
714
- }
715
- storeChange(event: any) {
716
- this.selectedStore = { storeId: event.storeId, storeName: event.storeName }
717
- console.log("🚀 ~ TicketFootfallNewComponent ~ storeChange ~ this.selectedStore:", this.selectedDateRange.startDate, this.dayjs(this.selectedDateRange.startDate, "DD-MM-YYYY", true).format("YYYY-MM-DD"))
718
- this.service
719
- .getfootfallcount({
720
- "storeId": this.selectedStore?.storeId,
721
- "Date": this.dayjs(this.selectedDateRange.startDate, "DD-MM-YYYY").format("YYYY-MM-DD"),
722
- })
723
- .pipe(takeUntil(this.destroy$))
724
- .subscribe({
725
- next: (res: any) => {
726
- if (res && res.code === 200) {
727
- // console.log(res.data)
728
- this.footfallcount = res?.data[0]?.footfallCount
729
-
730
-
731
- }
732
- }
733
- })
734
-
735
- }
736
- onStartDateChange(event: any) {
737
- if (this.dayjs(event.startDate).isValid()) {
738
- this.isCustomDate = (m: dayjs.Dayjs) => {
739
- const isValidDate = m > this.dayjs() || m > this.dayjs(event.startDate.add(90, 'days'));
740
- return isValidDate ? 'invalid-date' : false;
741
- }
742
- }
743
- }
744
-
745
-
746
-
747
- createInternalticket() {
748
- this.selectedStore = {}
749
- this.footfallcount = 0
750
- const modalRef = this.modalService.open(this.internalticket)
751
- modalRef.result.then((result) => {
752
- console.log("🚀 ~ TicketFootfallNewComponent ~ createInternalticket ~ result.isConfirmed:", result)
753
- if (result === 'isConfirmed') {
754
- let obj = {
755
- "storeId": this.selectedStore?.storeId,
756
- "dateString": dayjs(this.selectedDateRange.startDate, "DD-MM-YYYY").format("YYYY-MM-DD"),
757
- "storeName": this.selectedStore?.storeName,
758
- "ticketName": "footfall-directory",
759
- "footfallCount": this.footfallcount,
760
- "clientId": this.headerFilters?.client,
761
- "status": "Raised",
762
- "comments": ""
763
- }
764
- this.service
765
- .CreateinternalTicketApi(obj)
766
- .pipe(takeUntil(this.destroy$))
767
- .subscribe({
768
- next: (res: any) => {
769
- if (res && res?.code === 200) {
770
- this.ts.getSuccessToast("Ticket created successfully");
771
- this.modalService.dismissAll();
772
- const data = {
773
- totalfiles: this.footfallcount,
774
- filedetails: {
775
- clientId: this.headerFilters?.client,
776
- storeId: this.selectedStore?.storeId,
777
- Date: dayjs(this.selectedDateRange.startDate, "DD-MM-YYYY").format("YYYY-MM-DD"),
778
- auditId: "",
779
- userId: "",
780
- nextToken: "",
781
- },
782
- auditId: "",
783
- storedetails: {
784
- storeName: this.selectedStore?.storeName
785
- }
786
- };
787
- sessionStorage.setItem("totalfiles", JSON.stringify(data));
788
- sessionStorage.setItem("ticketType", "internal");
789
- this.select = 'ticketMethod';
790
- this.router.navigateByUrl('/manage/tickets/audit')
791
-
792
- } else {
793
- this.ts.getErrorToast("Error closing ticket");
794
- }
795
- this.cd.detectChanges();
796
- },
797
- error: (err: any) => {
798
- const errorMsg =
799
- err?.error?.message ||
800
- err?.error?.error ||
801
- err?.error ||
802
- err?.message ||
803
- "Error closing ticket";
804
- this.ts.getErrorToast(errorMsg);
805
- this.isCheckboxEnable = true;
806
- this.closeBtn = true;
807
- this.closeDisabled = false;
808
- }
809
- });
810
-
811
-
812
-
813
- }
814
- })
815
-
816
-
817
-
818
-
819
- }
820
- exportXLSX() {
821
- this.searchValue = this.searchValue?.trim() || "";
822
- this.offset = 1;
823
- this.limit = 10000;
824
- this.isExport = true;
825
- this.service
826
- .getTicketListExportApi(
827
- this.footfallList_req.client,
828
- this.footfallList_req.fromDate,
829
- this.footfallList_req.toDate,
830
- this.searchValue,
831
- this.limit,
832
- this.offset,
833
- this.isExport,
834
- this.sortedColumn,
835
- this.sortDirection,
836
- this.tangoType
837
- )
838
- .pipe(takeUntil(this.destroy$))
839
- .subscribe({
840
- next: (res: any) => {
841
- this.excelService.saveAsExcelFile(res, "footfall directory ticket ");
842
- },
843
- error: (err: any) => {
844
- this.ts.getErrorToast(
845
- "Error exporting data:" + err.error ? err.error : err.message
846
- );
847
- },
848
- });
849
- }
850
-
851
- onSort(column: string) {
852
- if (this.sortedColumn === column) {
853
- this.sortDirection = this.sortDirection === 1 ? -1 : 1;
854
- } else {
855
- this.sortedColumn = column;
856
- this.sortDirection = 1;
857
- }
858
- this.getTicketList(this.tangoType);
859
- }
860
- select = "ticketList";
861
- ticketData: any;
862
-
863
- startAudit() {
864
- let input = {
865
- storeId: this.ticketData?.storeId,
866
- dateString: this.ticketData?.issueDate,
867
- }
868
- this.service
869
- .checkTicketExists(input).pipe(takeUntil(this.destroy$))
870
- .subscribe({
871
- next: (res: any) => {
872
- if (res && res?.code === 200) {
873
- // console.log(res.data)
874
- let ticketList = res?.data
875
- let findTangoticket = ticketList.filter((data: any) => data?._source?.type === 'internal' && data?._source?.status === "Closed")
876
- let findstoreticket = ticketList.filter((data: any) => data?._source?.type === 'store' && data?._source?.status != "Closed")
877
-
878
- if (findTangoticket.length > 0) {
879
- const modalRef = this.modalService.open(TicketclosepopupComponent, {
880
- centered: true,
881
- size: "md",
882
- scrollable: true,
883
- backdrop: "static",
884
- });
885
- modalRef.componentInstance.tangoTicket = findTangoticket[0]._source;
886
- modalRef.componentInstance.storeTicket = findstoreticket[0]._source;
887
- modalRef.result.then((result) => {
888
-
889
- if (result.type === 'merge') {
890
- console.log("🚀 ~ TicketFootfallNewComponent ~ startAudit ~ findTangoticket:", findTangoticket)
891
-
892
- let obj = findTangoticket[0]?._source
893
- if (obj && obj?.mappingInfo.length > 0) {
894
- let mapData = obj.mappingInfo.filter((data: any) => data.type === "tangoreview")[0]
895
- console.log("🚀 ~ TicketFootfallNewComponent ~ startAudit ~ mapData:", mapData)
896
-
897
- let payload = {
898
- storeId: this.ticketData?.storeId,
899
- "dateString": this.ticketData?.issueDate,
900
- ticketType: 'store',
901
- mappingInfo: mapData
902
- }
903
- console.log("🚀 ~ TicketFootfallNewComponent ~ startAudit ~ payload:", payload)
904
- this.auditservice
905
- .saveaudit(payload).pipe(takeUntil(this.destroy$))
906
- .subscribe({
907
- next: (res: any) => {
908
- if (res && res?.code === 200) {
909
- this.router.navigate(['/manage/tickets'], { queryParams: { type: 'footfall' } })
910
- }
911
- }
912
- })
913
- }
914
- } else if (result.type == '') {
915
- const data = {
916
- totalfiles: this.ticketData?.footfall,
917
- filedetails: {
918
- clientId: this.headerFilters?.client,
919
- storeId: this.ticketData?.storeId,
920
- Date: this.ticketData?.issueDate,
921
- auditId: "",
922
- userId: "",
923
- nextToken: "",
924
- },
925
- auditId: "",
926
- storedetails: {
927
- storeName: this.ticketData?.storeName
928
- }
929
- };
930
- sessionStorage.setItem("totalfiles", JSON.stringify(data));
931
- sessionStorage.setItem("ticketType", this.tangoType);
932
-
933
- this.select = 'ticketMethod';
934
- this.router.navigateByUrl('/manage/tickets/audit')
935
- }
936
- });
937
- } else {
938
- const data = {
939
- totalfiles: this.ticketData?.footfall,
940
- filedetails: {
941
- clientId: this.headerFilters?.client,
942
- storeId: this.ticketData?.storeId,
943
- Date: this.ticketData?.issueDate,
944
- auditId: "",
945
- userId: "",
946
- nextToken: "",
947
- },
948
- auditId: "",
949
- storedetails: {
950
- storeName: this.ticketData?.storeName
951
- }
952
- };
953
- sessionStorage.setItem("totalfiles", JSON.stringify(data));
954
- sessionStorage.setItem("ticketType", this.tangoType);
955
-
956
- this.select = 'ticketMethod';
957
- this.router.navigateByUrl('/manage/tickets/audit')
958
- }
959
-
960
-
961
-
962
-
963
- }
964
- }
965
- })
966
-
967
-
968
- return
969
-
970
-
971
- }
972
-
973
- backToNavigation() {
974
- this.select = "ticketList";
975
- this.footfallTicketsData = [];
976
- this.getTicketSummary(this.tangoType);
977
- this.getTicketList(this.tangoType);
978
- this.closeBtn = false;
979
- this.isCheckboxEnable = false;
980
- this.arrowshow = !this.arrowshow;
981
- this.commentsAccordionOpen = false;
982
- this.selectedCommentCategory = null;
983
- }
984
- isCollapsed = false;
985
- toggleSidebar() {
986
- this.isCollapsed = !this.isCollapsed;
987
- }
988
-
989
- toggleFilter() {
990
- this.isFilterOpen = !this.isFilterOpen;
991
- }
992
- isFilterOpen = false; // controls show/hide panel
993
- filterForm: FormGroup;
994
- @Output() filterChange = new EventEmitter<any>();
995
- newForm() {
996
- this.filterForm = this.fb.group({
997
- status: [""],
998
- reviewerCondition: [""],
999
- reviewerValue: [""],
1000
- approverCondition: [""],
1001
- approverValue: [""],
1002
- tangoCondition: [""],
1003
- tangoValue: [""],
1004
- });
1005
- }
1006
-
1007
- applyFilter() {
1008
- this.filterChange.emit(this.filterForm.value);
1009
- this.isFilterOpen = false; // close after apply
1010
- }
1011
-
1012
- resetFilter() {
1013
- this.filterForm.reset();
1014
- this.filterChange.emit(this.filterForm.value);
1015
- }
1016
-
1017
- close() {
1018
- this.isFilterOpen = false;
1019
- }
1020
- StoresSearchValue: any = "";
1021
- sortedColumn1: string = "";
1022
- sortDirection1: number = 1;
1023
-
1024
- openTicketsList: any[] = [];
1025
- selectedStores: any[] = []; // holds selected rows
1026
- allSelected = false; // top "Select All" checkbox
1027
-
1028
- get storeCount(): number {
1029
- return this.openTicketsList?.length || 0;
1030
- }
1031
-
1032
- // check if a row is selected (used in template)
1033
- isSelected(store: any): boolean {
1034
- return this.selectedStores.includes(store);
1035
- }
1036
-
1037
-
1038
-
1039
- toggleStoreSelection(store: any): void {
1040
-
1041
- if (this.isSelected(store)) {
1042
-
1043
- this.selectedStores = this.selectedStores.filter((s) => s !== store);
1044
- } else {
1045
-
1046
- this.selectedStores = [...this.selectedStores, store];
1047
- }
1048
- this.closeMultiple = (this.selectedStores.length > 1) ? false : true;
1049
-
1050
-
1051
- this.allSelected =
1052
- this.selectedStores.length === this.openTicketsList.length &&
1053
- this.openTicketsList.length > 0;
1054
- }
1055
-
1056
- newallSelected = false;
1057
- toggleSelectAll(): void {
1058
- if (this.newallSelected) {
1059
- this.selectedStores = [];
1060
- this.newallSelected = false;
1061
- } else {
1062
- this.selectedStores = [...this.openTicketsList];
1063
- this.newallSelected = true;
1064
- }
1065
- }
1066
- ticketViewChanges(store: any) {
1067
- this.dataStoreView(store);
1068
- this.selectedStores = [store];
1069
-
1070
- this.newallSelected =
1071
- this.selectedStores.length === this.openTicketsList.length &&
1072
- this.openTicketsList.length > 0;
1073
- }
1074
-
1075
- originalImage: any = {
1076
- id: 4,
1077
- entryTime: "10:00 AM",
1078
- };
1079
- ticket: any = [];
1080
- imageUrl: any;
1081
- getFormattedEntryTime(entryTime: any) {
1082
- if (!entryTime) return "-";
1083
- const [hours, minutes, seconds] = entryTime.split(":").map(Number);
1084
- const date = new Date();
1085
- date.setHours(hours, minutes, seconds);
1086
- return date.toLocaleTimeString("en-US", {
1087
- hour: "2-digit",
1088
- minute: "2-digit",
1089
- // second: '2-digit',
1090
- hour12: true,
1091
- });
1092
- }
1093
- duplicates: any;
1094
-
1095
- // key = parent original.tempId, value = array of selected duplicate tempIds
1096
- selectedDuplicatesByParent: { [parentId: number]: number[] } = {};
1097
-
1098
- // is one duplicate selected?
1099
- isDuplicateSelected(parentId: number, duplicateId: number): boolean {
1100
- return (
1101
- this.selectedDuplicatesByParent[parentId]?.includes(duplicateId) || false
1102
- );
1103
- }
1104
-
1105
- // select / unselect single duplicate
1106
- onDuplicateCheckboxChange(
1107
- parentId: number,
1108
- duplicateId: number,
1109
- event: Event
1110
- ): void {
1111
- const checked = (event.target as HTMLInputElement).checked;
1112
-
1113
- if (!this.selectedDuplicatesByParent[parentId]) {
1114
- this.selectedDuplicatesByParent[parentId] = [];
1115
- }
1116
-
1117
- const list = this.selectedDuplicatesByParent[parentId];
1118
-
1119
- if (checked) {
1120
- if (!list.includes(duplicateId)) {
1121
- list.push(duplicateId);
1122
- }
1123
- } else {
1124
- this.selectedDuplicatesByParent[parentId] = list.filter(
1125
- (id) => id !== duplicateId
1126
- );
1127
- }
1128
- this.updateOverallSelectedIds();
1129
- }
1130
-
1131
- // are *all* duplicates under this parent selected?
1132
- areAllDuplicatesSelected(original: any): boolean {
1133
- const parentId = original.tempId;
1134
- const duplicates = original?.duplicateImage || [];
1135
- const selected = this.selectedDuplicatesByParent[parentId] || [];
1136
-
1137
- return duplicates.length > 0 && selected.length === duplicates.length;
1138
- }
1139
-
1140
- // handle "Select All" toggle for this original
1141
- onToggleSelectAllDuplicates(original: any, event: Event): void {
1142
- const checked = (event.target as HTMLInputElement).checked;
1143
- const parentId = original.tempId;
1144
-
1145
- const duplicates = (original?.duplicateImage || []).filter(
1146
- (d: any) => !this.isFinalStatus(d) // ⛔ skip approved/rejected
1147
- );
1148
-
1149
- if (checked) {
1150
- this.selectedDuplicatesByParent[parentId] = duplicates.map(
1151
- (d: any) => d.tempId
1152
- );
1153
- } else {
1154
- this.selectedDuplicatesByParent[parentId] = [];
1155
- }
1156
-
1157
- this.updateOverallSelectedIds();
1158
- }
1159
-
1160
-
1161
- // comments: any;
1162
-
1163
- commentModal(obj: any) {
1164
- const modalRef = this.modalService.open(FootfallPopupComponent, {
1165
- centered: true,
1166
- size: "md",
1167
- scrollable: true,
1168
- backdrop: "static",
1169
- });
1170
- modalRef.componentInstance.ticketId = obj.ticketId;
1171
- modalRef.result.then((result) => { });
1172
- }
1173
-
1174
- commentsAccordionOpen = false;
1175
- selectedCommentCategory: string | null = null;
1176
- getCategoryCommentCountForSource(
1177
- source: any,
1178
- category: string,
1179
- parentId?: number | string // optional, for duplicate
1180
- ): number {
1181
- if (!source?.commentsDetails || !category) return 0;
1182
-
1183
- let count = 0;
1184
-
1185
- for (const block of source.commentsDetails) {
1186
- // 🔹 DUPLICATE: no block.comments, use block itself
1187
- if (category === 'duplicate') {
1188
- if (
1189
- block.category === 'duplicate' &&
1190
- (parentId == null || String(block.parent) === String(parentId))
1191
- ) {
1192
- count += 1; // each block = one comment thread
1193
- }
1194
- continue;
1195
- }
1196
-
1197
- // 🔹 NON-DUPLICATE: old shapes
1198
- const comments = block?.comments || [];
1199
- if (!comments.length) continue;
1200
-
1201
- if (block.category) {
1202
- // New shape: category at block level
1203
- if (block.category === category) {
1204
- count += comments.length;
1205
- }
1206
- } else {
1207
- // Old shape: category on each comment
1208
- count += comments.filter((c: any) => c.category === category).length;
1209
- }
1210
- }
1211
-
1212
- return count;
1213
- }
1214
- getDuplicateCommentCountForParent(source: any, parentId: number | string): number {
1215
- if (!source?.commentsDetails) return 0;
1216
-
1217
- return source.commentsDetails.filter(
1218
- (b: any) =>
1219
- b.category === 'duplicate' && String(b.parent) === String(parentId)
1220
- ).length;
1221
- }
1222
-
1223
-
1224
-
1225
-
1226
- toggleCommentsAccordion(type: string) {
1227
- // If you click the same type again → close accordion
1228
- if (this.commentsAccordionOpen && this.selectedCommentCategory === type) {
1229
- this.commentsAccordionOpen = false;
1230
- this.selectedCommentCategory = null;
1231
- } else {
1232
- // Clicked a new type → open accordion for that type
1233
- this.commentsAccordionOpen = true;
1234
- this.selectedCommentCategory = type;
1235
- }
1236
- // console.log(this.selectedCommentCategory)
1237
- }
1238
-
1239
- ngOnDestroy(): void {
1240
- this.destroy$.next(true);
1241
- this.destroy$.complete();
1242
- }
1243
- activityData: any = [
1244
- {
1245
- "category": "duplicate",
1246
- "type": "tagging",
1247
- "parent": 1,
1248
- "comments": [
1249
- {
1250
-
1251
-
1252
- "message": "commnets message update here",
1253
- "createdByEmail": "sandeep.pal@yopmail.com",
1254
- "createdByUserName": "sandeep.pal",
1255
- "createdByRole": "user"
1256
- }
1257
-
1258
- ]
1259
- },
1260
- {
1261
- "category": "duplicate",
1262
- "type": "review",
1263
- "comments": [
1264
- {
1265
- "parent": 1,
1266
- "category": "duplicate",
1267
- "taggedImages": [
1268
- {
1269
- "id": "11-280_2025-12-02_18",
1270
- "tempId": 8,
1271
- "timeRange": "04PM-05PM",
1272
- "entryTime": "16:17:59",
1273
- "exitTime": "16:47:57",
1274
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1275
- "isChecked": true
1276
- },
1277
- {
1278
- "id": "11-280_2025-12-02_18",
1279
- "tempId": 11,
1280
- "timeRange": "04PM-05PM",
1281
- "entryTime": "16:17:59",
1282
- "exitTime": "16:47:57",
1283
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1284
- "isChecked": true
1285
- },
1286
- {
1287
- "id": "11-280_2025-12-02_18",
1288
- "tempId": 21,
1289
- "timeRange": "04PM-05PM",
1290
- "entryTime": "16:17:59",
1291
- "exitTime": "16:47:57",
1292
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1293
- "isChecked": true
1294
- },
1295
- {
1296
- "id": "11-280_2025-12-02_18",
1297
- "tempId": 11,
1298
- "timeRange": "04PM-05PM",
1299
- "entryTime": "16:17:59",
1300
- "exitTime": "16:47:57",
1301
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1302
- "isChecked": true
1303
- },
1304
- {
1305
- "id": "11-280_2025-12-02_18",
1306
- "tempId": 21,
1307
- "timeRange": "04PM-05PM",
1308
- "entryTime": "16:17:59",
1309
- "exitTime": "16:47:57",
1310
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1311
- "isChecked": true
1312
- }
1313
- ],
1314
- "createdByEmail": "sandeep.pal@yopmail.com",
1315
- "createdByUserName": "sandeep.pal",
1316
- "createdByRole": "user",
1317
- "status": "approved",
1318
- "message": "commnets message update here"
1319
- },
1320
- {
1321
- "parent": 1,
1322
- "category": "duplicate",
1323
- "taggedImages": [
1324
- {
1325
- "id": "11-280_2025-12-02_18",
1326
- "tempId": 18,
1327
- "timeRange": "04PM-05PM",
1328
- "entryTime": "16:17:59",
1329
- "exitTime": "16:47:57",
1330
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1331
- "isChecked": true
1332
- },
1333
- {
1334
- "id": "11-280_2025-12-02_18",
1335
- "tempId": 19,
1336
- "timeRange": "04PM-05PM",
1337
- "entryTime": "16:17:59",
1338
- "exitTime": "16:47:57",
1339
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1340
- "isChecked": true
1341
- },
1342
- {
1343
- "id": "11-280_2025-12-02_18",
1344
- "tempId": 20,
1345
- "timeRange": "04PM-05PM",
1346
- "entryTime": "16:17:59",
1347
- "exitTime": "16:47:57",
1348
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1349
- "isChecked": true
1350
- }
1351
- ],
1352
- "createdByEmail": "sandeep.pal@yopmail.com",
1353
- "createdByUserName": "sandeep.pal",
1354
- "createdByRole": "user",
1355
- "status": "rejected",
1356
- "message": "commnets message update here"
1357
- }
1358
-
1359
- ]
1360
- },
1361
- {
1362
- "category": "duplicate",
1363
- "type": "approve",
1364
- "comments": [
1365
- {
1366
- "parent": 1,
1367
- "category": "duplicate",
1368
- "taggedImages": [
1369
- {
1370
- "id": "11-280_2025-12-02_18",
1371
- "tempId": 18,
1372
- "timeRange": "04PM-05PM",
1373
- "entryTime": "16:17:59",
1374
- "exitTime": "16:47:57",
1375
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1376
- "isChecked": true
1377
- },
1378
- {
1379
- "id": "11-280_2025-12-02_18",
1380
- "tempId": 18,
1381
- "timeRange": "04PM-05PM",
1382
- "entryTime": "16:17:59",
1383
- "exitTime": "16:47:57",
1384
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1385
- "isChecked": true
1386
- },
1387
- {
1388
- "id": "11-280_2025-12-02_18",
1389
- "tempId": 18,
1390
- "timeRange": "04PM-05PM",
1391
- "entryTime": "16:17:59",
1392
- "exitTime": "16:47:57",
1393
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1394
- "isChecked": true
1395
- }
1396
- ],
1397
- "createdByEmail": "sandeep.pal@yopmail.com",
1398
- "createdByUserName": "sandeep.pal",
1399
- "createdByRole": "user",
1400
- "status": "approved",
1401
- "message": "commnets message update here"
1402
- }
1403
-
1404
- ]
1405
- }
1406
- ]
1407
-
1408
- activityData1: any = [
1409
-
1410
- {
1411
-
1412
- "category": "employee",
1413
-
1414
- "parent": null,
1415
-
1416
- "type": "tagging",
1417
-
1418
- "comments": [
1419
-
1420
- {
1421
-
1422
- "id": "11-280_2025-12-02_18",
1423
-
1424
- "tempId": 18,
1425
-
1426
- "timeRange": "04PM-05PM",
1427
-
1428
- "entryTime": "16:17:59",
1429
-
1430
- "exitTime": "16:47:57",
1431
-
1432
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1433
-
1434
- "isChecked": true,
1435
-
1436
- "createdAt": "2025-12-03T06:40:34.485Z",
1437
-
1438
- "createdByEmail": "sandeep.pal@yopmail.com",
1439
-
1440
- "createdByUserName": "sandeep.pal",
1441
-
1442
- "createdByRole": "user",
1443
-
1444
-
1445
-
1446
- "message": "commnets message update here"
1447
-
1448
- },
1449
-
1450
- {
1451
-
1452
- "id": "11-280_2025-12-02_18",
1453
-
1454
- "tempId": 18,
1455
-
1456
- "timeRange": "04PM-05PM",
1457
-
1458
- "entryTime": "16:17:59",
1459
-
1460
- "exitTime": "16:47:57",
1461
-
1462
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1463
-
1464
- "isChecked": true,
1465
-
1466
- "createdAt": "2025-12-03T06:40:34.485Z",
1467
-
1468
- "createdByEmail": "sandeep.pal@yopmail.com",
1469
-
1470
- "createdByUserName": "sandeep.pal",
1471
-
1472
- "createdByRole": "user",
1473
-
1474
-
1475
-
1476
- "message": "commnets message update here"
1477
-
1478
- },
1479
-
1480
- {
1481
-
1482
- "id": "11-280_2025-12-02_18",
1483
-
1484
- "tempId": 18,
1485
-
1486
- "timeRange": "04PM-05PM",
1487
-
1488
- "entryTime": "16:17:59",
1489
-
1490
- "exitTime": "16:47:57",
1491
-
1492
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1493
-
1494
- "isChecked": true,
1495
-
1496
- "createdAt": "2025-12-03T06:40:34.485Z",
1497
-
1498
- "createdByEmail": "sandeep.pal@yopmail.com",
1499
-
1500
- "createdByUserName": "sandeep.pal",
1501
-
1502
- "createdByRole": "user",
1503
-
1504
-
1505
-
1506
- "message": "commnets message update here"
1507
-
1508
- }
1509
-
1510
- ]
1511
-
1512
- },
1513
-
1514
- {
1515
-
1516
- "type": "review",
1517
-
1518
- "comments": [
1519
-
1520
- {
1521
-
1522
- "category": "employee",
1523
-
1524
- "taggedImages": [
1525
-
1526
- {
1527
-
1528
- "id": "11-280_2025-12-02_18",
1529
-
1530
- "tempId": 28,
1531
-
1532
- "timeRange": "04PM-05PM",
1533
-
1534
- "entryTime": "16:17:59",
1535
-
1536
- "exitTime": "16:47:57",
1537
-
1538
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1539
-
1540
- "isChecked": true
1541
-
1542
- },
1543
-
1544
- {
1545
-
1546
- "id": "11-280_2025-12-02_18",
1547
-
1548
- "tempId": 22,
1549
-
1550
- "timeRange": "04PM-05PM",
1551
-
1552
- "entryTime": "16:17:59",
1553
-
1554
- "exitTime": "16:47:57",
1555
-
1556
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1557
-
1558
- "isChecked": true
1559
-
1560
- },
1561
-
1562
- {
1563
-
1564
- "id": "11-280_2025-12-02_18",
1565
-
1566
- "tempId": 11,
1567
-
1568
- "timeRange": "04PM-05PM",
1569
-
1570
- "entryTime": "16:17:59",
1571
-
1572
- "exitTime": "16:47:57",
1573
-
1574
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1575
-
1576
- "isChecked": true
1577
-
1578
- },
1579
- {
1580
-
1581
- "id": "11-280_2025-12-02_18",
1582
-
1583
- "tempId": 12,
1584
-
1585
- "timeRange": "04PM-05PM",
1586
-
1587
- "entryTime": "16:17:59",
1588
-
1589
- "exitTime": "16:47:57",
1590
-
1591
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1592
-
1593
- "isChecked": true
1594
-
1595
- },
1596
-
1597
- {
1598
-
1599
- "id": "11-280_2025-12-02_18",
1600
-
1601
- "tempId": 13,
1602
-
1603
- "timeRange": "04PM-05PM",
1604
-
1605
- "entryTime": "16:17:59",
1606
-
1607
- "exitTime": "16:47:57",
1608
-
1609
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1610
-
1611
- "isChecked": true
1612
-
1613
- }
1614
-
1615
- ],
1616
-
1617
- "createdByEmail": "sandeep.pal@yopmail.com",
1618
-
1619
- "createdByUserName": "sandeep.pal",
1620
-
1621
- "createdByRole": "user",
1622
-
1623
- "status": "approved",
1624
-
1625
- "message": "commnets message update here"
1626
-
1627
- }
1628
-
1629
- ]
1630
-
1631
- },
1632
- {
1633
-
1634
- "type": "approve",
1635
-
1636
- "comments": [
1637
-
1638
- {
1639
-
1640
- "category": "employee",
1641
-
1642
- "taggedImages": [
1643
-
1644
- {
1645
-
1646
- "id": "11-280_2025-12-02_18",
1647
-
1648
- "tempId": 1,
1649
-
1650
- "timeRange": "04PM-05PM",
1651
-
1652
- "entryTime": "16:17:59",
1653
-
1654
- "exitTime": "16:47:57",
1655
-
1656
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1657
-
1658
- "isChecked": true
1659
-
1660
- },
1661
-
1662
- {
1663
-
1664
- "id": "11-280_2025-12-02_18",
1665
-
1666
- "tempId": 8,
1667
-
1668
- "timeRange": "04PM-05PM",
1669
-
1670
- "entryTime": "16:17:59",
1671
-
1672
- "exitTime": "16:47:57",
1673
-
1674
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1675
-
1676
- "isChecked": true
1677
-
1678
- },
1679
-
1680
- {
1681
-
1682
- "id": "11-280_2025-12-02_18",
1683
-
1684
- "tempId": 22,
1685
-
1686
- "timeRange": "04PM-05PM",
1687
-
1688
- "entryTime": "16:17:59",
1689
-
1690
- "exitTime": "16:47:57",
1691
-
1692
- "filePath": "11-280/02-12-2025/18_customer_entry_frame.jpeg",
1693
-
1694
- "isChecked": true
1695
-
1696
- }
1697
-
1698
- ],
1699
-
1700
- "createdByEmail": "sandeep.pal@yopmail.com",
1701
-
1702
- "createdByUserName": "sandeep.pal",
1703
-
1704
- "createdByRole": "user",
1705
-
1706
- "status": "rejected",
1707
-
1708
- "message": "commnets message update here"
1709
-
1710
- }
1711
-
1712
- ]
1713
-
1714
- }
1715
-
1716
- ]
1717
- comments: any = [
1718
- {
1719
- email: "ryan@lenskart.com",
1720
- avatar: "assets/avatars/ryan.png",
1721
- time: "2025-01-20T10:53:34",
1722
- mainText: "All the three are same.",
1723
- statusLabel: null, // no “Marked as …” line
1724
- statusType: null,
1725
- note: null,
1726
- images: [
1727
- {
1728
- url: "assets/img1.jpg",
1729
- tangoId: 4,
1730
- entryTime: "10:10 AM",
1731
- status: "reject",
1732
- },
1733
- {
1734
- url: "assets/img2.jpg",
1735
- tangoId: 4,
1736
- entryTime: "10:10 AM",
1737
- status: "reject",
1738
- },
1739
- // more images if you want
1740
- ],
1741
- moreCount: 12, // for the +12 card
1742
- },
1743
- {
1744
- email: "drew@lenskart.com",
1745
- avatar: "assets/avatars/drew.png",
1746
- time: "2025-01-20T10:53:34",
1747
- mainText: null,
1748
- statusLabel: "Reject",
1749
- statusType: "danger", // for red text
1750
- note: "Not agreed, seems not duplicate.",
1751
- images: [
1752
- {
1753
- url: "assets/img1.jpg",
1754
- tangoId: 4,
1755
- entryTime: "10:10 AM",
1756
- status: "reject",
1757
- },
1758
- {
1759
- url: "assets/img2.jpg",
1760
- tangoId: 4,
1761
- entryTime: "10:10 AM",
1762
- status: "reject",
1763
- },
1764
- ],
1765
- moreCount: 12,
1766
- },
1767
- {
1768
- email: "orlando@lenskart.com",
1769
- avatar: "assets/avatars/orlando.png",
1770
- time: "2025-01-20T10:53:34",
1771
- mainText: null,
1772
- statusLabel: "Approved",
1773
- statusType: "success", // green
1774
- note: "Agreed, hence approving the 3",
1775
- images: [
1776
- {
1777
- url: "assets/img1.jpg",
1778
- tangoId: 4,
1779
- entryTime: "10:10 AM",
1780
- status: "approve",
1781
- },
1782
- {
1783
- url: "assets/img2.jpg",
1784
- tangoId: 4,
1785
- entryTime: "10:10 AM",
1786
- status: "approve",
1787
- },
1788
- ],
1789
- moreCount: 0,
1790
- },
1791
- ];
1792
-
1793
- showRevisedDetails = false;
1794
- footfallNoData: boolean = false;
1795
- footfallLoading: boolean = false;
1796
- footfalloffset = 1;
1797
- footfalllimit = 1;
1798
- totalItemsFootfall: any = 0;
1799
- dataIndexId: string = "";
1800
- dateString: string = "";
1801
- storeIdValue: any = [];
1802
- lastSelectedTicket: any;
1803
- selecteValues: any = "";
1804
- hasInitialStoreSynced: boolean = false;
1805
-
1806
- pageSizeFootfall: number = 10;
1807
- paginationSizesFootfall: number[] = [10, 20, 30];
1808
- addStoreIfNotExists(store: any) {
1809
- if (this.hasInitialStoreSynced) return;
1810
-
1811
- const storeId = store?.storeId;
1812
- if (storeId && !this.selectedStores.includes(storeId)) {
1813
- this.selectedStores.push(storeId);
1814
- }
1815
-
1816
- this.hasInitialStoreSynced = true;
1817
- // this.allSelected =
1818
- // this.selectedStores.length === this.openTicketsList.length;
1819
- }
1820
- revopsTypes: string[] = [];
1821
- buildRevopsTypes(tickets: any[]) {
1822
-
1823
- const types = new Set<string>();
1824
-
1825
- (tickets || []).forEach((ticket: any) => {
1826
- const mappingInfo = ticket?._source?.mappingInfo || [];
1827
- mappingInfo.forEach((mapping: any) => {
1828
- if(mapping?.status === 'In-Progress' && ['review','approve'].includes(mapping.type)){
1829
- this.ticketStatus = mapping?.createdByEmail
1830
- }
1831
- if(mapping.status === 'Closed' && ['review'].includes(mapping.type)){
1832
- this.isCheckboxEnable =false;
1833
- return
1834
- }
1835
- // 🔹 ONE function handles review + approve
1836
- if (mapping.status === 'Closed' && ['approve', 'tangoreview'].includes(mapping.type)) {
1837
- this.openArrow();
1838
- this.approverClosed = true;
1839
- }
1840
-
1841
- this.updateCloseStateFromMapping(mapping);
1842
- // 🔹 Existing revopsTypes logic
1843
- (mapping?.revisedDetail || []).forEach((item: any) => {
1844
- if (item?.revopsType) {
1845
- types.add(item.revopsType);
1846
- } else if (item?.type && item?.name) {
1847
- types.add(item.type); // e.g. 'vendor', 'technician', 'houseKeeping'
1848
- }
1849
- });
1850
- });
1851
- });
1852
-
1853
- this.revopsTypes = Array.from(types);
1854
- this.cd.detectChanges();
1855
- }
1856
- ticketStatus:any
1857
- // Handles both: review & approve in ONE place
1858
- private updateCloseStateFromMapping(mapping: any): void {
1859
- if (!mapping || mapping.status !== 'In-Progress') {
1860
- return;
1861
- }
1862
- // Decide which stage this mapping belongs to
1863
- const stage: 'review' | 'approve' | null =
1864
- mapping.type === 'review'
1865
- ? 'review'
1866
- : mapping.type === 'approve'
1867
- ? 'approve'
1868
- : null;
1869
- if (!stage) return; // ignore other types
1870
-
1871
- const { total, done, fullyDone } = this.getActionCompletion(mapping, stage);
1872
-
1873
- // 🔹 Case 1: nothing to review → hide/disable button
1874
- if (done === 0) {
1875
- this.isCheckboxEnable = true;
1876
- this.closeBtn = true;
1877
- this.closeDisabled = true;
1878
- } else {
1879
-
1880
- this.isCheckboxEnable = true;
1881
- this.closeBtn = true;
1882
- this.closeDisabled = !fullyDone;
1883
- }
1884
- this.cd.detectChanges();
1885
-
1886
- const hasCorrectRole =
1887
- (this.hasReviewerAccess && this.selectedRole === "reviewer") ||
1888
- (this.hasApproverAccess && this.selectedRole === "approver");
1889
-
1890
- const isOwner =
1891
- this.usersDetails?.email === this.ticketStatus ||
1892
- this.usersDetails?.email === this.ticketStatus;
1893
- if (hasCorrectRole === isOwner) {
1894
- this.isCheckboxEnable = true;
1895
- } else {
1896
- this.isCheckboxEnable = false;
1897
- }
1898
- }
1899
-
1900
- private getActionCompletion(mapping: any, actionType: 'review' | 'approve') {
1901
- const details = mapping?.revisedDetail || [];
1902
- const allItems: any[] = [];
1903
-
1904
- for (const d of details) {
1905
- if (!d.isParent && d.revopsType !== 'duplicate') {
1906
- allItems.push(d);
1907
- }
1908
- if (d.isParent && Array.isArray(d.duplicateImage)) {
1909
- allItems.push(...d.duplicateImage);
1910
- }
1911
- }
1912
-
1913
- const isDone = (item: any) => {
1914
- const actions = item.actions || [];
1915
- const act = actions.find((a: any) => a.actionType === actionType);
1916
- return act && (act.action === 'approved' || act.action === 'rejected');
1917
- };
1918
-
1919
- const done = allItems.filter(isDone).length;
1920
- const total = allItems.length;
1921
- if (done > 1) {
1922
-
1923
- }
1924
- return { total, done, fullyDone: total > 0 && done === total };
1925
-
1926
- }
1927
-
1928
-
1929
- hasRevopsType(type: any) {
1930
- return this.revopsTypes.includes(type);
1931
- }
1932
-
1933
- private isFinalStatus(item: any): boolean {
1934
- if (this.permissionType === 'review' && this.selectedRole === 'reviewer') {
1935
- return this.isApproved(item) || this.isRejected(item);
1936
- }
1937
-
1938
- if (this.permissionType === 'approve' && this.selectedRole === 'approver') {
1939
- return this.isApproved1(item) || this.isRejected1(item);
1940
- }
1941
-
1942
- return false;
1943
- }
1944
-
1945
-
1946
-
1947
- overallSelectedIds: any;
1948
- overallSelect(event: any) {
1949
- const checked = (event.target as HTMLInputElement).checked;
1950
-
1951
- this.allSelected = checked;
1952
-
1953
- const parents = this.getAllParentItems() || [];
1954
-
1955
- if (checked) {
1956
- // non-duplicate types
1957
- (this.revopsTypes || []).forEach((type: string) => {
1958
- if (type === 'duplicate') return;
1959
-
1960
- const list = (this.getListByType(type) || []).filter(
1961
- (i: any) => !this.isFinalStatus(i)
1962
- );
1963
-
1964
- this.selectedByType[type] = list.map((i: any) => i.tempId);
1965
- this.selectAllByType[type] = list.length > 0;
1966
- });
1967
-
1968
- // duplicate type
1969
- this.selectedDuplicatesByParent = {};
1970
-
1971
- parents
1972
- .filter((p: any) => p.revopsType === 'duplicate')
1973
- .forEach((p: any) => {
1974
- const parentId = p.tempId;
1975
-
1976
- const duplicates = (p.duplicateImage || []).filter(
1977
- (d: any) => !this.isFinalStatus(d)
1978
- );
1979
-
1980
- this.selectedDuplicatesByParent[parentId] = duplicates.map(
1981
- (d: any) => d.tempId
1982
- );
1983
- });
1984
- } else {
1985
- // clear everything
1986
- (this.revopsTypes || []).forEach((type: string) => {
1987
- if (type === 'duplicate') return;
1988
- this.selectedByType[type] = [];
1989
- this.selectAllByType[type] = false;
1990
- });
1991
-
1992
- this.selectedDuplicatesByParent = {};
1993
- this.selectAllByType.duplicate = false;
1994
- this.overallSelectedIds = [];
1995
- }
1996
-
1997
- // 🔥 always recalc after change
1998
- this.updateOverallSelectedIds();
1999
- }
2000
-
2001
-
2002
-
2003
- getAllParentItems() {
2004
- if (!this.footfallTicketsData) {
2005
- return [];
2006
- }
2007
-
2008
- const allMappings: any[] = [];
2009
- const allRevised: any[] = [];
2010
-
2011
- // first level: collect all mappingInfo arrays
2012
- this.footfallTicketsData.forEach((ticket: any) => {
2013
- const mappings = ticket?._source?.mappingInfo || [];
2014
- allMappings.push.apply(allMappings, mappings); // concat
2015
- });
2016
-
2017
- // second level: collect all revisedDetail arrays
2018
- allMappings.forEach((mapping: any) => {
2019
- const details = mapping?.revisedDetail || [];
2020
- allRevised.push.apply(allRevised, details); // concat
2021
- });
2022
-
2023
- return allRevised;
2024
- }
2025
-
2026
- isCheckboxEnable = false;
2027
- closeBtn = false;
2028
- closeDisabled = false;
2029
- startReview() {
2030
- let obj = {
2031
- "storeId": this.ticketData?.storeId,
2032
- "dateString": this.ticketData?.issueDate,
2033
- "mode": "web"
2034
- }
2035
- // return
2036
- this.service.getUpdateTicketStatusApi(obj).pipe(takeUntil(this.destroy$)).subscribe({
2037
- next: (res: any) => {
2038
- if (res && res.code === 200) {
2039
- this.isCheckboxEnable = true; // call this on your "Review" button click
2040
- this.closeBtn = true;
2041
- this.closeDisabled = true;
2042
- this.getTicketList(this.tangoType)
2043
-
2044
- this.dataStoreView(this.ticketData);
2045
- }
2046
- },
2047
- error: (err: any) => {
2048
- },
2049
- complete: () => {
2050
- this.cd.detectChanges();
2051
- }
2052
- })
2053
- }
2054
- @ViewChild("closePopup") closePopup: ElementRef;
2055
- stopReview() {
2056
- const modalRef = this.modalService.open(this.closePopup, {
2057
- centered: true,
2058
- size: "md",
2059
- backdrop: "static",
2060
- keyboard: false,
2061
- });
2062
- this.isCheckboxEnable = false;
2063
- this.closeBtn = true;
2064
- }
2065
-
2066
- ticketView(data?: any) {
2067
- this.ticketData = data;
2068
-
2069
- this.allSelected = false;
2070
- if (!data) {
2071
- this.getOpenTicketList(data);
2072
- this.select = 'ticketMethod';
2073
- return;
2074
- }
2075
-
2076
- const status = (data.status || '').trim();
2077
- if (this.usersDetails.userType === 'tango') {
2078
- this.dataStoreView(data);
2079
- } else {
2080
- if (status === 'Open') {
2081
- // when ticket status is Open → use open-ticket API
2082
- this.getOpenTicketList(data);
2083
- } else {
2084
- // otherwise → use store view API
2085
- this.dataStoreView(data);
2086
- }
2087
- }
2088
- if (data.status === 'Open - Accuracy Issue') {
2089
- this.accuracyReasons()
2090
- }
2091
- this.select = 'ticketMethod'; // keep your existing tab; change if you use a different view key
2092
- }
2093
- accuracyReasons() {
2094
- this.service
2095
- .accuracyReasons(this.headerFilters?.client)
2096
- .pipe(takeUntil(this.destroy$))
2097
- .subscribe({
2098
- next: (res: any) => {
2099
- if (res && res.code === 200) {
2100
- this.accuracyList = res.data.result
2101
- }
2102
- }
2103
- })
2104
-
2105
- }
2106
- oncloseSubmit() {
2107
- const modalRef = this.modalService.open(this.closeacuuracyissue)
2108
- modalRef.result.then((result) => {
2109
- console.log("🚀 ~ TicketFootfallNewComponent ~ createInternalticket ~ result.isConfirmed:", result)
2110
- if (result === 'isConfirmed') {
2111
- console.log(this.footfallTicketsData[0]?._source)
2112
- let payload = {
2113
- storeId: this.footfallTicketsData[0]?._source?.storeId,
2114
- dateString: this.footfallTicketsData[0]?._source?.dateString,
2115
- comments: this.selectedIssue,
2116
- subComment: this.selectedsubIssue,
2117
- }
2118
- console.log("🚀 ~ TicketFootfallNewComponent ~ oncloseSubmit ~ payload:", payload)
2119
- this.service
2120
- .accuracyReasonsupdate(payload)
2121
- .pipe(takeUntil(this.destroy$))
2122
- .subscribe({
2123
- next: (res: any) => {
2124
- if (res && res.code === 200) {
2125
- this.ts.getSuccessToast('updated Sucessfully'),
2126
- this.router.navigate(['/manage/tickets'], { queryParams: { type: 'footfall' } })
2127
- }
2128
- }
2129
- })
2130
-
2131
-
2132
- }
2133
- })
2134
- }
2135
- dataStoreView(data?: any) {
2136
- this.footfallTicketsData = [];
2137
- this.footfallNoData = false;
2138
- this.footfallLoading = true;
2139
- this.allSelected = false;
2140
- const ticket = data?.ticketId;
2141
-
2142
- this.lastSelectedTicket = ticket;
2143
- this.addStoreIfNotExists(ticket);
2144
- this.imageUrl = this.service?.footfallCDN;
2145
- this.storeIdValue = this.selectedStores;
2146
-
2147
- this.service
2148
- .getTicketsNewApi(ticket)
2149
- .pipe(takeUntil(this.destroy$))
2150
- .subscribe({
2151
- next: (res: any) => {
2152
- if (res && res.code === 200) {
2153
- this.getIconType();
2154
- if (res?.data?.result?.length === 0) {
2155
- this.footfallTicketsData = [];
2156
- this.footfallNoData = true;
2157
- this.footfallLoading = false;
2158
- this.ts.getErrorToast("No data found for the selected filters");
2159
- } else {
2160
- this.footfallTicketsData = res?.data?.result ?? [];
2161
- console.log("🚀 ~ TicketFootfallNewComponent ~ dataStoreView ~ this.footfallTicketsData:", this.footfallTicketsData)
2162
-
2163
- this.buildRevopsTypes(this.footfallTicketsData);
2164
- this.totalItemsFootfall = res?.data?.count;
2165
- this.cd.detectChanges();
2166
- if (this.footfalllimit === 1) {
2167
- this.paginationSizes = [1];
2168
- this.pageSizeFootfall = 1;
2169
- } else {
2170
- const limit =
2171
- this.totalItemsFootfall < 10 ? this.totalItemsFootfall : 10;
2172
- this.paginationSizes = [limit];
2173
- this.pageSizeFootfall = limit;
2174
- }
2175
- this.footfallNoData = false;
2176
- this.footfallLoading = false;
2177
- this.cd.detectChanges();
2178
- // this.getCurrentReviewMapping();
2179
- }
2180
- } else {
2181
- this.footfallTicketsData = [];
2182
- this.footfallNoData = true;
2183
- this.footfallLoading = false;
2184
- }
2185
- },
2186
- error: (err: any) => {
2187
- if (err?.error?.code === 400)
2188
- this.ts.getErrorToast(
2189
- err?.error?.message ? err?.error?.message : err?.error?.error
2190
- );
2191
- this.footfallTicketsData = [];
2192
- this.footfallNoData = true;
2193
- this.footfallLoading = false;
2194
- },
2195
- complete: () => {
2196
- this.cd.detectChanges();
2197
- },
2198
- });
2199
- }
2200
- footfallTicketsData: any[] = [];
2201
- // ---- SELECTION STATE ----
2202
-
2203
- selectedByType: any = {};
2204
- selectAllByType: any = {};
2205
-
2206
- // ---- HELPERS ----
2207
-
2208
- // Get all revisedDetail items of a given type from footfallTicketsData
2209
- // Get all revisedDetail items of a given type from footfallTicketsData
2210
- getListByType(type: any) {
2211
- if (!this.footfallTicketsData) {
2212
- return [];
2213
- }
2214
-
2215
- const allRevised: any[] = [];
2216
-
2217
- // collect revisedDetail ONLY from mappingInfo with type === 'review'
2218
- this.footfallTicketsData.forEach((ticket: any) => {
2219
- const mappings = ticket?._source?.mappingInfo || [];
2220
-
2221
- mappings
2222
- .filter((m: any) => m?.type === 'review') // 🔹 IMPORTANT
2223
- .forEach((mapping: any) => {
2224
- const details = mapping?.revisedDetail || [];
2225
- allRevised.push(...details);
2226
- });
2227
- });
2228
-
2229
- // filter by type (junk / employee / houseKeeping)
2230
- const filtered = allRevised.filter(
2231
- (original: any) => original?.revopsType === type
2232
- );
2233
-
2234
- // 🔹 DEDUPE by tempId (or id) so we don't count same image twice
2235
- const uniqueByTempId = Array.from(
2236
- new Map(filtered.map((item: any) => [item.tempId, item])).values()
2237
- );
2238
-
2239
- return uniqueByTempId;
2240
- }
2241
-
2242
-
2243
-
2244
- private updateOverallSelectedIds(): void {
2245
- const ids: string[] = [];
2246
- const parents = this.getAllParentItems() || [];
2247
- const selectedTypesSet = new Set<string>();
2248
-
2249
- // Non-duplicate
2250
- (this.revopsTypes || [])
2251
- .filter((type) => type !== 'duplicate')
2252
- .forEach((type: string) => {
2253
- const list = this.getListByType(type) || [];
2254
- const selectedTemps = this.selectedByType[type] || [];
2255
-
2256
- if (selectedTemps.length > 0) selectedTypesSet.add(type);
2257
-
2258
- list.forEach((item: any) => {
2259
- if (
2260
- selectedTemps.includes(item.tempId) &&
2261
- item.id &&
2262
- !this.isFinalStatus(item) // ⛔ safeguard
2263
- ) {
2264
- ids.push(item.id);
2265
- } else if( selectedTemps.includes(item.tempId) &&
2266
- item.id &&
2267
- this.isFinalStatus(item)) {
2268
- ids.push(item.id);
2269
- }
2270
- });
2271
- });
2272
-
2273
- // Duplicate
2274
- parents
2275
- .filter((p: any) => p.revopsType === 'duplicate')
2276
- .forEach((parent: any) => {
2277
- const parentId = parent.tempId;
2278
- const selectedChildTemps =
2279
- this.selectedDuplicatesByParent[parentId] || [];
2280
-
2281
- if (selectedChildTemps.length > 0) {
2282
- selectedTypesSet.add('duplicate');
2283
- }
2284
-
2285
- (parent.duplicateImage || []).forEach((child: any) => {
2286
- if (
2287
- selectedChildTemps.includes(child.tempId) &&
2288
- !this.isFinalStatus(child) // ⛔ safeguard
2289
- ) {
2290
- if (child.id) {
2291
- ids.push(child.id);
2292
- } else if (parent.id) {
2293
- ids.push(parent.id);
2294
- }
2295
- }
2296
- });
2297
- });
2298
-
2299
- this.overallSelectedIds = Array.from(new Set(ids));
2300
- this.selectedCategories = Array.from(selectedTypesSet);
2301
- }
2302
-
2303
-
2304
-
2305
- selectedCategories: any = [];
2306
- labelMap: Record<string, string> = {};
2307
- toTitleCase(str: string): string {
2308
- return str.charAt(0).toUpperCase() + str.slice(1);
2309
- }
2310
-
2311
- buildLabelMap(tickets: any[]) {
2312
- tickets.forEach((ticket: any) => {
2313
- const mappingInfo = ticket?._source?.mappingInfo || [];
2314
-
2315
- mappingInfo.forEach((mapping: any) => {
2316
- (mapping?.revisedDetail || []).forEach((item: any) => {
2317
-
2318
- // CASE 1: normal entries (employee, vendor, technician, etc.)
2319
- if (item?.revopsType) {
2320
- const key = item.revopsType;
2321
- if (!this.labelMap[key]) {
2322
- this.labelMap[key] = this.toTitleCase(key);
2323
- }
2324
- }
2325
-
2326
- // CASE 2: summary rows with { name, type }
2327
- if (item?.type && item?.name) {
2328
- this.labelMap[item.type] = item.name;
2329
- }
2330
-
2331
- });
2332
- });
2333
- });
2334
- }
2335
- get selectedCategoriesLabel(): string {
2336
- if (!this.selectedCategories?.length) return "";
2337
-
2338
- if (this.selectedCategories.length === 1) {
2339
- const type = this.selectedCategories[0];
2340
- return this.labelMap[type] || this.toTitleCase(type);
2341
- }
2342
-
2343
- return this.selectedCategories
2344
- .map((t: any) => this.labelMap[t] || this.toTitleCase(t))
2345
- .join(", ");
2346
- }
2347
- type: any;
2348
- // "Select all" / "Unselect all" for a type
2349
- popupvalue: any;
2350
- // or just: popupvalue: string = '';
2351
-
2352
- onSelectAll(type: any, event: any) {
2353
- const checked = event?.target?.checked;
2354
-
2355
- this.selectAllByType[type] = checked;
2356
- this.type = type;
2357
- this.popupvalue = type;
2358
-
2359
- const list = this.getListByType(type) || [];
2360
-
2361
- // ⛔ skip items that are already approved / rejected
2362
- const selectable = list.filter((x: any) => !this.isFinalStatus(x));
2363
-
2364
- this.selectedByType[type] = checked
2365
- ? selectable.map((x: any) => x.tempId)
2366
- : [];
2367
-
2368
- this.updateOverallSelectedIds();
2369
- }
2370
-
2371
-
2372
- // Single checkbox change for one image
2373
- onImageCheckboxChange(type: string, id: any, event: any) {
2374
- const checked = event?.target?.checked;
2375
- if (!this.selectedByType[type]) {
2376
- this.selectedByType[type] = [];
2377
- }
2378
- if (this.selectAllByType[type] === undefined) {
2379
- this.selectAllByType[type] = false;
2380
- }
2381
-
2382
- const arr = this.selectedByType[type];
2383
-
2384
- if (checked) {
2385
- // add if not present
2386
- if (!arr.includes(id)) {
2387
- arr.push(id);
2388
- }
2389
- } else {
2390
- // remove if present
2391
- const idx = arr.indexOf(id);
2392
- if (idx > -1) {
2393
- arr.splice(idx, 1);
2394
- }
2395
- }
2396
-
2397
- // total items for this type
2398
- const total = this.getListByType(type).length;
2399
-
2400
- // auto-update "Select All" for that type
2401
- this.selectAllByType[type] = total > 0 && arr.length === total;
2402
-
2403
- this.updateOverallSelectedIds();
2404
- this.popupvalue = type;
2405
- }
2406
-
2407
-
2408
-
2409
- remarks: any;
2410
- @ViewChild("zoomPopup") zoomPopup: ElementRef;
2411
- popupType: any;
2412
- popupIds: any[] = [];
2413
- popupOpen(
2414
- type: any,
2415
- value?: any
2416
- ) {
2417
- // store type
2418
- this.popupType = type;
2419
- this.popupIds = this.overallSelectedIds || [];
2420
- const modalRef = this.modalService.open(this.zoomPopup, {
2421
- centered: true,
2422
- size: "md",
2423
- backdrop: "static",
2424
- keyboard: false,
2425
- });
2426
- }
2427
-
2428
- houseKeepingACCount: any;
2429
- employeeACCount: any;
2430
- duplicateACCount: any;
2431
- junkACCount: any;
2432
-
2433
- selectedDuplicateImagesList: any[] = [];
2434
- selectedHousekeepingImagesList: any[] = [];
2435
- selectedEmployeeImagesList: any[] = [];
2436
- selectedJunkImagesList: any[] = [];
2437
- submitValue(status: string = "approved", category: string = "duplicate") {
2438
- let permissionType =
2439
- this.hasApproverAccess && this.selectedRole === 'approver'
2440
- ? 'approve'
2441
- : this.hasReviewerAccess && this.selectedRole === 'reviewer'
2442
- ? 'review'
2443
- : null;
2444
- // Step 1: Validate based on current category
2445
- let obj = {
2446
- id: this.popupIds,
2447
- status: status,
2448
- type: this.hasApproverAccess && this.selectedRole == 'approver' ? "approve" : "review",
2449
- ...(this.remarks?.trim() && { comments: this.remarks.trim() })
2450
- };
2451
- this.service
2452
- .getUpdateTempStatusApi(obj)
2453
- .pipe(takeUntil(this.destroy$))
2454
- .subscribe({
2455
- next: (res: any) => {
2456
- if (res && res?.code === 200) {
2457
- this.ts.getSuccessToast(`${this.overallSelectedIds?.length ? this.overallSelectedIds?.length : this.popupValue ?
2458
- this.popupValue : '--'} ${this.selectedCategoriesLabel.charAt(0).toUpperCase() + this.selectedCategoriesLabel.slice(1)} ${status}`);
2459
-
2460
- this.cancel();
2461
-
2462
- this.dataStoreView(this.ticketData);
2463
-
2464
- this.remarks = "";
2465
- } else {
2466
- this.ts.getErrorToast("Error updating status");
2467
- }
2468
- },
2469
- error: (err: any) => {
2470
- const errorMsg =
2471
- err?.error?.message ||
2472
- err?.error?.error ||
2473
- err?.error ||
2474
- err?.message ||
2475
- "Error updating status";
2476
- this.ts.getErrorToast(errorMsg);
2477
- },
2478
- complete: () => {
2479
- this.cd.detectChanges();
2480
- },
2481
- });
2482
- }
2483
- resetSelections() {
2484
- this.selectedByType = {};
2485
- this.selectAllByType = {};
2486
- this.selectedDuplicatesByParent = {};
2487
- this.overallSelectedIds = [];
2488
- this.popupIds = [];
2489
- }
2490
-
2491
- cancel() {
2492
- this.modalService.dismissAll();
2493
- this.resetSelections();
2494
- this.allSelected = false;
2495
- }
2496
- openArrow() {
2497
- this.arrowshow = !this.arrowshow;
2498
- }
2499
-
2500
- sortOpen: 1 | -1 = 1; // default ascending
2501
-
2502
- onSortClick() {
2503
- this.sortOpen = this.sortOpen === 1 ? -1 : 1; // 👈 toggle and SAVE
2504
- this.getOpenTicketList();
2505
- }
2506
- searchStoresData() {
2507
- this.StoresSearchValue = this.StoresSearchValue
2508
- this.getOpenTicketList();
2509
- }
2510
- getOpenTicketList(data?: any) {
2511
- this.footfallLoading = true;
2512
- const selectedTicketId = data?.ticketId ?? data;
2513
-
2514
- const obj = {
2515
- clientId: [this.headerFilters?.client],
2516
- fromDate: this.headerFilters?.date?.startDate,
2517
- toDate: this.headerFilters?.date?.endDate,
2518
- type: this.hasApproverAccess && this.selectedRole == 'approver' ? "approve" : "review",
2519
- sortOrder: this.sortOpen,
2520
- searchValue: this.StoresSearchValue
2521
- };
2522
-
2523
- this.service
2524
- .getOpenTicketListApi(obj)
2525
- .pipe(takeUntil(this.destroy$))
2526
- .subscribe({
2527
- next: (res: any) => {
2528
- if (res && res?.code === 200) {
2529
- this.openTicketsList = res?.data ?? [];
2530
- this.selectedStores = [];
2531
- this.allSelected = false;
2532
-
2533
- if (!this.openTicketsList.length) {
2534
- this.footfallNoData = true;
2535
- this.footfallLoading = false
2536
- this.allSelected = false;
2537
- return;
2538
- }
2539
-
2540
- let storeToSelect: any | undefined;
2541
- if (selectedTicketId) {
2542
- storeToSelect = this.openTicketsList.find(
2543
- (s: any) => s.ticketId === selectedTicketId
2544
- );
2545
- }
2546
-
2547
- if (!storeToSelect) {
2548
- storeToSelect = this.openTicketsList[0];
2549
- }
2550
-
2551
- this.selectedStores = [storeToSelect];
2552
- this.dataStoreView(storeToSelect);
2553
- } else {
2554
- this.openTicketsList = [];
2555
- this.selectedStores = [];
2556
- this.footfallNoData = true;
2557
- this.allSelected = false;
2558
- }
2559
- },
2560
- error: () => {
2561
- this.openTicketsList = [];
2562
- this.selectedStores = [];
2563
- this.allSelected = false;
2564
- },
2565
- complete: () => {
2566
- this.cd.detectChanges();
2567
- },
2568
- });
2569
- }
2570
- isLockedByReviewer(original: any): boolean {
2571
- if (this.hasApproverAccess && this.selectedRole === 'approver') {
2572
- this.getCurrentRoleMapping();
2573
- return this.isApproved1(original) || this.isRejected1(original);
2574
- }
2575
- this.getCurrentReviewMapping();
2576
-
2577
- return this.isApproved(original) || this.isRejected(original);
2578
- }
2579
-
2580
- isApproved1(original: any): boolean {
2581
- const actions = original?.actions || [];
2582
- return actions.some(
2583
- (a: any) => a.actionType === "approve" && a.action === "approved"
2584
- );
2585
- }
2586
-
2587
- isRejected1(original: any): boolean {
2588
- const actions = original?.actions || [];
2589
- return actions.some(
2590
- (a: any) => a.actionType === "approve" && a.action === "rejected"
2591
- );
2592
- }
2593
-
2594
- isApproved(original: any): boolean {
2595
- const actions = original?.actions || [];
2596
- return actions.some(
2597
- (a: any) => a.actionType === "review" && a.action === "approved"
2598
- );
2599
- }
2600
-
2601
- isRejected(original: any): boolean {
2602
- const actions = original?.actions || [];
2603
- return actions.some(
2604
- (a: any) => a.actionType === "review" && a.action === "rejected"
2605
- );
2606
- }
2607
-
2608
-
2609
- resetCheckbox(
2610
- type: any,
2611
- original: any
2612
- ) {
2613
- if (!original?.actions || !Array.isArray(original.actions)) {
2614
- return;
2615
- }
2616
- console.log(original)
2617
- const actions = original.actions;
2618
-
2619
- // 1) Check if there is a decision (review/approve → approved/rejected)
2620
- const hasDecision = actions.some((a: any) =>
2621
- (
2622
- (a.actionType === "review" || a.actionType === "approve") &&
2623
- (a.action === "approved" || a.action === "rejected")
2624
- )
2625
- );
2626
-
2627
- // If NO approved/rejected → do nothing
2628
- if (!hasDecision) {
2629
- return;
2630
- }
2631
-
2632
- // 2) Remove only decision actions
2633
- original.actions = actions.filter(
2634
- (a: any) =>
2635
- !(
2636
- (a.actionType === "review" || a.actionType === "approve") &&
2637
- (a.action === "approved" || a.action === "rejected")
2638
- )
2639
- );
2640
-
2641
- // 3) Remove from selected array
2642
- const tempId = original.tempId;
2643
- const arr = this.selectedByType[type] || [];
2644
- const i = arr.indexOf(tempId);
2645
- if (i > -1) arr.splice(i, 1);
2646
-
2647
- // 4) Uncheck select all for that type
2648
- this.selectAllByType[type] = false;
2649
- }
2650
-
2651
-
2652
- viewMode: "tangoreview" | "approve" | "review" | "tagging" = "tagging";
2653
- getAction(original: any, type: "tagging" | "review" | "tangoreview" | "approve") {
2654
- return original?.actions?.find((a: any) => a.actionType === type) || null;
2655
- }
2656
-
2657
-
2658
- getCurrentReviewMapping(): any {
2659
- if (!this.footfallTicketsData || !this.footfallTicketsData.length) {
2660
- return null;
2661
- }
2662
-
2663
- // Go through all tickets and mappingInfo
2664
- for (const ticket of this.footfallTicketsData) {
2665
- const source = ticket?._source;
2666
- const mappingList = source?.mappingInfo || [];
2667
-
2668
- // pick the first mapping with type 'review' that has revisedDetail
2669
- const found = mappingList.find(
2670
- (m: any) => m?.type === 'review' && m?.revisedDetail?.length
2671
- );
2672
-
2673
- if (found) {
2674
- return found;
2675
- }
2676
- }
2677
-
2678
- return null;
2679
- }
2680
-
2681
-
2682
- // Decide if ticket is fully reviewed based on ALL mappingInfo[type='review']
2683
- isTicketFullyReviewed(mapping: any): boolean {
2684
- if (!mapping?.revisedDetail) return false;
2685
-
2686
- const details = mapping.revisedDetail as any[];
2687
-
2688
- const nonDupTypes: any = [];
2689
-
2690
- const nonDupItems = details.filter(d =>
2691
- nonDupTypes.includes(d.revopsType)
2692
- );
2693
-
2694
- const nonDupReviewedCount = nonDupItems.filter(d => {
2695
- const review = this.getAction(d, "review");
2696
- return review && (review.action === "approved" || review.action === "rejected");
2697
- }).length;
2698
-
2699
- const parentItems = details.filter(d => d.isParent);
2700
-
2701
- const dupChildren = parentItems.reduce((acc: any[], p: any) => {
2702
- return acc.concat(p.duplicateImage || []);
2703
- }, []);
2704
-
2705
- const dupReviewedCount = dupChildren.filter(d => {
2706
- const review = this.getAction(d, "review");
2707
- return review && (review.action === "approved" || review.action === "rejected");
2708
- }).length;
2709
-
2710
- const totalToReview = nonDupItems.length + dupChildren.length;
2711
- const totalReviewed = nonDupReviewedCount + dupReviewedCount;
2712
-
2713
- return totalToReview > 0 && totalReviewed === totalToReview;
2714
- }
2715
-
2716
-
2717
- getInitialsFromEmail(email?: string): string {
2718
- if (!email) return "";
2719
-
2720
- // take text before @
2721
- const namePart = email.split("@")[0];
2722
-
2723
- // split by . or space (e.g. "sandeep.pal" -> ["sandeep", "pal"])
2724
- const parts = namePart.split(/[.\s_-]+/).filter(Boolean);
2725
-
2726
- if (parts.length >= 2) {
2727
- return (parts[0][0] + parts[1][0]).toUpperCase(); // S + P = SP
2728
- }
2729
-
2730
- // fallback: first two chars of whole namePart
2731
- return namePart.substring(0, 2).toUpperCase();
2732
- }
2733
-
2734
- confirmCloseTicket() {
2735
- let permissionType = this.hasApproverAccess && this.selectedRole === 'approver'
2736
- ? 'approve'
2737
- : this.hasReviewerAccess && this.selectedRole === 'reviewer'
2738
- ? 'review'
2739
- : null;
2740
- let obj = {
2741
- ticketName: "footfall-directory",
2742
- storeId: this.footfallTicketsData[0]?._source?.storeId,
2743
- dateString: this.footfallTicketsData[0]?._source?.dateString,
2744
- type: this.hasApproverAccess && this.selectedRole == 'approver' ? "approve" : "review",
2745
- mode: "web",
2746
- };
2747
- this.service
2748
- .getCreateTicketListApi(obj)
2749
- .pipe(takeUntil(this.destroy$))
2750
- .subscribe({
2751
- next: (res: any) => {
2752
- if (res && res?.code === 200) {
2753
- this.ts.getSuccessToast("Ticket closed successfully");
2754
- this.modalService.dismissAll();
2755
- this.closeBtn = false;
2756
- this.closeDisabled = true;
2757
- this.dataStoreView(this.ticketData);
2758
- } else {
2759
- this.closeBtn = true;
2760
- this.closeDisabled = false;
2761
- this.ts.getErrorToast(res.error ? res.error : '');
2762
- }
2763
- this.cd.detectChanges();
2764
- },
2765
- error: (err: any) => {
2766
- const errorMsg =
2767
- err?.error?.message ||
2768
- err?.error?.error ||
2769
- err?.error ||
2770
- err?.message ||
2771
- "Error closing ticket";
2772
- this.ts.getErrorToast(errorMsg);
2773
- // this.isCheckboxEnable = true;
2774
- this.closeBtn = true;
2775
- this.closeDisabled = false;
2776
- }
2777
- });
2778
- }
2779
-
2780
- confirmCloseCancel() {
2781
- this.modalService.dismissAll();
2782
- this.closeBtn = false;
2783
- this.closeDisabled = true;
2784
- this.dataStoreView(this.ticketData);
2785
- }
2786
- @ViewChild("imagePreviewPopup") imagePreviewPopup: ElementRef;
2787
- previewList: any[] = [];
2788
- currentPreviewIndex = 0;
2789
- previewTitle = 'Tagged Duplicates';
2790
- get currentPreviewItem() {
2791
- if (!this.previewList || !this.previewList.length) {
2792
- return null;
2793
- }
2794
-
2795
- if (
2796
- this.currentPreviewIndex == null ||
2797
- this.currentPreviewIndex < 0 ||
2798
- this.currentPreviewIndex >= this.previewList.length
2799
- ) {
2800
- return null;
2801
- }
2802
-
2803
- return this.previewList[this.currentPreviewIndex];
2804
- }
2805
-
2806
-
2807
-
2808
- getPreviewImageUrl() {
2809
- const item = this.currentPreviewItem;
2810
- return item ? this.imageUrl + item.filePath : '';
2811
- }
2812
- overallMapping: any;
2813
- openImagePreview(list: any, target: any, startIndex: number, title: string) {
2814
-
2815
- this.overallMapping = list;
2816
- this.previewTitle = title;
2817
-
2818
- // Decide preview list
2819
- if (Array.isArray(target) && target.length) {
2820
- this.previewList = target;
2821
- console.log(this.previewList)
2822
- } else if (Array.isArray(list) && list.length) {
2823
- this.previewList = list; // Case 2
2824
- } else if (Array.isArray(list?.revisedDetail)) {
2825
- this.previewList = list.revisedDetail; // Case 3
2826
- } else {
2827
- this.previewList = []; // fallback
2828
- }
2829
-
2830
- // Set correct preview index
2831
- this.setPreviewIndex(startIndex, target);
2832
-
2833
- // Open popup
2834
- this.modalService.open(this.imagePreviewPopup, {
2835
- centered: true,
2836
- size: 'lg',
2837
- backdrop: 'static',
2838
- });
2839
- }
2840
-
2841
- private setPreviewIndex(startIndex: number | null | undefined, target: any) {
2842
-
2843
- let index = -1;
2844
-
2845
- // 1️⃣ Always try to find the clicked item by ID/tempId FIRST
2846
- if (target) {
2847
- index = this.previewList.findIndex((item: any) =>
2848
- (target.tempId && item.tempId === target.tempId) ||
2849
- (target.id && item.id === target.id)
2850
- );
2851
- }
2852
-
2853
- // 2️⃣ If not found by ID, fallback to startIndex (ONLY if valid)
2854
- if (
2855
- index < 0 &&
2856
- startIndex != null &&
2857
- startIndex >= 0 &&
2858
- startIndex < this.previewList.length
2859
- ) {
2860
- index = startIndex;
2861
- }
2862
-
2863
- // 3️⃣ Final fallback: use first image
2864
- if (index < 0) {
2865
- index = 0;
2866
- }
2867
-
2868
- // Save final index
2869
- this.currentPreviewIndex = index;
2870
- }
2871
-
2872
-
2873
-
2874
- // 1) Check if there is an APPROVE mapping with status Closed in overall mapping
2875
- overallApproveClosed(): boolean {
2876
- const list = this.overallMapping || [];
2877
-
2878
- return Array.isArray(list) && list.some((m: any) =>
2879
- m?.type === 'approve' &&
2880
- m?.status?.toLowerCase() === 'closed'
2881
- );
2882
- }
2883
-
2884
- // 2) Get approve action for a single image
2885
- getApproveActionForItem(item: any): any | null {
2886
- const actions = item?.actions || [];
2887
- return actions.find(
2888
- (a: any) =>
2889
- a.actionType === 'approve' &&
2890
- (a.action === 'approved' || a.action === 'rejected')
2891
- ) || null;
2892
- }
2893
-
2894
- // 3) Final function: can we show Accept / Reject for this image?
2895
- canShowPreviewActions(item: any): boolean {
2896
- // if overall approve is closed -> no buttons
2897
- if (this.overallApproveClosed()) {
2898
- return false;
2899
- }
2900
-
2901
- // if this image already has approve approved/rejected -> no buttons
2902
- const approveAction = this.getApproveActionForItem(item);
2903
- if (approveAction) {
2904
- return false;
2905
- }
2906
-
2907
- // otherwise -> show buttons
2908
- return true;
2909
- }
2910
-
2911
- prevPreview() {
2912
- if (this.previewList.length <= 1) return;
2913
- this.currentPreviewIndex =
2914
- this.currentPreviewIndex === 0
2915
- ? this.previewList.length - 1
2916
- : this.currentPreviewIndex - 1;
2917
- }
2918
-
2919
- nextPreview() {
2920
- if (this.previewList.length <= 1) return;
2921
- this.currentPreviewIndex =
2922
- this.currentPreviewIndex === this.previewList.length - 1
2923
- ? 0
2924
- : this.currentPreviewIndex + 1;
2925
- }
2926
- popupValue: any;
2927
- rejectedPopup(type: any, value: any) {
2928
- this.overallSelectedIds = [value?.id];
2929
- this.popupValue = 1;
2930
- this.popupOpen(type)
2931
- }
2932
- approvedPopup(type: any, value: any) {
2933
- this.overallSelectedIds = [value?.id]
2934
- this.popupValue = 1;
2935
- this.popupOpen(type)
2936
- }
2937
- get hasApproverAccess(): boolean {
2938
- const perm = this.usersDetails?.permission;
2939
- return !!(
2940
- perm?.FootfallDirectory_approver_isAdd ||
2941
- perm?.FootfallDirectory_approver_isEdit
2942
- );
2943
- }
2944
-
2945
- get hasReviewerAccess(): boolean {
2946
- const perm = this.usersDetails?.permission;
2947
- return !!(
2948
- perm?.FootfallDirectory_reviewer_isAdd ||
2949
- perm?.FootfallDirectory_reviewer_isEdit
2950
- );
2951
- }
2952
-
2953
- // call this in ngOnInit or after usersDetails is loaded
2954
- setDefaultRole() {
2955
- if (this.hasApproverAccess) {
2956
- this.selectedRole = 'approver';
2957
- } else if (this.hasReviewerAccess) {
2958
- this.selectedRole = 'reviewer';
2959
- } else {
2960
- this.selectedRole = '';
2961
- }
2962
- }
2963
-
2964
- SelectedRole(role: 'approver' | 'reviewer') {
2965
- if (role === 'approver' && !this.hasApproverAccess) return;
2966
- if (role === 'reviewer' && !this.hasReviewerAccess) return;
2967
-
2968
- this.selectedRole = role;
2969
- this.getTicketList(this.tangoType);
2970
- this.getTicketSummary(this.tangoType);
2971
- // 🔹 put your role-based logic here:
2972
- // e.g. filter mappingInfo, change API call, etc.
2973
- // this.loadRoleData(role);
2974
- }
2975
-
2976
-
2977
- showApproveDetails = false;
2978
- showTangoDetails = false;
2979
-
2980
- toggleRevisedDetails(mapping: any) {
2981
- if (mapping?.type === 'approve') {
2982
- this.showApproveDetails = !this.showApproveDetails;
2983
- } else if (mapping?.type === 'tangoreview') {
2984
- this.showTangoDetails = !this.showTangoDetails;
2985
- }
2986
- }
2987
-
2988
- reviwerList: any;
2989
- reviewrReassignApi() {
2990
- this.service.getReviewerApi(this.headerFilters?.client, 'approve', this.tangoType).pipe(takeUntil(this.destroy$))
2991
- .subscribe({
2992
- next: (res: any) => {
2993
- if (res && res?.code === 200) {
2994
- this.reviwerList = res?.data;
2995
-
2996
- } else {
2997
- this.reviwerList = [];
2998
- }
2999
- }, error: (err) => {
3000
-
3001
- },
3002
- })
3003
-
3004
- }
3005
- reviewrApi() {
3006
- this.service.getReviewerApi(this.headerFilters?.client, 'approve', this.tangoType).pipe(takeUntil(this.destroy$))
3007
- .subscribe({
3008
- next: (res: any) => {
3009
- if (res && res?.code === 200) {
3010
- this.reviwerList = res?.data;
3011
- this.viewTicket('store');
3012
- } else {
3013
- this.reviwerList = [];
3014
- }
3015
- }, error: (err) => {
3016
-
3017
- },
3018
- })
3019
-
3020
- }
3021
- reviewerChange(event: any) {
3022
- this.selectedReviewer = event;
3023
- }
3024
- assignTicketView() {
3025
-
3026
- if (this.tangoType === 'internal') {
3027
- let obj = {
3028
- email: this.selectedReviewer?.email,
3029
- userName: this.selectedReviewer?.userName,
3030
- storeId: this.ticketData?.storeId,
3031
- dateString: this.ticketData?.issueDate,
3032
- }
3033
- this.service.reAssignAudit(obj).pipe(takeUntil(this.destroy$))
3034
- .subscribe({
3035
- next: (res: any) => {
3036
- if (res && res?.code === 200) {
3037
- this.ts.getSuccessToast(res?.data?.message ? res?.data?.message : 'Ticket assigned successfully');
3038
- this.backToNavigation();
3039
- this.cancelassign();
3040
- }
3041
- }
3042
- })
3043
- return
3044
- }
3045
-
3046
-
3047
- let obj = {
3048
- email: this.selectedReviewer?.email,
3049
- userName: this.selectedReviewer?.userName,
3050
- actionType: this.hasApproverAccess && this.selectedRole === 'approver' ? "approve" : "review",
3051
- storeId: this.ticketData?.storeId,
3052
- dateString: this.ticketData?.issueDate,
3053
- }
3054
- this.service.getTicketAssignApi(obj).pipe(takeUntil(this.destroy$))
3055
- .subscribe({
3056
- next: (res: any) => {
3057
- if (res && res?.code === 200) {
3058
- this.ts.getSuccessToast(res?.data?.message ? res?.data?.message : 'Ticket assigned successfully');
3059
- this.backToNavigation();
3060
- this.cancelassign();
3061
- }
3062
- else {
3063
- this.ts.getErrorToast("Error updating status");
3064
- }
3065
- }, error: (err) => {
3066
- const errorMsg =
3067
- err?.error?.message ||
3068
- err?.error?.error ||
3069
- err?.error ||
3070
- err?.message ||
3071
- "Error closing ticket";
3072
- this.ts.getErrorToast(errorMsg);
3073
- },
3074
- })
3075
- }
3076
- cancelassign() {
3077
- this.modalService.dismissAll();
3078
- this.reviwerList = [];
3079
- this.selectedReviewer = [];
3080
- }
3081
- isTicketMenuOpen = false;
3082
-
3083
- toggleTicketMenu(event: MouseEvent) {
3084
- event.stopPropagation();
3085
- this.isTicketMenuOpen = !this.isTicketMenuOpen;
3086
- }
3087
-
3088
- // close when clicking outside
3089
- @HostListener('document:click')
3090
- onDocumentClick() {
3091
- this.isTicketMenuOpen = false;
3092
- }
3093
-
3094
- onReassignClick() {
3095
- this.isTicketMenuOpen = false;
3096
- // 🔹 open your popup here
3097
- this.openReassignPopup(); // implement this
3098
- }
3099
- @ViewChild("ReassignTicketPopup") ReassignTicketPopup: ElementRef;
3100
- openReassignPopup() {
3101
- const modalRef = this.modalService.open(this.ReassignTicketPopup, {
3102
- centered: true,
3103
- size: "md",
3104
- scrollable: true,
3105
- backdrop: "static",
3106
- });
3107
- this.reviewrReassignApi();
3108
- }
3109
- onExportClick() {
3110
- this.isTicketMenuOpen = false;
3111
- // 🔹 call your export API / logic here
3112
- // this.exportTicket();
3113
- }
3114
-
3115
- getMultipleTicketClose() {
3116
- // if (!this.selectedStores || !this.selectedStores.length) {
3117
- // this.ts.getErrorToast('Please select at least one ticket');
3118
- // return;
3119
- // }
3120
- let ticketList: any;
3121
-
3122
- if (this.selectedStores?.length) {
3123
- // When multiple stores are selected
3124
- ticketList = this.selectedStores.map((store: any) => ({
3125
- storeId: store.storeId,
3126
- dateString: store.dateString || store.issueDate,
3127
- }));
3128
- } else {
3129
- // When ticket rows are selected instead
3130
- ticketList = this.selectedTicketRows.map((store: any) => ({
3131
- storeId: store.storeId,
3132
- dateString: store.dateString || store.issueDate,
3133
- }));
3134
- }
3135
-
3136
- const obj = {
3137
- ticketList,
3138
- mode: 'web',
3139
- };
3140
-
3141
- this.service.getMultiCloseTicketApi(obj).subscribe({
3142
- next: (res: any) => {
3143
- if (res && res.code === 200) {
3144
- // success
3145
- this.ts.getSuccessToast('Tickets closed successfully');
3146
- this.dataStoreView(this.selectedStores);
3147
- this.selectedStores = [];
3148
- this.allSelected = false;
3149
- } else {
3150
- // this.toastService.getErrorToast('Failed to close tickets');
3151
- }
3152
- },
3153
- error: (err) => {
3154
- console.error('Failed to close tickets', err);
3155
- this.ts.getErrorToast('Failed to close tickets');
3156
- },
3157
- });
3158
- }
3159
- isRowSelectable(row: any): boolean {
3160
- return row?.status?.toLowerCase() === 'open' || row?.status === 'Reviewed-Closed';
3161
- }
3162
- selectedTicketRows: any = [];
3163
- // check if a row is currently selected
3164
- isRowSelected(row: any): boolean {
3165
- return this.selectedTicketRows.includes(row);
3166
- }
3167
-
3168
- // rows that CAN be selected (status open)
3169
- getSelectableRows(): any[] {
3170
- return (this.footfallListData || []).filter((r: any) => this.isRowSelectable(r));
3171
- }
3172
-
3173
- // header "select all" checkbox state
3174
- areAllSelectableRowsSelected(): boolean {
3175
- const selectable = this.getSelectableRows();
3176
- return (
3177
- selectable.length > 0 &&
3178
- selectable.every(r => this.selectedTicketRows.includes(r))
3179
- );
3180
- }
3181
- closeMultiple = true;
3182
-
3183
-
3184
-
3185
- private updateCloseDisabled() {
3186
- this.closeMultiple = this.selectedTicketRows.length === 0;
3187
- }
3188
- // single row checkbox toggle
3189
- toggleRowSelection(row: any, event: Event): void {
3190
- if (!this.isRowSelectable(row)) return;
3191
-
3192
- const target = event.target as HTMLInputElement;
3193
- const checked = target.checked;
3194
-
3195
- if (checked) {
3196
- if (!this.isRowSelected(row)) {
3197
- this.selectedTicketRows = [...this.selectedTicketRows, row];
3198
- }
3199
- } else {
3200
- this.selectedTicketRows = this.selectedTicketRows.filter((r: any) => r !== row);
3201
- }
3202
- this.updateCloseDisabled();
3203
- }
3204
-
3205
- // header "Select All" toggle
3206
- toggleSelectAllRows(event: Event): void {
3207
- const target = event.target as HTMLInputElement;
3208
- const checked = target.checked;
3209
-
3210
- if (checked) {
3211
- this.selectedTicketRows = this.getSelectableRows().slice();
3212
- } else {
3213
- this.selectedTicketRows = [];
3214
- }
3215
- this.updateCloseDisabled();
3216
- }
3217
- showImagesModal = false;
3218
- selectedImagesForPopup: any[] = [];
3219
- selectedComment: any | null = null;
3220
-
3221
- openImagesPopup(comment: any): void {
3222
- // console.log(comment)
3223
- // return
3224
- this.selectedComment = comment;
3225
- this.selectedImagesForPopup = comment?.taggedImages || [];
3226
- this.showImagesModal = true;
3227
- }
3228
-
3229
- closeImagesPopup(): void {
3230
- this.showImagesModal = false;
3231
- this.selectedImagesForPopup = [];
3232
- this.selectedComment = null;
3233
- }
3234
-
3235
- getIconTypeList: any;
3236
- tagIconMap: Record<string, string> = {}; // type -> iconName
3237
-
3238
- getIconType() {
3239
- this.service
3240
- .getTypeFunction(this.headerFilters?.client)
3241
- .pipe(takeUntil(this.destroy$))
3242
- .subscribe({
3243
- next: (res: any) => {
3244
- if (res && res?.code === 200) {
3245
- this.getIconTypeList = res?.data?.footfallDirectoryConfigs || {};
3246
-
3247
- const taggingLimitation = this.getIconTypeList?.taggingLimitation || [];
3248
-
3249
- this.tagIconMap = {};
3250
- taggingLimitation.forEach((item: any) => {
3251
- if (item?.type && item?.iconName) {
3252
- this.tagIconMap[item.type] = item.iconName;
3253
- }
3254
- });
3255
- } else {
3256
- this.getIconTypeList = {};
3257
- this.tagIconMap = {};
3258
- }
3259
- },
3260
- error: (err) => {
3261
- this.getIconTypeList = {};
3262
- this.tagIconMap = {};
3263
- },
3264
- });
3265
- }
3266
- onFilterPanelClosed() {
3267
- // nothing required, but you can do logging here
3268
- }
3269
-
3270
- onFilterApply(filters: any) {
3271
- this.filterPayload = filters;
3272
- this.offset = 0;
3273
- this.getTicketList(this.tangoType);
3274
- }
3275
- revFilter: 'all' | 'accept' | 'reject' | 'pending' = 'all';
3276
- // private getActionStatus(item: any): 'accept' | 'reject' | 'pending' | null {
3277
- // const actions = item?.actions || [];
3278
-
3279
- // const approveAction = actions.find(
3280
- // (a: any) => a?.actionType === 'approve' || a?.actionType === 'review'
3281
- // );
3282
-
3283
- // if (!approveAction) return 'pending';
3284
-
3285
- // const act = (approveAction.action || '').toLowerCase();
3286
-
3287
- // if (act === 'approved' || act === 'accept' || act === 'accepted') {
3288
- // return 'accept';
3289
- // }
3290
-
3291
- // if (act === 'rejected' || act === 'reject') {
3292
- // return 'reject';
3293
- // }
3294
-
3295
- // return 'pending';
3296
- // }
3297
- onFilterChange(): any {
3298
- if (this.revFilter === 'all') return true;
3299
- console.log(this.revFilter, this.footfallTicketsData[0]?._source.mappingInfo[this.footfallTicketsData[0]._source?.mappingInfo?.length - 1])
3300
-
3301
- this.footfallTicketsData.map((mapdata: any) => {
3302
- mapdata._source.mappingInfo.map((data: any) => {
3303
- if (data.status === this.footfallTicketsData[0]._source.status) {
3304
- if (data.revisedDetail.revopsType === 'duplicate') {
3305
- console.log(data.duplicateImage.actions)
3306
-
3307
- }
3308
- }
3309
-
3310
-
3311
-
3312
- })
3313
-
3314
- })
3315
-
3316
-
3317
- }
3318
- private getItemStatus(item: any): 'accept' | 'reject' | 'pending' {
3319
- const actions = item?.actions || [];
3320
-
3321
- const act = actions.find(
3322
- (a: any) => a.actionType === 'approve' || a.actionType === 'review'
3323
- );
3324
-
3325
- if (!act) return 'pending';
3326
-
3327
- const s = (act.action || '').toLowerCase();
3328
-
3329
- if (s === 'approved' || s === 'accept' || s === 'accepted') return 'accept';
3330
- if (s === 'rejected' || s === 'reject') return 'reject';
3331
-
3332
- return 'pending';
3333
- }
3334
- matchesStatusFilter(item: any): boolean {
3335
- if (!item) return false;
3336
- const status = this.getItemStatus(item);
3337
-
3338
- if (this.revFilter === 'all') return true;
3339
- if (this.revFilter === 'accept') return status === 'accept';
3340
- if (this.revFilter === 'reject') return status === 'reject';
3341
- if (this.revFilter === 'pending') return status === 'pending';
3342
-
3343
- return true;
3344
- }
3345
-
3346
- // onFilterChange() {
3347
- // console.log("Filter changed:", this.revFilter);
3348
- // }
3349
-
3350
-
3351
- }