tango-app-ui-ticket-user 3.5.2 → 3.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/components/preinstallation-info/preinstallation-info.component.mjs +17 -4
- package/fesm2022/tango-app-ui-ticket-user.mjs +16 -3
- package/fesm2022/tango-app-ui-ticket-user.mjs.map +1 -1
- package/lib/components/preinstallation-info/preinstallation-info.component.d.ts +5 -1
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ import * as i5 from "@ng-bootstrap/ng-bootstrap";
|
|
|
11
11
|
* Same role as InstallationComponent for TE_INS tickets: the ticket header +
|
|
12
12
|
* activity trail come from the tickets API, the LIVE camera-placement state
|
|
13
13
|
* comes from the store-builder CCTV row, and the page gates the next step:
|
|
14
|
-
* autoPlacement -> "Auto placement in progress", editor locked
|
|
14
|
+
* autoPlacement -> "Auto placement in progress", editor locked, source preview
|
|
15
15
|
* failed -> failure reason + Retry
|
|
16
16
|
* open -> "Open DXF editor" (assignee gated server-side)
|
|
17
17
|
* approvalPending-> waiting for approval, drawing viewable
|
|
@@ -34,6 +34,8 @@ export class PreinstallationInfoComponent {
|
|
|
34
34
|
fileUrls = {};
|
|
35
35
|
loading = true;
|
|
36
36
|
cctvLoading = false;
|
|
37
|
+
/** Refresh clicked and the ticket reload is still in flight (icon turns) */
|
|
38
|
+
reloading = false;
|
|
37
39
|
cctvError = '';
|
|
38
40
|
retrying = false;
|
|
39
41
|
destroy$ = new Subject();
|
|
@@ -101,6 +103,7 @@ export class PreinstallationInfoComponent {
|
|
|
101
103
|
.subscribe({
|
|
102
104
|
next: (res) => {
|
|
103
105
|
this.loading = false;
|
|
106
|
+
this.reloading = false;
|
|
104
107
|
if (res && res.code == 200) {
|
|
105
108
|
this.ticketInfo = res.data;
|
|
106
109
|
if (this.floorId) {
|
|
@@ -110,6 +113,7 @@ export class PreinstallationInfoComponent {
|
|
|
110
113
|
},
|
|
111
114
|
error: () => {
|
|
112
115
|
this.loading = false;
|
|
116
|
+
this.reloading = false;
|
|
113
117
|
this.toastService.getErrorToast('Unable to load the ticket.');
|
|
114
118
|
},
|
|
115
119
|
});
|
|
@@ -136,6 +140,7 @@ export class PreinstallationInfoComponent {
|
|
|
136
140
|
});
|
|
137
141
|
}
|
|
138
142
|
refresh() {
|
|
143
|
+
this.reloading = true;
|
|
139
144
|
this.viewTicket();
|
|
140
145
|
}
|
|
141
146
|
retryPlacement() {
|
|
@@ -165,14 +170,22 @@ export class PreinstallationInfoComponent {
|
|
|
165
170
|
queryParams: { floorId: this.floorId, mode: 'edit' },
|
|
166
171
|
});
|
|
167
172
|
}
|
|
173
|
+
/** The source drawing, read-only, while auto placement is still running. */
|
|
174
|
+
openPreview() {
|
|
175
|
+
if (!this.floorId)
|
|
176
|
+
return;
|
|
177
|
+
this.router.navigate(['/manage/planogram/cctv-preview'], {
|
|
178
|
+
queryParams: { floorId: this.floorId, from: 'ticket' },
|
|
179
|
+
});
|
|
180
|
+
}
|
|
168
181
|
back() {
|
|
169
182
|
this.router.navigate(['/tickets/users'], { queryParams: { type: 'installation' } });
|
|
170
183
|
}
|
|
171
184
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreinstallationInfoComponent, deps: [{ token: i1.ActivatedRoute }, { token: i1.Router }, { token: i2.TicketsService }, { token: i3.ToastService }], target: i0.ɵɵFactoryTarget.Component });
|
|
172
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PreinstallationInfoComponent, selector: "lib-preinstallation-info", ngImport: i0, template: "<div class=\"container-fluid p-5 preinstall-page\">\n\n <!-- Header -->\n <div class=\"card mb-5\">\n <div class=\"card-body d-flex flex-wrap align-items-center justify-content-between gap-4 py-4\">\n <div class=\"d-flex align-items-center gap-4\">\n <button type=\"button\" class=\"btn btn-sm btn-light back-btn\" (click)=\"back()\" aria-label=\"Back to workspace\">\n <i class=\"bi bi-arrow-left\"></i>\n </button>\n <div>\n <div class=\"d-flex align-items-center gap-3\">\n <h4 class=\"mb-0\">{{ ticketInfo?.ticketId || ticketId }}</h4>\n <span class=\"badge state-badge\" [ngClass]=\"'state-' + placementState\">\n {{\n placementState === 'waitingLayout' ? 'Waiting for layout' :\n placementState === 'processing' ? 'Auto placement in progress' :\n placementState === 'failed' ? 'Placement failed' :\n placementState === 'open' ? 'Ready for placement review' :\n placementState === 'approvalPending' ? 'Waiting for approval' :\n placementState === 'approved' ? 'Approved' :\n placementState === 'unavailable' ? 'Status unavailable' : 'Loading'\n }}\n </span>\n </div>\n <div class=\"text-muted fs-7 mt-1\">\n Pre-Installation, camera placement\n <span *ngIf=\"ticketInfo?.basicDetails?.storeName\">, {{ ticketInfo?.basicDetails?.storeName }}</span>\n <span>, Floor {{ floorNumber }}</span>\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"refresh()\">\n <i class=\"bi bi-arrow-clockwise me-1\"></i>Refresh\n </button>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-center py-10 text-muted\">Loading ticket...</div>\n\n <div *ngIf=\"!loading && ticketInfo\" class=\"row g-5\">\n\n <!-- Placement state / action panel -->\n <div class=\"col-12 col-lg-7\">\n <div class=\"card h-100\">\n <div class=\"card-body\">\n <div class=\"card-head mb-4\">\n <h5 class=\"mb-0\">Camera placement</h5>\n <!-- The imported drawing itself, for the internal team to inspect in\n CAD when the engine cannot handle it or before editing. -->\n <a *ngIf=\"sourceDxfUrl\" [href]=\"sourceDxfUrl\" target=\"_blank\" rel=\"noopener\"\n class=\"btn btn-sm btn-light row-btn\" [ngbTooltip]=\"sourceDxfName\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <span class=\"ms-1\">Download source DXF</span>\n </a>\n </div>\n\n <div [ngSwitch]=\"placementState\" class=\"state-panel\">\n\n <div *ngSwitchCase=\"'waitingLayout'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Waiting for the CAD layout.</p>\n <p class=\"text-muted mb-0\">The store's floor drawing has not been imported yet. Auto placement starts right after the CAD import.</p>\n </div>\n\n <div *ngSwitchCase=\"'processing'\" class=\"state-box\">\n <div class=\"d-flex align-items-center gap-3 mb-1\">\n <span class=\"spinner-border spinner-border-sm text-primary\" role=\"status\" aria-hidden=\"true\"></span>\n <p class=\"mb-0 fw-semibold\">Auto camera placement is running.</p>\n </div>\n <p class=\"text-muted mb-0\">The DXF editor unlocks when placement completes. Use Refresh to check again.</p>\n </div>\n\n <div *ngSwitchCase=\"'failed'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Auto placement failed.</p>\n <p class=\"text-muted mb-3\">{{ placement?.errorMsg || cctvRow?.errorMsg || 'The placement engine reported a failure.' }}</p>\n <!-- Retry hidden until the autoPlace Lambda exists (LAMBDAURL.autoPlace is empty).\n Re-enable by removing this comment; retryPlacement() is still wired in the component.\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" [disabled]=\"retrying\" (click)=\"retryPlacement()\">\n <span *ngIf=\"retrying\" class=\"spinner-border spinner-border-sm me-2\" role=\"status\" aria-hidden=\"true\"></span>\n {{ retrying ? 'Retrying...' : 'Retry auto placement' }}\n </button>\n -->\n </div>\n\n <div *ngSwitchCase=\"'open'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">The drawing is ready for camera review.</p>\n <p class=\"text-muted mb-3\">\n Adjust the auto-placed cameras in the DXF editor, then submit for approval.\n <span *ngIf=\"cctvRow?.isRework\" class=\"text-warning fw-semibold\">This floor was sent back for rework.</span>\n </p>\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-pencil-square me-1\"></i>Open DXF editor\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approvalPending'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Submitted, waiting for approval.</p>\n <p class=\"text-muted mb-3\">The approval team reviews it from the onboard table. You can still view the drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-eye me-1\"></i>View drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approved'\" class=\"state-box state-box-success\">\n <p class=\"mb-1 fw-semibold\">Camera placement approved.</p>\n <p class=\"text-muted mb-0\">This ticket is closed. The installation ticket takes over once every floor of the store is approved.</p>\n </div>\n\n <div *ngSwitchCase=\"'unavailable'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Placement status unavailable.</p>\n <p class=\"text-muted mb-3\">{{ cctvError }}</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"loadCctvDetail()\">Try again</button>\n </div>\n\n <div *ngSwitchDefault class=\"state-box\">\n <p class=\"text-muted mb-0\">Loading placement status...</p>\n </div>\n </div>\n\n <!-- Version timeline -->\n <div *ngIf=\"versions.length\" class=\"mt-6\">\n <h6 class=\"text-muted mb-3\">Drawing versions</h6>\n <div class=\"version-list\">\n <div class=\"version-row d-flex align-items-center justify-content-between\" *ngFor=\"let file of versions\">\n <div>\n <span class=\"fw-semibold\">v{{ file.version }}</span>\n <span class=\"text-muted ms-2 fs-7\">{{ file.kind === 'auto' ? 'Auto placement' : file.kind === 'approver' ? 'Approver edit' : 'Internal edit' }}</span>\n <span class=\"text-muted ms-2 fs-7\" *ngIf=\"file.cameraCount != null\">{{ file.cameraCount }} cameras</span>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <span class=\"text-muted fs-7\">{{ file.uploadedAt | date: 'dd MMM, y HH:mm' }}</span>\n <a *ngIf=\"fileUrls[file.key]\" [href]=\"fileUrls[file.key]\" target=\"_blank\" rel=\"noopener\" class=\"btn btn-sm btn-light row-btn\">\n <i class=\"bi bi-download\"></i>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Ticket info + activity -->\n <div class=\"col-12 col-lg-5\">\n <div class=\"card mb-5\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Ticket details</h5>\n <div class=\"detail-grid\">\n <div class=\"detail-row\"><span class=\"text-muted\">Store</span><span>{{ ticketInfo?.basicDetails?.storeName || '-' }} ({{ ticketInfo?.basicDetails?.storeId || storeId }})</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Brand</span><span>{{ ticketInfo?.basicDetails?.clientName || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Floor</span><span>Floor {{ floorNumber }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Ticket status</span><span class=\"text-capitalize\">{{ ticketInfo?.status || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Created</span><span>{{ ticketInfo?.createdAt | date: 'dd MMM, y HH:mm' }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.assigneeName\"><span class=\"text-muted\">Assignee</span><span>{{ cctvRow?.assigneeName }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.reworkCount\"><span class=\"text-muted\">Rework count</span><span>{{ cctvRow?.reworkCount }}</span></div>\n </div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Activity</h5>\n <div *ngIf=\"!activity.length\" class=\"text-muted fs-7\">No activity yet.</div>\n <div class=\"activity-list\">\n <div class=\"activity-row\" *ngFor=\"let entry of activity\">\n <div class=\"activity-dot\" aria-hidden=\"true\"></div>\n <div class=\"flex-grow-1\">\n <div class=\"d-flex justify-content-between gap-3\">\n <span class=\"fw-semibold fs-7\">{{ entry.description || entry.actionType }}</span>\n <span class=\"text-muted fs-8 text-nowrap\">{{ entry.timeStamp | date: 'dd MMM, HH:mm' }}</span>\n </div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.comment\">{{ entry.comment }}</div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.IdentifiedBy && entry.IdentifiedBy !== 'Tango'\">by {{ entry.IdentifiedBy }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".preinstall-page .back-btn,.preinstall-page .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.preinstall-page .back-btn .row-btn,.preinstall-page .card-head .row-btn{display:inline-flex;align-items:center;white-space:nowrap}.preinstall-page .row-btn{height:40px;display:inline-flex;align-items:center}.preinstall-page .state-badge{padding:6px 12px;font-weight:600;border-radius:999px}.preinstall-page .state-badge.state-waitingLayout,.preinstall-page .state-badge.state-loading,.preinstall-page .state-badge.state-unavailable{background:#f1f3f5;color:#5e6278}.preinstall-page .state-badge.state-processing{background:#eef6ff;color:#1b84ff}.preinstall-page .state-badge.state-failed{background:#fff5f8;color:#f1416c}.preinstall-page .state-badge.state-open{background:#e8fff3;color:#17c653}.preinstall-page .state-badge.state-approvalPending{background:#fff8dd;color:#f6c000}.preinstall-page .state-badge.state-approved{background:#e8fff3;color:#17c653}.preinstall-page .state-panel .state-box{border:1px solid #e9ecef;border-radius:8px;padding:16px 20px;opacity:0;transform:translateY(4px);animation:preinstallFade .25s ease forwards}.preinstall-page .state-panel .state-box.state-box-danger{border-color:#ffd6e1;background:#fff9fb}.preinstall-page .state-panel .state-box.state-box-success{border-color:#c8f2d8;background:#f6fffa}.preinstall-page .version-list .version-row{padding:10px 0;border-bottom:1px dashed #e9ecef}.preinstall-page .version-list .version-row:last-child{border-bottom:none}.preinstall-page .detail-grid .detail-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed #f1f3f5;font-size:13px}.preinstall-page .detail-grid .detail-row:last-child{border-bottom:none}.preinstall-page .activity-list{max-height:420px;overflow-y:auto}.preinstall-page .activity-list .activity-row{display:flex;gap:10px;padding:8px 0}.preinstall-page .activity-list .activity-row .activity-dot{width:8px;height:8px;border-radius:50%;background:#1b84ff;margin-top:6px;flex-shrink:0}@keyframes preinstallFade{to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion: reduce){.preinstall-page .state-panel .state-box{animation:none;opacity:1;transform:none}}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i4.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i4.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: i5.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "pipe", type: i4.DatePipe, name: "date" }] });
|
|
185
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PreinstallationInfoComponent, selector: "lib-preinstallation-info", ngImport: i0, template: "<div class=\"container-fluid p-5 preinstall-page\">\n\n <!-- Header -->\n <div class=\"card mb-5\">\n <div class=\"card-body d-flex flex-wrap align-items-center justify-content-between gap-4 py-4\">\n <div class=\"d-flex align-items-center gap-4\">\n <button type=\"button\" class=\"btn btn-sm btn-light back-btn\" (click)=\"back()\" aria-label=\"Back to workspace\">\n <i class=\"bi bi-arrow-left\"></i>\n </button>\n <div>\n <div class=\"d-flex align-items-center gap-3\">\n <h4 class=\"mb-0\">{{ ticketInfo?.ticketId || ticketId }}</h4>\n <span class=\"badge state-badge\" [ngClass]=\"'state-' + placementState\">\n {{\n placementState === 'waitingLayout' ? 'Waiting for layout' :\n placementState === 'processing' ? 'Auto placement in progress' :\n placementState === 'failed' ? 'Placement failed' :\n placementState === 'open' ? 'Ready for placement review' :\n placementState === 'approvalPending' ? 'Waiting for approval' :\n placementState === 'approved' ? 'Approved' :\n placementState === 'unavailable' ? 'Status unavailable' : 'Loading'\n }}\n </span>\n </div>\n <div class=\"text-muted fs-7 mt-1\">\n Pre-Installation, camera placement\n <span *ngIf=\"ticketInfo?.basicDetails?.storeName\">, {{ ticketInfo?.basicDetails?.storeName }}</span>\n <span>, Floor {{ floorNumber }}</span>\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"refresh()\">\n <i class=\"bi bi-arrow-clockwise me-1\" [class.spinning]=\"reloading || cctvLoading\"></i>Refresh\n </button>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-center py-10 text-muted\">Loading ticket...</div>\n\n <div *ngIf=\"!loading && ticketInfo\" class=\"row g-5\">\n\n <!-- Placement state / action panel -->\n <div class=\"col-12 col-lg-7\">\n <div class=\"card h-100\">\n <div class=\"card-body\">\n <div class=\"card-head mb-4\">\n <h5 class=\"mb-0\">Camera placement</h5>\n <!-- The imported drawing itself, for the internal team to inspect in\n CAD when the engine cannot handle it or before editing. -->\n <a *ngIf=\"sourceDxfUrl\" [href]=\"sourceDxfUrl\" target=\"_blank\" rel=\"noopener\"\n class=\"btn btn-sm btn-light row-btn\" [ngbTooltip]=\"sourceDxfName\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <span class=\"ms-1\">Download source DXF</span>\n </a>\n </div>\n\n <div [ngSwitch]=\"placementState\" class=\"state-panel\">\n\n <div *ngSwitchCase=\"'waitingLayout'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Waiting for the CAD layout.</p>\n <p class=\"text-muted mb-0\">The store's floor drawing has not been imported yet. Auto placement starts right after the CAD import.</p>\n </div>\n\n <div *ngSwitchCase=\"'processing'\" class=\"state-box\">\n <div class=\"d-flex align-items-center gap-3 mb-1\">\n <span class=\"spinner-border spinner-border-sm text-primary\" role=\"status\" aria-hidden=\"true\"></span>\n <p class=\"mb-0 fw-semibold\">Auto camera placement is running.</p>\n </div>\n <p class=\"text-muted mb-3\">The DXF editor unlocks when placement completes. Use Refresh to check again. Meanwhile you can look at the source drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openPreview()\">\n <i class=\"bi bi-eye me-1\"></i>View source drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'failed'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Auto placement failed.</p>\n <p class=\"text-muted mb-3\">{{ placement?.errorMsg || cctvRow?.errorMsg || 'The placement engine reported a failure.' }}</p>\n <!-- Retry hidden until the autoPlace Lambda exists (LAMBDAURL.autoPlace is empty).\n Re-enable by removing this comment; retryPlacement() is still wired in the component.\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" [disabled]=\"retrying\" (click)=\"retryPlacement()\">\n <span *ngIf=\"retrying\" class=\"spinner-border spinner-border-sm me-2\" role=\"status\" aria-hidden=\"true\"></span>\n {{ retrying ? 'Retrying...' : 'Retry auto placement' }}\n </button>\n -->\n </div>\n\n <div *ngSwitchCase=\"'open'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">The drawing is ready for camera review.</p>\n <p class=\"text-muted mb-3\">\n Adjust the auto-placed cameras in the DXF editor, then submit for approval.\n <span *ngIf=\"cctvRow?.isRework\" class=\"text-warning fw-semibold\">This floor was sent back for rework.</span>\n </p>\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-pencil-square me-1\"></i>Open DXF editor\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approvalPending'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Submitted, waiting for approval.</p>\n <p class=\"text-muted mb-3\">The approval team reviews it from the onboard table. You can still view the drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-eye me-1\"></i>View drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approved'\" class=\"state-box state-box-success\">\n <p class=\"mb-1 fw-semibold\">Camera placement approved.</p>\n <p class=\"text-muted mb-0\">This ticket is closed. The installation ticket takes over once every floor of the store is approved.</p>\n </div>\n\n <div *ngSwitchCase=\"'unavailable'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Placement status unavailable.</p>\n <p class=\"text-muted mb-3\">{{ cctvError }}</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"loadCctvDetail()\">Try again</button>\n </div>\n\n <div *ngSwitchDefault class=\"state-box\">\n <p class=\"text-muted mb-0\">Loading placement status...</p>\n </div>\n </div>\n\n <!-- Version timeline -->\n <div *ngIf=\"versions.length\" class=\"mt-6\">\n <h6 class=\"text-muted mb-3\">Drawing versions</h6>\n <div class=\"version-list\">\n <div class=\"version-row d-flex align-items-center justify-content-between\" *ngFor=\"let file of versions\">\n <div>\n <span class=\"fw-semibold\">v{{ file.version }}</span>\n <span class=\"text-muted ms-2 fs-7\">{{ file.kind === 'auto' ? 'Auto placement' : file.kind === 'approver' ? 'Approver edit' : 'Internal edit' }}</span>\n <span class=\"text-muted ms-2 fs-7\" *ngIf=\"file.cameraCount != null\">{{ file.cameraCount }} cameras</span>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <span class=\"text-muted fs-7\">{{ file.uploadedAt | date: 'dd MMM, y HH:mm' }}</span>\n <a *ngIf=\"fileUrls[file.key]\" [href]=\"fileUrls[file.key]\" target=\"_blank\" rel=\"noopener\" class=\"btn btn-sm btn-light row-btn\">\n <i class=\"bi bi-download\"></i>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Ticket info + activity -->\n <div class=\"col-12 col-lg-5\">\n <div class=\"card mb-5\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Ticket details</h5>\n <div class=\"detail-grid\">\n <div class=\"detail-row\"><span class=\"text-muted\">Store</span><span>{{ ticketInfo?.basicDetails?.storeName || '-' }} ({{ ticketInfo?.basicDetails?.storeId || storeId }})</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Brand</span><span>{{ ticketInfo?.basicDetails?.clientName || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Floor</span><span>Floor {{ floorNumber }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Ticket status</span><span class=\"text-capitalize\">{{ ticketInfo?.status || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Created</span><span>{{ ticketInfo?.createdAt | date: 'dd MMM, y HH:mm' }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.assigneeName\"><span class=\"text-muted\">Assignee</span><span>{{ cctvRow?.assigneeName }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.reworkCount\"><span class=\"text-muted\">Rework count</span><span>{{ cctvRow?.reworkCount }}</span></div>\n </div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Activity</h5>\n <div *ngIf=\"!activity.length\" class=\"text-muted fs-7\">No activity yet.</div>\n <div class=\"activity-list\">\n <div class=\"activity-row\" *ngFor=\"let entry of activity\">\n <div class=\"activity-dot\" aria-hidden=\"true\"></div>\n <div class=\"flex-grow-1\">\n <div class=\"d-flex justify-content-between gap-3\">\n <span class=\"fw-semibold fs-7\">{{ entry.description || entry.actionType }}</span>\n <span class=\"text-muted fs-8 text-nowrap\">{{ entry.timeStamp | date: 'dd MMM, HH:mm' }}</span>\n </div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.comment\">{{ entry.comment }}</div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.IdentifiedBy && entry.IdentifiedBy !== 'Tango'\">by {{ entry.IdentifiedBy }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".preinstall-page .back-btn,.preinstall-page .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.preinstall-page .back-btn .row-btn,.preinstall-page .card-head .row-btn{display:inline-flex;align-items:center;white-space:nowrap}.preinstall-page .row-btn{height:40px;display:inline-flex;align-items:center}.preinstall-page .state-badge{padding:6px 12px;font-weight:600;border-radius:999px}.preinstall-page .state-badge.state-waitingLayout,.preinstall-page .state-badge.state-loading,.preinstall-page .state-badge.state-unavailable{background:#f1f3f5;color:#5e6278}.preinstall-page .state-badge.state-processing{background:#eef6ff;color:#1b84ff}.preinstall-page .state-badge.state-failed{background:#fff5f8;color:#f1416c}.preinstall-page .state-badge.state-open{background:#e8fff3;color:#17c653}.preinstall-page .state-badge.state-approvalPending{background:#fff8dd;color:#f6c000}.preinstall-page .state-badge.state-approved{background:#e8fff3;color:#17c653}.preinstall-page .state-panel .state-box{border:1px solid #e9ecef;border-radius:8px;padding:16px 20px;opacity:0;transform:translateY(4px);animation:preinstallFade .25s ease forwards}.preinstall-page .state-panel .state-box.state-box-danger{border-color:#ffd6e1;background:#fff9fb}.preinstall-page .state-panel .state-box.state-box-success{border-color:#c8f2d8;background:#f6fffa}.preinstall-page .version-list .version-row{padding:10px 0;border-bottom:1px dashed #e9ecef}.preinstall-page .version-list .version-row:last-child{border-bottom:none}.preinstall-page .detail-grid .detail-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed #f1f3f5;font-size:13px}.preinstall-page .detail-grid .detail-row:last-child{border-bottom:none}.preinstall-page .activity-list{max-height:420px;overflow-y:auto}.preinstall-page .activity-list .activity-row{display:flex;gap:10px;padding:8px 0}.preinstall-page .activity-list .activity-row .activity-dot{width:8px;height:8px;border-radius:50%;background:#1b84ff;margin-top:6px;flex-shrink:0}@keyframes preinstallFade{to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion: reduce){.preinstall-page .state-panel .state-box{animation:none;opacity:1;transform:none}}.preinstall-page .bi-arrow-clockwise{display:inline-block;line-height:1}.preinstall-page .bi-arrow-clockwise:before{display:inline-block;transform-origin:50% 50%}.preinstall-page .bi-arrow-clockwise.spinning:before{animation:preinstallSpin .8s linear infinite}@keyframes preinstallSpin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.preinstall-page .bi-arrow-clockwise.spinning:before{animation:none}}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i4.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i4.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: i5.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "pipe", type: i4.DatePipe, name: "date" }] });
|
|
173
186
|
}
|
|
174
187
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreinstallationInfoComponent, decorators: [{
|
|
175
188
|
type: Component,
|
|
176
|
-
args: [{ selector: 'lib-preinstallation-info', template: "<div class=\"container-fluid p-5 preinstall-page\">\n\n <!-- Header -->\n <div class=\"card mb-5\">\n <div class=\"card-body d-flex flex-wrap align-items-center justify-content-between gap-4 py-4\">\n <div class=\"d-flex align-items-center gap-4\">\n <button type=\"button\" class=\"btn btn-sm btn-light back-btn\" (click)=\"back()\" aria-label=\"Back to workspace\">\n <i class=\"bi bi-arrow-left\"></i>\n </button>\n <div>\n <div class=\"d-flex align-items-center gap-3\">\n <h4 class=\"mb-0\">{{ ticketInfo?.ticketId || ticketId }}</h4>\n <span class=\"badge state-badge\" [ngClass]=\"'state-' + placementState\">\n {{\n placementState === 'waitingLayout' ? 'Waiting for layout' :\n placementState === 'processing' ? 'Auto placement in progress' :\n placementState === 'failed' ? 'Placement failed' :\n placementState === 'open' ? 'Ready for placement review' :\n placementState === 'approvalPending' ? 'Waiting for approval' :\n placementState === 'approved' ? 'Approved' :\n placementState === 'unavailable' ? 'Status unavailable' : 'Loading'\n }}\n </span>\n </div>\n <div class=\"text-muted fs-7 mt-1\">\n Pre-Installation, camera placement\n <span *ngIf=\"ticketInfo?.basicDetails?.storeName\">, {{ ticketInfo?.basicDetails?.storeName }}</span>\n <span>, Floor {{ floorNumber }}</span>\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"refresh()\">\n <i class=\"bi bi-arrow-clockwise me-1\"></i>Refresh\n </button>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-center py-10 text-muted\">Loading ticket...</div>\n\n <div *ngIf=\"!loading && ticketInfo\" class=\"row g-5\">\n\n <!-- Placement state / action panel -->\n <div class=\"col-12 col-lg-7\">\n <div class=\"card h-100\">\n <div class=\"card-body\">\n <div class=\"card-head mb-4\">\n <h5 class=\"mb-0\">Camera placement</h5>\n <!-- The imported drawing itself, for the internal team to inspect in\n CAD when the engine cannot handle it or before editing. -->\n <a *ngIf=\"sourceDxfUrl\" [href]=\"sourceDxfUrl\" target=\"_blank\" rel=\"noopener\"\n class=\"btn btn-sm btn-light row-btn\" [ngbTooltip]=\"sourceDxfName\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <span class=\"ms-1\">Download source DXF</span>\n </a>\n </div>\n\n <div [ngSwitch]=\"placementState\" class=\"state-panel\">\n\n <div *ngSwitchCase=\"'waitingLayout'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Waiting for the CAD layout.</p>\n <p class=\"text-muted mb-0\">The store's floor drawing has not been imported yet. Auto placement starts right after the CAD import.</p>\n </div>\n\n <div *ngSwitchCase=\"'processing'\" class=\"state-box\">\n <div class=\"d-flex align-items-center gap-3 mb-1\">\n <span class=\"spinner-border spinner-border-sm text-primary\" role=\"status\" aria-hidden=\"true\"></span>\n <p class=\"mb-0 fw-semibold\">Auto camera placement is running.</p>\n </div>\n <p class=\"text-muted mb-0\">The DXF editor unlocks when placement completes. Use Refresh to check again.</p>\n </div>\n\n <div *ngSwitchCase=\"'failed'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Auto placement failed.</p>\n <p class=\"text-muted mb-3\">{{ placement?.errorMsg || cctvRow?.errorMsg || 'The placement engine reported a failure.' }}</p>\n <!-- Retry hidden until the autoPlace Lambda exists (LAMBDAURL.autoPlace is empty).\n Re-enable by removing this comment; retryPlacement() is still wired in the component.\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" [disabled]=\"retrying\" (click)=\"retryPlacement()\">\n <span *ngIf=\"retrying\" class=\"spinner-border spinner-border-sm me-2\" role=\"status\" aria-hidden=\"true\"></span>\n {{ retrying ? 'Retrying...' : 'Retry auto placement' }}\n </button>\n -->\n </div>\n\n <div *ngSwitchCase=\"'open'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">The drawing is ready for camera review.</p>\n <p class=\"text-muted mb-3\">\n Adjust the auto-placed cameras in the DXF editor, then submit for approval.\n <span *ngIf=\"cctvRow?.isRework\" class=\"text-warning fw-semibold\">This floor was sent back for rework.</span>\n </p>\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-pencil-square me-1\"></i>Open DXF editor\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approvalPending'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Submitted, waiting for approval.</p>\n <p class=\"text-muted mb-3\">The approval team reviews it from the onboard table. You can still view the drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-eye me-1\"></i>View drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approved'\" class=\"state-box state-box-success\">\n <p class=\"mb-1 fw-semibold\">Camera placement approved.</p>\n <p class=\"text-muted mb-0\">This ticket is closed. The installation ticket takes over once every floor of the store is approved.</p>\n </div>\n\n <div *ngSwitchCase=\"'unavailable'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Placement status unavailable.</p>\n <p class=\"text-muted mb-3\">{{ cctvError }}</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"loadCctvDetail()\">Try again</button>\n </div>\n\n <div *ngSwitchDefault class=\"state-box\">\n <p class=\"text-muted mb-0\">Loading placement status...</p>\n </div>\n </div>\n\n <!-- Version timeline -->\n <div *ngIf=\"versions.length\" class=\"mt-6\">\n <h6 class=\"text-muted mb-3\">Drawing versions</h6>\n <div class=\"version-list\">\n <div class=\"version-row d-flex align-items-center justify-content-between\" *ngFor=\"let file of versions\">\n <div>\n <span class=\"fw-semibold\">v{{ file.version }}</span>\n <span class=\"text-muted ms-2 fs-7\">{{ file.kind === 'auto' ? 'Auto placement' : file.kind === 'approver' ? 'Approver edit' : 'Internal edit' }}</span>\n <span class=\"text-muted ms-2 fs-7\" *ngIf=\"file.cameraCount != null\">{{ file.cameraCount }} cameras</span>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <span class=\"text-muted fs-7\">{{ file.uploadedAt | date: 'dd MMM, y HH:mm' }}</span>\n <a *ngIf=\"fileUrls[file.key]\" [href]=\"fileUrls[file.key]\" target=\"_blank\" rel=\"noopener\" class=\"btn btn-sm btn-light row-btn\">\n <i class=\"bi bi-download\"></i>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Ticket info + activity -->\n <div class=\"col-12 col-lg-5\">\n <div class=\"card mb-5\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Ticket details</h5>\n <div class=\"detail-grid\">\n <div class=\"detail-row\"><span class=\"text-muted\">Store</span><span>{{ ticketInfo?.basicDetails?.storeName || '-' }} ({{ ticketInfo?.basicDetails?.storeId || storeId }})</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Brand</span><span>{{ ticketInfo?.basicDetails?.clientName || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Floor</span><span>Floor {{ floorNumber }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Ticket status</span><span class=\"text-capitalize\">{{ ticketInfo?.status || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Created</span><span>{{ ticketInfo?.createdAt | date: 'dd MMM, y HH:mm' }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.assigneeName\"><span class=\"text-muted\">Assignee</span><span>{{ cctvRow?.assigneeName }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.reworkCount\"><span class=\"text-muted\">Rework count</span><span>{{ cctvRow?.reworkCount }}</span></div>\n </div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Activity</h5>\n <div *ngIf=\"!activity.length\" class=\"text-muted fs-7\">No activity yet.</div>\n <div class=\"activity-list\">\n <div class=\"activity-row\" *ngFor=\"let entry of activity\">\n <div class=\"activity-dot\" aria-hidden=\"true\"></div>\n <div class=\"flex-grow-1\">\n <div class=\"d-flex justify-content-between gap-3\">\n <span class=\"fw-semibold fs-7\">{{ entry.description || entry.actionType }}</span>\n <span class=\"text-muted fs-8 text-nowrap\">{{ entry.timeStamp | date: 'dd MMM, HH:mm' }}</span>\n </div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.comment\">{{ entry.comment }}</div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.IdentifiedBy && entry.IdentifiedBy !== 'Tango'\">by {{ entry.IdentifiedBy }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".preinstall-page .back-btn,.preinstall-page .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.preinstall-page .back-btn .row-btn,.preinstall-page .card-head .row-btn{display:inline-flex;align-items:center;white-space:nowrap}.preinstall-page .row-btn{height:40px;display:inline-flex;align-items:center}.preinstall-page .state-badge{padding:6px 12px;font-weight:600;border-radius:999px}.preinstall-page .state-badge.state-waitingLayout,.preinstall-page .state-badge.state-loading,.preinstall-page .state-badge.state-unavailable{background:#f1f3f5;color:#5e6278}.preinstall-page .state-badge.state-processing{background:#eef6ff;color:#1b84ff}.preinstall-page .state-badge.state-failed{background:#fff5f8;color:#f1416c}.preinstall-page .state-badge.state-open{background:#e8fff3;color:#17c653}.preinstall-page .state-badge.state-approvalPending{background:#fff8dd;color:#f6c000}.preinstall-page .state-badge.state-approved{background:#e8fff3;color:#17c653}.preinstall-page .state-panel .state-box{border:1px solid #e9ecef;border-radius:8px;padding:16px 20px;opacity:0;transform:translateY(4px);animation:preinstallFade .25s ease forwards}.preinstall-page .state-panel .state-box.state-box-danger{border-color:#ffd6e1;background:#fff9fb}.preinstall-page .state-panel .state-box.state-box-success{border-color:#c8f2d8;background:#f6fffa}.preinstall-page .version-list .version-row{padding:10px 0;border-bottom:1px dashed #e9ecef}.preinstall-page .version-list .version-row:last-child{border-bottom:none}.preinstall-page .detail-grid .detail-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed #f1f3f5;font-size:13px}.preinstall-page .detail-grid .detail-row:last-child{border-bottom:none}.preinstall-page .activity-list{max-height:420px;overflow-y:auto}.preinstall-page .activity-list .activity-row{display:flex;gap:10px;padding:8px 0}.preinstall-page .activity-list .activity-row .activity-dot{width:8px;height:8px;border-radius:50%;background:#1b84ff;margin-top:6px;flex-shrink:0}@keyframes preinstallFade{to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion: reduce){.preinstall-page .state-panel .state-box{animation:none;opacity:1;transform:none}}\n"] }]
|
|
189
|
+
args: [{ selector: 'lib-preinstallation-info', template: "<div class=\"container-fluid p-5 preinstall-page\">\n\n <!-- Header -->\n <div class=\"card mb-5\">\n <div class=\"card-body d-flex flex-wrap align-items-center justify-content-between gap-4 py-4\">\n <div class=\"d-flex align-items-center gap-4\">\n <button type=\"button\" class=\"btn btn-sm btn-light back-btn\" (click)=\"back()\" aria-label=\"Back to workspace\">\n <i class=\"bi bi-arrow-left\"></i>\n </button>\n <div>\n <div class=\"d-flex align-items-center gap-3\">\n <h4 class=\"mb-0\">{{ ticketInfo?.ticketId || ticketId }}</h4>\n <span class=\"badge state-badge\" [ngClass]=\"'state-' + placementState\">\n {{\n placementState === 'waitingLayout' ? 'Waiting for layout' :\n placementState === 'processing' ? 'Auto placement in progress' :\n placementState === 'failed' ? 'Placement failed' :\n placementState === 'open' ? 'Ready for placement review' :\n placementState === 'approvalPending' ? 'Waiting for approval' :\n placementState === 'approved' ? 'Approved' :\n placementState === 'unavailable' ? 'Status unavailable' : 'Loading'\n }}\n </span>\n </div>\n <div class=\"text-muted fs-7 mt-1\">\n Pre-Installation, camera placement\n <span *ngIf=\"ticketInfo?.basicDetails?.storeName\">, {{ ticketInfo?.basicDetails?.storeName }}</span>\n <span>, Floor {{ floorNumber }}</span>\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"refresh()\">\n <i class=\"bi bi-arrow-clockwise me-1\" [class.spinning]=\"reloading || cctvLoading\"></i>Refresh\n </button>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-center py-10 text-muted\">Loading ticket...</div>\n\n <div *ngIf=\"!loading && ticketInfo\" class=\"row g-5\">\n\n <!-- Placement state / action panel -->\n <div class=\"col-12 col-lg-7\">\n <div class=\"card h-100\">\n <div class=\"card-body\">\n <div class=\"card-head mb-4\">\n <h5 class=\"mb-0\">Camera placement</h5>\n <!-- The imported drawing itself, for the internal team to inspect in\n CAD when the engine cannot handle it or before editing. -->\n <a *ngIf=\"sourceDxfUrl\" [href]=\"sourceDxfUrl\" target=\"_blank\" rel=\"noopener\"\n class=\"btn btn-sm btn-light row-btn\" [ngbTooltip]=\"sourceDxfName\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <span class=\"ms-1\">Download source DXF</span>\n </a>\n </div>\n\n <div [ngSwitch]=\"placementState\" class=\"state-panel\">\n\n <div *ngSwitchCase=\"'waitingLayout'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Waiting for the CAD layout.</p>\n <p class=\"text-muted mb-0\">The store's floor drawing has not been imported yet. Auto placement starts right after the CAD import.</p>\n </div>\n\n <div *ngSwitchCase=\"'processing'\" class=\"state-box\">\n <div class=\"d-flex align-items-center gap-3 mb-1\">\n <span class=\"spinner-border spinner-border-sm text-primary\" role=\"status\" aria-hidden=\"true\"></span>\n <p class=\"mb-0 fw-semibold\">Auto camera placement is running.</p>\n </div>\n <p class=\"text-muted mb-3\">The DXF editor unlocks when placement completes. Use Refresh to check again. Meanwhile you can look at the source drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openPreview()\">\n <i class=\"bi bi-eye me-1\"></i>View source drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'failed'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Auto placement failed.</p>\n <p class=\"text-muted mb-3\">{{ placement?.errorMsg || cctvRow?.errorMsg || 'The placement engine reported a failure.' }}</p>\n <!-- Retry hidden until the autoPlace Lambda exists (LAMBDAURL.autoPlace is empty).\n Re-enable by removing this comment; retryPlacement() is still wired in the component.\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" [disabled]=\"retrying\" (click)=\"retryPlacement()\">\n <span *ngIf=\"retrying\" class=\"spinner-border spinner-border-sm me-2\" role=\"status\" aria-hidden=\"true\"></span>\n {{ retrying ? 'Retrying...' : 'Retry auto placement' }}\n </button>\n -->\n </div>\n\n <div *ngSwitchCase=\"'open'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">The drawing is ready for camera review.</p>\n <p class=\"text-muted mb-3\">\n Adjust the auto-placed cameras in the DXF editor, then submit for approval.\n <span *ngIf=\"cctvRow?.isRework\" class=\"text-warning fw-semibold\">This floor was sent back for rework.</span>\n </p>\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-pencil-square me-1\"></i>Open DXF editor\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approvalPending'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Submitted, waiting for approval.</p>\n <p class=\"text-muted mb-3\">The approval team reviews it from the onboard table. You can still view the drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-eye me-1\"></i>View drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approved'\" class=\"state-box state-box-success\">\n <p class=\"mb-1 fw-semibold\">Camera placement approved.</p>\n <p class=\"text-muted mb-0\">This ticket is closed. The installation ticket takes over once every floor of the store is approved.</p>\n </div>\n\n <div *ngSwitchCase=\"'unavailable'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Placement status unavailable.</p>\n <p class=\"text-muted mb-3\">{{ cctvError }}</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"loadCctvDetail()\">Try again</button>\n </div>\n\n <div *ngSwitchDefault class=\"state-box\">\n <p class=\"text-muted mb-0\">Loading placement status...</p>\n </div>\n </div>\n\n <!-- Version timeline -->\n <div *ngIf=\"versions.length\" class=\"mt-6\">\n <h6 class=\"text-muted mb-3\">Drawing versions</h6>\n <div class=\"version-list\">\n <div class=\"version-row d-flex align-items-center justify-content-between\" *ngFor=\"let file of versions\">\n <div>\n <span class=\"fw-semibold\">v{{ file.version }}</span>\n <span class=\"text-muted ms-2 fs-7\">{{ file.kind === 'auto' ? 'Auto placement' : file.kind === 'approver' ? 'Approver edit' : 'Internal edit' }}</span>\n <span class=\"text-muted ms-2 fs-7\" *ngIf=\"file.cameraCount != null\">{{ file.cameraCount }} cameras</span>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <span class=\"text-muted fs-7\">{{ file.uploadedAt | date: 'dd MMM, y HH:mm' }}</span>\n <a *ngIf=\"fileUrls[file.key]\" [href]=\"fileUrls[file.key]\" target=\"_blank\" rel=\"noopener\" class=\"btn btn-sm btn-light row-btn\">\n <i class=\"bi bi-download\"></i>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Ticket info + activity -->\n <div class=\"col-12 col-lg-5\">\n <div class=\"card mb-5\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Ticket details</h5>\n <div class=\"detail-grid\">\n <div class=\"detail-row\"><span class=\"text-muted\">Store</span><span>{{ ticketInfo?.basicDetails?.storeName || '-' }} ({{ ticketInfo?.basicDetails?.storeId || storeId }})</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Brand</span><span>{{ ticketInfo?.basicDetails?.clientName || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Floor</span><span>Floor {{ floorNumber }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Ticket status</span><span class=\"text-capitalize\">{{ ticketInfo?.status || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Created</span><span>{{ ticketInfo?.createdAt | date: 'dd MMM, y HH:mm' }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.assigneeName\"><span class=\"text-muted\">Assignee</span><span>{{ cctvRow?.assigneeName }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.reworkCount\"><span class=\"text-muted\">Rework count</span><span>{{ cctvRow?.reworkCount }}</span></div>\n </div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Activity</h5>\n <div *ngIf=\"!activity.length\" class=\"text-muted fs-7\">No activity yet.</div>\n <div class=\"activity-list\">\n <div class=\"activity-row\" *ngFor=\"let entry of activity\">\n <div class=\"activity-dot\" aria-hidden=\"true\"></div>\n <div class=\"flex-grow-1\">\n <div class=\"d-flex justify-content-between gap-3\">\n <span class=\"fw-semibold fs-7\">{{ entry.description || entry.actionType }}</span>\n <span class=\"text-muted fs-8 text-nowrap\">{{ entry.timeStamp | date: 'dd MMM, HH:mm' }}</span>\n </div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.comment\">{{ entry.comment }}</div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.IdentifiedBy && entry.IdentifiedBy !== 'Tango'\">by {{ entry.IdentifiedBy }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".preinstall-page .back-btn,.preinstall-page .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.preinstall-page .back-btn .row-btn,.preinstall-page .card-head .row-btn{display:inline-flex;align-items:center;white-space:nowrap}.preinstall-page .row-btn{height:40px;display:inline-flex;align-items:center}.preinstall-page .state-badge{padding:6px 12px;font-weight:600;border-radius:999px}.preinstall-page .state-badge.state-waitingLayout,.preinstall-page .state-badge.state-loading,.preinstall-page .state-badge.state-unavailable{background:#f1f3f5;color:#5e6278}.preinstall-page .state-badge.state-processing{background:#eef6ff;color:#1b84ff}.preinstall-page .state-badge.state-failed{background:#fff5f8;color:#f1416c}.preinstall-page .state-badge.state-open{background:#e8fff3;color:#17c653}.preinstall-page .state-badge.state-approvalPending{background:#fff8dd;color:#f6c000}.preinstall-page .state-badge.state-approved{background:#e8fff3;color:#17c653}.preinstall-page .state-panel .state-box{border:1px solid #e9ecef;border-radius:8px;padding:16px 20px;opacity:0;transform:translateY(4px);animation:preinstallFade .25s ease forwards}.preinstall-page .state-panel .state-box.state-box-danger{border-color:#ffd6e1;background:#fff9fb}.preinstall-page .state-panel .state-box.state-box-success{border-color:#c8f2d8;background:#f6fffa}.preinstall-page .version-list .version-row{padding:10px 0;border-bottom:1px dashed #e9ecef}.preinstall-page .version-list .version-row:last-child{border-bottom:none}.preinstall-page .detail-grid .detail-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed #f1f3f5;font-size:13px}.preinstall-page .detail-grid .detail-row:last-child{border-bottom:none}.preinstall-page .activity-list{max-height:420px;overflow-y:auto}.preinstall-page .activity-list .activity-row{display:flex;gap:10px;padding:8px 0}.preinstall-page .activity-list .activity-row .activity-dot{width:8px;height:8px;border-radius:50%;background:#1b84ff;margin-top:6px;flex-shrink:0}@keyframes preinstallFade{to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion: reduce){.preinstall-page .state-panel .state-box{animation:none;opacity:1;transform:none}}.preinstall-page .bi-arrow-clockwise{display:inline-block;line-height:1}.preinstall-page .bi-arrow-clockwise:before{display:inline-block;transform-origin:50% 50%}.preinstall-page .bi-arrow-clockwise.spinning:before{animation:preinstallSpin .8s linear infinite}@keyframes preinstallSpin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.preinstall-page .bi-arrow-clockwise.spinning:before{animation:none}}\n"] }]
|
|
177
190
|
}], ctorParameters: () => [{ type: i1.ActivatedRoute }, { type: i1.Router }, { type: i2.TicketsService }, { type: i3.ToastService }] });
|
|
178
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlaW5zdGFsbGF0aW9uLWluZm8uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvdGFuZ28tdGlja2V0LXVzZXIvc3JjL2xpYi9jb21wb25lbnRzL3ByZWluc3RhbGxhdGlvbi1pbmZvL3ByZWluc3RhbGxhdGlvbi1pbmZvLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RhbmdvLXRpY2tldC11c2VyL3NyYy9saWIvY29tcG9uZW50cy9wcmVpbnN0YWxsYXRpb24taW5mby9wcmVpbnN0YWxsYXRpb24taW5mby5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFxQixNQUFNLGVBQWUsQ0FBQztBQUU3RCxPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLE1BQU0sQ0FBQzs7Ozs7OztBQUkxQzs7Ozs7Ozs7Ozs7O0dBWUc7QUFNSCxNQUFNLE9BQU8sNEJBQTRCO0lBaUI3QjtJQUNBO0lBQ0E7SUFDQTtJQW5CVixRQUFRLENBQWdCO0lBQ3hCLE9BQU8sQ0FBZ0I7SUFDdkIsVUFBVSxDQUFNO0lBQ2hCLE9BQU8sQ0FBTTtJQUNiLFNBQVMsQ0FBTTtJQUNmLHlEQUF5RDtJQUN6RCxTQUFTLEdBQVEsSUFBSSxDQUFDO0lBQ3RCLFFBQVEsR0FBUSxFQUFFLENBQUM7SUFDbkIsT0FBTyxHQUFZLElBQUksQ0FBQztJQUN4QixXQUFXLEdBQVksS0FBSyxDQUFDO0lBQzdCLFNBQVMsR0FBVyxFQUFFLENBQUM7SUFDdkIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUVULFFBQVEsR0FBRyxJQUFJLE9BQU8sRUFBRSxDQUFDO0lBRTFDLFlBQ1UsS0FBcUIsRUFDckIsTUFBYyxFQUNkLE9BQXVCLEVBQ3ZCLFlBQTBCO1FBSDFCLFVBQUssR0FBTCxLQUFLLENBQWdCO1FBQ3JCLFdBQU0sR0FBTixNQUFNLENBQVE7UUFDZCxZQUFPLEdBQVAsT0FBTyxDQUFnQjtRQUN2QixpQkFBWSxHQUFaLFlBQVksQ0FBYztRQUVsQyxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDN0QsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFFRCxRQUFRO1FBQ04sSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQ3BCLENBQUM7SUFFRCxXQUFXO1FBQ1QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRUQsSUFBSSxPQUFPO1FBQ1QsT0FBTyxJQUFJLENBQUMsVUFBVSxFQUFFLGVBQWUsRUFBRSxPQUFPLElBQUksRUFBRSxDQUFDO0lBQ3pELENBQUM7SUFFRCxJQUFJLFdBQVc7UUFDYixPQUFPLElBQUksQ0FBQyxVQUFVLEVBQUUsZUFBZSxFQUFFLFdBQVcsSUFBSSxHQUFHLENBQUM7SUFDOUQsQ0FBQztJQUVELG1GQUFtRjtJQUNuRixJQUFJLFlBQVk7UUFDZCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQztRQUNwQyxPQUFPLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQ25ELENBQUM7SUFFRCxJQUFJLGFBQWE7UUFDZixNQUFNLEdBQUcsR0FBVyxJQUFJLENBQUMsT0FBTyxFQUFFLFNBQVMsSUFBSSxFQUFFLENBQUM7UUFDbEQsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxJQUFJLFlBQVksQ0FBQztJQUM5QyxDQUFDO0lBRUQsdUVBQXVFO0lBQ3ZFLElBQUksY0FBYztRQUNoQixJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsTUFBTSxLQUFLLFFBQVE7WUFBRSxPQUFPLFVBQVUsQ0FBQztRQUM1RCxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU87WUFBRSxPQUFPLGVBQWUsQ0FBQztRQUMxQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU87WUFBRSxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO1FBRXJFLHlFQUF5RTtRQUN6RSx5RUFBeUU7UUFDekUsMkVBQTJFO1FBQzNFLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDO1FBQ3pDLElBQUksU0FBUyxLQUFLLFNBQVMsSUFBSSxTQUFTLEtBQUssWUFBWTtZQUFFLE9BQU8sWUFBWSxDQUFDO1FBQy9FLElBQUksU0FBUyxLQUFLLFFBQVE7WUFBRSxPQUFPLFFBQVEsQ0FBQztRQUM1QyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxLQUFLLGVBQWU7WUFBRSxPQUFPLFlBQVksQ0FBQztRQUNqRSxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO0lBQzdCLENBQUM7SUFFRCxJQUFJLFFBQVE7UUFDVixNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDaEYsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsT0FBTyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFFRCxJQUFJLFFBQVE7UUFDVixNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDdkcsT0FBTyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVELFVBQVU7UUFDUixJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7YUFDakQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7YUFDOUIsU0FBUyxDQUFDO1lBQ1QsSUFBSSxFQUFFLENBQUMsR0FBUSxFQUFFLEVBQUU7Z0JBQ2pCLElBQUksQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDO2dCQUNyQixJQUFJLEdBQUcsSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLEdBQUcsRUFBRTtvQkFDMUIsSUFBSSxDQUFDLFVBQVUsR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDO29CQUMzQixJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUU7d0JBQ2hCLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztxQkFDdkI7aUJBQ0Y7WUFDSCxDQUFDO1lBQ0QsS0FBSyxFQUFFLEdBQUcsRUFBRTtnQkFDVixJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztnQkFDckIsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsNEJBQTRCLENBQUMsQ0FBQztZQUNoRSxDQUFDO1NBQ0YsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVELGNBQWM7UUFDWixJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU87WUFBRSxPQUFPO1FBQzFCLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDO1FBQ3BCLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7YUFDckMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7YUFDOUIsU0FBUyxDQUFDO1lBQ1QsSUFBSSxFQUFFLENBQUMsR0FBUSxFQUFFLEVBQUU7Z0JBQ2pCLElBQUksQ0FBQyxXQUFXLEdBQUcsS0FBSyxDQUFDO2dCQUN6QixJQUFJLENBQUMsT0FBTyxHQUFHLEdBQUcsRUFBRSxJQUFJLEVBQUUsR0FBRyxJQUFJLElBQUksQ0FBQztnQkFDdEMsSUFBSSxDQUFDLFNBQVMsR0FBRyxHQUFHLEVBQUUsSUFBSSxFQUFFLEtBQUssSUFBSSxJQUFJLENBQUM7Z0JBQzFDLElBQUksQ0FBQyxRQUFRLEdBQUcsR0FBRyxFQUFFLElBQUksRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDO2dCQUN0QyxJQUFJLENBQUMsU0FBUyxHQUFHLEdBQUcsRUFBRSxJQUFJLEVBQUUsU0FBUyxJQUFJLElBQUksQ0FBQztZQUNoRCxDQUFDO1lBQ0QsS0FBSyxFQUFFLEdBQUcsRUFBRTtnQkFDVixJQUFJLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQztnQkFDekIsSUFBSSxDQUFDLFNBQVMsR0FBRyw2Q0FBNkMsQ0FBQztZQUNqRSxDQUFDO1NBQ0YsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVELE9BQU87UUFDTCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQUVELGNBQWM7UUFDWixJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsUUFBUTtZQUFFLE9BQU87UUFDM0MsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7UUFDckIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO2FBQzlDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2FBQzlCLFNBQVMsQ0FBQztZQUNULElBQUksRUFBRSxDQUFDLEdBQVEsRUFBRSxFQUFFO2dCQUNqQixJQUFJLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQztnQkFDdEIsSUFBSSxDQUFDLFlBQVksQ0FBQyxlQUFlLENBQy9CLEdBQUcsRUFBRSxJQUFJLEVBQUUsVUFBVSxDQUFDLENBQUM7b0JBQ3JCLGtDQUFrQyxDQUFDLENBQUM7b0JBQ3BDLDJEQUEyRCxDQUM5RCxDQUFDO2dCQUNGLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN4QixDQUFDO1lBQ0QsS0FBSyxFQUFFLEdBQUcsRUFBRTtnQkFDVixJQUFJLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQztnQkFDdEIsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsb0NBQW9DLENBQUMsQ0FBQztZQUN4RSxDQUFDO1NBQ0YsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVELFVBQVU7UUFDUixJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU87WUFBRSxPQUFPO1FBQzFCLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsK0JBQStCLENBQUMsRUFBRTtZQUN0RCxXQUFXLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFO1NBQ3JELENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxJQUFJO1FBQ0YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLEVBQUUsV0FBVyxFQUFFLEVBQUUsSUFBSSxFQUFFLGNBQWMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN0RixDQUFDO3dHQTVKVSw0QkFBNEI7NEZBQTVCLDRCQUE0QixnRUN4QnpDLCsvVUF5TEE7OzRGRGpLYSw0QkFBNEI7a0JBTHhDLFNBQVM7K0JBQ0UsMEJBQTBCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkRlc3Ryb3ksIE9uSW5pdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWN0aXZhdGVkUm91dGUsIFJvdXRlciB9IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XG5pbXBvcnQgeyBTdWJqZWN0LCB0YWtlVW50aWwgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IFRvYXN0U2VydmljZSB9IGZyb20gJ3RhbmdvLWFwcC11aS1zaGFyZWQnO1xuaW1wb3J0IHsgVGlja2V0c1NlcnZpY2UgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy90aWNrZXQtaW5mcmEuc2VydmljZSc7XG5cbi8qKlxuICogUHJlLUluc3RhbGxhdGlvbiB0aWNrZXQgZGV0YWlsIHBhZ2UgKHdvcmtzcGFjZSBlbnRyeSBmb3IgVEVfUFJFIHRpY2tldHMpLlxuICogU2FtZSByb2xlIGFzIEluc3RhbGxhdGlvbkNvbXBvbmVudCBmb3IgVEVfSU5TIHRpY2tldHM6IHRoZSB0aWNrZXQgaGVhZGVyICtcbiAqIGFjdGl2aXR5IHRyYWlsIGNvbWUgZnJvbSB0aGUgdGlja2V0cyBBUEksIHRoZSBMSVZFIGNhbWVyYS1wbGFjZW1lbnQgc3RhdGVcbiAqIGNvbWVzIGZyb20gdGhlIHN0b3JlLWJ1aWxkZXIgQ0NUViByb3csIGFuZCB0aGUgcGFnZSBnYXRlcyB0aGUgbmV4dCBzdGVwOlxuICogICBhdXRvUGxhY2VtZW50ICAtPiBcIkF1dG8gcGxhY2VtZW50IGluIHByb2dyZXNzXCIsIGVkaXRvciBsb2NrZWRcbiAqICAgZmFpbGVkICAgICAgICAgLT4gZmFpbHVyZSByZWFzb24gKyBSZXRyeVxuICogICBvcGVuICAgICAgICAgICAtPiBcIk9wZW4gRFhGIGVkaXRvclwiIChhc3NpZ25lZSBnYXRlZCBzZXJ2ZXItc2lkZSlcbiAqICAgYXBwcm92YWxQZW5kaW5nLT4gd2FpdGluZyBmb3IgYXBwcm92YWwsIGRyYXdpbmcgdmlld2FibGVcbiAqICAgYXBwcm92ZWQgICAgICAgLT4gZG9uZSAodGlja2V0IGNsb3NlcyB2aWEgdGhlIGFwcHJvdmUgaG9vaylcbiAqIExvYWRpbmcgdGhlIENDVFYgZGV0YWlsIGFsc28gdHJpZ2dlcnMgdGhlIHNlcnZlci1zaWRlIE9TIHJlY29uY2lsZSwgc28gdGhpc1xuICogcGFnZSBrZWVwcyB0aGUgcm93IGZyZXNoLCB0aGUgam9iIHRoZSBDQ1RWIHRhYmxlIHVzZWQgdG8gZG8uXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2xpYi1wcmVpbnN0YWxsYXRpb24taW5mbycsXG4gIHRlbXBsYXRlVXJsOiAnLi9wcmVpbnN0YWxsYXRpb24taW5mby5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsOiAnLi9wcmVpbnN0YWxsYXRpb24taW5mby5jb21wb25lbnQuc2NzcycsXG59KVxuZXhwb3J0IGNsYXNzIFByZWluc3RhbGxhdGlvbkluZm9Db21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIHRpY2tldElkOiBzdHJpbmcgfCBudWxsO1xuICBzdG9yZUlkOiBzdHJpbmcgfCBudWxsO1xuICB0aWNrZXRJbmZvOiBhbnk7XG4gIGNjdHZSb3c6IGFueTtcbiAgY2N0dkZsb29yOiBhbnk7XG4gIC8qKiBsaXZlIHBsYWNlbWVudCBzdGF0ZSBzdHJhaWdodCBvZmYgdGhlIHN0YXR1cyBpbmRleCAqL1xuICBwbGFjZW1lbnQ6IGFueSA9IG51bGw7XG4gIGZpbGVVcmxzOiBhbnkgPSB7fTtcbiAgbG9hZGluZzogYm9vbGVhbiA9IHRydWU7XG4gIGNjdHZMb2FkaW5nOiBib29sZWFuID0gZmFsc2U7XG4gIGNjdHZFcnJvcjogc3RyaW5nID0gJyc7XG4gIHJldHJ5aW5nOiBib29sZWFuID0gZmFsc2U7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBkZXN0cm95JCA9IG5ldyBTdWJqZWN0KCk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByb3V0ZTogQWN0aXZhdGVkUm91dGUsXG4gICAgcHJpdmF0ZSByb3V0ZXI6IFJvdXRlcixcbiAgICBwcml2YXRlIHNlcnZpY2U6IFRpY2tldHNTZXJ2aWNlLFxuICAgIHByaXZhdGUgdG9hc3RTZXJ2aWNlOiBUb2FzdFNlcnZpY2UsXG4gICkge1xuICAgIHRoaXMudGlja2V0SWQgPSB0aGlzLnJvdXRlLnNuYXBzaG90LnBhcmFtTWFwLmdldCgndGlja2V0SWQnKTtcbiAgICB0aGlzLnN0b3JlSWQgPSB0aGlzLnJvdXRlLnNuYXBzaG90LnBhcmFtTWFwLmdldCgnc3RvcmVJZCcpO1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgdGhpcy52aWV3VGlja2V0KCk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLmRlc3Ryb3kkLm5leHQodHJ1ZSk7XG4gICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICB9XG5cbiAgZ2V0IGZsb29ySWQoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy50aWNrZXRJbmZvPy5wcmVJbnN0YWxsYXRpb24/LmZsb29ySWQgfHwgJyc7XG4gIH1cblxuICBnZXQgZmxvb3JOdW1iZXIoKTogYW55IHtcbiAgICByZXR1cm4gdGhpcy50aWNrZXRJbmZvPy5wcmVJbnN0YWxsYXRpb24/LmZsb29yTnVtYmVyIHx8ICctJztcbiAgfVxuXG4gIC8qKiBQcmVzaWduZWQgbGluayB0byB0aGUgaW1wb3J0ZWQgZHJhd2luZyAodjEpLCBmcm9tIHRoZSBkZXRhaWwgY2FsbCdzIHVybCBtYXAuICovXG4gIGdldCBzb3VyY2VEeGZVcmwoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgY29uc3Qga2V5ID0gdGhpcy5jY3R2Um93Py5zb3VyY2VLZXk7XG4gICAgcmV0dXJuIGtleSA/IHRoaXMuZmlsZVVybHM/LltrZXldIHx8IG51bGwgOiBudWxsO1xuICB9XG5cbiAgZ2V0IHNvdXJjZUR4Zk5hbWUoKTogc3RyaW5nIHtcbiAgICBjb25zdCBrZXk6IHN0cmluZyA9IHRoaXMuY2N0dlJvdz8uc291cmNlS2V5IHx8ICcnO1xuICAgIHJldHVybiBrZXkuc3BsaXQoJy8nKS5wb3AoKSB8fCAnc291cmNlLmR4Zic7XG4gIH1cblxuICAvLyBUaGUgcGFnZSdzIHNpbmdsZSBzdGF0ZSBzd2l0Y2gsIGRlcml2ZWQgZnJvbSB0aWNrZXQgKyBsaXZlIENDVFYgcm93LlxuICBnZXQgcGxhY2VtZW50U3RhdGUoKTogc3RyaW5nIHtcbiAgICBpZiAodGhpcy50aWNrZXRJbmZvPy5zdGF0dXMgPT09ICdjbG9zZWQnKSByZXR1cm4gJ2FwcHJvdmVkJztcbiAgICBpZiAoIXRoaXMuZmxvb3JJZCkgcmV0dXJuICd3YWl0aW5nTGF5b3V0JztcbiAgICBpZiAoIXRoaXMuY2N0dlJvdykgcmV0dXJuIHRoaXMuY2N0dkVycm9yID8gJ3VuYXZhaWxhYmxlJyA6ICdsb2FkaW5nJztcblxuICAgIC8vIFRoZSBzdGF0dXMgaW5kZXggb3ducyB0aGUgcGxhY2VtZW50IGhhbGY6IHF1ZXVlZCBhbmQgcnVubmluZyBib3RoIHJlYWRcbiAgICAvLyBhcyBcImluIHByb2dyZXNzXCIsIGFuZCBhIGZhaWx1cmUgY2FycmllcyBpdHMgcmVhc29uLiBPbmNlIHRoZSBwbGFjZW1lbnRcbiAgICAvLyBpcyBkb25lIHRoZSB3b3JrZmxvdyByb3cgdGFrZXMgb3ZlciAob3BlbiB8IGFwcHJvdmFsUGVuZGluZyB8IGFwcHJvdmVkKS5cbiAgICBjb25zdCBqb2JTdGF0dXMgPSB0aGlzLnBsYWNlbWVudD8uc3RhdHVzO1xuICAgIGlmIChqb2JTdGF0dXMgPT09ICdwZW5kaW5nJyB8fCBqb2JTdGF0dXMgPT09ICdwcm9jZXNzaW5nJykgcmV0dXJuICdwcm9jZXNzaW5nJztcbiAgICBpZiAoam9iU3RhdHVzID09PSAnZmFpbGVkJykgcmV0dXJuICdmYWlsZWQnO1xuICAgIGlmICh0aGlzLmNjdHZSb3cuc3RhdHVzID09PSAnYXV0b1BsYWNlbWVudCcpIHJldHVybiAncHJvY2Vzc2luZyc7XG4gICAgcmV0dXJuIHRoaXMuY2N0dlJvdy5zdGF0dXM7XG4gIH1cblxuICBnZXQgdmVyc2lvbnMoKTogYW55W10ge1xuICAgIGNvbnN0IGZpbGVzID0gQXJyYXkuaXNBcnJheSh0aGlzLmNjdHZSb3c/LmZpbGVzKSA/IFsuLi50aGlzLmNjdHZSb3cuZmlsZXNdIDogW107XG4gICAgcmV0dXJuIGZpbGVzLnNvcnQoKGEsIGIpID0+IChiPy52ZXJzaW9uID8/IDApIC0gKGE/LnZlcnNpb24gPz8gMCkpO1xuICB9XG5cbiAgZ2V0IGFjdGl2aXR5KCk6IGFueVtdIHtcbiAgICBjb25zdCBsaXN0ID0gQXJyYXkuaXNBcnJheSh0aGlzLnRpY2tldEluZm8/LnRpY2tldEFjdGl2aXR5KSA/IFsuLi50aGlzLnRpY2tldEluZm8udGlja2V0QWN0aXZpdHldIDogW107XG4gICAgcmV0dXJuIGxpc3QucmV2ZXJzZSgpO1xuICB9XG5cbiAgdmlld1RpY2tldCgpOiB2b2lkIHtcbiAgICB0aGlzLnNlcnZpY2Uudmlld1RpY2tldCh7IHRpY2tldElkOiB0aGlzLnRpY2tldElkIH0pXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKHJlczogYW55KSA9PiB7XG4gICAgICAgICAgdGhpcy5sb2FkaW5nID0gZmFsc2U7XG4gICAgICAgICAgaWYgKHJlcyAmJiByZXMuY29kZSA9PSAyMDApIHtcbiAgICAgICAgICAgIHRoaXMudGlja2V0SW5mbyA9IHJlcy5kYXRhO1xuICAgICAgICAgICAgaWYgKHRoaXMuZmxvb3JJZCkge1xuICAgICAgICAgICAgICB0aGlzLmxvYWRDY3R2RGV0YWlsKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBlcnJvcjogKCkgPT4ge1xuICAgICAgICAgIHRoaXMubG9hZGluZyA9IGZhbHNlO1xuICAgICAgICAgIHRoaXMudG9hc3RTZXJ2aWNlLmdldEVycm9yVG9hc3QoJ1VuYWJsZSB0byBsb2FkIHRoZSB0aWNrZXQuJyk7XG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgfVxuXG4gIGxvYWRDY3R2RGV0YWlsKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5mbG9vcklkKSByZXR1cm47XG4gICAgdGhpcy5jY3R2TG9hZGluZyA9IHRydWU7XG4gICAgdGhpcy5jY3R2RXJyb3IgPSAnJztcbiAgICB0aGlzLnNlcnZpY2UuZ2V0Q2N0dkRldGFpbCh0aGlzLmZsb29ySWQpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKHJlczogYW55KSA9PiB7XG4gICAgICAgICAgdGhpcy5jY3R2TG9hZGluZyA9IGZhbHNlO1xuICAgICAgICAgIHRoaXMuY2N0dlJvdyA9IHJlcz8uZGF0YT8ucm93IHx8IG51bGw7XG4gICAgICAgICAgdGhpcy5jY3R2Rmxvb3IgPSByZXM/LmRhdGE/LmZsb29yIHx8IG51bGw7XG4gICAgICAgICAgdGhpcy5maWxlVXJscyA9IHJlcz8uZGF0YT8udXJscyB8fCB7fTtcbiAgICAgICAgICB0aGlzLnBsYWNlbWVudCA9IHJlcz8uZGF0YT8ucGxhY2VtZW50IHx8IG51bGw7XG4gICAgICAgIH0sXG4gICAgICAgIGVycm9yOiAoKSA9PiB7XG4gICAgICAgICAgdGhpcy5jY3R2TG9hZGluZyA9IGZhbHNlO1xuICAgICAgICAgIHRoaXMuY2N0dkVycm9yID0gJ1VuYWJsZSB0byBsb2FkIHRoZSBjYW1lcmEgcGxhY2VtZW50IHN0YXR1cy4nO1xuICAgICAgICB9LFxuICAgICAgfSk7XG4gIH1cblxuICByZWZyZXNoKCk6IHZvaWQge1xuICAgIHRoaXMudmlld1RpY2tldCgpO1xuICB9XG5cbiAgcmV0cnlQbGFjZW1lbnQoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmZsb29ySWQgfHwgdGhpcy5yZXRyeWluZykgcmV0dXJuO1xuICAgIHRoaXMucmV0cnlpbmcgPSB0cnVlO1xuICAgIHRoaXMuc2VydmljZS5jY3R2UmV0cnkoeyBmbG9vcklkOiB0aGlzLmZsb29ySWQgfSlcbiAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSlcbiAgICAgIC5zdWJzY3JpYmUoe1xuICAgICAgICBuZXh0OiAocmVzOiBhbnkpID0+IHtcbiAgICAgICAgICB0aGlzLnJldHJ5aW5nID0gZmFsc2U7XG4gICAgICAgICAgdGhpcy50b2FzdFNlcnZpY2UuZ2V0U3VjY2Vzc1RvYXN0KFxuICAgICAgICAgICAgcmVzPy5kYXRhPy5zdGFydGVkTm93ID9cbiAgICAgICAgICAgICAgJ0F1dG8gcGxhY2VtZW50IGlzIHJ1bm5pbmcgYWdhaW4uJyA6XG4gICAgICAgICAgICAgICdRdWV1ZWQgZm9yIGF1dG8gcGxhY2VtZW50LiBJdCB3aWxsIHN0YXJ0IG9uIHRoZSBuZXh0IHJ1bi4nLFxuICAgICAgICAgICk7XG4gICAgICAgICAgdGhpcy5sb2FkQ2N0dkRldGFpbCgpO1xuICAgICAgICB9LFxuICAgICAgICBlcnJvcjogKCkgPT4ge1xuICAgICAgICAgIHRoaXMucmV0cnlpbmcgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLnRvYXN0U2VydmljZS5nZXRFcnJvclRvYXN0KCdVbmFibGUgdG8gcmV0cnkuIFBsZWFzZSB0cnkgYWdhaW4uJyk7XG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgfVxuXG4gIG9wZW5FZGl0b3IoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmZsb29ySWQpIHJldHVybjtcbiAgICB0aGlzLnJvdXRlci5uYXZpZ2F0ZShbJy9tYW5hZ2UvcGxhbm9ncmFtL2NjdHYtZWRpdG9yJ10sIHtcbiAgICAgIHF1ZXJ5UGFyYW1zOiB7IGZsb29ySWQ6IHRoaXMuZmxvb3JJZCwgbW9kZTogJ2VkaXQnIH0sXG4gICAgfSk7XG4gIH1cblxuICBiYWNrKCk6IHZvaWQge1xuICAgIHRoaXMucm91dGVyLm5hdmlnYXRlKFsnL3RpY2tldHMvdXNlcnMnXSwgeyBxdWVyeVBhcmFtczogeyB0eXBlOiAnaW5zdGFsbGF0aW9uJyB9IH0pO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwiY29udGFpbmVyLWZsdWlkIHAtNSBwcmVpbnN0YWxsLXBhZ2VcIj5cblxuICA8IS0tIEhlYWRlciAtLT5cbiAgPGRpdiBjbGFzcz1cImNhcmQgbWItNVwiPlxuICAgIDxkaXYgY2xhc3M9XCJjYXJkLWJvZHkgZC1mbGV4IGZsZXgtd3JhcCBhbGlnbi1pdGVtcy1jZW50ZXIganVzdGlmeS1jb250ZW50LWJldHdlZW4gZ2FwLTQgcHktNFwiPlxuICAgICAgPGRpdiBjbGFzcz1cImQtZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgZ2FwLTRcIj5cbiAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJidG4gYnRuLXNtIGJ0bi1saWdodCBiYWNrLWJ0blwiIChjbGljayk9XCJiYWNrKClcIiBhcmlhLWxhYmVsPVwiQmFjayB0byB3b3Jrc3BhY2VcIj5cbiAgICAgICAgICA8aSBjbGFzcz1cImJpIGJpLWFycm93LWxlZnRcIj48L2k+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgICA8ZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJkLWZsZXggYWxpZ24taXRlbXMtY2VudGVyIGdhcC0zXCI+XG4gICAgICAgICAgICA8aDQgY2xhc3M9XCJtYi0wXCI+e3sgdGlja2V0SW5mbz8udGlja2V0SWQgfHwgdGlja2V0SWQgfX08L2g0PlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJiYWRnZSBzdGF0ZS1iYWRnZVwiIFtuZ0NsYXNzXT1cIidzdGF0ZS0nICsgcGxhY2VtZW50U3RhdGVcIj5cbiAgICAgICAgICAgICAge3tcbiAgICAgICAgICAgICAgICBwbGFjZW1lbnRTdGF0ZSA9PT0gJ3dhaXRpbmdMYXlvdXQnID8gJ1dhaXRpbmcgZm9yIGxheW91dCcgOlxuICAgICAgICAgICAgICAgIHBsYWNlbWVudFN0YXRlID09PSAncHJvY2Vzc2luZycgPyAnQXV0byBwbGFjZW1lbnQgaW4gcHJvZ3Jlc3MnIDpcbiAgICAgICAgICAgICAgICBwbGFjZW1lbnRTdGF0ZSA9PT0gJ2ZhaWxlZCcgPyAnUGxhY2VtZW50IGZhaWxlZCcgOlxuICAgICAgICAgICAgICAgIHBsYWNlbWVudFN0YXRlID09PSAnb3BlbicgPyAnUmVhZHkgZm9yIHBsYWNlbWVudCByZXZpZXcnIDpcbiAgICAgICAgICAgICAgICBwbGFjZW1lbnRTdGF0ZSA9PT0gJ2FwcHJvdmFsUGVuZGluZycgPyAnV2FpdGluZyBmb3IgYXBwcm92YWwnIDpcbiAgICAgICAgICAgICAgICBwbGFjZW1lbnRTdGF0ZSA9PT0gJ2FwcHJvdmVkJyA/ICdBcHByb3ZlZCcgOlxuICAgICAgICAgICAgICAgIHBsYWNlbWVudFN0YXRlID09PSAndW5hdmFpbGFibGUnID8gJ1N0YXR1cyB1bmF2YWlsYWJsZScgOiAnTG9hZGluZydcbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwidGV4dC1tdXRlZCBmcy03IG10LTFcIj5cbiAgICAgICAgICAgIFByZS1JbnN0YWxsYXRpb24sIGNhbWVyYSBwbGFjZW1lbnRcbiAgICAgICAgICAgIDxzcGFuICpuZ0lmPVwidGlja2V0SW5mbz8uYmFzaWNEZXRhaWxzPy5zdG9yZU5hbWVcIj4sIHt7IHRpY2tldEluZm8/LmJhc2ljRGV0YWlscz8uc3RvcmVOYW1lIH19PC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4+LCBGbG9vciB7eyBmbG9vck51bWJlciB9fTwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXYgY2xhc3M9XCJkLWZsZXggYWxpZ24taXRlbXMtY2VudGVyIGdhcC0zXCI+XG4gICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiYnRuIGJ0bi1zbSBidG4tbGlnaHQgcm93LWJ0blwiIChjbGljayk9XCJyZWZyZXNoKClcIj5cbiAgICAgICAgICA8aSBjbGFzcz1cImJpIGJpLWFycm93LWNsb2Nrd2lzZSBtZS0xXCI+PC9pPlJlZnJlc2hcbiAgICAgICAgPC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPGRpdiAqbmdJZj1cImxvYWRpbmdcIiBjbGFzcz1cInRleHQtY2VudGVyIHB5LTEwIHRleHQtbXV0ZWRcIj5Mb2FkaW5nIHRpY2tldC4uLjwvZGl2PlxuXG4gIDxkaXYgKm5nSWY9XCIhbG9hZGluZyAmJiB0aWNrZXRJbmZvXCIgY2xhc3M9XCJyb3cgZy01XCI+XG5cbiAgICA8IS0tIFBsYWNlbWVudCBzdGF0ZSAvIGFjdGlvbiBwYW5lbCAtLT5cbiAgICA8ZGl2IGNsYXNzPVwiY29sLTEyIGNvbC1sZy03XCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiY2FyZCBoLTEwMFwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiY2FyZC1ib2R5XCI+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImNhcmQtaGVhZCBtYi00XCI+XG4gICAgICAgICAgICA8aDUgY2xhc3M9XCJtYi0wXCI+Q2FtZXJhIHBsYWNlbWVudDwvaDU+XG4gICAgICAgICAgICA8IS0tIFRoZSBpbXBvcnRlZCBkcmF3aW5nIGl0c2VsZiwgZm9yIHRoZSBpbnRlcm5hbCB0ZWFtIHRvIGluc3BlY3QgaW5cbiAgICAgICAgICAgICAgICAgQ0FEIHdoZW4gdGhlIGVuZ2luZSBjYW5ub3QgaGFuZGxlIGl0IG9yIGJlZm9yZSBlZGl0aW5nLiAtLT5cbiAgICAgICAgICAgIDxhICpuZ0lmPVwic291cmNlRHhmVXJsXCIgW2hyZWZdPVwic291cmNlRHhmVXJsXCIgdGFyZ2V0PVwiX2JsYW5rXCIgcmVsPVwibm9vcGVuZXJcIlxuICAgICAgICAgICAgICBjbGFzcz1cImJ0biBidG4tc20gYnRuLWxpZ2h0IHJvdy1idG5cIiBbbmdiVG9vbHRpcF09XCJzb3VyY2VEeGZOYW1lXCI+XG4gICAgICAgICAgICAgIDxzdmcgd2lkdGg9XCIxNlwiIGhlaWdodD1cIjE2XCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPlxuICAgICAgICAgICAgICAgIDxwYXRoIGQ9XCJNMTIgM3YxMm0wIDAgNC00bS00IDQtNC00TTQgMTd2MmEyIDIgMCAwIDAgMiAyaDEyYTIgMiAwIDAgMCAyLTJ2LTJcIlxuICAgICAgICAgICAgICAgICAgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMS44XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCIgLz5cbiAgICAgICAgICAgICAgPC9zdmc+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibXMtMVwiPkRvd25sb2FkIHNvdXJjZSBEWEY8L3NwYW4+XG4gICAgICAgICAgICA8L2E+XG4gICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICA8ZGl2IFtuZ1N3aXRjaF09XCJwbGFjZW1lbnRTdGF0ZVwiIGNsYXNzPVwic3RhdGUtcGFuZWxcIj5cblxuICAgICAgICAgICAgPGRpdiAqbmdTd2l0Y2hDYXNlPVwiJ3dhaXRpbmdMYXlvdXQnXCIgY2xhc3M9XCJzdGF0ZS1ib3hcIj5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJtYi0xIGZ3LXNlbWlib2xkXCI+V2FpdGluZyBmb3IgdGhlIENBRCBsYXlvdXQuPC9wPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cInRleHQtbXV0ZWQgbWItMFwiPlRoZSBzdG9yZSdzIGZsb29yIGRyYXdpbmcgaGFzIG5vdCBiZWVuIGltcG9ydGVkIHlldC4gQXV0byBwbGFjZW1lbnQgc3RhcnRzIHJpZ2h0IGFmdGVyIHRoZSBDQUQgaW1wb3J0LjwvcD5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8ZGl2ICpuZ1N3aXRjaENhc2U9XCIncHJvY2Vzc2luZydcIiBjbGFzcz1cInN0YXRlLWJveFwiPlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBnYXAtMyBtYi0xXCI+XG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJzcGlubmVyLWJvcmRlciBzcGlubmVyLWJvcmRlci1zbSB0ZXh0LXByaW1hcnlcIiByb2xlPVwic3RhdHVzXCIgYXJpYS1oaWRkZW49XCJ0cnVlXCI+PC9zcGFuPlxuICAgICAgICAgICAgICAgIDxwIGNsYXNzPVwibWItMCBmdy1zZW1pYm9sZFwiPkF1dG8gY2FtZXJhIHBsYWNlbWVudCBpcyBydW5uaW5nLjwvcD5cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwidGV4dC1tdXRlZCBtYi0wXCI+VGhlIERYRiBlZGl0b3IgdW5sb2NrcyB3aGVuIHBsYWNlbWVudCBjb21wbGV0ZXMuIFVzZSBSZWZyZXNoIHRvIGNoZWNrIGFnYWluLjwvcD5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8ZGl2ICpuZ1N3aXRjaENhc2U9XCInZmFpbGVkJ1wiIGNsYXNzPVwic3RhdGUtYm94IHN0YXRlLWJveC1kYW5nZXJcIj5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJtYi0xIGZ3LXNlbWlib2xkXCI+QXV0byBwbGFjZW1lbnQgZmFpbGVkLjwvcD5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJ0ZXh0LW11dGVkIG1iLTNcIj57eyBwbGFjZW1lbnQ/LmVycm9yTXNnIHx8IGNjdHZSb3c/LmVycm9yTXNnIHx8ICdUaGUgcGxhY2VtZW50IGVuZ2luZSByZXBvcnRlZCBhIGZhaWx1cmUuJyB9fTwvcD5cbiAgICAgICAgICAgICAgPCEtLSBSZXRyeSBoaWRkZW4gdW50aWwgdGhlIGF1dG9QbGFjZSBMYW1iZGEgZXhpc3RzIChMQU1CREFVUkwuYXV0b1BsYWNlIGlzIGVtcHR5KS5cbiAgICAgICAgICAgICAgICAgICBSZS1lbmFibGUgYnkgcmVtb3ZpbmcgdGhpcyBjb21tZW50OyByZXRyeVBsYWNlbWVudCgpIGlzIHN0aWxsIHdpcmVkIGluIHRoZSBjb21wb25lbnQuXG4gICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiYnRuIGJ0bi1zbSBidG4tcHJpbWFyeSByb3ctYnRuXCIgW2Rpc2FibGVkXT1cInJldHJ5aW5nXCIgKGNsaWNrKT1cInJldHJ5UGxhY2VtZW50KClcIj5cbiAgICAgICAgICAgICAgICA8c3BhbiAqbmdJZj1cInJldHJ5aW5nXCIgY2xhc3M9XCJzcGlubmVyLWJvcmRlciBzcGlubmVyLWJvcmRlci1zbSBtZS0yXCIgcm9sZT1cInN0YXR1c1wiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPjwvc3Bhbj5cbiAgICAgICAgICAgICAgICB7eyByZXRyeWluZyA/ICdSZXRyeWluZy4uLicgOiAnUmV0cnkgYXV0byBwbGFjZW1lbnQnIH19XG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgICAtLT5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8ZGl2ICpuZ1N3aXRjaENhc2U9XCInb3BlbidcIiBjbGFzcz1cInN0YXRlLWJveFwiPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cIm1iLTEgZnctc2VtaWJvbGRcIj5UaGUgZHJhd2luZyBpcyByZWFkeSBmb3IgY2FtZXJhIHJldmlldy48L3A+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwidGV4dC1tdXRlZCBtYi0zXCI+XG4gICAgICAgICAgICAgICAgQWRqdXN0IHRoZSBhdXRvLXBsYWNlZCBjYW1lcmFzIGluIHRoZSBEWEYgZWRpdG9yLCB0aGVuIHN1Ym1pdCBmb3IgYXBwcm92YWwuXG4gICAgICAgICAgICAgICAgPHNwYW4gKm5nSWY9XCJjY3R2Um93Py5pc1Jld29ya1wiIGNsYXNzPVwidGV4dC13YXJuaW5nIGZ3LXNlbWlib2xkXCI+VGhpcyBmbG9vciB3YXMgc2VudCBiYWNrIGZvciByZXdvcmsuPC9zcGFuPlxuICAgICAgICAgICAgICA8L3A+XG4gICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiYnRuIGJ0bi1zbSBidG4tcHJpbWFyeSByb3ctYnRuXCIgKGNsaWNrKT1cIm9wZW5FZGl0b3IoKVwiPlxuICAgICAgICAgICAgICAgIDxpIGNsYXNzPVwiYmkgYmktcGVuY2lsLXNxdWFyZSBtZS0xXCI+PC9pPk9wZW4gRFhGIGVkaXRvclxuICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8ZGl2ICpuZ1N3aXRjaENhc2U9XCInYXBwcm92YWxQZW5kaW5nJ1wiIGNsYXNzPVwic3RhdGUtYm94XCI+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwibWItMSBmdy1zZW1pYm9sZFwiPlN1Ym1pdHRlZCwgd2FpdGluZyBmb3IgYXBwcm92YWwuPC9wPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cInRleHQtbXV0ZWQgbWItM1wiPlRoZSBhcHByb3ZhbCB0ZWFtIHJldmlld3MgaXQgZnJvbSB0aGUgb25ib2FyZCB0YWJsZS4gWW91IGNhbiBzdGlsbCB2aWV3IHRoZSBkcmF3aW5nLjwvcD5cbiAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJidG4gYnRuLXNtIGJ0bi1saWdodCByb3ctYnRuXCIgKGNsaWNrKT1cIm9wZW5FZGl0b3IoKVwiPlxuICAgICAgICAgICAgICAgIDxpIGNsYXNzPVwiYmkgYmktZXllIG1lLTFcIj48L2k+VmlldyBkcmF3aW5nXG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgIDxkaXYgKm5nU3dpdGNoQ2FzZT1cIidhcHByb3ZlZCdcIiBjbGFzcz1cInN0YXRlLWJveCBzdGF0ZS1ib3gtc3VjY2Vzc1wiPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cIm1iLTEgZnctc2VtaWJvbGRcIj5DYW1lcmEgcGxhY2VtZW50IGFwcHJvdmVkLjwvcD5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJ0ZXh0LW11dGVkIG1iLTBcIj5UaGlzIHRpY2tldCBpcyBjbG9zZWQuIFRoZSBpbnN0YWxsYXRpb24gdGlja2V0IHRha2VzIG92ZXIgb25jZSBldmVyeSBmbG9vciBvZiB0aGUgc3RvcmUgaXMgYXBwcm92ZWQuPC9wPlxuICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgIDxkaXYgKm5nU3dpdGNoQ2FzZT1cIid1bmF2YWlsYWJsZSdcIiBjbGFzcz1cInN0YXRlLWJveCBzdGF0ZS1ib3gtZGFuZ2VyXCI+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwibWItMSBmdy1zZW1pYm9sZFwiPlBsYWNlbWVudCBzdGF0dXMgdW5hdmFpbGFibGUuPC9wPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cInRleHQtbXV0ZWQgbWItM1wiPnt7IGNjdHZFcnJvciB9fTwvcD5cbiAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJidG4gYnRuLXNtIGJ0bi1saWdodCByb3ctYnRuXCIgKGNsaWNrKT1cImxvYWRDY3R2RGV0YWlsKClcIj5UcnkgYWdhaW48L2J1dHRvbj5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8ZGl2ICpuZ1N3aXRjaERlZmF1bHQgY2xhc3M9XCJzdGF0ZS1ib3hcIj5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJ0ZXh0LW11dGVkIG1iLTBcIj5Mb2FkaW5nIHBsYWNlbWVudCBzdGF0dXMuLi48L3A+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgIDwhLS0gVmVyc2lvbiB0aW1lbGluZSAtLT5cbiAgICAgICAgICA8ZGl2ICpuZ0lmPVwidmVyc2lvbnMubGVuZ3RoXCIgY2xhc3M9XCJtdC02XCI+XG4gICAgICAgICAgICA8aDYgY2xhc3M9XCJ0ZXh0LW11dGVkIG1iLTNcIj5EcmF3aW5nIHZlcnNpb25zPC9oNj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ2ZXJzaW9uLWxpc3RcIj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInZlcnNpb24tcm93IGQtZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIganVzdGlmeS1jb250ZW50LWJldHdlZW5cIiAqbmdGb3I9XCJsZXQgZmlsZSBvZiB2ZXJzaW9uc1wiPlxuICAgICAgICAgICAgICAgIDxkaXY+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImZ3LXNlbWlib2xkXCI+dnt7IGZpbGUudmVyc2lvbiB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZCBtcy0yIGZzLTdcIj57eyBmaWxlLmtpbmQgPT09ICdhdXRvJyA/ICdBdXRvIHBsYWNlbWVudCcgOiBmaWxlLmtpbmQgPT09ICdhcHByb3ZlcicgPyAnQXBwcm92ZXIgZWRpdCcgOiAnSW50ZXJuYWwgZWRpdCcgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtbXV0ZWQgbXMtMiBmcy03XCIgKm5nSWY9XCJmaWxlLmNhbWVyYUNvdW50ICE9IG51bGxcIj57eyBmaWxlLmNhbWVyYUNvdW50IH19IGNhbWVyYXM8L3NwYW4+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImQtZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgZ2FwLTNcIj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZCBmcy03XCI+e3sgZmlsZS51cGxvYWRlZEF0IHwgZGF0ZTogJ2RkIE1NTSwgeSBISDptbScgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8YSAqbmdJZj1cImZpbGVVcmxzW2ZpbGUua2V5XVwiIFtocmVmXT1cImZpbGVVcmxzW2ZpbGUua2V5XVwiIHRhcmdldD1cIl9ibGFua1wiIHJlbD1cIm5vb3BlbmVyXCIgY2xhc3M9XCJidG4gYnRuLXNtIGJ0bi1saWdodCByb3ctYnRuXCI+XG4gICAgICAgICAgICAgICAgICAgIDxpIGNsYXNzPVwiYmkgYmktZG93bmxvYWRcIj48L2k+XG4gICAgICAgICAgICAgICAgICA8L2E+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG5cbiAgICA8IS0tIFRpY2tldCBpbmZvICsgYWN0aXZpdHkgLS0+XG4gICAgPGRpdiBjbGFzcz1cImNvbC0xMiBjb2wtbGctNVwiPlxuICAgICAgPGRpdiBjbGFzcz1cImNhcmQgbWItNVwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiY2FyZC1ib2R5XCI+XG4gICAgICAgICAgPGg1IGNsYXNzPVwibWItNFwiPlRpY2tldCBkZXRhaWxzPC9oNT5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiZGV0YWlsLWdyaWRcIj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkZXRhaWwtcm93XCI+PHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkXCI+U3RvcmU8L3NwYW4+PHNwYW4+e3sgdGlja2V0SW5mbz8uYmFzaWNEZXRhaWxzPy5zdG9yZU5hbWUgfHwgJy0nIH19ICh7eyB0aWNrZXRJbmZvPy5iYXNpY0RldGFpbHM/LnN0b3JlSWQgfHwgc3RvcmVJZCB9fSk8L3NwYW4+PC9kaXY+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZGV0YWlsLXJvd1wiPjxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZFwiPkJyYW5kPC9zcGFuPjxzcGFuPnt7IHRpY2tldEluZm8/LmJhc2ljRGV0YWlscz8uY2xpZW50TmFtZSB8fCAnLScgfX08L3NwYW4+PC9kaXY+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZGV0YWlsLXJvd1wiPjxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZFwiPkZsb29yPC9zcGFuPjxzcGFuPkZsb29yIHt7IGZsb29yTnVtYmVyIH19PC9zcGFuPjwvZGl2PlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImRldGFpbC1yb3dcIj48c3BhbiBjbGFzcz1cInRleHQtbXV0ZWRcIj5UaWNrZXQgc3RhdHVzPC9zcGFuPjxzcGFuIGNsYXNzPVwidGV4dC1jYXBpdGFsaXplXCI+e3sgdGlja2V0SW5mbz8uc3RhdHVzIHx8ICctJyB9fTwvc3Bhbj48L2Rpdj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkZXRhaWwtcm93XCI+PHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkXCI+Q3JlYXRlZDwvc3Bhbj48c3Bhbj57eyB0aWNrZXRJbmZvPy5jcmVhdGVkQXQgfCBkYXRlOiAnZGQgTU1NLCB5IEhIOm1tJyB9fTwvc3Bhbj48L2Rpdj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkZXRhaWwtcm93XCIgKm5nSWY9XCJjY3R2Um93Py5hc3NpZ25lZU5hbWVcIj48c3BhbiBjbGFzcz1cInRleHQtbXV0ZWRcIj5Bc3NpZ25lZTwvc3Bhbj48c3Bhbj57eyBjY3R2Um93Py5hc3NpZ25lZU5hbWUgfX08L3NwYW4+PC9kaXY+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZGV0YWlsLXJvd1wiICpuZ0lmPVwiY2N0dlJvdz8ucmV3b3JrQ291bnRcIj48c3BhbiBjbGFzcz1cInRleHQtbXV0ZWRcIj5SZXdvcmsgY291bnQ8L3NwYW4+PHNwYW4+e3sgY2N0dlJvdz8ucmV3b3JrQ291bnQgfX08L3NwYW4+PC9kaXY+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxkaXYgY2xhc3M9XCJjYXJkXCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJjYXJkLWJvZHlcIj5cbiAgICAgICAgICA8aDUgY2xhc3M9XCJtYi00XCI+QWN0aXZpdHk8L2g1PlxuICAgICAgICAgIDxkaXYgKm5nSWY9XCIhYWN0aXZpdHkubGVuZ3RoXCIgY2xhc3M9XCJ0ZXh0LW11dGVkIGZzLTdcIj5ObyBhY3Rpdml0eSB5ZXQuPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImFjdGl2aXR5LWxpc3RcIj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJhY3Rpdml0eS1yb3dcIiAqbmdGb3I9XCJsZXQgZW50cnkgb2YgYWN0aXZpdHlcIj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImFjdGl2aXR5LWRvdFwiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPjwvZGl2PlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZmxleC1ncm93LTFcIj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGp1c3RpZnktY29udGVudC1iZXR3ZWVuIGdhcC0zXCI+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImZ3LXNlbWlib2xkIGZzLTdcIj57eyBlbnRyeS5kZXNjcmlwdGlvbiB8fCBlbnRyeS5hY3Rpb25UeXBlIH19PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkIGZzLTggdGV4dC1ub3dyYXBcIj57eyBlbnRyeS50aW1lU3RhbXAgfCBkYXRlOiAnZGQgTU1NLCBISDptbScgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInRleHQtbXV0ZWQgZnMtOFwiICpuZ0lmPVwiZW50cnkuY29tbWVudFwiPnt7IGVudHJ5LmNvbW1lbnQgfX08L2Rpdj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidGV4dC1tdXRlZCBmcy04XCIgKm5nSWY9XCJlbnRyeS5JZGVudGlmaWVkQnkgJiYgZW50cnkuSWRlbnRpZmllZEJ5ICE9PSAnVGFuZ28nXCI+Ynkge3sgZW50cnkuSWRlbnRpZmllZEJ5IH19PC9kaXY+XG4gICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuPC9kaXY+XG4iXX0=
|
|
191
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlaW5zdGFsbGF0aW9uLWluZm8uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvdGFuZ28tdGlja2V0LXVzZXIvc3JjL2xpYi9jb21wb25lbnRzL3ByZWluc3RhbGxhdGlvbi1pbmZvL3ByZWluc3RhbGxhdGlvbi1pbmZvLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RhbmdvLXRpY2tldC11c2VyL3NyYy9saWIvY29tcG9uZW50cy9wcmVpbnN0YWxsYXRpb24taW5mby9wcmVpbnN0YWxsYXRpb24taW5mby5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFxQixNQUFNLGVBQWUsQ0FBQztBQUU3RCxPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLE1BQU0sQ0FBQzs7Ozs7OztBQUkxQzs7Ozs7Ozs7Ozs7O0dBWUc7QUFNSCxNQUFNLE9BQU8sNEJBQTRCO0lBbUI3QjtJQUNBO0lBQ0E7SUFDQTtJQXJCVixRQUFRLENBQWdCO0lBQ3hCLE9BQU8sQ0FBZ0I7SUFDdkIsVUFBVSxDQUFNO0lBQ2hCLE9BQU8sQ0FBTTtJQUNiLFNBQVMsQ0FBTTtJQUNmLHlEQUF5RDtJQUN6RCxTQUFTLEdBQVEsSUFBSSxDQUFDO0lBQ3RCLFFBQVEsR0FBUSxFQUFFLENBQUM7SUFDbkIsT0FBTyxHQUFZLElBQUksQ0FBQztJQUN4QixXQUFXLEdBQVksS0FBSyxDQUFDO0lBQzdCLDRFQUE0RTtJQUM1RSxTQUFTLEdBQVksS0FBSyxDQUFDO0lBQzNCLFNBQVMsR0FBVyxFQUFFLENBQUM7SUFDdkIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUVULFFBQVEsR0FBRyxJQUFJLE9BQU8sRUFBRSxDQUFDO0lBRTFDLFlBQ1UsS0FBcUIsRUFDckIsTUFBYyxFQUNkLE9BQXVCLEVBQ3ZCLFlBQTBCO1FBSDFCLFVBQUss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VyxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRTtTQUN2RCxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsSUFBSTtRQUNGLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxFQUFFLFdBQVcsRUFBRSxFQUFFLElBQUksRUFBRSxjQUFjLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDdEYsQ0FBQzt3R0F6S1UsNEJBQTRCOzRGQUE1Qiw0QkFBNEIsZ0VDeEJ6QyxreVZBNExBOzs0RkRwS2EsNEJBQTRCO2tCQUx4QyxTQUFTOytCQUNFLDBCQUEwQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgT25EZXN0cm95LCBPbkluaXQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEFjdGl2YXRlZFJvdXRlLCBSb3V0ZXIgfSBmcm9tICdAYW5ndWxhci9yb3V0ZXInO1xuaW1wb3J0IHsgU3ViamVjdCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBUb2FzdFNlcnZpY2UgfSBmcm9tICd0YW5nby1hcHAtdWktc2hhcmVkJztcbmltcG9ydCB7IFRpY2tldHNTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvdGlja2V0LWluZnJhLnNlcnZpY2UnO1xuXG4vKipcbiAqIFByZS1JbnN0YWxsYXRpb24gdGlja2V0IGRldGFpbCBwYWdlICh3b3Jrc3BhY2UgZW50cnkgZm9yIFRFX1BSRSB0aWNrZXRzKS5cbiAqIFNhbWUgcm9sZSBhcyBJbnN0YWxsYXRpb25Db21wb25lbnQgZm9yIFRFX0lOUyB0aWNrZXRzOiB0aGUgdGlja2V0IGhlYWRlciArXG4gKiBhY3Rpdml0eSB0cmFpbCBjb21lIGZyb20gdGhlIHRpY2tldHMgQVBJLCB0aGUgTElWRSBjYW1lcmEtcGxhY2VtZW50IHN0YXRlXG4gKiBjb21lcyBmcm9tIHRoZSBzdG9yZS1idWlsZGVyIENDVFYgcm93LCBhbmQgdGhlIHBhZ2UgZ2F0ZXMgdGhlIG5leHQgc3RlcDpcbiAqICAgYXV0b1BsYWNlbWVudCAgLT4gXCJBdXRvIHBsYWNlbWVudCBpbiBwcm9ncmVzc1wiLCBlZGl0b3IgbG9ja2VkLCBzb3VyY2UgcHJldmlld1xuICogICBmYWlsZWQgICAgICAgICAtPiBmYWlsdXJlIHJlYXNvbiArIFJldHJ5XG4gKiAgIG9wZW4gICAgICAgICAgIC0+IFwiT3BlbiBEWEYgZWRpdG9yXCIgKGFzc2lnbmVlIGdhdGVkIHNlcnZlci1zaWRlKVxuICogICBhcHByb3ZhbFBlbmRpbmctPiB3YWl0aW5nIGZvciBhcHByb3ZhbCwgZHJhd2luZyB2aWV3YWJsZVxuICogICBhcHByb3ZlZCAgICAgICAtPiBkb25lICh0aWNrZXQgY2xvc2VzIHZpYSB0aGUgYXBwcm92ZSBob29rKVxuICogTG9hZGluZyB0aGUgQ0NUViBkZXRhaWwgYWxzbyB0cmlnZ2VycyB0aGUgc2VydmVyLXNpZGUgT1MgcmVjb25jaWxlLCBzbyB0aGlzXG4gKiBwYWdlIGtlZXBzIHRoZSByb3cgZnJlc2gsIHRoZSBqb2IgdGhlIENDVFYgdGFibGUgdXNlZCB0byBkby5cbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbGliLXByZWluc3RhbGxhdGlvbi1pbmZvJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3ByZWluc3RhbGxhdGlvbi1pbmZvLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmw6ICcuL3ByZWluc3RhbGxhdGlvbi1pbmZvLmNvbXBvbmVudC5zY3NzJyxcbn0pXG5leHBvcnQgY2xhc3MgUHJlaW5zdGFsbGF0aW9uSW5mb0NvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgdGlja2V0SWQ6IHN0cmluZyB8IG51bGw7XG4gIHN0b3JlSWQ6IHN0cmluZyB8IG51bGw7XG4gIHRpY2tldEluZm86IGFueTtcbiAgY2N0dlJvdzogYW55O1xuICBjY3R2Rmxvb3I6IGFueTtcbiAgLyoqIGxpdmUgcGxhY2VtZW50IHN0YXRlIHN0cmFpZ2h0IG9mZiB0aGUgc3RhdHVzIGluZGV4ICovXG4gIHBsYWNlbWVudDogYW55ID0gbnVsbDtcbiAgZmlsZVVybHM6IGFueSA9IHt9O1xuICBsb2FkaW5nOiBib29sZWFuID0gdHJ1ZTtcbiAgY2N0dkxvYWRpbmc6IGJvb2xlYW4gPSBmYWxzZTtcbiAgLyoqIFJlZnJlc2ggY2xpY2tlZCBhbmQgdGhlIHRpY2tldCByZWxvYWQgaXMgc3RpbGwgaW4gZmxpZ2h0IChpY29uIHR1cm5zKSAqL1xuICByZWxvYWRpbmc6IGJvb2xlYW4gPSBmYWxzZTtcbiAgY2N0dkVycm9yOiBzdHJpbmcgPSAnJztcbiAgcmV0cnlpbmc6IGJvb2xlYW4gPSBmYWxzZTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGRlc3Ryb3kkID0gbmV3IFN1YmplY3QoKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSxcbiAgICBwcml2YXRlIHJvdXRlcjogUm91dGVyLFxuICAgIHByaXZhdGUgc2VydmljZTogVGlja2V0c1NlcnZpY2UsXG4gICAgcHJpdmF0ZSB0b2FzdFNlcnZpY2U6IFRvYXN0U2VydmljZSxcbiAgKSB7XG4gICAgdGhpcy50aWNrZXRJZCA9IHRoaXMucm91dGUuc25hcHNob3QucGFyYW1NYXAuZ2V0KCd0aWNrZXRJZCcpO1xuICAgIHRoaXMuc3RvcmVJZCA9IHRoaXMucm91dGUuc25hcHNob3QucGFyYW1NYXAuZ2V0KCdzdG9yZUlkJyk7XG4gIH1cblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLnZpZXdUaWNrZXQoKTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuZGVzdHJveSQubmV4dCh0cnVlKTtcbiAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XG4gIH1cblxuICBnZXQgZmxvb3JJZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLnRpY2tldEluZm8/LnByZUluc3RhbGxhdGlvbj8uZmxvb3JJZCB8fCAnJztcbiAgfVxuXG4gIGdldCBmbG9vck51bWJlcigpOiBhbnkge1xuICAgIHJldHVybiB0aGlzLnRpY2tldEluZm8/LnByZUluc3RhbGxhdGlvbj8uZmxvb3JOdW1iZXIgfHwgJy0nO1xuICB9XG5cbiAgLyoqIFByZXNpZ25lZCBsaW5rIHRvIHRoZSBpbXBvcnRlZCBkcmF3aW5nICh2MSksIGZyb20gdGhlIGRldGFpbCBjYWxsJ3MgdXJsIG1hcC4gKi9cbiAgZ2V0IHNvdXJjZUR4ZlVybCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICBjb25zdCBrZXkgPSB0aGlzLmNjdHZSb3c/LnNvdXJjZUtleTtcbiAgICByZXR1cm4ga2V5ID8gdGhpcy5maWxlVXJscz8uW2tleV0gfHwgbnVsbCA6IG51bGw7XG4gIH1cblxuICBnZXQgc291cmNlRHhmTmFtZSgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGtleTogc3RyaW5nID0gdGhpcy5jY3R2Um93Py5zb3VyY2VLZXkgfHwgJyc7XG4gICAgcmV0dXJuIGtleS5zcGxpdCgnLycpLnBvcCgpIHx8ICdzb3VyY2UuZHhmJztcbiAgfVxuXG4gIC8vIFRoZSBwYWdlJ3Mgc2luZ2xlIHN0YXRlIHN3aXRjaCwgZGVyaXZlZCBmcm9tIHRpY2tldCArIGxpdmUgQ0NUViByb3cuXG4gIGdldCBwbGFjZW1lbnRTdGF0ZSgpOiBzdHJpbmcge1xuICAgIGlmICh0aGlzLnRpY2tldEluZm8/LnN0YXR1cyA9PT0gJ2Nsb3NlZCcpIHJldHVybiAnYXBwcm92ZWQnO1xuICAgIGlmICghdGhpcy5mbG9vcklkKSByZXR1cm4gJ3dhaXRpbmdMYXlvdXQnO1xuICAgIGlmICghdGhpcy5jY3R2Um93KSByZXR1cm4gdGhpcy5jY3R2RXJyb3IgPyAndW5hdmFpbGFibGUnIDogJ2xvYWRpbmcnO1xuXG4gICAgLy8gVGhlIHN0YXR1cyBpbmRleCBvd25zIHRoZSBwbGFjZW1lbnQgaGFsZjogcXVldWVkIGFuZCBydW5uaW5nIGJvdGggcmVhZFxuICAgIC8vIGFzIFwiaW4gcHJvZ3Jlc3NcIiwgYW5kIGEgZmFpbHVyZSBjYXJyaWVzIGl0cyByZWFzb24uIE9uY2UgdGhlIHBsYWNlbWVudFxuICAgIC8vIGlzIGRvbmUgdGhlIHdvcmtmbG93IHJvdyB0YWtlcyBvdmVyIChvcGVuIHwgYXBwcm92YWxQZW5kaW5nIHwgYXBwcm92ZWQpLlxuICAgIGNvbnN0IGpvYlN0YXR1cyA9IHRoaXMucGxhY2VtZW50Py5zdGF0dXM7XG4gICAgaWYgKGpvYlN0YXR1cyA9PT0gJ3BlbmRpbmcnIHx8IGpvYlN0YXR1cyA9PT0gJ3Byb2Nlc3NpbmcnKSByZXR1cm4gJ3Byb2Nlc3NpbmcnO1xuICAgIGlmIChqb2JTdGF0dXMgPT09ICdmYWlsZWQnKSByZXR1cm4gJ2ZhaWxlZCc7XG4gICAgaWYgKHRoaXMuY2N0dlJvdy5zdGF0dXMgPT09ICdhdXRvUGxhY2VtZW50JykgcmV0dXJuICdwcm9jZXNzaW5nJztcbiAgICByZXR1cm4gdGhpcy5jY3R2Um93LnN0YXR1cztcbiAgfVxuXG4gIGdldCB2ZXJzaW9ucygpOiBhbnlbXSB7XG4gICAgY29uc3QgZmlsZXMgPSBBcnJheS5pc0FycmF5KHRoaXMuY2N0dlJvdz8uZmlsZXMpID8gWy4uLnRoaXMuY2N0dlJvdy5maWxlc10gOiBbXTtcbiAgICByZXR1cm4gZmlsZXMuc29ydCgoYSwgYikgPT4gKGI/LnZlcnNpb24gPz8gMCkgLSAoYT8udmVyc2lvbiA/PyAwKSk7XG4gIH1cblxuICBnZXQgYWN0aXZpdHkoKTogYW55W10ge1xuICAgIGNvbnN0IGxpc3QgPSBBcnJheS5pc0FycmF5KHRoaXMudGlja2V0SW5mbz8udGlja2V0QWN0aXZpdHkpID8gWy4uLnRoaXMudGlja2V0SW5mby50aWNrZXRBY3Rpdml0eV0gOiBbXTtcbiAgICByZXR1cm4gbGlzdC5yZXZlcnNlKCk7XG4gIH1cblxuICB2aWV3VGlja2V0KCk6IHZvaWQge1xuICAgIHRoaXMuc2VydmljZS52aWV3VGlja2V0KHsgdGlja2V0SWQ6IHRoaXMudGlja2V0SWQgfSlcbiAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSlcbiAgICAgIC5zdWJzY3JpYmUoe1xuICAgICAgICBuZXh0OiAocmVzOiBhbnkpID0+IHtcbiAgICAgICAgICB0aGlzLmxvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLnJlbG9hZGluZyA9IGZhbHNlO1xuICAgICAgICAgIGlmIChyZXMgJiYgcmVzLmNvZGUgPT0gMjAwKSB7XG4gICAgICAgICAgICB0aGlzLnRpY2tldEluZm8gPSByZXMuZGF0YTtcbiAgICAgICAgICAgIGlmICh0aGlzLmZsb29ySWQpIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2FkQ2N0dkRldGFpbCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgZXJyb3I6ICgpID0+IHtcbiAgICAgICAgICB0aGlzLmxvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLnJlbG9hZGluZyA9IGZhbHNlO1xuICAgICAgICAgIHRoaXMudG9hc3RTZXJ2aWNlLmdldEVycm9yVG9hc3QoJ1VuYWJsZSB0byBsb2FkIHRoZSB0aWNrZXQuJyk7XG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgfVxuXG4gIGxvYWRDY3R2RGV0YWlsKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5mbG9vcklkKSByZXR1cm47XG4gICAgdGhpcy5jY3R2TG9hZGluZyA9IHRydWU7XG4gICAgdGhpcy5jY3R2RXJyb3IgPSAnJztcbiAgICB0aGlzLnNlcnZpY2UuZ2V0Q2N0dkRldGFpbCh0aGlzLmZsb29ySWQpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKHJlczogYW55KSA9PiB7XG4gICAgICAgICAgdGhpcy5jY3R2TG9hZGluZyA9IGZhbHNlO1xuICAgICAgICAgIHRoaXMuY2N0dlJvdyA9IHJlcz8uZGF0YT8ucm93IHx8IG51bGw7XG4gICAgICAgICAgdGhpcy5jY3R2Rmxvb3IgPSByZXM/LmRhdGE/LmZsb29yIHx8IG51bGw7XG4gICAgICAgICAgdGhpcy5maWxlVXJscyA9IHJlcz8uZGF0YT8udXJscyB8fCB7fTtcbiAgICAgICAgICB0aGlzLnBsYWNlbWVudCA9IHJlcz8uZGF0YT8ucGxhY2VtZW50IHx8IG51bGw7XG4gICAgICAgIH0sXG4gICAgICAgIGVycm9yOiAoKSA9PiB7XG4gICAgICAgICAgdGhpcy5jY3R2TG9hZGluZyA9IGZhbHNlO1xuICAgICAgICAgIHRoaXMuY2N0dkVycm9yID0gJ1VuYWJsZSB0byBsb2FkIHRoZSBjYW1lcmEgcGxhY2VtZW50IHN0YXR1cy4nO1xuICAgICAgICB9LFxuICAgICAgfSk7XG4gIH1cblxuICByZWZyZXNoKCk6IHZvaWQge1xuICAgIHRoaXMucmVsb2FkaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnZpZXdUaWNrZXQoKTtcbiAgfVxuXG4gIHJldHJ5UGxhY2VtZW50KCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5mbG9vcklkIHx8IHRoaXMucmV0cnlpbmcpIHJldHVybjtcbiAgICB0aGlzLnJldHJ5aW5nID0gdHJ1ZTtcbiAgICB0aGlzLnNlcnZpY2UuY2N0dlJldHJ5KHsgZmxvb3JJZDogdGhpcy5mbG9vcklkIH0pXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKHJlczogYW55KSA9PiB7XG4gICAgICAgICAgdGhpcy5yZXRyeWluZyA9IGZhbHNlO1xuICAgICAgICAgIHRoaXMudG9hc3RTZXJ2aWNlLmdldFN1Y2Nlc3NUb2FzdChcbiAgICAgICAgICAgIHJlcz8uZGF0YT8uc3RhcnRlZE5vdyA/XG4gICAgICAgICAgICAgICdBdXRvIHBsYWNlbWVudCBpcyBydW5uaW5nIGFnYWluLicgOlxuICAgICAgICAgICAgICAnUXVldWVkIGZvciBhdXRvIHBsYWNlbWVudC4gSXQgd2lsbCBzdGFydCBvbiB0aGUgbmV4dCBydW4uJyxcbiAgICAgICAgICApO1xuICAgICAgICAgIHRoaXMubG9hZENjdHZEZXRhaWwoKTtcbiAgICAgICAgfSxcbiAgICAgICAgZXJyb3I6ICgpID0+IHtcbiAgICAgICAgICB0aGlzLnJldHJ5aW5nID0gZmFsc2U7XG4gICAgICAgICAgdGhpcy50b2FzdFNlcnZpY2UuZ2V0RXJyb3JUb2FzdCgnVW5hYmxlIHRvIHJldHJ5LiBQbGVhc2UgdHJ5IGFnYWluLicpO1xuICAgICAgICB9LFxuICAgICAgfSk7XG4gIH1cblxuICBvcGVuRWRpdG9yKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5mbG9vcklkKSByZXR1cm47XG4gICAgdGhpcy5yb3V0ZXIubmF2aWdhdGUoWycvbWFuYWdlL3BsYW5vZ3JhbS9jY3R2LWVkaXRvciddLCB7XG4gICAgICBxdWVyeVBhcmFtczogeyBmbG9vcklkOiB0aGlzLmZsb29ySWQsIG1vZGU6ICdlZGl0JyB9LFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIFRoZSBzb3VyY2UgZHJhd2luZywgcmVhZC1vbmx5LCB3aGlsZSBhdXRvIHBsYWNlbWVudCBpcyBzdGlsbCBydW5uaW5nLiAqL1xuICBvcGVuUHJldmlldygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuZmxvb3JJZCkgcmV0dXJuO1xuICAgIHRoaXMucm91dGVyLm5hdmlnYXRlKFsnL21hbmFnZS9wbGFub2dyYW0vY2N0di1wcmV2aWV3J10sIHtcbiAgICAgIHF1ZXJ5UGFyYW1zOiB7IGZsb29ySWQ6IHRoaXMuZmxvb3JJZCwgZnJvbTogJ3RpY2tldCcgfSxcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2soKTogdm9pZCB7XG4gICAgdGhpcy5yb3V0ZXIubmF2aWdhdGUoWycvdGlja2V0cy91c2VycyddLCB7IHF1ZXJ5UGFyYW1zOiB7IHR5cGU6ICdpbnN0YWxsYXRpb24nIH0gfSk7XG4gIH1cbn1cbiIsIjxkaXYgY2xhc3M9XCJjb250YWluZXItZmx1aWQgcC01IHByZWluc3RhbGwtcGFnZVwiPlxuXG4gIDwhLS0gSGVhZGVyIC0tPlxuICA8ZGl2IGNsYXNzPVwiY2FyZCBtYi01XCI+XG4gICAgPGRpdiBjbGFzcz1cImNhcmQtYm9keSBkLWZsZXggZmxleC13cmFwIGFsaWduLWl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNvbnRlbnQtYmV0d2VlbiBnYXAtNCBweS00XCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBnYXAtNFwiPlxuICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImJ0biBidG4tc20gYnRuLWxpZ2h0IGJhY2stYnRuXCIgKGNsaWNrKT1cImJhY2soKVwiIGFyaWEtbGFiZWw9XCJCYWNrIHRvIHdvcmtzcGFjZVwiPlxuICAgICAgICAgIDxpIGNsYXNzPVwiYmkgYmktYXJyb3ctbGVmdFwiPjwvaT5cbiAgICAgICAgPC9idXR0b24+XG4gICAgICAgIDxkaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImQtZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgZ2FwLTNcIj5cbiAgICAgICAgICAgIDxoNCBjbGFzcz1cIm1iLTBcIj57eyB0aWNrZXRJbmZvPy50aWNrZXRJZCB8fCB0aWNrZXRJZCB9fTwvaDQ+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImJhZGdlIHN0YXRlLWJhZGdlXCIgW25nQ2xhc3NdPVwiJ3N0YXRlLScgKyBwbGFjZW1lbnRTdGF0ZVwiPlxuICAgICAgICAgICAgICB7e1xuICAgICAgICAgICAgICAgIHBsYWNlbWVudFN0YXRlID09PSAnd2FpdGluZ0xheW91dCcgPyAnV2FpdGluZyBmb3IgbGF5b3V0JyA6XG4gICAgICAgICAgICAgICAgcGxhY2VtZW50U3RhdGUgPT09ICdwcm9jZXNzaW5nJyA/ICdBdXRvIHBsYWNlbWVudCBpbiBwcm9ncmVzcycgOlxuICAgICAgICAgICAgICAgIHBsYWNlbWVudFN0YXRlID09PSAnZmFpbGVkJyA/ICdQbGFjZW1lbnQgZmFpbGVkJyA6XG4gICAgICAgICAgICAgICAgcGxhY2VtZW50U3RhdGUgPT09ICdvcGVuJyA/ICdSZWFkeSBmb3IgcGxhY2VtZW50IHJldmlldycgOlxuICAgICAgICAgICAgICAgIHBsYWNlbWVudFN0YXRlID09PSAnYXBwcm92YWxQZW5kaW5nJyA/ICdXYWl0aW5nIGZvciBhcHByb3ZhbCcgOlxuICAgICAgICAgICAgICAgIHBsYWNlbWVudFN0YXRlID09PSAnYXBwcm92ZWQnID8gJ0FwcHJvdmVkJyA6XG4gICAgICAgICAgICAgICAgcGxhY2VtZW50U3RhdGUgPT09ICd1bmF2YWlsYWJsZScgPyAnU3RhdHVzIHVuYXZhaWxhYmxlJyA6ICdMb2FkaW5nJ1xuICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0ZXh0LW11dGVkIGZzLTcgbXQtMVwiPlxuICAgICAgICAgICAgUHJlLUluc3RhbGxhdGlvbiwgY2FtZXJhIHBsYWNlbWVudFxuICAgICAgICAgICAgPHNwYW4gKm5nSWY9XCJ0aWNrZXRJbmZvPy5iYXNpY0RldGFpbHM/LnN0b3JlTmFtZVwiPiwge3sgdGlja2V0SW5mbz8uYmFzaWNEZXRhaWxzPy5zdG9yZU5hbWUgfX08L3NwYW4+XG4gICAgICAgICAgICA8c3Bhbj4sIEZsb29yIHt7IGZsb29yTnVtYmVyIH19PC9zcGFuPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgICAgPGRpdiBjbGFzcz1cImQtZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgZ2FwLTNcIj5cbiAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJidG4gYnRuLXNtIGJ0bi1saWdodCByb3ctYnRuXCIgKGNsaWNrKT1cInJlZnJlc2goKVwiPlxuICAgICAgICAgIDxpIGNsYXNzPVwiYmkgYmktYXJyb3ctY2xvY2t3aXNlIG1lLTFcIiBbY2xhc3Muc3Bpbm5pbmddPVwicmVsb2FkaW5nIHx8IGNjdHZMb2FkaW5nXCI+PC9pPlJlZnJlc2hcbiAgICAgICAgPC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPGRpdiAqbmdJZj1cImxvYWRpbmdcIiBjbGFzcz1cInRleHQtY2VudGVyIHB5LTEwIHRleHQtbXV0ZWRcIj5Mb2FkaW5nIHRpY2tldC4uLjwvZGl2PlxuXG4gIDxkaXYgKm5nSWY9XCIhbG9hZGluZyAmJiB0aWNrZXRJbmZvXCIgY2xhc3M9XCJyb3cgZy01XCI+XG5cbiAgICA8IS0tIFBsYWNlbWVudCBzdGF0ZSAvIGFjdGlvbiBwYW5lbCAtLT5cbiAgICA8ZGl2IGNsYXNzPVwiY29sLTEyIGNvbC1sZy03XCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiY2FyZCBoLTEwMFwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiY2FyZC1ib2R5XCI+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImNhcmQtaGVhZCBtYi00XCI+XG4gICAgICAgICAgICA8aDUgY2xhc3M9XCJtYi0wXCI+Q2FtZXJhIHBsYWNlbWVudDwvaDU+XG4gICAgICAgICAgICA8IS0tIFRoZSBpbXBvcnRlZCBkcmF3aW5nIGl0c2VsZiwgZm9yIHRoZSBpbnRlcm5hbCB0ZWFtIHRvIGluc3BlY3QgaW5cbiAgICAgICAgICAgICAgICAgQ0FEIHdoZW4gdGhlIGVuZ2luZSBjYW5ub3QgaGFuZGxlIGl0IG9yIGJlZm9yZSBlZGl0aW5nLiAtLT5cbiAgICAgICAgICAgIDxhICpuZ0lmPVwic291cmNlRHhmVXJsXCIgW2hyZWZdPVwic291cmNlRHhmVXJsXCIgdGFyZ2V0PVwiX2JsYW5rXCIgcmVsPVwibm9vcGVuZXJcIlxuICAgICAgICAgICAgICBjbGFzcz1cImJ0biBidG4tc20gYnRuLWxpZ2h0IHJvdy1idG5cIiBbbmdiVG9vbHRpcF09XCJzb3VyY2VEeGZOYW1lXCI+XG4gICAgICAgICAgICAgIDxzdmcgd2lkdGg9XCIxNlwiIGhlaWdodD1cIjE2XCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPlxuICAgICAgICAgICAgICAgIDxwYXRoIGQ9XCJNMTIgM3YxMm0wIDAgNC00bS00IDQtNC00TTQgMTd2MmEyIDIgMCAwIDAgMiAyaDEyYTIgMiAwIDAgMCAyLTJ2LTJcIlxuICAgICAgICAgICAgICAgICAgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMS44XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCIgLz5cbiAgICAgICAgICAgICAgPC9zdmc+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibXMtMVwiPkRvd25sb2FkIHNvdXJjZSBEWEY8L3NwYW4+XG4gICAgICAgICAgICA8L2E+XG4gICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICA8ZGl2IFtuZ1N3aXRjaF09XCJwbGFjZW1lbnRTdGF0ZVwiIGNsYXNzPVwic3RhdGUtcGFuZWxcIj5cblxuICAgICAgICAgICAgPGRpdiAqbmdTd2l0Y2hDYXNlPVwiJ3dhaXRpbmdMYXlvdXQnXCIgY2xhc3M9XCJzdGF0ZS1ib3hcIj5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJtYi0xIGZ3LXNlbWlib2xkXCI+V2FpdGluZyBmb3IgdGhlIENBRCBsYXlvdXQuPC9wPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cInRleHQtbXV0ZWQgbWItMFwiPlRoZSBzdG9yZSdzIGZsb29yIGRyYXdpbmcgaGFzIG5vdCBiZWVuIGltcG9ydGVkIHlldC4gQXV0byBwbGFjZW1lbnQgc3RhcnRzIHJpZ2h0IGFmdGVyIHRoZSBDQUQgaW1wb3J0LjwvcD5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8ZGl2ICpuZ1N3aXRjaENhc2U9XCIncHJvY2Vzc2luZydcIiBjbGFzcz1cInN0YXRlLWJveFwiPlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBnYXAtMyBtYi0xXCI+XG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJzcGlubmVyLWJvcmRlciBzcGlubmVyLWJvcmRlci1zbSB0ZXh0LXByaW1hcnlcIiByb2xlPVwic3RhdHVzXCIgYXJpYS1oaWRkZW49XCJ0cnVlXCI+PC9zcGFuPlxuICAgICAgICAgICAgICAgIDxwIGNsYXNzPVwibWItMCBmdy1zZW1pYm9sZFwiPkF1dG8gY2FtZXJhIHBsYWNlbWVudCBpcyBydW5uaW5nLjwvcD5cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwidGV4dC1tdXRlZCBtYi0zXCI+VGhlIERYRiBlZGl0b3IgdW5sb2NrcyB3aGVuIHBsYWNlbWVudCBjb21wbGV0ZXMuIFVzZSBSZWZyZXNoIHRvIGNoZWNrIGFnYWluLiBNZWFud2hpbGUgeW91IGNhbiBsb29rIGF0IHRoZSBzb3VyY2UgZHJhd2luZy48L3A+XG4gICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwiYnRuIGJ0bi1zbSBidG4tbGlnaHQgcm93LWJ0blwiIChjbGljayk9XCJvcGVuUHJldmlldygpXCI+XG4gICAgICAgICAgICAgICAgPGkgY2xhc3M9XCJiaSBiaS1leWUgbWUtMVwiPjwvaT5WaWV3IHNvdXJjZSBkcmF3aW5nXG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgIDxkaXYgKm5nU3dpdGNoQ2FzZT1cIidmYWlsZWQnXCIgY2xhc3M9XCJzdGF0ZS1ib3ggc3RhdGUtYm94LWRhbmdlclwiPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cIm1iLTEgZnctc2VtaWJvbGRcIj5BdXRvIHBsYWNlbWVudCBmYWlsZWQuPC9wPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cInRleHQtbXV0ZWQgbWItM1wiPnt7IHBsYWNlbWVudD8uZXJyb3JNc2cgfHwgY2N0dlJvdz8uZXJyb3JNc2cgfHwgJ1RoZSBwbGFjZW1lbnQgZW5naW5lIHJlcG9ydGVkIGEgZmFpbHVyZS4nIH19PC9wPlxuICAgICAgICAgICAgICA8IS0tIFJldHJ5IGhpZGRlbiB1bnRpbCB0aGUgYXV0b1BsYWNlIExhbWJkYSBleGlzdHMgKExBTUJEQVVSTC5hdXRvUGxhY2UgaXMgZW1wdHkpLlxuICAgICAgICAgICAgICAgICAgIFJlLWVuYWJsZSBieSByZW1vdmluZyB0aGlzIGNvbW1lbnQ7IHJldHJ5UGxhY2VtZW50KCkgaXMgc3RpbGwgd2lyZWQgaW4gdGhlIGNvbXBvbmVudC5cbiAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJidG4gYnRuLXNtIGJ0bi1wcmltYXJ5IHJvdy1idG5cIiBbZGlzYWJsZWRdPVwicmV0cnlpbmdcIiAoY2xpY2spPVwicmV0cnlQbGFjZW1lbnQoKVwiPlxuICAgICAgICAgICAgICAgIDxzcGFuICpuZ0lmPVwicmV0cnlpbmdcIiBjbGFzcz1cInNwaW5uZXItYm9yZGVyIHNwaW5uZXItYm9yZGVyLXNtIG1lLTJcIiByb2xlPVwic3RhdHVzXCIgYXJpYS1oaWRkZW49XCJ0cnVlXCI+PC9zcGFuPlxuICAgICAgICAgICAgICAgIHt7IHJldHJ5aW5nID8gJ1JldHJ5aW5nLi4uJyA6ICdSZXRyeSBhdXRvIHBsYWNlbWVudCcgfX1cbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgIC0tPlxuICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgIDxkaXYgKm5nU3dpdGNoQ2FzZT1cIidvcGVuJ1wiIGNsYXNzPVwic3RhdGUtYm94XCI+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwibWItMSBmdy1zZW1pYm9sZFwiPlRoZSBkcmF3aW5nIGlzIHJlYWR5IGZvciBjYW1lcmEgcmV2aWV3LjwvcD5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJ0ZXh0LW11dGVkIG1iLTNcIj5cbiAgICAgICAgICAgICAgICBBZGp1c3QgdGhlIGF1dG8tcGxhY2VkIGNhbWVyYXMgaW4gdGhlIERYRiBlZGl0b3IsIHRoZW4gc3VibWl0IGZvciBhcHByb3ZhbC5cbiAgICAgICAgICAgICAgICA8c3BhbiAqbmdJZj1cImNjdHZSb3c/LmlzUmV3b3JrXCIgY2xhc3M9XCJ0ZXh0LXdhcm5pbmcgZnctc2VtaWJvbGRcIj5UaGlzIGZsb29yIHdhcyBzZW50IGJhY2sgZm9yIHJld29yay48L3NwYW4+XG4gICAgICAgICAgICAgIDwvcD5cbiAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJidG4gYnRuLXNtIGJ0bi1wcmltYXJ5IHJvdy1idG5cIiAoY2xpY2spPVwib3BlbkVkaXRvcigpXCI+XG4gICAgICAgICAgICAgICAgPGkgY2xhc3M9XCJiaSBiaS1wZW5jaWwtc3F1YXJlIG1lLTFcIj48L2k+T3BlbiBEWEYgZWRpdG9yXG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgIDxkaXYgKm5nU3dpdGNoQ2FzZT1cIidhcHByb3ZhbFBlbmRpbmcnXCIgY2xhc3M9XCJzdGF0ZS1ib3hcIj5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJtYi0xIGZ3LXNlbWlib2xkXCI+U3VibWl0dGVkLCB3YWl0aW5nIGZvciBhcHByb3ZhbC48L3A+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwidGV4dC1tdXRlZCBtYi0zXCI+VGhlIGFwcHJvdmFsIHRlYW0gcmV2aWV3cyBpdCBmcm9tIHRoZSBvbmJvYXJkIHRhYmxlLiBZb3UgY2FuIHN0aWxsIHZpZXcgdGhlIGRyYXdpbmcuPC9wPlxuICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImJ0biBidG4tc20gYnRuLWxpZ2h0IHJvdy1idG5cIiAoY2xpY2spPVwib3BlbkVkaXRvcigpXCI+XG4gICAgICAgICAgICAgICAgPGkgY2xhc3M9XCJiaSBiaS1leWUgbWUtMVwiPjwvaT5WaWV3IGRyYXdpbmdcbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgICAgPGRpdiAqbmdTd2l0Y2hDYXNlPVwiJ2FwcHJvdmVkJ1wiIGNsYXNzPVwic3RhdGUtYm94IHN0YXRlLWJveC1zdWNjZXNzXCI+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwibWItMSBmdy1zZW1pYm9sZFwiPkNhbWVyYSBwbGFjZW1lbnQgYXBwcm92ZWQuPC9wPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cInRleHQtbXV0ZWQgbWItMFwiPlRoaXMgdGlja2V0IGlzIGNsb3NlZC4gVGhlIGluc3RhbGxhdGlvbiB0aWNrZXQgdGFrZXMgb3ZlciBvbmNlIGV2ZXJ5IGZsb29yIG9mIHRoZSBzdG9yZSBpcyBhcHByb3ZlZC48L3A+XG4gICAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgICAgPGRpdiAqbmdTd2l0Y2hDYXNlPVwiJ3VuYXZhaWxhYmxlJ1wiIGNsYXNzPVwic3RhdGUtYm94IHN0YXRlLWJveC1kYW5nZXJcIj5cbiAgICAgICAgICAgICAgPHAgY2xhc3M9XCJtYi0xIGZ3LXNlbWlib2xkXCI+UGxhY2VtZW50IHN0YXR1cyB1bmF2YWlsYWJsZS48L3A+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwidGV4dC1tdXRlZCBtYi0zXCI+e3sgY2N0dkVycm9yIH19PC9wPlxuICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImJ0biBidG4tc20gYnRuLWxpZ2h0IHJvdy1idG5cIiAoY2xpY2spPVwibG9hZENjdHZEZXRhaWwoKVwiPlRyeSBhZ2FpbjwvYnV0dG9uPlxuICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgIDxkaXYgKm5nU3dpdGNoRGVmYXVsdCBjbGFzcz1cInN0YXRlLWJveFwiPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cInRleHQtbXV0ZWQgbWItMFwiPkxvYWRpbmcgcGxhY2VtZW50IHN0YXR1cy4uLjwvcD5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgPCEtLSBWZXJzaW9uIHRpbWVsaW5lIC0tPlxuICAgICAgICAgIDxkaXYgKm5nSWY9XCJ2ZXJzaW9ucy5sZW5ndGhcIiBjbGFzcz1cIm10LTZcIj5cbiAgICAgICAgICAgIDxoNiBjbGFzcz1cInRleHQtbXV0ZWQgbWItM1wiPkRyYXdpbmcgdmVyc2lvbnM8L2g2PlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInZlcnNpb24tbGlzdFwiPlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidmVyc2lvbi1yb3cgZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNvbnRlbnQtYmV0d2VlblwiICpuZ0Zvcj1cImxldCBmaWxlIG9mIHZlcnNpb25zXCI+XG4gICAgICAgICAgICAgICAgPGRpdj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZnctc2VtaWJvbGRcIj52e3sgZmlsZS52ZXJzaW9uIH19PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkIG1zLTIgZnMtN1wiPnt7IGZpbGUua2luZCA9PT0gJ2F1dG8nID8gJ0F1dG8gcGxhY2VtZW50JyA6IGZpbGUua2luZCA9PT0gJ2FwcHJvdmVyJyA/ICdBcHByb3ZlciBlZGl0JyA6ICdJbnRlcm5hbCBlZGl0JyB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZCBtcy0yIGZzLTdcIiAqbmdJZj1cImZpbGUuY2FtZXJhQ291bnQgIT0gbnVsbFwiPnt7IGZpbGUuY2FtZXJhQ291bnQgfX0gY2FtZXJhczwvc3Bhbj5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBnYXAtM1wiPlxuICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkIGZzLTdcIj57eyBmaWxlLnVwbG9hZGVkQXQgfCBkYXRlOiAnZGQgTU1NLCB5IEhIOm1tJyB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDxhICpuZ0lmPVwiZmlsZVVybHNbZmlsZS5rZXldXCIgW2hyZWZdPVwiZmlsZVVybHNbZmlsZS5rZXldXCIgdGFyZ2V0PVwiX2JsYW5rXCIgcmVsPVwibm9vcGVuZXJcIiBjbGFzcz1cImJ0biBidG4tc20gYnRuLWxpZ2h0IHJvdy1idG5cIj5cbiAgICAgICAgICAgICAgICAgICAgPGkgY2xhc3M9XCJiaSBiaS1kb3dubG9hZFwiPjwvaT5cbiAgICAgICAgICAgICAgICAgIDwvYT5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDwhLS0gVGlja2V0IGluZm8gKyBhY3Rpdml0eSAtLT5cbiAgICA8ZGl2IGNsYXNzPVwiY29sLTEyIGNvbC1sZy01XCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiY2FyZCBtYi01XCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJjYXJkLWJvZHlcIj5cbiAgICAgICAgICA8aDUgY2xhc3M9XCJtYi00XCI+VGlja2V0IGRldGFpbHM8L2g1PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJkZXRhaWwtZ3JpZFwiPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImRldGFpbC1yb3dcIj48c3BhbiBjbGFzcz1cInRleHQtbXV0ZWRcIj5TdG9yZTwvc3Bhbj48c3Bhbj57eyB0aWNrZXRJbmZvPy5iYXNpY0RldGFpbHM/LnN0b3JlTmFtZSB8fCAnLScgfX0gKHt7IHRpY2tldEluZm8/LmJhc2ljRGV0YWlscz8uc3RvcmVJZCB8fCBzdG9yZUlkIH19KTwvc3Bhbj48L2Rpdj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkZXRhaWwtcm93XCI+PHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkXCI+QnJhbmQ8L3NwYW4+PHNwYW4+e3sgdGlja2V0SW5mbz8uYmFzaWNEZXRhaWxzPy5jbGllbnROYW1lIHx8ICctJyB9fTwvc3Bhbj48L2Rpdj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkZXRhaWwtcm93XCI+PHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkXCI+Rmxvb3I8L3NwYW4+PHNwYW4+Rmxvb3Ige3sgZmxvb3JOdW1iZXIgfX08L3NwYW4+PC9kaXY+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZGV0YWlsLXJvd1wiPjxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZFwiPlRpY2tldCBzdGF0dXM8L3NwYW4+PHNwYW4gY2xhc3M9XCJ0ZXh0LWNhcGl0YWxpemVcIj57eyB0aWNrZXRJbmZvPy5zdGF0dXMgfHwgJy0nIH19PC9zcGFuPjwvZGl2PlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImRldGFpbC1yb3dcIj48c3BhbiBjbGFzcz1cInRleHQtbXV0ZWRcIj5DcmVhdGVkPC9zcGFuPjxzcGFuPnt7IHRpY2tldEluZm8/LmNyZWF0ZWRBdCB8IGRhdGU6ICdkZCBNTU0sIHkgSEg6bW0nIH19PC9zcGFuPjwvZGl2PlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImRldGFpbC1yb3dcIiAqbmdJZj1cImNjdHZSb3c/LmFzc2lnbmVlTmFtZVwiPjxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZFwiPkFzc2lnbmVlPC9zcGFuPjxzcGFuPnt7IGNjdHZSb3c/LmFzc2lnbmVlTmFtZSB9fTwvc3Bhbj48L2Rpdj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkZXRhaWwtcm93XCIgKm5nSWY9XCJjY3R2Um93Py5yZXdvcmtDb3VudFwiPjxzcGFuIGNsYXNzPVwidGV4dC1tdXRlZFwiPlJld29yayBjb3VudDwvc3Bhbj48c3Bhbj57eyBjY3R2Um93Py5yZXdvcmtDb3VudCB9fTwvc3Bhbj48L2Rpdj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPGRpdiBjbGFzcz1cImNhcmRcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImNhcmQtYm9keVwiPlxuICAgICAgICAgIDxoNSBjbGFzcz1cIm1iLTRcIj5BY3Rpdml0eTwvaDU+XG4gICAgICAgICAgPGRpdiAqbmdJZj1cIiFhY3Rpdml0eS5sZW5ndGhcIiBjbGFzcz1cInRleHQtbXV0ZWQgZnMtN1wiPk5vIGFjdGl2aXR5IHlldC48L2Rpdj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiYWN0aXZpdHktbGlzdFwiPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImFjdGl2aXR5LXJvd1wiICpuZ0Zvcj1cImxldCBlbnRyeSBvZiBhY3Rpdml0eVwiPlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiYWN0aXZpdHktZG90XCIgYXJpYS1oaWRkZW49XCJ0cnVlXCI+PC9kaXY+XG4gICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmbGV4LWdyb3ctMVwiPlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkLWZsZXgganVzdGlmeS1jb250ZW50LWJldHdlZW4gZ2FwLTNcIj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZnctc2VtaWJvbGQgZnMtN1wiPnt7IGVudHJ5LmRlc2NyaXB0aW9uIHx8IGVudHJ5LmFjdGlvblR5cGUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtbXV0ZWQgZnMtOCB0ZXh0LW5vd3JhcFwiPnt7IGVudHJ5LnRpbWVTdGFtcCB8IGRhdGU6ICdkZCBNTU0sIEhIOm1tJyB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidGV4dC1tdXRlZCBmcy04XCIgKm5nSWY9XCJlbnRyeS5jb21tZW50XCI+e3sgZW50cnkuY29tbWVudCB9fTwvZGl2PlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0ZXh0LW11dGVkIGZzLThcIiAqbmdJZj1cImVudHJ5LklkZW50aWZpZWRCeSAmJiBlbnRyeS5JZGVudGlmaWVkQnkgIT09ICdUYW5nbydcIj5ieSB7eyBlbnRyeS5JZGVudGlmaWVkQnkgfX08L2Rpdj5cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L2Rpdj5cbiJdfQ==
|
|
@@ -4145,7 +4145,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
4145
4145
|
* Same role as InstallationComponent for TE_INS tickets: the ticket header +
|
|
4146
4146
|
* activity trail come from the tickets API, the LIVE camera-placement state
|
|
4147
4147
|
* comes from the store-builder CCTV row, and the page gates the next step:
|
|
4148
|
-
* autoPlacement -> "Auto placement in progress", editor locked
|
|
4148
|
+
* autoPlacement -> "Auto placement in progress", editor locked, source preview
|
|
4149
4149
|
* failed -> failure reason + Retry
|
|
4150
4150
|
* open -> "Open DXF editor" (assignee gated server-side)
|
|
4151
4151
|
* approvalPending-> waiting for approval, drawing viewable
|
|
@@ -4168,6 +4168,8 @@ class PreinstallationInfoComponent {
|
|
|
4168
4168
|
fileUrls = {};
|
|
4169
4169
|
loading = true;
|
|
4170
4170
|
cctvLoading = false;
|
|
4171
|
+
/** Refresh clicked and the ticket reload is still in flight (icon turns) */
|
|
4172
|
+
reloading = false;
|
|
4171
4173
|
cctvError = '';
|
|
4172
4174
|
retrying = false;
|
|
4173
4175
|
destroy$ = new Subject();
|
|
@@ -4235,6 +4237,7 @@ class PreinstallationInfoComponent {
|
|
|
4235
4237
|
.subscribe({
|
|
4236
4238
|
next: (res) => {
|
|
4237
4239
|
this.loading = false;
|
|
4240
|
+
this.reloading = false;
|
|
4238
4241
|
if (res && res.code == 200) {
|
|
4239
4242
|
this.ticketInfo = res.data;
|
|
4240
4243
|
if (this.floorId) {
|
|
@@ -4244,6 +4247,7 @@ class PreinstallationInfoComponent {
|
|
|
4244
4247
|
},
|
|
4245
4248
|
error: () => {
|
|
4246
4249
|
this.loading = false;
|
|
4250
|
+
this.reloading = false;
|
|
4247
4251
|
this.toastService.getErrorToast('Unable to load the ticket.');
|
|
4248
4252
|
},
|
|
4249
4253
|
});
|
|
@@ -4270,6 +4274,7 @@ class PreinstallationInfoComponent {
|
|
|
4270
4274
|
});
|
|
4271
4275
|
}
|
|
4272
4276
|
refresh() {
|
|
4277
|
+
this.reloading = true;
|
|
4273
4278
|
this.viewTicket();
|
|
4274
4279
|
}
|
|
4275
4280
|
retryPlacement() {
|
|
@@ -4299,15 +4304,23 @@ class PreinstallationInfoComponent {
|
|
|
4299
4304
|
queryParams: { floorId: this.floorId, mode: 'edit' },
|
|
4300
4305
|
});
|
|
4301
4306
|
}
|
|
4307
|
+
/** The source drawing, read-only, while auto placement is still running. */
|
|
4308
|
+
openPreview() {
|
|
4309
|
+
if (!this.floorId)
|
|
4310
|
+
return;
|
|
4311
|
+
this.router.navigate(['/manage/planogram/cctv-preview'], {
|
|
4312
|
+
queryParams: { floorId: this.floorId, from: 'ticket' },
|
|
4313
|
+
});
|
|
4314
|
+
}
|
|
4302
4315
|
back() {
|
|
4303
4316
|
this.router.navigate(['/tickets/users'], { queryParams: { type: 'installation' } });
|
|
4304
4317
|
}
|
|
4305
4318
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreinstallationInfoComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: i1$1.Router }, { token: TicketsService }, { token: i3.ToastService }], target: i0.ɵɵFactoryTarget.Component });
|
|
4306
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PreinstallationInfoComponent, selector: "lib-preinstallation-info", ngImport: i0, template: "<div class=\"container-fluid p-5 preinstall-page\">\n\n <!-- Header -->\n <div class=\"card mb-5\">\n <div class=\"card-body d-flex flex-wrap align-items-center justify-content-between gap-4 py-4\">\n <div class=\"d-flex align-items-center gap-4\">\n <button type=\"button\" class=\"btn btn-sm btn-light back-btn\" (click)=\"back()\" aria-label=\"Back to workspace\">\n <i class=\"bi bi-arrow-left\"></i>\n </button>\n <div>\n <div class=\"d-flex align-items-center gap-3\">\n <h4 class=\"mb-0\">{{ ticketInfo?.ticketId || ticketId }}</h4>\n <span class=\"badge state-badge\" [ngClass]=\"'state-' + placementState\">\n {{\n placementState === 'waitingLayout' ? 'Waiting for layout' :\n placementState === 'processing' ? 'Auto placement in progress' :\n placementState === 'failed' ? 'Placement failed' :\n placementState === 'open' ? 'Ready for placement review' :\n placementState === 'approvalPending' ? 'Waiting for approval' :\n placementState === 'approved' ? 'Approved' :\n placementState === 'unavailable' ? 'Status unavailable' : 'Loading'\n }}\n </span>\n </div>\n <div class=\"text-muted fs-7 mt-1\">\n Pre-Installation, camera placement\n <span *ngIf=\"ticketInfo?.basicDetails?.storeName\">, {{ ticketInfo?.basicDetails?.storeName }}</span>\n <span>, Floor {{ floorNumber }}</span>\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"refresh()\">\n <i class=\"bi bi-arrow-clockwise me-1\"></i>Refresh\n </button>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-center py-10 text-muted\">Loading ticket...</div>\n\n <div *ngIf=\"!loading && ticketInfo\" class=\"row g-5\">\n\n <!-- Placement state / action panel -->\n <div class=\"col-12 col-lg-7\">\n <div class=\"card h-100\">\n <div class=\"card-body\">\n <div class=\"card-head mb-4\">\n <h5 class=\"mb-0\">Camera placement</h5>\n <!-- The imported drawing itself, for the internal team to inspect in\n CAD when the engine cannot handle it or before editing. -->\n <a *ngIf=\"sourceDxfUrl\" [href]=\"sourceDxfUrl\" target=\"_blank\" rel=\"noopener\"\n class=\"btn btn-sm btn-light row-btn\" [ngbTooltip]=\"sourceDxfName\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <span class=\"ms-1\">Download source DXF</span>\n </a>\n </div>\n\n <div [ngSwitch]=\"placementState\" class=\"state-panel\">\n\n <div *ngSwitchCase=\"'waitingLayout'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Waiting for the CAD layout.</p>\n <p class=\"text-muted mb-0\">The store's floor drawing has not been imported yet. Auto placement starts right after the CAD import.</p>\n </div>\n\n <div *ngSwitchCase=\"'processing'\" class=\"state-box\">\n <div class=\"d-flex align-items-center gap-3 mb-1\">\n <span class=\"spinner-border spinner-border-sm text-primary\" role=\"status\" aria-hidden=\"true\"></span>\n <p class=\"mb-0 fw-semibold\">Auto camera placement is running.</p>\n </div>\n <p class=\"text-muted mb-0\">The DXF editor unlocks when placement completes. Use Refresh to check again.</p>\n </div>\n\n <div *ngSwitchCase=\"'failed'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Auto placement failed.</p>\n <p class=\"text-muted mb-3\">{{ placement?.errorMsg || cctvRow?.errorMsg || 'The placement engine reported a failure.' }}</p>\n <!-- Retry hidden until the autoPlace Lambda exists (LAMBDAURL.autoPlace is empty).\n Re-enable by removing this comment; retryPlacement() is still wired in the component.\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" [disabled]=\"retrying\" (click)=\"retryPlacement()\">\n <span *ngIf=\"retrying\" class=\"spinner-border spinner-border-sm me-2\" role=\"status\" aria-hidden=\"true\"></span>\n {{ retrying ? 'Retrying...' : 'Retry auto placement' }}\n </button>\n -->\n </div>\n\n <div *ngSwitchCase=\"'open'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">The drawing is ready for camera review.</p>\n <p class=\"text-muted mb-3\">\n Adjust the auto-placed cameras in the DXF editor, then submit for approval.\n <span *ngIf=\"cctvRow?.isRework\" class=\"text-warning fw-semibold\">This floor was sent back for rework.</span>\n </p>\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-pencil-square me-1\"></i>Open DXF editor\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approvalPending'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Submitted, waiting for approval.</p>\n <p class=\"text-muted mb-3\">The approval team reviews it from the onboard table. You can still view the drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-eye me-1\"></i>View drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approved'\" class=\"state-box state-box-success\">\n <p class=\"mb-1 fw-semibold\">Camera placement approved.</p>\n <p class=\"text-muted mb-0\">This ticket is closed. The installation ticket takes over once every floor of the store is approved.</p>\n </div>\n\n <div *ngSwitchCase=\"'unavailable'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Placement status unavailable.</p>\n <p class=\"text-muted mb-3\">{{ cctvError }}</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"loadCctvDetail()\">Try again</button>\n </div>\n\n <div *ngSwitchDefault class=\"state-box\">\n <p class=\"text-muted mb-0\">Loading placement status...</p>\n </div>\n </div>\n\n <!-- Version timeline -->\n <div *ngIf=\"versions.length\" class=\"mt-6\">\n <h6 class=\"text-muted mb-3\">Drawing versions</h6>\n <div class=\"version-list\">\n <div class=\"version-row d-flex align-items-center justify-content-between\" *ngFor=\"let file of versions\">\n <div>\n <span class=\"fw-semibold\">v{{ file.version }}</span>\n <span class=\"text-muted ms-2 fs-7\">{{ file.kind === 'auto' ? 'Auto placement' : file.kind === 'approver' ? 'Approver edit' : 'Internal edit' }}</span>\n <span class=\"text-muted ms-2 fs-7\" *ngIf=\"file.cameraCount != null\">{{ file.cameraCount }} cameras</span>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <span class=\"text-muted fs-7\">{{ file.uploadedAt | date: 'dd MMM, y HH:mm' }}</span>\n <a *ngIf=\"fileUrls[file.key]\" [href]=\"fileUrls[file.key]\" target=\"_blank\" rel=\"noopener\" class=\"btn btn-sm btn-light row-btn\">\n <i class=\"bi bi-download\"></i>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Ticket info + activity -->\n <div class=\"col-12 col-lg-5\">\n <div class=\"card mb-5\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Ticket details</h5>\n <div class=\"detail-grid\">\n <div class=\"detail-row\"><span class=\"text-muted\">Store</span><span>{{ ticketInfo?.basicDetails?.storeName || '-' }} ({{ ticketInfo?.basicDetails?.storeId || storeId }})</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Brand</span><span>{{ ticketInfo?.basicDetails?.clientName || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Floor</span><span>Floor {{ floorNumber }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Ticket status</span><span class=\"text-capitalize\">{{ ticketInfo?.status || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Created</span><span>{{ ticketInfo?.createdAt | date: 'dd MMM, y HH:mm' }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.assigneeName\"><span class=\"text-muted\">Assignee</span><span>{{ cctvRow?.assigneeName }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.reworkCount\"><span class=\"text-muted\">Rework count</span><span>{{ cctvRow?.reworkCount }}</span></div>\n </div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Activity</h5>\n <div *ngIf=\"!activity.length\" class=\"text-muted fs-7\">No activity yet.</div>\n <div class=\"activity-list\">\n <div class=\"activity-row\" *ngFor=\"let entry of activity\">\n <div class=\"activity-dot\" aria-hidden=\"true\"></div>\n <div class=\"flex-grow-1\">\n <div class=\"d-flex justify-content-between gap-3\">\n <span class=\"fw-semibold fs-7\">{{ entry.description || entry.actionType }}</span>\n <span class=\"text-muted fs-8 text-nowrap\">{{ entry.timeStamp | date: 'dd MMM, HH:mm' }}</span>\n </div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.comment\">{{ entry.comment }}</div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.IdentifiedBy && entry.IdentifiedBy !== 'Tango'\">by {{ entry.IdentifiedBy }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".preinstall-page .back-btn,.preinstall-page .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.preinstall-page .back-btn .row-btn,.preinstall-page .card-head .row-btn{display:inline-flex;align-items:center;white-space:nowrap}.preinstall-page .row-btn{height:40px;display:inline-flex;align-items:center}.preinstall-page .state-badge{padding:6px 12px;font-weight:600;border-radius:999px}.preinstall-page .state-badge.state-waitingLayout,.preinstall-page .state-badge.state-loading,.preinstall-page .state-badge.state-unavailable{background:#f1f3f5;color:#5e6278}.preinstall-page .state-badge.state-processing{background:#eef6ff;color:#1b84ff}.preinstall-page .state-badge.state-failed{background:#fff5f8;color:#f1416c}.preinstall-page .state-badge.state-open{background:#e8fff3;color:#17c653}.preinstall-page .state-badge.state-approvalPending{background:#fff8dd;color:#f6c000}.preinstall-page .state-badge.state-approved{background:#e8fff3;color:#17c653}.preinstall-page .state-panel .state-box{border:1px solid #e9ecef;border-radius:8px;padding:16px 20px;opacity:0;transform:translateY(4px);animation:preinstallFade .25s ease forwards}.preinstall-page .state-panel .state-box.state-box-danger{border-color:#ffd6e1;background:#fff9fb}.preinstall-page .state-panel .state-box.state-box-success{border-color:#c8f2d8;background:#f6fffa}.preinstall-page .version-list .version-row{padding:10px 0;border-bottom:1px dashed #e9ecef}.preinstall-page .version-list .version-row:last-child{border-bottom:none}.preinstall-page .detail-grid .detail-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed #f1f3f5;font-size:13px}.preinstall-page .detail-grid .detail-row:last-child{border-bottom:none}.preinstall-page .activity-list{max-height:420px;overflow-y:auto}.preinstall-page .activity-list .activity-row{display:flex;gap:10px;padding:8px 0}.preinstall-page .activity-list .activity-row .activity-dot{width:8px;height:8px;border-radius:50%;background:#1b84ff;margin-top:6px;flex-shrink:0}@keyframes preinstallFade{to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion: reduce){.preinstall-page .state-panel .state-box{animation:none;opacity:1;transform:none}}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i4.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i4.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: i1$2.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "pipe", type: i4.DatePipe, name: "date" }] });
|
|
4319
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PreinstallationInfoComponent, selector: "lib-preinstallation-info", ngImport: i0, template: "<div class=\"container-fluid p-5 preinstall-page\">\n\n <!-- Header -->\n <div class=\"card mb-5\">\n <div class=\"card-body d-flex flex-wrap align-items-center justify-content-between gap-4 py-4\">\n <div class=\"d-flex align-items-center gap-4\">\n <button type=\"button\" class=\"btn btn-sm btn-light back-btn\" (click)=\"back()\" aria-label=\"Back to workspace\">\n <i class=\"bi bi-arrow-left\"></i>\n </button>\n <div>\n <div class=\"d-flex align-items-center gap-3\">\n <h4 class=\"mb-0\">{{ ticketInfo?.ticketId || ticketId }}</h4>\n <span class=\"badge state-badge\" [ngClass]=\"'state-' + placementState\">\n {{\n placementState === 'waitingLayout' ? 'Waiting for layout' :\n placementState === 'processing' ? 'Auto placement in progress' :\n placementState === 'failed' ? 'Placement failed' :\n placementState === 'open' ? 'Ready for placement review' :\n placementState === 'approvalPending' ? 'Waiting for approval' :\n placementState === 'approved' ? 'Approved' :\n placementState === 'unavailable' ? 'Status unavailable' : 'Loading'\n }}\n </span>\n </div>\n <div class=\"text-muted fs-7 mt-1\">\n Pre-Installation, camera placement\n <span *ngIf=\"ticketInfo?.basicDetails?.storeName\">, {{ ticketInfo?.basicDetails?.storeName }}</span>\n <span>, Floor {{ floorNumber }}</span>\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"refresh()\">\n <i class=\"bi bi-arrow-clockwise me-1\" [class.spinning]=\"reloading || cctvLoading\"></i>Refresh\n </button>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-center py-10 text-muted\">Loading ticket...</div>\n\n <div *ngIf=\"!loading && ticketInfo\" class=\"row g-5\">\n\n <!-- Placement state / action panel -->\n <div class=\"col-12 col-lg-7\">\n <div class=\"card h-100\">\n <div class=\"card-body\">\n <div class=\"card-head mb-4\">\n <h5 class=\"mb-0\">Camera placement</h5>\n <!-- The imported drawing itself, for the internal team to inspect in\n CAD when the engine cannot handle it or before editing. -->\n <a *ngIf=\"sourceDxfUrl\" [href]=\"sourceDxfUrl\" target=\"_blank\" rel=\"noopener\"\n class=\"btn btn-sm btn-light row-btn\" [ngbTooltip]=\"sourceDxfName\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <span class=\"ms-1\">Download source DXF</span>\n </a>\n </div>\n\n <div [ngSwitch]=\"placementState\" class=\"state-panel\">\n\n <div *ngSwitchCase=\"'waitingLayout'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Waiting for the CAD layout.</p>\n <p class=\"text-muted mb-0\">The store's floor drawing has not been imported yet. Auto placement starts right after the CAD import.</p>\n </div>\n\n <div *ngSwitchCase=\"'processing'\" class=\"state-box\">\n <div class=\"d-flex align-items-center gap-3 mb-1\">\n <span class=\"spinner-border spinner-border-sm text-primary\" role=\"status\" aria-hidden=\"true\"></span>\n <p class=\"mb-0 fw-semibold\">Auto camera placement is running.</p>\n </div>\n <p class=\"text-muted mb-3\">The DXF editor unlocks when placement completes. Use Refresh to check again. Meanwhile you can look at the source drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openPreview()\">\n <i class=\"bi bi-eye me-1\"></i>View source drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'failed'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Auto placement failed.</p>\n <p class=\"text-muted mb-3\">{{ placement?.errorMsg || cctvRow?.errorMsg || 'The placement engine reported a failure.' }}</p>\n <!-- Retry hidden until the autoPlace Lambda exists (LAMBDAURL.autoPlace is empty).\n Re-enable by removing this comment; retryPlacement() is still wired in the component.\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" [disabled]=\"retrying\" (click)=\"retryPlacement()\">\n <span *ngIf=\"retrying\" class=\"spinner-border spinner-border-sm me-2\" role=\"status\" aria-hidden=\"true\"></span>\n {{ retrying ? 'Retrying...' : 'Retry auto placement' }}\n </button>\n -->\n </div>\n\n <div *ngSwitchCase=\"'open'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">The drawing is ready for camera review.</p>\n <p class=\"text-muted mb-3\">\n Adjust the auto-placed cameras in the DXF editor, then submit for approval.\n <span *ngIf=\"cctvRow?.isRework\" class=\"text-warning fw-semibold\">This floor was sent back for rework.</span>\n </p>\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-pencil-square me-1\"></i>Open DXF editor\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approvalPending'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Submitted, waiting for approval.</p>\n <p class=\"text-muted mb-3\">The approval team reviews it from the onboard table. You can still view the drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-eye me-1\"></i>View drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approved'\" class=\"state-box state-box-success\">\n <p class=\"mb-1 fw-semibold\">Camera placement approved.</p>\n <p class=\"text-muted mb-0\">This ticket is closed. The installation ticket takes over once every floor of the store is approved.</p>\n </div>\n\n <div *ngSwitchCase=\"'unavailable'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Placement status unavailable.</p>\n <p class=\"text-muted mb-3\">{{ cctvError }}</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"loadCctvDetail()\">Try again</button>\n </div>\n\n <div *ngSwitchDefault class=\"state-box\">\n <p class=\"text-muted mb-0\">Loading placement status...</p>\n </div>\n </div>\n\n <!-- Version timeline -->\n <div *ngIf=\"versions.length\" class=\"mt-6\">\n <h6 class=\"text-muted mb-3\">Drawing versions</h6>\n <div class=\"version-list\">\n <div class=\"version-row d-flex align-items-center justify-content-between\" *ngFor=\"let file of versions\">\n <div>\n <span class=\"fw-semibold\">v{{ file.version }}</span>\n <span class=\"text-muted ms-2 fs-7\">{{ file.kind === 'auto' ? 'Auto placement' : file.kind === 'approver' ? 'Approver edit' : 'Internal edit' }}</span>\n <span class=\"text-muted ms-2 fs-7\" *ngIf=\"file.cameraCount != null\">{{ file.cameraCount }} cameras</span>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <span class=\"text-muted fs-7\">{{ file.uploadedAt | date: 'dd MMM, y HH:mm' }}</span>\n <a *ngIf=\"fileUrls[file.key]\" [href]=\"fileUrls[file.key]\" target=\"_blank\" rel=\"noopener\" class=\"btn btn-sm btn-light row-btn\">\n <i class=\"bi bi-download\"></i>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Ticket info + activity -->\n <div class=\"col-12 col-lg-5\">\n <div class=\"card mb-5\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Ticket details</h5>\n <div class=\"detail-grid\">\n <div class=\"detail-row\"><span class=\"text-muted\">Store</span><span>{{ ticketInfo?.basicDetails?.storeName || '-' }} ({{ ticketInfo?.basicDetails?.storeId || storeId }})</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Brand</span><span>{{ ticketInfo?.basicDetails?.clientName || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Floor</span><span>Floor {{ floorNumber }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Ticket status</span><span class=\"text-capitalize\">{{ ticketInfo?.status || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Created</span><span>{{ ticketInfo?.createdAt | date: 'dd MMM, y HH:mm' }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.assigneeName\"><span class=\"text-muted\">Assignee</span><span>{{ cctvRow?.assigneeName }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.reworkCount\"><span class=\"text-muted\">Rework count</span><span>{{ cctvRow?.reworkCount }}</span></div>\n </div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Activity</h5>\n <div *ngIf=\"!activity.length\" class=\"text-muted fs-7\">No activity yet.</div>\n <div class=\"activity-list\">\n <div class=\"activity-row\" *ngFor=\"let entry of activity\">\n <div class=\"activity-dot\" aria-hidden=\"true\"></div>\n <div class=\"flex-grow-1\">\n <div class=\"d-flex justify-content-between gap-3\">\n <span class=\"fw-semibold fs-7\">{{ entry.description || entry.actionType }}</span>\n <span class=\"text-muted fs-8 text-nowrap\">{{ entry.timeStamp | date: 'dd MMM, HH:mm' }}</span>\n </div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.comment\">{{ entry.comment }}</div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.IdentifiedBy && entry.IdentifiedBy !== 'Tango'\">by {{ entry.IdentifiedBy }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".preinstall-page .back-btn,.preinstall-page .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.preinstall-page .back-btn .row-btn,.preinstall-page .card-head .row-btn{display:inline-flex;align-items:center;white-space:nowrap}.preinstall-page .row-btn{height:40px;display:inline-flex;align-items:center}.preinstall-page .state-badge{padding:6px 12px;font-weight:600;border-radius:999px}.preinstall-page .state-badge.state-waitingLayout,.preinstall-page .state-badge.state-loading,.preinstall-page .state-badge.state-unavailable{background:#f1f3f5;color:#5e6278}.preinstall-page .state-badge.state-processing{background:#eef6ff;color:#1b84ff}.preinstall-page .state-badge.state-failed{background:#fff5f8;color:#f1416c}.preinstall-page .state-badge.state-open{background:#e8fff3;color:#17c653}.preinstall-page .state-badge.state-approvalPending{background:#fff8dd;color:#f6c000}.preinstall-page .state-badge.state-approved{background:#e8fff3;color:#17c653}.preinstall-page .state-panel .state-box{border:1px solid #e9ecef;border-radius:8px;padding:16px 20px;opacity:0;transform:translateY(4px);animation:preinstallFade .25s ease forwards}.preinstall-page .state-panel .state-box.state-box-danger{border-color:#ffd6e1;background:#fff9fb}.preinstall-page .state-panel .state-box.state-box-success{border-color:#c8f2d8;background:#f6fffa}.preinstall-page .version-list .version-row{padding:10px 0;border-bottom:1px dashed #e9ecef}.preinstall-page .version-list .version-row:last-child{border-bottom:none}.preinstall-page .detail-grid .detail-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed #f1f3f5;font-size:13px}.preinstall-page .detail-grid .detail-row:last-child{border-bottom:none}.preinstall-page .activity-list{max-height:420px;overflow-y:auto}.preinstall-page .activity-list .activity-row{display:flex;gap:10px;padding:8px 0}.preinstall-page .activity-list .activity-row .activity-dot{width:8px;height:8px;border-radius:50%;background:#1b84ff;margin-top:6px;flex-shrink:0}@keyframes preinstallFade{to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion: reduce){.preinstall-page .state-panel .state-box{animation:none;opacity:1;transform:none}}.preinstall-page .bi-arrow-clockwise{display:inline-block;line-height:1}.preinstall-page .bi-arrow-clockwise:before{display:inline-block;transform-origin:50% 50%}.preinstall-page .bi-arrow-clockwise.spinning:before{animation:preinstallSpin .8s linear infinite}@keyframes preinstallSpin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.preinstall-page .bi-arrow-clockwise.spinning:before{animation:none}}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i4.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i4.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: i1$2.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "pipe", type: i4.DatePipe, name: "date" }] });
|
|
4307
4320
|
}
|
|
4308
4321
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PreinstallationInfoComponent, decorators: [{
|
|
4309
4322
|
type: Component,
|
|
4310
|
-
args: [{ selector: 'lib-preinstallation-info', template: "<div class=\"container-fluid p-5 preinstall-page\">\n\n <!-- Header -->\n <div class=\"card mb-5\">\n <div class=\"card-body d-flex flex-wrap align-items-center justify-content-between gap-4 py-4\">\n <div class=\"d-flex align-items-center gap-4\">\n <button type=\"button\" class=\"btn btn-sm btn-light back-btn\" (click)=\"back()\" aria-label=\"Back to workspace\">\n <i class=\"bi bi-arrow-left\"></i>\n </button>\n <div>\n <div class=\"d-flex align-items-center gap-3\">\n <h4 class=\"mb-0\">{{ ticketInfo?.ticketId || ticketId }}</h4>\n <span class=\"badge state-badge\" [ngClass]=\"'state-' + placementState\">\n {{\n placementState === 'waitingLayout' ? 'Waiting for layout' :\n placementState === 'processing' ? 'Auto placement in progress' :\n placementState === 'failed' ? 'Placement failed' :\n placementState === 'open' ? 'Ready for placement review' :\n placementState === 'approvalPending' ? 'Waiting for approval' :\n placementState === 'approved' ? 'Approved' :\n placementState === 'unavailable' ? 'Status unavailable' : 'Loading'\n }}\n </span>\n </div>\n <div class=\"text-muted fs-7 mt-1\">\n Pre-Installation, camera placement\n <span *ngIf=\"ticketInfo?.basicDetails?.storeName\">, {{ ticketInfo?.basicDetails?.storeName }}</span>\n <span>, Floor {{ floorNumber }}</span>\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"refresh()\">\n <i class=\"bi bi-arrow-clockwise me-1\"></i>Refresh\n </button>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-center py-10 text-muted\">Loading ticket...</div>\n\n <div *ngIf=\"!loading && ticketInfo\" class=\"row g-5\">\n\n <!-- Placement state / action panel -->\n <div class=\"col-12 col-lg-7\">\n <div class=\"card h-100\">\n <div class=\"card-body\">\n <div class=\"card-head mb-4\">\n <h5 class=\"mb-0\">Camera placement</h5>\n <!-- The imported drawing itself, for the internal team to inspect in\n CAD when the engine cannot handle it or before editing. -->\n <a *ngIf=\"sourceDxfUrl\" [href]=\"sourceDxfUrl\" target=\"_blank\" rel=\"noopener\"\n class=\"btn btn-sm btn-light row-btn\" [ngbTooltip]=\"sourceDxfName\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <span class=\"ms-1\">Download source DXF</span>\n </a>\n </div>\n\n <div [ngSwitch]=\"placementState\" class=\"state-panel\">\n\n <div *ngSwitchCase=\"'waitingLayout'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Waiting for the CAD layout.</p>\n <p class=\"text-muted mb-0\">The store's floor drawing has not been imported yet. Auto placement starts right after the CAD import.</p>\n </div>\n\n <div *ngSwitchCase=\"'processing'\" class=\"state-box\">\n <div class=\"d-flex align-items-center gap-3 mb-1\">\n <span class=\"spinner-border spinner-border-sm text-primary\" role=\"status\" aria-hidden=\"true\"></span>\n <p class=\"mb-0 fw-semibold\">Auto camera placement is running.</p>\n </div>\n <p class=\"text-muted mb-0\">The DXF editor unlocks when placement completes. Use Refresh to check again.</p>\n </div>\n\n <div *ngSwitchCase=\"'failed'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Auto placement failed.</p>\n <p class=\"text-muted mb-3\">{{ placement?.errorMsg || cctvRow?.errorMsg || 'The placement engine reported a failure.' }}</p>\n <!-- Retry hidden until the autoPlace Lambda exists (LAMBDAURL.autoPlace is empty).\n Re-enable by removing this comment; retryPlacement() is still wired in the component.\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" [disabled]=\"retrying\" (click)=\"retryPlacement()\">\n <span *ngIf=\"retrying\" class=\"spinner-border spinner-border-sm me-2\" role=\"status\" aria-hidden=\"true\"></span>\n {{ retrying ? 'Retrying...' : 'Retry auto placement' }}\n </button>\n -->\n </div>\n\n <div *ngSwitchCase=\"'open'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">The drawing is ready for camera review.</p>\n <p class=\"text-muted mb-3\">\n Adjust the auto-placed cameras in the DXF editor, then submit for approval.\n <span *ngIf=\"cctvRow?.isRework\" class=\"text-warning fw-semibold\">This floor was sent back for rework.</span>\n </p>\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-pencil-square me-1\"></i>Open DXF editor\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approvalPending'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Submitted, waiting for approval.</p>\n <p class=\"text-muted mb-3\">The approval team reviews it from the onboard table. You can still view the drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-eye me-1\"></i>View drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approved'\" class=\"state-box state-box-success\">\n <p class=\"mb-1 fw-semibold\">Camera placement approved.</p>\n <p class=\"text-muted mb-0\">This ticket is closed. The installation ticket takes over once every floor of the store is approved.</p>\n </div>\n\n <div *ngSwitchCase=\"'unavailable'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Placement status unavailable.</p>\n <p class=\"text-muted mb-3\">{{ cctvError }}</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"loadCctvDetail()\">Try again</button>\n </div>\n\n <div *ngSwitchDefault class=\"state-box\">\n <p class=\"text-muted mb-0\">Loading placement status...</p>\n </div>\n </div>\n\n <!-- Version timeline -->\n <div *ngIf=\"versions.length\" class=\"mt-6\">\n <h6 class=\"text-muted mb-3\">Drawing versions</h6>\n <div class=\"version-list\">\n <div class=\"version-row d-flex align-items-center justify-content-between\" *ngFor=\"let file of versions\">\n <div>\n <span class=\"fw-semibold\">v{{ file.version }}</span>\n <span class=\"text-muted ms-2 fs-7\">{{ file.kind === 'auto' ? 'Auto placement' : file.kind === 'approver' ? 'Approver edit' : 'Internal edit' }}</span>\n <span class=\"text-muted ms-2 fs-7\" *ngIf=\"file.cameraCount != null\">{{ file.cameraCount }} cameras</span>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <span class=\"text-muted fs-7\">{{ file.uploadedAt | date: 'dd MMM, y HH:mm' }}</span>\n <a *ngIf=\"fileUrls[file.key]\" [href]=\"fileUrls[file.key]\" target=\"_blank\" rel=\"noopener\" class=\"btn btn-sm btn-light row-btn\">\n <i class=\"bi bi-download\"></i>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Ticket info + activity -->\n <div class=\"col-12 col-lg-5\">\n <div class=\"card mb-5\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Ticket details</h5>\n <div class=\"detail-grid\">\n <div class=\"detail-row\"><span class=\"text-muted\">Store</span><span>{{ ticketInfo?.basicDetails?.storeName || '-' }} ({{ ticketInfo?.basicDetails?.storeId || storeId }})</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Brand</span><span>{{ ticketInfo?.basicDetails?.clientName || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Floor</span><span>Floor {{ floorNumber }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Ticket status</span><span class=\"text-capitalize\">{{ ticketInfo?.status || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Created</span><span>{{ ticketInfo?.createdAt | date: 'dd MMM, y HH:mm' }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.assigneeName\"><span class=\"text-muted\">Assignee</span><span>{{ cctvRow?.assigneeName }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.reworkCount\"><span class=\"text-muted\">Rework count</span><span>{{ cctvRow?.reworkCount }}</span></div>\n </div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Activity</h5>\n <div *ngIf=\"!activity.length\" class=\"text-muted fs-7\">No activity yet.</div>\n <div class=\"activity-list\">\n <div class=\"activity-row\" *ngFor=\"let entry of activity\">\n <div class=\"activity-dot\" aria-hidden=\"true\"></div>\n <div class=\"flex-grow-1\">\n <div class=\"d-flex justify-content-between gap-3\">\n <span class=\"fw-semibold fs-7\">{{ entry.description || entry.actionType }}</span>\n <span class=\"text-muted fs-8 text-nowrap\">{{ entry.timeStamp | date: 'dd MMM, HH:mm' }}</span>\n </div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.comment\">{{ entry.comment }}</div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.IdentifiedBy && entry.IdentifiedBy !== 'Tango'\">by {{ entry.IdentifiedBy }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".preinstall-page .back-btn,.preinstall-page .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.preinstall-page .back-btn .row-btn,.preinstall-page .card-head .row-btn{display:inline-flex;align-items:center;white-space:nowrap}.preinstall-page .row-btn{height:40px;display:inline-flex;align-items:center}.preinstall-page .state-badge{padding:6px 12px;font-weight:600;border-radius:999px}.preinstall-page .state-badge.state-waitingLayout,.preinstall-page .state-badge.state-loading,.preinstall-page .state-badge.state-unavailable{background:#f1f3f5;color:#5e6278}.preinstall-page .state-badge.state-processing{background:#eef6ff;color:#1b84ff}.preinstall-page .state-badge.state-failed{background:#fff5f8;color:#f1416c}.preinstall-page .state-badge.state-open{background:#e8fff3;color:#17c653}.preinstall-page .state-badge.state-approvalPending{background:#fff8dd;color:#f6c000}.preinstall-page .state-badge.state-approved{background:#e8fff3;color:#17c653}.preinstall-page .state-panel .state-box{border:1px solid #e9ecef;border-radius:8px;padding:16px 20px;opacity:0;transform:translateY(4px);animation:preinstallFade .25s ease forwards}.preinstall-page .state-panel .state-box.state-box-danger{border-color:#ffd6e1;background:#fff9fb}.preinstall-page .state-panel .state-box.state-box-success{border-color:#c8f2d8;background:#f6fffa}.preinstall-page .version-list .version-row{padding:10px 0;border-bottom:1px dashed #e9ecef}.preinstall-page .version-list .version-row:last-child{border-bottom:none}.preinstall-page .detail-grid .detail-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed #f1f3f5;font-size:13px}.preinstall-page .detail-grid .detail-row:last-child{border-bottom:none}.preinstall-page .activity-list{max-height:420px;overflow-y:auto}.preinstall-page .activity-list .activity-row{display:flex;gap:10px;padding:8px 0}.preinstall-page .activity-list .activity-row .activity-dot{width:8px;height:8px;border-radius:50%;background:#1b84ff;margin-top:6px;flex-shrink:0}@keyframes preinstallFade{to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion: reduce){.preinstall-page .state-panel .state-box{animation:none;opacity:1;transform:none}}\n"] }]
|
|
4323
|
+
args: [{ selector: 'lib-preinstallation-info', template: "<div class=\"container-fluid p-5 preinstall-page\">\n\n <!-- Header -->\n <div class=\"card mb-5\">\n <div class=\"card-body d-flex flex-wrap align-items-center justify-content-between gap-4 py-4\">\n <div class=\"d-flex align-items-center gap-4\">\n <button type=\"button\" class=\"btn btn-sm btn-light back-btn\" (click)=\"back()\" aria-label=\"Back to workspace\">\n <i class=\"bi bi-arrow-left\"></i>\n </button>\n <div>\n <div class=\"d-flex align-items-center gap-3\">\n <h4 class=\"mb-0\">{{ ticketInfo?.ticketId || ticketId }}</h4>\n <span class=\"badge state-badge\" [ngClass]=\"'state-' + placementState\">\n {{\n placementState === 'waitingLayout' ? 'Waiting for layout' :\n placementState === 'processing' ? 'Auto placement in progress' :\n placementState === 'failed' ? 'Placement failed' :\n placementState === 'open' ? 'Ready for placement review' :\n placementState === 'approvalPending' ? 'Waiting for approval' :\n placementState === 'approved' ? 'Approved' :\n placementState === 'unavailable' ? 'Status unavailable' : 'Loading'\n }}\n </span>\n </div>\n <div class=\"text-muted fs-7 mt-1\">\n Pre-Installation, camera placement\n <span *ngIf=\"ticketInfo?.basicDetails?.storeName\">, {{ ticketInfo?.basicDetails?.storeName }}</span>\n <span>, Floor {{ floorNumber }}</span>\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"refresh()\">\n <i class=\"bi bi-arrow-clockwise me-1\" [class.spinning]=\"reloading || cctvLoading\"></i>Refresh\n </button>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"loading\" class=\"text-center py-10 text-muted\">Loading ticket...</div>\n\n <div *ngIf=\"!loading && ticketInfo\" class=\"row g-5\">\n\n <!-- Placement state / action panel -->\n <div class=\"col-12 col-lg-7\">\n <div class=\"card h-100\">\n <div class=\"card-body\">\n <div class=\"card-head mb-4\">\n <h5 class=\"mb-0\">Camera placement</h5>\n <!-- The imported drawing itself, for the internal team to inspect in\n CAD when the engine cannot handle it or before editing. -->\n <a *ngIf=\"sourceDxfUrl\" [href]=\"sourceDxfUrl\" target=\"_blank\" rel=\"noopener\"\n class=\"btn btn-sm btn-light row-btn\" [ngbTooltip]=\"sourceDxfName\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <span class=\"ms-1\">Download source DXF</span>\n </a>\n </div>\n\n <div [ngSwitch]=\"placementState\" class=\"state-panel\">\n\n <div *ngSwitchCase=\"'waitingLayout'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Waiting for the CAD layout.</p>\n <p class=\"text-muted mb-0\">The store's floor drawing has not been imported yet. Auto placement starts right after the CAD import.</p>\n </div>\n\n <div *ngSwitchCase=\"'processing'\" class=\"state-box\">\n <div class=\"d-flex align-items-center gap-3 mb-1\">\n <span class=\"spinner-border spinner-border-sm text-primary\" role=\"status\" aria-hidden=\"true\"></span>\n <p class=\"mb-0 fw-semibold\">Auto camera placement is running.</p>\n </div>\n <p class=\"text-muted mb-3\">The DXF editor unlocks when placement completes. Use Refresh to check again. Meanwhile you can look at the source drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openPreview()\">\n <i class=\"bi bi-eye me-1\"></i>View source drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'failed'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Auto placement failed.</p>\n <p class=\"text-muted mb-3\">{{ placement?.errorMsg || cctvRow?.errorMsg || 'The placement engine reported a failure.' }}</p>\n <!-- Retry hidden until the autoPlace Lambda exists (LAMBDAURL.autoPlace is empty).\n Re-enable by removing this comment; retryPlacement() is still wired in the component.\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" [disabled]=\"retrying\" (click)=\"retryPlacement()\">\n <span *ngIf=\"retrying\" class=\"spinner-border spinner-border-sm me-2\" role=\"status\" aria-hidden=\"true\"></span>\n {{ retrying ? 'Retrying...' : 'Retry auto placement' }}\n </button>\n -->\n </div>\n\n <div *ngSwitchCase=\"'open'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">The drawing is ready for camera review.</p>\n <p class=\"text-muted mb-3\">\n Adjust the auto-placed cameras in the DXF editor, then submit for approval.\n <span *ngIf=\"cctvRow?.isRework\" class=\"text-warning fw-semibold\">This floor was sent back for rework.</span>\n </p>\n <button type=\"button\" class=\"btn btn-sm btn-primary row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-pencil-square me-1\"></i>Open DXF editor\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approvalPending'\" class=\"state-box\">\n <p class=\"mb-1 fw-semibold\">Submitted, waiting for approval.</p>\n <p class=\"text-muted mb-3\">The approval team reviews it from the onboard table. You can still view the drawing.</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"openEditor()\">\n <i class=\"bi bi-eye me-1\"></i>View drawing\n </button>\n </div>\n\n <div *ngSwitchCase=\"'approved'\" class=\"state-box state-box-success\">\n <p class=\"mb-1 fw-semibold\">Camera placement approved.</p>\n <p class=\"text-muted mb-0\">This ticket is closed. The installation ticket takes over once every floor of the store is approved.</p>\n </div>\n\n <div *ngSwitchCase=\"'unavailable'\" class=\"state-box state-box-danger\">\n <p class=\"mb-1 fw-semibold\">Placement status unavailable.</p>\n <p class=\"text-muted mb-3\">{{ cctvError }}</p>\n <button type=\"button\" class=\"btn btn-sm btn-light row-btn\" (click)=\"loadCctvDetail()\">Try again</button>\n </div>\n\n <div *ngSwitchDefault class=\"state-box\">\n <p class=\"text-muted mb-0\">Loading placement status...</p>\n </div>\n </div>\n\n <!-- Version timeline -->\n <div *ngIf=\"versions.length\" class=\"mt-6\">\n <h6 class=\"text-muted mb-3\">Drawing versions</h6>\n <div class=\"version-list\">\n <div class=\"version-row d-flex align-items-center justify-content-between\" *ngFor=\"let file of versions\">\n <div>\n <span class=\"fw-semibold\">v{{ file.version }}</span>\n <span class=\"text-muted ms-2 fs-7\">{{ file.kind === 'auto' ? 'Auto placement' : file.kind === 'approver' ? 'Approver edit' : 'Internal edit' }}</span>\n <span class=\"text-muted ms-2 fs-7\" *ngIf=\"file.cameraCount != null\">{{ file.cameraCount }} cameras</span>\n </div>\n <div class=\"d-flex align-items-center gap-3\">\n <span class=\"text-muted fs-7\">{{ file.uploadedAt | date: 'dd MMM, y HH:mm' }}</span>\n <a *ngIf=\"fileUrls[file.key]\" [href]=\"fileUrls[file.key]\" target=\"_blank\" rel=\"noopener\" class=\"btn btn-sm btn-light row-btn\">\n <i class=\"bi bi-download\"></i>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Ticket info + activity -->\n <div class=\"col-12 col-lg-5\">\n <div class=\"card mb-5\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Ticket details</h5>\n <div class=\"detail-grid\">\n <div class=\"detail-row\"><span class=\"text-muted\">Store</span><span>{{ ticketInfo?.basicDetails?.storeName || '-' }} ({{ ticketInfo?.basicDetails?.storeId || storeId }})</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Brand</span><span>{{ ticketInfo?.basicDetails?.clientName || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Floor</span><span>Floor {{ floorNumber }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Ticket status</span><span class=\"text-capitalize\">{{ ticketInfo?.status || '-' }}</span></div>\n <div class=\"detail-row\"><span class=\"text-muted\">Created</span><span>{{ ticketInfo?.createdAt | date: 'dd MMM, y HH:mm' }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.assigneeName\"><span class=\"text-muted\">Assignee</span><span>{{ cctvRow?.assigneeName }}</span></div>\n <div class=\"detail-row\" *ngIf=\"cctvRow?.reworkCount\"><span class=\"text-muted\">Rework count</span><span>{{ cctvRow?.reworkCount }}</span></div>\n </div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-body\">\n <h5 class=\"mb-4\">Activity</h5>\n <div *ngIf=\"!activity.length\" class=\"text-muted fs-7\">No activity yet.</div>\n <div class=\"activity-list\">\n <div class=\"activity-row\" *ngFor=\"let entry of activity\">\n <div class=\"activity-dot\" aria-hidden=\"true\"></div>\n <div class=\"flex-grow-1\">\n <div class=\"d-flex justify-content-between gap-3\">\n <span class=\"fw-semibold fs-7\">{{ entry.description || entry.actionType }}</span>\n <span class=\"text-muted fs-8 text-nowrap\">{{ entry.timeStamp | date: 'dd MMM, HH:mm' }}</span>\n </div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.comment\">{{ entry.comment }}</div>\n <div class=\"text-muted fs-8\" *ngIf=\"entry.IdentifiedBy && entry.IdentifiedBy !== 'Tango'\">by {{ entry.IdentifiedBy }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".preinstall-page .back-btn,.preinstall-page .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.preinstall-page .back-btn .row-btn,.preinstall-page .card-head .row-btn{display:inline-flex;align-items:center;white-space:nowrap}.preinstall-page .row-btn{height:40px;display:inline-flex;align-items:center}.preinstall-page .state-badge{padding:6px 12px;font-weight:600;border-radius:999px}.preinstall-page .state-badge.state-waitingLayout,.preinstall-page .state-badge.state-loading,.preinstall-page .state-badge.state-unavailable{background:#f1f3f5;color:#5e6278}.preinstall-page .state-badge.state-processing{background:#eef6ff;color:#1b84ff}.preinstall-page .state-badge.state-failed{background:#fff5f8;color:#f1416c}.preinstall-page .state-badge.state-open{background:#e8fff3;color:#17c653}.preinstall-page .state-badge.state-approvalPending{background:#fff8dd;color:#f6c000}.preinstall-page .state-badge.state-approved{background:#e8fff3;color:#17c653}.preinstall-page .state-panel .state-box{border:1px solid #e9ecef;border-radius:8px;padding:16px 20px;opacity:0;transform:translateY(4px);animation:preinstallFade .25s ease forwards}.preinstall-page .state-panel .state-box.state-box-danger{border-color:#ffd6e1;background:#fff9fb}.preinstall-page .state-panel .state-box.state-box-success{border-color:#c8f2d8;background:#f6fffa}.preinstall-page .version-list .version-row{padding:10px 0;border-bottom:1px dashed #e9ecef}.preinstall-page .version-list .version-row:last-child{border-bottom:none}.preinstall-page .detail-grid .detail-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed #f1f3f5;font-size:13px}.preinstall-page .detail-grid .detail-row:last-child{border-bottom:none}.preinstall-page .activity-list{max-height:420px;overflow-y:auto}.preinstall-page .activity-list .activity-row{display:flex;gap:10px;padding:8px 0}.preinstall-page .activity-list .activity-row .activity-dot{width:8px;height:8px;border-radius:50%;background:#1b84ff;margin-top:6px;flex-shrink:0}@keyframes preinstallFade{to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion: reduce){.preinstall-page .state-panel .state-box{animation:none;opacity:1;transform:none}}.preinstall-page .bi-arrow-clockwise{display:inline-block;line-height:1}.preinstall-page .bi-arrow-clockwise:before{display:inline-block;transform-origin:50% 50%}.preinstall-page .bi-arrow-clockwise.spinning:before{animation:preinstallSpin .8s linear infinite}@keyframes preinstallSpin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.preinstall-page .bi-arrow-clockwise.spinning:before{animation:none}}\n"] }]
|
|
4311
4324
|
}], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: TicketsService }, { type: i3.ToastService }] });
|
|
4312
4325
|
|
|
4313
4326
|
const routes = [
|