tango-app-ui-store-builder 1.2.21 → 1.2.22

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.
@@ -162,6 +162,7 @@ export class FixtureConformanceComponent {
162
162
  case 'ok': return 'Conforms';
163
163
  case 'shelf-count-mismatch': return 'Shelf count mismatch';
164
164
  case 'zone-mismatch': return 'Zone mismatch';
165
+ case 'type-mismatch': return 'Shelf type mismatch';
165
166
  case 'no-library': return 'No library match';
166
167
  case 'ambiguous': return 'Ambiguous library';
167
168
  case 'no-shelves': return 'No shelves';
@@ -172,7 +173,8 @@ export class FixtureConformanceComponent {
172
173
  switch (reason) {
173
174
  case 'ok': return 'badge-ok';
174
175
  case 'shelf-count-mismatch':
175
- case 'zone-mismatch': return 'badge-warn';
176
+ case 'zone-mismatch':
177
+ case 'type-mismatch': return 'badge-warn';
176
178
  case 'no-library':
177
179
  case 'ambiguous':
178
180
  case 'no-shelves': return 'badge-muted';
@@ -189,10 +191,10 @@ export class FixtureConformanceComponent {
189
191
  this.destroy$.complete();
190
192
  }
191
193
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FixtureConformanceComponent, deps: [{ token: i1.StoreBuilderService }, { token: i2.GlobalStateService }], target: i0.ɵɵFactoryTarget.Component });
192
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FixtureConformanceComponent, selector: "lib-fixture-conformance", ngImport: i0, template: "<section class=\"fixture-conformance p-3\">\n <lib-tool-page-header\n title=\"Sync Fixtures to Library\"\n description=\"Check a store's fixtures against the library, then sync shelf count and zones for the ones that don't match.\"\n ></lib-tool-page-header>\n\n <div class=\"card p-4\">\n <!-- Store Select -->\n <div class=\"mb-3\" style=\"max-width: 600px;\">\n <label class=\"form-label\">Store</label>\n <lib-store-select\n [options]=\"storeOptions\"\n [singleSelect]=\"true\"\n (selectedChange)=\"onStoresSelected($event)\">\n </lib-store-select>\n </div>\n\n <!-- Fixture List -->\n @if (selectedStoreName) {\n @if (isLoadingFixtures) {\n <div class=\"text-muted\">Loading fixtures...</div>\n } @else if (fixtures.length === 0) {\n <div class=\"text-muted\">No fixtures found for this store.</div>\n } @else {\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Fixtures\n <span class=\"text-muted small\">({{ selectedFixtureIds.length }} of {{ fixtures.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-primary btn-sm\"\n (click)=\"onAudit()\"\n [disabled]=\"selectedFixtureIds.length === 0 || isAuditing\">\n {{ isAuditing ? 'Auditing...' : 'Audit Selected' }}\n </button>\n </div>\n\n <div class=\"table-responsive mb-2\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixturesSelected\"\n (change)=\"toggleAllFixtures($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Fixture ID</th>\n <th>Category</th>\n <th style=\"width: 90px;\">Width</th>\n </tr>\n </thead>\n <tbody>\n @for (f of fixtures; track f._id) {\n <tr (click)=\"f.selected = !f.selected\" class=\"selectable-row\">\n <td (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" [(ngModel)]=\"f.selected\">\n </td>\n <td>{{ f.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ f.headerLabel || '\u2014' }}</td>\n <td class=\"fixture-id\">{{ f._id }}</td>\n <td>{{ f.fixtureCategory || '\u2014' }}</td>\n <td>{{ f.width || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Audit Results -->\n @if (hasAudited) {\n <hr class=\"my-4\" />\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Audit Result\n <span class=\"text-muted small\">({{ fixableRows.length }} fixable, {{ selectedAuditIds.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-success btn-sm\"\n (click)=\"onApplyFix()\"\n [disabled]=\"selectedAuditIds.length === 0 || isApplying\">\n {{ isApplying ? 'Applying...' : 'Apply Fix' }}\n </button>\n </div>\n\n @if (auditRows.length === 0) {\n <div class=\"text-muted\">No results.</div>\n } @else {\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixableSelected\"\n [disabled]=\"fixableRows.length === 0\"\n (change)=\"toggleAllFixable($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Category</th>\n <th style=\"width: 70px;\">Width</th>\n <th style=\"width: 110px;\">Shelves</th>\n <th>Current Zones</th>\n <th>Library Zones</th>\n <th style=\"width: 160px;\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (r of auditRows; track r.fixtureId) {\n <tr [class.row-fixable]=\"r.fixable\">\n <td>\n <input type=\"checkbox\" [(ngModel)]=\"r.selected\" [disabled]=\"!r.fixable\">\n </td>\n <td>{{ r.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ r.header || '\u2014' }}</td>\n <td>{{ r.category || '\u2014' }}</td>\n <td>{{ r.width || '\u2014' }}</td>\n <td>\n @if (r.libShelfCount != null) {\n <span [class.text-danger]=\"r.curShelfCount !== r.libShelfCount\">\n {{ r.curShelfCount }} \u2192 {{ r.libShelfCount }}\n </span>\n } @else {\n {{ r.curShelfCount }}\n }\n </td>\n <td class=\"zones\">{{ r.currentZones || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libZones || '\u2014' }}</td>\n <td>\n <span class=\"status-badge\" [ngClass]=\"reasonClass(r.reason)\">\n {{ reasonLabel(r.reason) }}\n </span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Apply summary per fixture -->\n @if (applyResults.length > 0) {\n <hr class=\"my-4\" />\n <h6 class=\"section-heading mb-2\">Apply Summary</h6>\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 90px;\">Fixture #</th>\n <th style=\"width: 140px;\">Status</th>\n <th>Shelves</th>\n <th>Compliance Synced</th>\n <th>Note</th>\n </tr>\n </thead>\n <tbody>\n @for (ar of applyResults; track ar.fixtureId) {\n <tr>\n <td>{{ ar.fixtureNumber ?? '\u2014' }}</td>\n <td>\n <span class=\"status-badge\"\n [class.badge-ok]=\"ar.status === 'fixed' || ar.status === 'already-conform'\"\n [class.badge-warn]=\"ar.status === 'skipped'\"\n [class.badge-danger]=\"ar.status === 'failed'\">\n {{ ar.status }}\n </span>\n </td>\n <td>\n @if (ar.shelvesFrom != null) {\n {{ ar.shelvesFrom }} \u2192 {{ ar.shelvesTo }}\n } @else { \u2014 }\n </td>\n <td>{{ ar.complianceSynced ?? 0 }}</td>\n <td class=\"text-muted small\">{{ ar.error || ar.reason || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Message -->\n @if (message) {\n <div class=\"mt-3 alert\" [class.alert-success]=\"messageType === 'success'\"\n [class.alert-danger]=\"messageType === 'error'\">\n {{ message }}\n </div>\n }\n </div>\n</section>\n", styles: [".fixture-conformance .section-heading{color:#344054;font-weight:600}.fixture-conformance .fc-table th{background-color:#f9fafb;font-weight:600;font-size:13px;color:#475467;white-space:nowrap}.fixture-conformance .fc-table td{font-size:13px;vertical-align:middle}.fixture-conformance .fc-table .zones{font-family:Courier New,monospace;font-size:12px;color:#475467;word-break:break-word}.fixture-conformance .fc-table .fixture-id{font-family:Courier New,monospace;font-size:12px;color:#667085}.fixture-conformance .fc-table .selectable-row{cursor:pointer}.fixture-conformance .fc-table .selectable-row:hover{background-color:#f9fafb}.fixture-conformance .fc-table .row-fixable{background-color:#fffaf0}.fixture-conformance .status-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fixture-conformance .status-badge.badge-ok{background-color:#ecfdf3;color:#027a48}.fixture-conformance .status-badge.badge-warn{background-color:#fffaeb;color:#b54708}.fixture-conformance .status-badge.badge-muted{background-color:#f2f4f7;color:#475467}.fixture-conformance .status-badge.badge-danger{background-color:#fef3f2;color:#b42318}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.StoreSelectComponent, selector: "lib-store-select", inputs: ["options", "initialSelected", "singleSelect"], outputs: ["selectedChange"] }, { kind: "component", type: i6.ToolPageHeaderComponent, selector: "lib-tool-page-header", inputs: ["title", "description", "backLink"] }] });
194
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FixtureConformanceComponent, selector: "lib-fixture-conformance", ngImport: i0, template: "<section class=\"fixture-conformance p-3\">\n <lib-tool-page-header\n title=\"Sync Fixtures to Library\"\n description=\"Check a store's fixtures against the library, then sync shelf count and zones for the ones that don't match.\"\n ></lib-tool-page-header>\n\n <div class=\"card p-4\">\n <!-- Store Select -->\n <div class=\"mb-3\" style=\"max-width: 600px;\">\n <label class=\"form-label\">Store</label>\n <lib-store-select\n [options]=\"storeOptions\"\n [singleSelect]=\"true\"\n (selectedChange)=\"onStoresSelected($event)\">\n </lib-store-select>\n </div>\n\n <!-- Fixture List -->\n @if (selectedStoreName) {\n @if (isLoadingFixtures) {\n <div class=\"text-muted\">Loading fixtures...</div>\n } @else if (fixtures.length === 0) {\n <div class=\"text-muted\">No fixtures found for this store.</div>\n } @else {\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Fixtures\n <span class=\"text-muted small\">({{ selectedFixtureIds.length }} of {{ fixtures.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-primary btn-sm\"\n (click)=\"onAudit()\"\n [disabled]=\"selectedFixtureIds.length === 0 || isAuditing\">\n {{ isAuditing ? 'Auditing...' : 'Audit Selected' }}\n </button>\n </div>\n\n <div class=\"table-responsive mb-2\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixturesSelected\"\n (change)=\"toggleAllFixtures($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Fixture ID</th>\n <th>Category</th>\n <th style=\"width: 90px;\">Width</th>\n </tr>\n </thead>\n <tbody>\n @for (f of fixtures; track f._id) {\n <tr (click)=\"f.selected = !f.selected\" class=\"selectable-row\">\n <td (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" [(ngModel)]=\"f.selected\">\n </td>\n <td>{{ f.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ f.headerLabel || '\u2014' }}</td>\n <td class=\"fixture-id\">{{ f._id }}</td>\n <td>{{ f.fixtureCategory || '\u2014' }}</td>\n <td>{{ f.width || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Audit Results -->\n @if (hasAudited) {\n <hr class=\"my-4\" />\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Audit Result\n <span class=\"text-muted small\">({{ fixableRows.length }} fixable, {{ selectedAuditIds.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-success btn-sm\"\n (click)=\"onApplyFix()\"\n [disabled]=\"selectedAuditIds.length === 0 || isApplying\">\n {{ isApplying ? 'Applying...' : 'Apply Fix' }}\n </button>\n </div>\n\n @if (auditRows.length === 0) {\n <div class=\"text-muted\">No results.</div>\n } @else {\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixableSelected\"\n [disabled]=\"fixableRows.length === 0\"\n (change)=\"toggleAllFixable($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Category</th>\n <th style=\"width: 70px;\">Width</th>\n <th style=\"width: 110px;\">Shelves</th>\n <th>Current Zones</th>\n <th>Library Zones</th>\n <th>Current Types</th>\n <th>Library Types</th>\n <th style=\"width: 160px;\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (r of auditRows; track r.fixtureId) {\n <tr [class.row-fixable]=\"r.fixable\">\n <td>\n <input type=\"checkbox\" [(ngModel)]=\"r.selected\" [disabled]=\"!r.fixable\">\n </td>\n <td>{{ r.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ r.header || '\u2014' }}</td>\n <td>{{ r.category || '\u2014' }}</td>\n <td>{{ r.width || '\u2014' }}</td>\n <td>\n @if (r.libShelfCount != null) {\n <span [class.text-danger]=\"r.curShelfCount !== r.libShelfCount\">\n {{ r.curShelfCount }} \u2192 {{ r.libShelfCount }}\n </span>\n } @else {\n {{ r.curShelfCount }}\n }\n </td>\n <td class=\"zones\">{{ r.currentZones || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libZones || '\u2014' }}</td>\n <td class=\"zones\" [class.text-danger]=\"r.reason === 'type-mismatch'\">{{ r.currentTypes || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libTypes || '\u2014' }}</td>\n <td>\n <span class=\"status-badge\" [ngClass]=\"reasonClass(r.reason)\">\n {{ reasonLabel(r.reason) }}\n </span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Apply summary per fixture -->\n @if (applyResults.length > 0) {\n <hr class=\"my-4\" />\n <h6 class=\"section-heading mb-2\">Apply Summary</h6>\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 90px;\">Fixture #</th>\n <th style=\"width: 140px;\">Status</th>\n <th>Shelves</th>\n <th>Compliance Synced</th>\n <th>Note</th>\n </tr>\n </thead>\n <tbody>\n @for (ar of applyResults; track ar.fixtureId) {\n <tr>\n <td>{{ ar.fixtureNumber ?? '\u2014' }}</td>\n <td>\n <span class=\"status-badge\"\n [class.badge-ok]=\"ar.status === 'fixed' || ar.status === 'already-conform'\"\n [class.badge-warn]=\"ar.status === 'skipped'\"\n [class.badge-danger]=\"ar.status === 'failed'\">\n {{ ar.status }}\n </span>\n </td>\n <td>\n @if (ar.shelvesFrom != null) {\n {{ ar.shelvesFrom }} \u2192 {{ ar.shelvesTo }}\n } @else { \u2014 }\n </td>\n <td>{{ ar.complianceSynced ?? 0 }}</td>\n <td class=\"text-muted small\">{{ ar.error || ar.reason || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Message -->\n @if (message) {\n <div class=\"mt-3 alert\" [class.alert-success]=\"messageType === 'success'\"\n [class.alert-danger]=\"messageType === 'error'\">\n {{ message }}\n </div>\n }\n </div>\n</section>\n", styles: [".fixture-conformance .section-heading{color:#344054;font-weight:600}.fixture-conformance .fc-table th{background-color:#f9fafb;font-weight:600;font-size:13px;color:#475467;white-space:nowrap}.fixture-conformance .fc-table td{font-size:13px;vertical-align:middle}.fixture-conformance .fc-table .zones{font-family:Courier New,monospace;font-size:12px;color:#475467;word-break:break-word}.fixture-conformance .fc-table .fixture-id{font-family:Courier New,monospace;font-size:12px;color:#667085}.fixture-conformance .fc-table .selectable-row{cursor:pointer}.fixture-conformance .fc-table .selectable-row:hover{background-color:#f9fafb}.fixture-conformance .fc-table .row-fixable{background-color:#fffaf0}.fixture-conformance .status-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fixture-conformance .status-badge.badge-ok{background-color:#ecfdf3;color:#027a48}.fixture-conformance .status-badge.badge-warn{background-color:#fffaeb;color:#b54708}.fixture-conformance .status-badge.badge-muted{background-color:#f2f4f7;color:#475467}.fixture-conformance .status-badge.badge-danger{background-color:#fef3f2;color:#b42318}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.StoreSelectComponent, selector: "lib-store-select", inputs: ["options", "initialSelected", "singleSelect"], outputs: ["selectedChange"] }, { kind: "component", type: i6.ToolPageHeaderComponent, selector: "lib-tool-page-header", inputs: ["title", "description", "backLink"] }] });
193
195
  }
194
196
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FixtureConformanceComponent, decorators: [{
195
197
  type: Component,
196
- args: [{ selector: 'lib-fixture-conformance', template: "<section class=\"fixture-conformance p-3\">\n <lib-tool-page-header\n title=\"Sync Fixtures to Library\"\n description=\"Check a store's fixtures against the library, then sync shelf count and zones for the ones that don't match.\"\n ></lib-tool-page-header>\n\n <div class=\"card p-4\">\n <!-- Store Select -->\n <div class=\"mb-3\" style=\"max-width: 600px;\">\n <label class=\"form-label\">Store</label>\n <lib-store-select\n [options]=\"storeOptions\"\n [singleSelect]=\"true\"\n (selectedChange)=\"onStoresSelected($event)\">\n </lib-store-select>\n </div>\n\n <!-- Fixture List -->\n @if (selectedStoreName) {\n @if (isLoadingFixtures) {\n <div class=\"text-muted\">Loading fixtures...</div>\n } @else if (fixtures.length === 0) {\n <div class=\"text-muted\">No fixtures found for this store.</div>\n } @else {\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Fixtures\n <span class=\"text-muted small\">({{ selectedFixtureIds.length }} of {{ fixtures.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-primary btn-sm\"\n (click)=\"onAudit()\"\n [disabled]=\"selectedFixtureIds.length === 0 || isAuditing\">\n {{ isAuditing ? 'Auditing...' : 'Audit Selected' }}\n </button>\n </div>\n\n <div class=\"table-responsive mb-2\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixturesSelected\"\n (change)=\"toggleAllFixtures($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Fixture ID</th>\n <th>Category</th>\n <th style=\"width: 90px;\">Width</th>\n </tr>\n </thead>\n <tbody>\n @for (f of fixtures; track f._id) {\n <tr (click)=\"f.selected = !f.selected\" class=\"selectable-row\">\n <td (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" [(ngModel)]=\"f.selected\">\n </td>\n <td>{{ f.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ f.headerLabel || '\u2014' }}</td>\n <td class=\"fixture-id\">{{ f._id }}</td>\n <td>{{ f.fixtureCategory || '\u2014' }}</td>\n <td>{{ f.width || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Audit Results -->\n @if (hasAudited) {\n <hr class=\"my-4\" />\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Audit Result\n <span class=\"text-muted small\">({{ fixableRows.length }} fixable, {{ selectedAuditIds.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-success btn-sm\"\n (click)=\"onApplyFix()\"\n [disabled]=\"selectedAuditIds.length === 0 || isApplying\">\n {{ isApplying ? 'Applying...' : 'Apply Fix' }}\n </button>\n </div>\n\n @if (auditRows.length === 0) {\n <div class=\"text-muted\">No results.</div>\n } @else {\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixableSelected\"\n [disabled]=\"fixableRows.length === 0\"\n (change)=\"toggleAllFixable($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Category</th>\n <th style=\"width: 70px;\">Width</th>\n <th style=\"width: 110px;\">Shelves</th>\n <th>Current Zones</th>\n <th>Library Zones</th>\n <th style=\"width: 160px;\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (r of auditRows; track r.fixtureId) {\n <tr [class.row-fixable]=\"r.fixable\">\n <td>\n <input type=\"checkbox\" [(ngModel)]=\"r.selected\" [disabled]=\"!r.fixable\">\n </td>\n <td>{{ r.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ r.header || '\u2014' }}</td>\n <td>{{ r.category || '\u2014' }}</td>\n <td>{{ r.width || '\u2014' }}</td>\n <td>\n @if (r.libShelfCount != null) {\n <span [class.text-danger]=\"r.curShelfCount !== r.libShelfCount\">\n {{ r.curShelfCount }} \u2192 {{ r.libShelfCount }}\n </span>\n } @else {\n {{ r.curShelfCount }}\n }\n </td>\n <td class=\"zones\">{{ r.currentZones || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libZones || '\u2014' }}</td>\n <td>\n <span class=\"status-badge\" [ngClass]=\"reasonClass(r.reason)\">\n {{ reasonLabel(r.reason) }}\n </span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Apply summary per fixture -->\n @if (applyResults.length > 0) {\n <hr class=\"my-4\" />\n <h6 class=\"section-heading mb-2\">Apply Summary</h6>\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 90px;\">Fixture #</th>\n <th style=\"width: 140px;\">Status</th>\n <th>Shelves</th>\n <th>Compliance Synced</th>\n <th>Note</th>\n </tr>\n </thead>\n <tbody>\n @for (ar of applyResults; track ar.fixtureId) {\n <tr>\n <td>{{ ar.fixtureNumber ?? '\u2014' }}</td>\n <td>\n <span class=\"status-badge\"\n [class.badge-ok]=\"ar.status === 'fixed' || ar.status === 'already-conform'\"\n [class.badge-warn]=\"ar.status === 'skipped'\"\n [class.badge-danger]=\"ar.status === 'failed'\">\n {{ ar.status }}\n </span>\n </td>\n <td>\n @if (ar.shelvesFrom != null) {\n {{ ar.shelvesFrom }} \u2192 {{ ar.shelvesTo }}\n } @else { \u2014 }\n </td>\n <td>{{ ar.complianceSynced ?? 0 }}</td>\n <td class=\"text-muted small\">{{ ar.error || ar.reason || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Message -->\n @if (message) {\n <div class=\"mt-3 alert\" [class.alert-success]=\"messageType === 'success'\"\n [class.alert-danger]=\"messageType === 'error'\">\n {{ message }}\n </div>\n }\n </div>\n</section>\n", styles: [".fixture-conformance .section-heading{color:#344054;font-weight:600}.fixture-conformance .fc-table th{background-color:#f9fafb;font-weight:600;font-size:13px;color:#475467;white-space:nowrap}.fixture-conformance .fc-table td{font-size:13px;vertical-align:middle}.fixture-conformance .fc-table .zones{font-family:Courier New,monospace;font-size:12px;color:#475467;word-break:break-word}.fixture-conformance .fc-table .fixture-id{font-family:Courier New,monospace;font-size:12px;color:#667085}.fixture-conformance .fc-table .selectable-row{cursor:pointer}.fixture-conformance .fc-table .selectable-row:hover{background-color:#f9fafb}.fixture-conformance .fc-table .row-fixable{background-color:#fffaf0}.fixture-conformance .status-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fixture-conformance .status-badge.badge-ok{background-color:#ecfdf3;color:#027a48}.fixture-conformance .status-badge.badge-warn{background-color:#fffaeb;color:#b54708}.fixture-conformance .status-badge.badge-muted{background-color:#f2f4f7;color:#475467}.fixture-conformance .status-badge.badge-danger{background-color:#fef3f2;color:#b42318}\n"] }]
198
+ args: [{ selector: 'lib-fixture-conformance', template: "<section class=\"fixture-conformance p-3\">\n <lib-tool-page-header\n title=\"Sync Fixtures to Library\"\n description=\"Check a store's fixtures against the library, then sync shelf count and zones for the ones that don't match.\"\n ></lib-tool-page-header>\n\n <div class=\"card p-4\">\n <!-- Store Select -->\n <div class=\"mb-3\" style=\"max-width: 600px;\">\n <label class=\"form-label\">Store</label>\n <lib-store-select\n [options]=\"storeOptions\"\n [singleSelect]=\"true\"\n (selectedChange)=\"onStoresSelected($event)\">\n </lib-store-select>\n </div>\n\n <!-- Fixture List -->\n @if (selectedStoreName) {\n @if (isLoadingFixtures) {\n <div class=\"text-muted\">Loading fixtures...</div>\n } @else if (fixtures.length === 0) {\n <div class=\"text-muted\">No fixtures found for this store.</div>\n } @else {\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Fixtures\n <span class=\"text-muted small\">({{ selectedFixtureIds.length }} of {{ fixtures.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-primary btn-sm\"\n (click)=\"onAudit()\"\n [disabled]=\"selectedFixtureIds.length === 0 || isAuditing\">\n {{ isAuditing ? 'Auditing...' : 'Audit Selected' }}\n </button>\n </div>\n\n <div class=\"table-responsive mb-2\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixturesSelected\"\n (change)=\"toggleAllFixtures($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Fixture ID</th>\n <th>Category</th>\n <th style=\"width: 90px;\">Width</th>\n </tr>\n </thead>\n <tbody>\n @for (f of fixtures; track f._id) {\n <tr (click)=\"f.selected = !f.selected\" class=\"selectable-row\">\n <td (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" [(ngModel)]=\"f.selected\">\n </td>\n <td>{{ f.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ f.headerLabel || '\u2014' }}</td>\n <td class=\"fixture-id\">{{ f._id }}</td>\n <td>{{ f.fixtureCategory || '\u2014' }}</td>\n <td>{{ f.width || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Audit Results -->\n @if (hasAudited) {\n <hr class=\"my-4\" />\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Audit Result\n <span class=\"text-muted small\">({{ fixableRows.length }} fixable, {{ selectedAuditIds.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-success btn-sm\"\n (click)=\"onApplyFix()\"\n [disabled]=\"selectedAuditIds.length === 0 || isApplying\">\n {{ isApplying ? 'Applying...' : 'Apply Fix' }}\n </button>\n </div>\n\n @if (auditRows.length === 0) {\n <div class=\"text-muted\">No results.</div>\n } @else {\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixableSelected\"\n [disabled]=\"fixableRows.length === 0\"\n (change)=\"toggleAllFixable($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Category</th>\n <th style=\"width: 70px;\">Width</th>\n <th style=\"width: 110px;\">Shelves</th>\n <th>Current Zones</th>\n <th>Library Zones</th>\n <th>Current Types</th>\n <th>Library Types</th>\n <th style=\"width: 160px;\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (r of auditRows; track r.fixtureId) {\n <tr [class.row-fixable]=\"r.fixable\">\n <td>\n <input type=\"checkbox\" [(ngModel)]=\"r.selected\" [disabled]=\"!r.fixable\">\n </td>\n <td>{{ r.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ r.header || '\u2014' }}</td>\n <td>{{ r.category || '\u2014' }}</td>\n <td>{{ r.width || '\u2014' }}</td>\n <td>\n @if (r.libShelfCount != null) {\n <span [class.text-danger]=\"r.curShelfCount !== r.libShelfCount\">\n {{ r.curShelfCount }} \u2192 {{ r.libShelfCount }}\n </span>\n } @else {\n {{ r.curShelfCount }}\n }\n </td>\n <td class=\"zones\">{{ r.currentZones || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libZones || '\u2014' }}</td>\n <td class=\"zones\" [class.text-danger]=\"r.reason === 'type-mismatch'\">{{ r.currentTypes || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libTypes || '\u2014' }}</td>\n <td>\n <span class=\"status-badge\" [ngClass]=\"reasonClass(r.reason)\">\n {{ reasonLabel(r.reason) }}\n </span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Apply summary per fixture -->\n @if (applyResults.length > 0) {\n <hr class=\"my-4\" />\n <h6 class=\"section-heading mb-2\">Apply Summary</h6>\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 90px;\">Fixture #</th>\n <th style=\"width: 140px;\">Status</th>\n <th>Shelves</th>\n <th>Compliance Synced</th>\n <th>Note</th>\n </tr>\n </thead>\n <tbody>\n @for (ar of applyResults; track ar.fixtureId) {\n <tr>\n <td>{{ ar.fixtureNumber ?? '\u2014' }}</td>\n <td>\n <span class=\"status-badge\"\n [class.badge-ok]=\"ar.status === 'fixed' || ar.status === 'already-conform'\"\n [class.badge-warn]=\"ar.status === 'skipped'\"\n [class.badge-danger]=\"ar.status === 'failed'\">\n {{ ar.status }}\n </span>\n </td>\n <td>\n @if (ar.shelvesFrom != null) {\n {{ ar.shelvesFrom }} \u2192 {{ ar.shelvesTo }}\n } @else { \u2014 }\n </td>\n <td>{{ ar.complianceSynced ?? 0 }}</td>\n <td class=\"text-muted small\">{{ ar.error || ar.reason || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Message -->\n @if (message) {\n <div class=\"mt-3 alert\" [class.alert-success]=\"messageType === 'success'\"\n [class.alert-danger]=\"messageType === 'error'\">\n {{ message }}\n </div>\n }\n </div>\n</section>\n", styles: [".fixture-conformance .section-heading{color:#344054;font-weight:600}.fixture-conformance .fc-table th{background-color:#f9fafb;font-weight:600;font-size:13px;color:#475467;white-space:nowrap}.fixture-conformance .fc-table td{font-size:13px;vertical-align:middle}.fixture-conformance .fc-table .zones{font-family:Courier New,monospace;font-size:12px;color:#475467;word-break:break-word}.fixture-conformance .fc-table .fixture-id{font-family:Courier New,monospace;font-size:12px;color:#667085}.fixture-conformance .fc-table .selectable-row{cursor:pointer}.fixture-conformance .fc-table .selectable-row:hover{background-color:#f9fafb}.fixture-conformance .fc-table .row-fixable{background-color:#fffaf0}.fixture-conformance .status-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fixture-conformance .status-badge.badge-ok{background-color:#ecfdf3;color:#027a48}.fixture-conformance .status-badge.badge-warn{background-color:#fffaeb;color:#b54708}.fixture-conformance .status-badge.badge-muted{background-color:#f2f4f7;color:#475467}.fixture-conformance .status-badge.badge-danger{background-color:#fef3f2;color:#b42318}\n"] }]
197
199
  }], ctorParameters: () => [{ type: i1.StoreBuilderService }, { type: i2.GlobalStateService }] });
198
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZml4dHVyZS1jb25mb3JtYW5jZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90YW5nby1zdG9yZS1idWlsZGVyL3NyYy9saWIvY29tcG9uZW50cy9wbGFuby10b29scy9maXh0dXJlLWNvbmZvcm1hbmNlL2ZpeHR1cmUtY29uZm9ybWFuY2UuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvdGFuZ28tc3RvcmUtYnVpbGRlci9zcmMvbGliL2NvbXBvbmVudHMvcGxhbm8tdG9vbHMvZml4dHVyZS1jb25mb3JtYW5jZS9maXh0dXJlLWNvbmZvcm1hbmNlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQXFCLE1BQU0sZUFBZSxDQUFDO0FBRzdELE9BQU8sRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxNQUFNLENBQUM7Ozs7Ozs7O0FBb0R0RixNQUFNLE9BQU8sMkJBQTJCO0lBc0I1QjtJQUNBO0lBdEJWLFFBQVEsR0FBVyxFQUFFLENBQUM7SUFFdEIsWUFBWSxHQUFrQixFQUFFLENBQUM7SUFDakMsaUJBQWlCLEdBQVcsRUFBRSxDQUFDO0lBRS9CLFFBQVEsR0FBeUIsRUFBRSxDQUFDO0lBQ3BDLGlCQUFpQixHQUFHLEtBQUssQ0FBQztJQUUxQixTQUFTLEdBQWUsRUFBRSxDQUFDO0lBQzNCLFVBQVUsR0FBRyxLQUFLLENBQUM7SUFDbkIsVUFBVSxHQUFHLEtBQUssQ0FBQztJQUVuQixZQUFZLEdBQWtCLEVBQUUsQ0FBQztJQUNqQyxVQUFVLEdBQUcsS0FBSyxDQUFDO0lBRW5CLE9BQU8sR0FBRyxFQUFFLENBQUM7SUFDYixXQUFXLEdBQXdCLFNBQVMsQ0FBQztJQUU1QixRQUFRLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FB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QUssWUFBWSxDQUFDO1lBQ2xCLEtBQUssV0FBVyxDQUFDO1lBQ2pCLEtBQUssWUFBWSxDQUFDLENBQUMsT0FBTyxhQUFhLENBQUM7WUFDeEMsT0FBTyxDQUFDLENBQUMsT0FBTyxhQUFhLENBQUM7U0FDL0I7SUFDSCxDQUFDO0lBRUQsV0FBVyxDQUFDLEdBQVcsRUFBRSxJQUF5QjtRQUNoRCxJQUFJLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQztRQUNuQixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztRQUN4QixVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRCxXQUFXO1FBQ1QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNyQixJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQzNCLENBQUM7d0dBbE5VLDJCQUEyQjs0RkFBM0IsMkJBQTJCLCtEQ3ZEeEMsNGdQQW1NQTs7NEZENUlhLDJCQUEyQjtrQkFMdkMsU0FBUzsrQkFDRSx5QkFBeUIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIE9uSW5pdCwgT25EZXN0cm95IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBTdG9yZUJ1aWxkZXJTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vLi4vc2VydmljZXMvc3RvcmUtYnVpbGRlci5zZXJ2aWNlJztcbmltcG9ydCB7IEdsb2JhbFN0YXRlU2VydmljZSB9IGZyb20gJ3RhbmdvLWFwcC11aS1nbG9iYWwnO1xuaW1wb3J0IHsgU3ViamVjdCwgdGFrZVVudGlsLCBkZWJvdW5jZVRpbWUsIGZpbHRlciwgZGlzdGluY3RVbnRpbENoYW5nZWQgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IFN0b3JlT3B0aW9uIH0gZnJvbSAnLi4vLi4vY29sbGVjdGlvbi11cGRhdGUtYWkvY29tcG9uZW50cy9zdG9yZS1zZWxlY3Qvc3RvcmUtc2VsZWN0LmNvbXBvbmVudCc7XG5cbmludGVyZmFjZSBDb25mb3JtYW5jZUZpeHR1cmUge1xuICBfaWQ6IHN0cmluZztcbiAgZmxvb3JJZDogc3RyaW5nO1xuICBmaXh0dXJlTnVtYmVyOiBudW1iZXIgfCBudWxsO1xuICBmaXh0dXJlQ2F0ZWdvcnk6IHN0cmluZztcbiAgd2lkdGg6IG51bWJlciB8IHN0cmluZztcbiAgaGVhZGVyTGFiZWw6IHN0cmluZztcbiAgc2VsZWN0ZWQ6IGJvb2xlYW47XG59XG5cbnR5cGUgUmVhc29uID1cbiAgfCAnb2snXG4gIHwgJ3NoZWxmLWNvdW50LW1pc21hdGNoJ1xuICB8ICd6b25lLW1pc21hdGNoJ1xuICB8ICduby1saWJyYXJ5J1xuICB8ICdhbWJpZ3VvdXMnXG4gIHwgJ25vLXNoZWx2ZXMnO1xuXG5pbnRlcmZhY2UgQXVkaXRSb3cge1xuICBmaXh0dXJlSWQ6IHN0cmluZztcbiAgZml4dHVyZU51bWJlcjogbnVtYmVyIHwgbnVsbDtcbiAgaGVhZGVyOiBzdHJpbmc7XG4gIGNhdGVnb3J5OiBzdHJpbmc7XG4gIHdpZHRoOiBudW1iZXIgfCBzdHJpbmc7XG4gIGN1clNoZWxmQ291bnQ6IG51bWJlcjtcbiAgbGliU2hlbGZDb3VudDogbnVtYmVyIHwgbnVsbDtcbiAgY3VycmVudFpvbmVzOiBzdHJpbmc7XG4gIGxpYlpvbmVzOiBzdHJpbmc7XG4gIHJlYXNvbjogUmVhc29uO1xuICBmaXhhYmxlOiBib29sZWFuO1xuICBzZWxlY3RlZDogYm9vbGVhbjtcbn1cblxuaW50ZXJmYWNlIEFwcGx5UmVzdWx0IHtcbiAgZml4dHVyZUlkOiBzdHJpbmc7XG4gIGZpeHR1cmVOdW1iZXI6IG51bWJlciB8IG51bGw7XG4gIHN0YXR1czogJ2ZpeGVkJyB8ICdhbHJlYWR5LWNvbmZvcm0nIHwgJ3NraXBwZWQnIHwgJ2ZhaWxlZCc7XG4gIHNoZWx2ZXNGcm9tPzogbnVtYmVyO1xuICBzaGVsdmVzVG8/OiBudW1iZXI7XG4gIGNvbXBsaWFuY2VTeW5jZWQ/OiBudW1iZXI7XG4gIHJlYXNvbj86IHN0cmluZztcbiAgZXJyb3I/OiBzdHJpbmc7XG59XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2xpYi1maXh0dXJlLWNvbmZvcm1hbmNlJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2ZpeHR1cmUtY29uZm9ybWFuY2UuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybDogJy4vZml4dHVyZS1jb25mb3JtYW5jZS5jb21wb25lbnQuc2NzcycsXG59KVxuZXhwb3J0IGNsYXNzIEZpeHR1cmVDb25mb3JtYW5jZUNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgY2xpZW50SWQ6IHN0cmluZyA9ICcnO1xuXG4gIHN0b3JlT3B0aW9uczogU3RvcmVPcHRpb25bXSA9IFtdO1xuICBzZWxlY3RlZFN0b3JlTmFtZTogc3RyaW5nID0gJyc7XG5cbiAgZml4dHVyZXM6IENvbmZvcm1hbmNlRml4dHVyZVtdID0gW107XG4gIGlzTG9hZGluZ0ZpeHR1cmVzID0gZmFsc2U7XG5cbiAgYXVkaXRSb3dzOiBBdWRpdFJvd1tdID0gW107XG4gIGhhc0F1ZGl0ZWQgPSBmYWxzZTtcbiAgaXNBdWRpdGluZyA9IGZhbHNlO1xuXG4gIGFwcGx5UmVzdWx0czogQXBwbHlSZXN1bHRbXSA9IFtdO1xuICBpc0FwcGx5aW5nID0gZmFsc2U7XG5cbiAgbWVzc2FnZSA9ICcnO1xuICBtZXNzYWdlVHlwZTogJ3N1Y2Nlc3MnIHwgJ2Vycm9yJyA9ICdzdWNjZXNzJztcblxuICBwcml2YXRlIHJlYWRvbmx5IGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHNiczogU3RvcmVCdWlsZGVyU2VydmljZSxcbiAgICBwcml2YXRlIGdzOiBHbG9iYWxTdGF0ZVNlcnZpY2UsXG4gICkge31cblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLmdzLmRhdGFSYW5nZVZhbHVlXG4gICAgICAucGlwZShcbiAgICAgICAgdGFrZVVudGlsKHRoaXMuZGVzdHJveSQpLFxuICAgICAgICBkZWJvdW5jZVRpbWUoMjAwKSxcbiAgICAgICAgZmlsdGVyKChkYXRhKSA9PiAhIWRhdGEpLFxuICAgICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpLFxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZSgoZGF0YTogYW55KSA9PiB7XG4gICAgICAgIGlmIChkYXRhPy5jbGllbnQpIHtcbiAgICAgICAgICB0aGlzLmNsaWVudElkID0gZGF0YS5jbGllbnQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc3QgaGVhZGVyRGF0YSA9IEpTT04ucGFyc2UoXG4gICAgICAgICAgICBsb2NhbFN0b3JhZ2UuZ2V0SXRlbSgnaGVhZGVyLWZpbHRlcnMnKSBhcyBzdHJpbmcsXG4gICAgICAgICAgKTtcbiAgICAgICAgICB0aGlzLmNsaWVudElkID0gaGVhZGVyRGF0YT8uY2xpZW50O1xuICAgICAgICB9XG4gICAgICAgIHRoaXMubG9hZFN0b3JlcygpO1xuICAgICAgfSk7XG4gIH1cblxuICBsb2FkU3RvcmVzKCkge1xuICAgIHRoaXMuc2JzLmdldFN0b3JlT3JDbHVzdGVyTGlzdCh7IHNlYXJjaE1vZGU6ICdzdG9yZScgfSkuc3Vic2NyaWJlKChyZXM6IGFueSkgPT4ge1xuICAgICAgaWYgKHJlcz8uZGF0YSkge1xuICAgICAgICB0aGlzLnN0b3JlT3B0aW9ucyA9IHJlcy5kYXRhO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgb25TdG9yZXNTZWxlY3RlZChzdG9yZXM6IFN0b3JlT3B0aW9uW10pIHtcbiAgICBjb25zdCBzdG9yZSA9IHN0b3Jlc1swXTtcbiAgICBjb25zdCBuZXdOYW1lID0gc3RvcmUgPyBzdG9yZS5sYWJlbCA6ICcnO1xuICAgIGlmIChuZXdOYW1lID09PSB0aGlzLnNlbGVjdGVkU3RvcmVOYW1lKSByZXR1cm47XG5cbiAgICB0aGlzLnNlbGVjdGVkU3RvcmVOYW1lID0gbmV3TmFtZTtcbiAgICB0aGlzLnJlc2V0QXVkaXQoKTtcbiAgICB0aGlzLmZpeHR1cmVzID0gW107XG4gICAgaWYgKHRoaXMuc2VsZWN0ZWRTdG9yZU5hbWUpIHtcbiAgICAgIHRoaXMubG9hZEZpeHR1cmVzKCk7XG4gICAgfVxuICB9XG5cbiAgbG9hZEZpeHR1cmVzKCkge1xuICAgIGlmICghdGhpcy5zZWxlY3RlZFN0b3JlTmFtZSkgcmV0dXJuO1xuICAgIHRoaXMuaXNMb2FkaW5nRml4dHVyZXMgPSB0cnVlO1xuICAgIHRoaXMuc2JzLmxpc3RGaXh0dXJlc0ZvckNvbmZvcm1hbmNlKHRoaXMuc2VsZWN0ZWRTdG9yZU5hbWUsIHRoaXMuY2xpZW50SWQpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAocmVzOiBhbnkpID0+IHtcbiAgICAgICAgdGhpcy5maXh0dXJlcyA9IChyZXM/LmRhdGEgfHwgW10pLm1hcCgoZjogYW55KSA9PiAoe1xuICAgICAgICAgIC4uLmYsXG4gICAgICAgICAgc2VsZWN0ZWQ6IGZhbHNlLFxuICAgICAgICB9KSk7XG4gICAgICAgIHRoaXMuaXNMb2FkaW5nRml4dHVyZXMgPSBmYWxzZTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogKCkgPT4ge1xuICAgICAgICB0aGlzLmZpeHR1cmVzID0gW107XG4gICAgICAgIHRoaXMuaXNMb2FkaW5nRml4dHVyZXMgPSBmYWxzZTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICAvLyAtLS0tIEZpeHR1cmUgc2VsZWN0aW9uIC0tLS1cbiAgZ2V0IHNlbGVjdGVkRml4dHVyZUlkcygpOiBzdHJpbmdbXSB7XG4gICAgcmV0dXJuIHRoaXMuZml4dHVyZXMuZmlsdGVyKChmKSA9PiBmLnNlbGVjdGVkKS5tYXAoKGYpID0+IGYuX2lkKTtcbiAgfVxuXG4gIGdldCBhbGxGaXh0dXJlc1NlbGVjdGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmZpeHR1cmVzLmxlbmd0aCA+IDAgJiYgdGhpcy5maXh0dXJlcy5ldmVyeSgoZikgPT4gZi5zZWxlY3RlZCk7XG4gIH1cblxuICB0b2dnbGVBbGxGaXh0dXJlcyhjaGVja2VkOiBib29sZWFuKSB7XG4gICAgdGhpcy5maXh0dXJlcy5mb3JFYWNoKChmKSA9PiAoZi5zZWxlY3RlZCA9IGNoZWNrZWQpKTtcbiAgfVxuXG4gIC8vIC0tLS0gQXVkaXQgLS0tLVxuICBvbkF1ZGl0KCkge1xuICAgIGNvbnN0IGlkcyA9IHRoaXMuc2VsZWN0ZWRGaXh0dXJlSWRzO1xuICAgIGlmICghaWRzLmxlbmd0aCB8fCB0aGlzLmlzQXVkaXRpbmcpIHJldHVybjtcblxuICAgIHRoaXMuaXNBdWRpdGluZyA9IHRydWU7XG4gICAgdGhpcy5yZXNldEF1ZGl0KCk7XG4gICAgdGhpcy5zYnMuYXVkaXRGaXh0dXJlQ29uZm9ybWFuY2UoeyBjbGllbnRJZDogdGhpcy5jbGllbnRJZCwgZml4dHVyZUlkczogaWRzIH0pLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAocmVzOiBhbnkpID0+IHtcbiAgICAgICAgY29uc3Qgcm93czogQXVkaXRSb3dbXSA9IChyZXM/LmRhdGE/LnJvd3MgfHwgW10pLm1hcCgocjogYW55KSA9PiAoe1xuICAgICAgICAgIC4uLnIsXG4gICAgICAgICAgLy8gUHJlLXNlbGVjdCB0aGUgZml4YWJsZSByb3dzIHNvIGFwcGx5IHRhcmdldHMgdGhlbSBieSBkZWZhdWx0LlxuICAgICAgICAgIHNlbGVjdGVkOiAhIXIuZml4YWJsZSxcbiAgICAgICAgfSkpO1xuICAgICAgICB0aGlzLmF1ZGl0Um93cyA9IHJvd3M7XG4gICAgICAgIHRoaXMuaGFzQXVkaXRlZCA9IHRydWU7XG4gICAgICAgIHRoaXMuaXNBdWRpdGluZyA9IGZhbHNlO1xuICAgICAgfSxcbiAgICAgIGVycm9yOiAoZXJyOiBhbnkpID0+IHtcbiAgICAgICAgdGhpcy5pc0F1ZGl0aW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc2hvd01lc3NhZ2UoZXJyPy5lcnJvcj8ubWVzc2FnZSB8fCAnRmFpbGVkIHRvIGF1ZGl0IGZpeHR1cmVzLicsICdlcnJvcicpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHJlc2V0QXVkaXQoKSB7XG4gICAgdGhpcy5hdWRpdFJvd3MgPSBbXTtcbiAgICB0aGlzLmFwcGx5UmVzdWx0cyA9IFtdO1xuICAgIHRoaXMuaGFzQXVkaXRlZCA9IGZhbHNlO1xuICB9XG5cbiAgLy8gLS0tLSBBdWRpdC1yb3cgc2VsZWN0aW9uIChvbmx5IGZpeGFibGUgcm93cyBhcmUgc2VsZWN0YWJsZSkgLS0tLVxuICBnZXQgc2VsZWN0ZWRBdWRpdElkcygpOiBzdHJpbmdbXSB7XG4gICAgcmV0dXJuIHRoaXMuYXVkaXRSb3dzLmZpbHRlcigocikgPT4gci5maXhhYmxlICYmIHIuc2VsZWN0ZWQpLm1hcCgocikgPT4gci5maXh0dXJlSWQpO1xuICB9XG5cbiAgZ2V0IGZpeGFibGVSb3dzKCk6IEF1ZGl0Um93W10ge1xuICAgIHJldHVybiB0aGlzLmF1ZGl0Um93cy5maWx0ZXIoKHIpID0+IHIuZml4YWJsZSk7XG4gIH1cblxuICBnZXQgYWxsRml4YWJsZVNlbGVjdGVkKCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGZpeGFibGUgPSB0aGlzLmZpeGFibGVSb3dzO1xuICAgIHJldHVybiBmaXhhYmxlLmxlbmd0aCA+IDAgJiYgZml4YWJsZS5ldmVyeSgocikgPT4gci5zZWxlY3RlZCk7XG4gIH1cblxuICB0b2dnbGVBbGxGaXhhYmxlKGNoZWNrZWQ6IGJvb2xlYW4pIHtcbiAgICB0aGlzLmF1ZGl0Um93cy5mb3JFYWNoKChyKSA9PiB7XG4gICAgICBpZiAoci5maXhhYmxlKSByLnNlbGVjdGVkID0gY2hlY2tlZDtcbiAgICB9KTtcbiAgfVxuXG4gIC8vIC0tLS0gQXBwbHkgLS0tLVxuICBvbkFwcGx5Rml4KCkge1xuICAgIGNvbnN0IGlkcyA9IHRoaXMuc2VsZWN0ZWRBdWRpdElkcztcbiAgICBpZiAoIWlkcy5sZW5ndGggfHwgdGhpcy5pc0FwcGx5aW5nKSByZXR1cm47XG5cbiAgICB0aGlzLmlzQXBwbHlpbmcgPSB0cnVlO1xuICAgIHRoaXMuc2JzXG4gICAgICAuYXBwbHlGaXh0dXJlQ29uZm9ybWFuY2UoeyBjbGllbnRJZDogdGhpcy5jbGllbnRJZCwgZml4dHVyZUlkczogaWRzLCBzeW5jQ29tcGxpYW5jZTogdHJ1ZSB9KVxuICAgICAgLnN1YnNjcmliZSh7XG4gICAgICAgIG5leHQ6IChyZXM6IGFueSkgPT4ge1xuICAgICAgICAgIHRoaXMuYXBwbHlSZXN1bHRzID0gcmVzPy5kYXRhPy5yZXN1bHRzIHx8IFtdO1xuICAgICAgICAgIGNvbnN0IHMgPSByZXM/LmRhdGE/LnN1bW1hcnkgfHwge307XG4gICAgICAgICAgdGhpcy5zaG93TWVzc2FnZShcbiAgICAgICAgICAgIGBBcHBsaWVkOiAke3MuZml4dHVyZXNDaGFuZ2VkID8/IDB9IGZpeHR1cmUocykgdXBkYXRlZCwgJHtzLmNvbXBsaWFuY2VTeW5jZWQgPz8gMH0gY29tcGxpYW5jZSByb3cocykgc3luY2VkJHtzLnNraXBwZWQgPyBgLCAke3Muc2tpcHBlZH0gc2tpcHBlZGAgOiAnJ30uYCxcbiAgICAgICAgICAgICdzdWNjZXNzJyxcbiAgICAgICAgICApO1xuICAgICAgICAgIHRoaXMuaXNBcHBseWluZyA9IGZhbHNlO1xuICAgICAgICAgIC8vIFJlLWF1ZGl0IHNvIHRoZSB0YWJsZSByZWZsZWN0cyB0aGUgbmV3IChjb25mb3JtZWQpIHN0YXRlLlxuICAgICAgICAgIHRoaXMub25BdWRpdCgpO1xuICAgICAgICB9LFxuICAgICAgICBlcnJvcjogKGVycjogYW55KSA9PiB7XG4gICAgICAgICAgdGhpcy5pc0FwcGx5aW5nID0gZmFsc2U7XG4gICAgICAgICAgdGhpcy5zaG93TWVzc2FnZShlcnI/LmVycm9yPy5tZXNzYWdlIHx8ICdGYWlsZWQgdG8gYXBwbHkgZml4ZXMuJywgJ2Vycm9yJyk7XG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgfVxuXG4gIHJlYXNvbkxhYmVsKHJlYXNvbjogUmVhc29uKTogc3RyaW5nIHtcbiAgICBzd2l0Y2ggKHJlYXNvbikge1xuICAgICAgY2FzZSAnb2snOiByZXR1cm4gJ0NvbmZvcm1zJztcbiAgICAgIGNhc2UgJ3NoZWxmLWNvdW50LW1pc21hdGNoJzogcmV0dXJuICdTaGVsZiBjb3VudCBtaXNtYXRjaCc7XG4gICAgICBjYXNlICd6b25lLW1pc21hdGNoJzogcmV0dXJuICdab25lIG1pc21hdGNoJztcbiAgICAgIGNhc2UgJ25vLWxpYnJhcnknOiByZXR1cm4gJ05vIGxpYnJhcnkgbWF0Y2gnO1xuICAgICAgY2FzZSAnYW1iaWd1b3VzJzogcmV0dXJuICdBbWJpZ3VvdXMgbGlicmFyeSc7XG4gICAgICBjYXNlICduby1zaGVsdmVzJzogcmV0dXJuICdObyBzaGVsdmVzJztcbiAgICAgIGRlZmF1bHQ6IHJldHVybiByZWFzb247XG4gICAgfVxuICB9XG5cbiAgcmVhc29uQ2xhc3MocmVhc29uOiBSZWFzb24pOiBzdHJpbmcge1xuICAgIHN3aXRjaCAocmVhc29uKSB7XG4gICAgICBjYXNlICdvayc6IHJldHVybiAnYmFkZ2Utb2snO1xuICAgICAgY2FzZSAnc2hlbGYtY291bnQtbWlzbWF0Y2gnOlxuICAgICAgY2FzZSAnem9uZS1taXNtYXRjaCc6IHJldHVybiAnYmFkZ2Utd2Fybic7XG4gICAgICBjYXNlICduby1saWJyYXJ5JzpcbiAgICAgIGNhc2UgJ2FtYmlndW91cyc6XG4gICAgICBjYXNlICduby1zaGVsdmVzJzogcmV0dXJuICdiYWRnZS1tdXRlZCc7XG4gICAgICBkZWZhdWx0OiByZXR1cm4gJ2JhZGdlLW11dGVkJztcbiAgICB9XG4gIH1cblxuICBzaG93TWVzc2FnZShtc2c6IHN0cmluZywgdHlwZTogJ3N1Y2Nlc3MnIHwgJ2Vycm9yJykge1xuICAgIHRoaXMubWVzc2FnZSA9IG1zZztcbiAgICB0aGlzLm1lc3NhZ2VUeXBlID0gdHlwZTtcbiAgICBzZXRUaW1lb3V0KCgpID0+ICh0aGlzLm1lc3NhZ2UgPSAnJyksIDYwMDApO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XG4gICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICB9XG59XG4iLCI8c2VjdGlvbiBjbGFzcz1cImZpeHR1cmUtY29uZm9ybWFuY2UgcC0zXCI+XG4gIDxsaWItdG9vbC1wYWdlLWhlYWRlclxuICAgIHRpdGxlPVwiU3luYyBGaXh0dXJlcyB0byBMaWJyYXJ5XCJcbiAgICBkZXNjcmlwdGlvbj1cIkNoZWNrIGEgc3RvcmUncyBmaXh0dXJlcyBhZ2FpbnN0IHRoZSBsaWJyYXJ5LCB0aGVuIHN5bmMgc2hlbGYgY291bnQgYW5kIHpvbmVzIGZvciB0aGUgb25lcyB0aGF0IGRvbid0IG1hdGNoLlwiXG4gID48L2xpYi10b29sLXBhZ2UtaGVhZGVyPlxuXG4gIDxkaXYgY2xhc3M9XCJjYXJkIHAtNFwiPlxuICAgIDwhLS0gU3RvcmUgU2VsZWN0IC0tPlxuICAgIDxkaXYgY2xhc3M9XCJtYi0zXCIgc3R5bGU9XCJtYXgtd2lkdGg6IDYwMHB4O1wiPlxuICAgICAgPGxhYmVsIGNsYXNzPVwiZm9ybS1sYWJlbFwiPlN0b3JlPC9sYWJlbD5cbiAgICAgIDxsaWItc3RvcmUtc2VsZWN0XG4gICAgICAgIFtvcHRpb25zXT1cInN0b3JlT3B0aW9uc1wiXG4gICAgICAgIFtzaW5nbGVTZWxlY3RdPVwidHJ1ZVwiXG4gICAgICAgIChzZWxlY3RlZENoYW5nZSk9XCJvblN0b3Jlc1NlbGVjdGVkKCRldmVudClcIj5cbiAgICAgIDwvbGliLXN0b3JlLXNlbGVjdD5cbiAgICA8L2Rpdj5cblxuICAgIDwhLS0gRml4dHVyZSBMaXN0IC0tPlxuICAgIEBpZiAoc2VsZWN0ZWRTdG9yZU5hbWUpIHtcbiAgICAgIEBpZiAoaXNMb2FkaW5nRml4dHVyZXMpIHtcbiAgICAgICAgPGRpdiBjbGFzcz1cInRleHQtbXV0ZWRcIj5Mb2FkaW5nIGZpeHR1cmVzLi4uPC9kaXY+XG4gICAgICB9IEBlbHNlIGlmIChmaXh0dXJlcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgPGRpdiBjbGFzcz1cInRleHQtbXV0ZWRcIj5ObyBmaXh0dXJlcyBmb3VuZCBmb3IgdGhpcyBzdG9yZS48L2Rpdj5cbiAgICAgIH0gQGVsc2Uge1xuICAgICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNvbnRlbnQtYmV0d2VlbiBtYi0yXCI+XG4gICAgICAgICAgPGg2IGNsYXNzPVwic2VjdGlvbi1oZWFkaW5nIG1iLTBcIj5cbiAgICAgICAgICAgIEZpeHR1cmVzXG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtbXV0ZWQgc21hbGxcIj4oe3sgc2VsZWN0ZWRGaXh0dXJlSWRzLmxlbmd0aCB9fSBvZiB7eyBmaXh0dXJlcy5sZW5ndGggfX0gc2VsZWN0ZWQpPC9zcGFuPlxuICAgICAgICAgIDwvaDY+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLXByaW1hcnkgYnRuLXNtXCJcbiAgICAgICAgICAgIChjbGljayk9XCJvbkF1ZGl0KClcIlxuICAgICAgICAgICAgW2Rpc2FibGVkXT1cInNlbGVjdGVkRml4dHVyZUlkcy5sZW5ndGggPT09IDAgfHwgaXNBdWRpdGluZ1wiPlxuICAgICAgICAgICAge3sgaXNBdWRpdGluZyA/ICdBdWRpdGluZy4uLicgOiAnQXVkaXQgU2VsZWN0ZWQnIH19XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxkaXYgY2xhc3M9XCJ0YWJsZS1yZXNwb25zaXZlIG1iLTJcIj5cbiAgICAgICAgICA8dGFibGUgY2xhc3M9XCJ0YWJsZSB0YWJsZS1zbSBhbGlnbi1taWRkbGUgZmMtdGFibGVcIj5cbiAgICAgICAgICAgIDx0aGVhZD5cbiAgICAgICAgICAgICAgPHRyPlxuICAgICAgICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOiA0MnB4O1wiPlxuICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICAgICAgICAgIFtjaGVja2VkXT1cImFsbEZpeHR1cmVzU2VsZWN0ZWRcIlxuICAgICAgICAgICAgICAgICAgICAoY2hhbmdlKT1cInRvZ2dsZUFsbEZpeHR1cmVzKCRhbnkoJGV2ZW50LnRhcmdldCkuY2hlY2tlZClcIj5cbiAgICAgICAgICAgICAgICA8L3RoPlxuICAgICAgICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOiA5MHB4O1wiPkZpeHR1cmUgIzwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkhlYWRlcjwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkZpeHR1cmUgSUQ8L3RoPlxuICAgICAgICAgICAgICAgIDx0aD5DYXRlZ29yeTwvdGg+XG4gICAgICAgICAgICAgICAgPHRoIHN0eWxlPVwid2lkdGg6IDkwcHg7XCI+V2lkdGg8L3RoPlxuICAgICAgICAgICAgICA8L3RyPlxuICAgICAgICAgICAgPC90aGVhZD5cbiAgICAgICAgICAgIDx0Ym9keT5cbiAgICAgICAgICAgICAgQGZvciAoZiBvZiBmaXh0dXJlczsgdHJhY2sgZi5faWQpIHtcbiAgICAgICAgICAgICAgICA8dHIgKGNsaWNrKT1cImYuc2VsZWN0ZWQgPSAhZi5zZWxlY3RlZFwiIGNsYXNzPVwic2VsZWN0YWJsZS1yb3dcIj5cbiAgICAgICAgICAgICAgICAgIDx0ZCAoY2xpY2spPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCI+XG4gICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiY2hlY2tib3hcIiBbKG5nTW9kZWwpXT1cImYuc2VsZWN0ZWRcIj5cbiAgICAgICAgICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+e3sgZi5maXh0dXJlTnVtYmVyID8/ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZD57eyBmLmhlYWRlckxhYmVsIHx8ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cImZpeHR1cmUtaWRcIj57eyBmLl9pZCB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+e3sgZi5maXh0dXJlQ2F0ZWdvcnkgfHwgJ+KAlCcgfX08L3RkPlxuICAgICAgICAgICAgICAgICAgPHRkPnt7IGYud2lkdGggfHwgJ+KAlCcgfX08L3RkPlxuICAgICAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvdGJvZHk+XG4gICAgICAgICAgPC90YWJsZT5cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgfVxuXG4gICAgPCEtLSBBdWRpdCBSZXN1bHRzIC0tPlxuICAgIEBpZiAoaGFzQXVkaXRlZCkge1xuICAgICAgPGhyIGNsYXNzPVwibXktNFwiIC8+XG4gICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNvbnRlbnQtYmV0d2VlbiBtYi0yXCI+XG4gICAgICAgIDxoNiBjbGFzcz1cInNlY3Rpb24taGVhZGluZyBtYi0wXCI+XG4gICAgICAgICAgQXVkaXQgUmVzdWx0XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkIHNtYWxsXCI+KHt7IGZpeGFibGVSb3dzLmxlbmd0aCB9fSBmaXhhYmxlLCB7eyBzZWxlY3RlZEF1ZGl0SWRzLmxlbmd0aCB9fSBzZWxlY3RlZCk8L3NwYW4+XG4gICAgICAgIDwvaDY+XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICBjbGFzcz1cImJ0biBidG4tc3VjY2VzcyBidG4tc21cIlxuICAgICAgICAgIChjbGljayk9XCJvbkFwcGx5Rml4KClcIlxuICAgICAgICAgIFtkaXNhYmxlZF09XCJzZWxlY3RlZEF1ZGl0SWRzLmxlbmd0aCA9PT0gMCB8fCBpc0FwcGx5aW5nXCI+XG4gICAgICAgICAge3sgaXNBcHBseWluZyA/ICdBcHBseWluZy4uLicgOiAnQXBwbHkgRml4JyB9fVxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIDwvZGl2PlxuXG4gICAgICBAaWYgKGF1ZGl0Um93cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgPGRpdiBjbGFzcz1cInRleHQtbXV0ZWRcIj5ObyByZXN1bHRzLjwvZGl2PlxuICAgICAgfSBAZWxzZSB7XG4gICAgICAgIDxkaXYgY2xhc3M9XCJ0YWJsZS1yZXNwb25zaXZlXCI+XG4gICAgICAgICAgPHRhYmxlIGNsYXNzPVwidGFibGUgdGFibGUtc20gYWxpZ24tbWlkZGxlIGZjLXRhYmxlXCI+XG4gICAgICAgICAgICA8dGhlYWQ+XG4gICAgICAgICAgICAgIDx0cj5cbiAgICAgICAgICAgICAgICA8dGggc3R5bGU9XCJ3aWR0aDogNDJweDtcIj5cbiAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiY2hlY2tib3hcIlxuICAgICAgICAgICAgICAgICAgICBbY2hlY2tlZF09XCJhbGxGaXhhYmxlU2VsZWN0ZWRcIlxuICAgICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiZml4YWJsZVJvd3MubGVuZ3RoID09PSAwXCJcbiAgICAgICAgICAgICAgICAgICAgKGNoYW5nZSk9XCJ0b2dnbGVBbGxGaXhhYmxlKCRhbnkoJGV2ZW50LnRhcmdldCkuY2hlY2tlZClcIj5cbiAgICAgICAgICAgICAgICA8L3RoPlxuICAgICAgICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOiA5MHB4O1wiPkZpeHR1cmUgIzwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkhlYWRlcjwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkNhdGVnb3J5PC90aD5cbiAgICAgICAgICAgICAgICA8dGggc3R5bGU9XCJ3aWR0aDogNzBweDtcIj5XaWR0aDwvdGg+XG4gICAgICAgICAgICAgICAgPHRoIHN0eWxlPVwid2lkdGg6IDExMHB4O1wiPlNoZWx2ZXM8L3RoPlxuICAgICAgICAgICAgICAgIDx0aD5DdXJyZW50IFpvbmVzPC90aD5cbiAgICAgICAgICAgICAgICA8dGg+TGlicmFyeSBab25lczwvdGg+XG4gICAgICAgICAgICAgICAgPHRoIHN0eWxlPVwid2lkdGg6IDE2MHB4O1wiPlN0YXR1czwvdGg+XG4gICAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgICA8L3RoZWFkPlxuICAgICAgICAgICAgPHRib2R5PlxuICAgICAgICAgICAgICBAZm9yIChyIG9mIGF1ZGl0Um93czsgdHJhY2sgci5maXh0dXJlSWQpIHtcbiAgICAgICAgICAgICAgICA8dHIgW2NsYXNzLnJvdy1maXhhYmxlXT1cInIuZml4YWJsZVwiPlxuICAgICAgICAgICAgICAgICAgPHRkPlxuICAgICAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT1cImNoZWNrYm94XCIgWyhuZ01vZGVsKV09XCJyLnNlbGVjdGVkXCIgW2Rpc2FibGVkXT1cIiFyLmZpeGFibGVcIj5cbiAgICAgICAgICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+e3sgci5maXh0dXJlTnVtYmVyID8/ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZD57eyByLmhlYWRlciB8fCAn4oCUJyB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+e3sgci5jYXRlZ29yeSB8fCAn4oCUJyB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+e3sgci53aWR0aCB8fCAn4oCUJyB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+XG4gICAgICAgICAgICAgICAgICAgIEBpZiAoci5saWJTaGVsZkNvdW50ICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBbY2xhc3MudGV4dC1kYW5nZXJdPVwici5jdXJTaGVsZkNvdW50ICE9PSByLmxpYlNoZWxmQ291bnRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IHIuY3VyU2hlbGZDb3VudCB9fSDihpIge3sgci5saWJTaGVsZkNvdW50IH19XG4gICAgICAgICAgICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICB7eyByLmN1clNoZWxmQ291bnQgfX1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgPC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInpvbmVzXCI+e3sgci5jdXJyZW50Wm9uZXMgfHwgJ+KAlCcgfX08L3RkPlxuICAgICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwiem9uZXNcIj57eyByLmxpYlpvbmVzIHx8ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZD5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJzdGF0dXMtYmFkZ2VcIiBbbmdDbGFzc109XCJyZWFzb25DbGFzcyhyLnJlYXNvbilcIj5cbiAgICAgICAgICAgICAgICAgICAgICB7eyByZWFzb25MYWJlbChyLnJlYXNvbikgfX1cbiAgICAgICAgICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgICAgICAgPC90ZD5cbiAgICAgICAgICAgICAgICA8L3RyPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8L3Rib2R5PlxuICAgICAgICAgIDwvdGFibGU+XG4gICAgICAgIDwvZGl2PlxuICAgICAgfVxuICAgIH1cblxuICAgIDwhLS0gQXBwbHkgc3VtbWFyeSBwZXIgZml4dHVyZSAtLT5cbiAgICBAaWYgKGFwcGx5UmVzdWx0cy5sZW5ndGggPiAwKSB7XG4gICAgICA8aHIgY2xhc3M9XCJteS00XCIgLz5cbiAgICAgIDxoNiBjbGFzcz1cInNlY3Rpb24taGVhZGluZyBtYi0yXCI+QXBwbHkgU3VtbWFyeTwvaDY+XG4gICAgICA8ZGl2IGNsYXNzPVwidGFibGUtcmVzcG9uc2l2ZVwiPlxuICAgICAgICA8dGFibGUgY2xhc3M9XCJ0YWJsZSB0YWJsZS1zbSBhbGlnbi1taWRkbGUgZmMtdGFibGVcIj5cbiAgICAgICAgICA8dGhlYWQ+XG4gICAgICAgICAgICA8dHI+XG4gICAgICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOiA5MHB4O1wiPkZpeHR1cmUgIzwvdGg+XG4gICAgICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOiAxNDBweDtcIj5TdGF0dXM8L3RoPlxuICAgICAgICAgICAgICA8dGg+U2hlbHZlczwvdGg+XG4gICAgICAgICAgICAgIDx0aD5Db21wbGlhbmNlIFN5bmNlZDwvdGg+XG4gICAgICAgICAgICAgIDx0aD5Ob3RlPC90aD5cbiAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgPC90aGVhZD5cbiAgICAgICAgICA8dGJvZHk+XG4gICAgICAgICAgICBAZm9yIChhciBvZiBhcHBseVJlc3VsdHM7IHRyYWNrIGFyLmZpeHR1cmVJZCkge1xuICAgICAgICAgICAgICA8dHI+XG4gICAgICAgICAgICAgICAgPHRkPnt7IGFyLmZpeHR1cmVOdW1iZXIgPz8gJ+KAlCcgfX08L3RkPlxuICAgICAgICAgICAgICAgIDx0ZD5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwic3RhdHVzLWJhZGdlXCJcbiAgICAgICAgICAgICAgICAgICAgW2NsYXNzLmJhZGdlLW9rXT1cImFyLnN0YXR1cyA9PT0gJ2ZpeGVkJyB8fCBhci5zdGF0dXMgPT09ICdhbHJlYWR5LWNvbmZvcm0nXCJcbiAgICAgICAgICAgICAgICAgICAgW2NsYXNzLmJhZGdlLXdhcm5dPVwiYXIuc3RhdHVzID09PSAnc2tpcHBlZCdcIlxuICAgICAgICAgICAgICAgICAgICBbY2xhc3MuYmFkZ2UtZGFuZ2VyXT1cImFyLnN0YXR1cyA9PT0gJ2ZhaWxlZCdcIj5cbiAgICAgICAgICAgICAgICAgICAge3sgYXIuc3RhdHVzIH19XG4gICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgPC90ZD5cbiAgICAgICAgICAgICAgICA8dGQ+XG4gICAgICAgICAgICAgICAgICBAaWYgKGFyLnNoZWx2ZXNGcm9tICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAge3sgYXIuc2hlbHZlc0Zyb20gfX0g4oaSIHt7IGFyLnNoZWx2ZXNUbyB9fVxuICAgICAgICAgICAgICAgICAgfSBAZWxzZSB7IOKAlCB9XG4gICAgICAgICAgICAgICAgPC90ZD5cbiAgICAgICAgICAgICAgICA8dGQ+e3sgYXIuY29tcGxpYW5jZVN5bmNlZCA/PyAwIH19PC90ZD5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJ0ZXh0LW11dGVkIHNtYWxsXCI+e3sgYXIuZXJyb3IgfHwgYXIucmVhc29uIHx8ICcnIH19PC90ZD5cbiAgICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L3Rib2R5PlxuICAgICAgICA8L3RhYmxlPlxuICAgICAgPC9kaXY+XG4gICAgfVxuXG4gICAgPCEtLSBNZXNzYWdlIC0tPlxuICAgIEBpZiAobWVzc2FnZSkge1xuICAgICAgPGRpdiBjbGFzcz1cIm10LTMgYWxlcnRcIiBbY2xhc3MuYWxlcnQtc3VjY2Vzc109XCJtZXNzYWdlVHlwZSA9PT0gJ3N1Y2Nlc3MnXCJcbiAgICAgICAgW2NsYXNzLmFsZXJ0LWRhbmdlcl09XCJtZXNzYWdlVHlwZSA9PT0gJ2Vycm9yJ1wiPlxuICAgICAgICB7eyBtZXNzYWdlIH19XG4gICAgICA8L2Rpdj5cbiAgICB9XG4gIDwvZGl2PlxuPC9zZWN0aW9uPlxuIl19
200
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZml4dHVyZS1jb25mb3JtYW5jZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90YW5nby1zdG9yZS1idWlsZGVyL3NyYy9saWIvY29tcG9uZW50cy9wbGFuby10b29scy9maXh0dXJlLWNvbmZvcm1hbmNlL2ZpeHR1cmUtY29uZm9ybWFuY2UuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvdGFuZ28tc3RvcmUtYnVpbGRlci9zcmMvbGliL2NvbXBvbmVudHMvcGxhbm8tdG9vbHMvZml4dHVyZS1jb25mb3JtYW5jZS9maXh0dXJlLWNvbmZvcm1hbmNlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQXFCLE1BQU0sZUFBZSxDQUFDO0FBRzdELE9BQU8sRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxNQUFNLENBQUM7Ozs7Ozs7O0FBdUR0RixNQUFNLE9BQU8sMkJBQTJCO0lBc0I1QjtJQUNBO0lBdEJWLFFBQVEsR0FBVyxFQUFFLENBQUM7SUFFdEIsWUFBWSxHQUFrQixFQUFFLENBQUM7SUFDakMsaUJBQWlCLEdBQVcsRUFBRSxDQUFDO0lBRS9CLFFBQVEsR0FBeUIsRUFBRSxDQUFDO0lBQ3BDLGlCQUFpQixHQUFHLEtBQUssQ0FBQztJQUUxQixTQUFTLEdBQWUsRUFBRSxDQUFDO0lBQzNCLFVBQVUsR0FBRyxLQUFLLENBQUM7SUFDbkIsVUFBVSxHQUFHLEtBQUssQ0FBQztJQUVuQixZQUFZLEdBQWtCLEVBQUUsQ0FBQztJQUNqQyxVQUFVLEdBQUcsS0FBSyxDQUFDO0lBRW5CLE9BQU8sR0FBRyxFQUFFLENBQUM7SUFDYixXQUFXLEdBQXdCLFNBQVMsQ0FBQztJQUU1QixRQUFRLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FB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S0FBSyxlQUFlLENBQUM7WUFDckIsS0FBSyxlQUFlLENBQUMsQ0FBQyxPQUFPLFlBQVksQ0FBQztZQUMxQyxLQUFLLFlBQVksQ0FBQztZQUNsQixLQUFLLFdBQVcsQ0FBQztZQUNqQixLQUFLLFlBQVksQ0FBQyxDQUFDLE9BQU8sYUFBYSxDQUFDO1lBQ3hDLE9BQU8sQ0FBQyxDQUFDLE9BQU8sYUFBYSxDQUFDO1NBQy9CO0lBQ0gsQ0FBQztJQUVELFdBQVcsQ0FBQyxHQUFXLEVBQUUsSUFBeUI7UUFDaEQsSUFBSSxDQUFDLE9BQU8sR0FBRyxHQUFHLENBQUM7UUFDbkIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7UUFDeEIsVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBRUQsV0FBVztRQUNULElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUMzQixDQUFDO3dHQXBOVSwyQkFBMkI7NEZBQTNCLDJCQUEyQiwrREMxRHhDLHV5UEF1TUE7OzRGRDdJYSwyQkFBMkI7a0JBTHZDLFNBQVM7K0JBQ0UseUJBQXlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkluaXQsIE9uRGVzdHJveSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3RvcmVCdWlsZGVyU2VydmljZSB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL3N0b3JlLWJ1aWxkZXIuc2VydmljZSc7XG5pbXBvcnQgeyBHbG9iYWxTdGF0ZVNlcnZpY2UgfSBmcm9tICd0YW5nby1hcHAtdWktZ2xvYmFsJztcbmltcG9ydCB7IFN1YmplY3QsIHRha2VVbnRpbCwgZGVib3VuY2VUaW1lLCBmaWx0ZXIsIGRpc3RpbmN0VW50aWxDaGFuZ2VkIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBTdG9yZU9wdGlvbiB9IGZyb20gJy4uLy4uL2NvbGxlY3Rpb24tdXBkYXRlLWFpL2NvbXBvbmVudHMvc3RvcmUtc2VsZWN0L3N0b3JlLXNlbGVjdC5jb21wb25lbnQnO1xuXG5pbnRlcmZhY2UgQ29uZm9ybWFuY2VGaXh0dXJlIHtcbiAgX2lkOiBzdHJpbmc7XG4gIGZsb29ySWQ6IHN0cmluZztcbiAgZml4dHVyZU51bWJlcjogbnVtYmVyIHwgbnVsbDtcbiAgZml4dHVyZUNhdGVnb3J5OiBzdHJpbmc7XG4gIHdpZHRoOiBudW1iZXIgfCBzdHJpbmc7XG4gIGhlYWRlckxhYmVsOiBzdHJpbmc7XG4gIHNlbGVjdGVkOiBib29sZWFuO1xufVxuXG50eXBlIFJlYXNvbiA9XG4gIHwgJ29rJ1xuICB8ICdzaGVsZi1jb3VudC1taXNtYXRjaCdcbiAgfCAnem9uZS1taXNtYXRjaCdcbiAgfCAndHlwZS1taXNtYXRjaCdcbiAgfCAnbm8tbGlicmFyeSdcbiAgfCAnYW1iaWd1b3VzJ1xuICB8ICduby1zaGVsdmVzJztcblxuaW50ZXJmYWNlIEF1ZGl0Um93IHtcbiAgZml4dHVyZUlkOiBzdHJpbmc7XG4gIGZpeHR1cmVOdW1iZXI6IG51bWJlciB8IG51bGw7XG4gIGhlYWRlcjogc3RyaW5nO1xuICBjYXRlZ29yeTogc3RyaW5nO1xuICB3aWR0aDogbnVtYmVyIHwgc3RyaW5nO1xuICBjdXJTaGVsZkNvdW50OiBudW1iZXI7XG4gIGxpYlNoZWxmQ291bnQ6IG51bWJlciB8IG51bGw7XG4gIGN1cnJlbnRab25lczogc3RyaW5nO1xuICBsaWJab25lczogc3RyaW5nO1xuICBjdXJyZW50VHlwZXM6IHN0cmluZztcbiAgbGliVHlwZXM6IHN0cmluZztcbiAgcmVhc29uOiBSZWFzb247XG4gIGZpeGFibGU6IGJvb2xlYW47XG4gIHNlbGVjdGVkOiBib29sZWFuO1xufVxuXG5pbnRlcmZhY2UgQXBwbHlSZXN1bHQge1xuICBmaXh0dXJlSWQ6IHN0cmluZztcbiAgZml4dHVyZU51bWJlcjogbnVtYmVyIHwgbnVsbDtcbiAgc3RhdHVzOiAnZml4ZWQnIHwgJ2FscmVhZHktY29uZm9ybScgfCAnc2tpcHBlZCcgfCAnZmFpbGVkJztcbiAgc2hlbHZlc0Zyb20/OiBudW1iZXI7XG4gIHNoZWx2ZXNUbz86IG51bWJlcjtcbiAgY29tcGxpYW5jZVN5bmNlZD86IG51bWJlcjtcbiAgcmVhc29uPzogc3RyaW5nO1xuICBlcnJvcj86IHN0cmluZztcbn1cblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbGliLWZpeHR1cmUtY29uZm9ybWFuY2UnLFxuICB0ZW1wbGF0ZVVybDogJy4vZml4dHVyZS1jb25mb3JtYW5jZS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsOiAnLi9maXh0dXJlLWNvbmZvcm1hbmNlLmNvbXBvbmVudC5zY3NzJyxcbn0pXG5leHBvcnQgY2xhc3MgRml4dHVyZUNvbmZvcm1hbmNlQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuICBjbGllbnRJZDogc3RyaW5nID0gJyc7XG5cbiAgc3RvcmVPcHRpb25zOiBTdG9yZU9wdGlvbltdID0gW107XG4gIHNlbGVjdGVkU3RvcmVOYW1lOiBzdHJpbmcgPSAnJztcblxuICBmaXh0dXJlczogQ29uZm9ybWFuY2VGaXh0dXJlW10gPSBbXTtcbiAgaXNMb2FkaW5nRml4dHVyZXMgPSBmYWxzZTtcblxuICBhdWRpdFJvd3M6IEF1ZGl0Um93W10gPSBbXTtcbiAgaGFzQXVkaXRlZCA9IGZhbHNlO1xuICBpc0F1ZGl0aW5nID0gZmFsc2U7XG5cbiAgYXBwbHlSZXN1bHRzOiBBcHBseVJlc3VsdFtdID0gW107XG4gIGlzQXBwbHlpbmcgPSBmYWxzZTtcblxuICBtZXNzYWdlID0gJyc7XG4gIG1lc3NhZ2VUeXBlOiAnc3VjY2VzcycgfCAnZXJyb3InID0gJ3N1Y2Nlc3MnO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgc2JzOiBTdG9yZUJ1aWxkZXJTZXJ2aWNlLFxuICAgIHByaXZhdGUgZ3M6IEdsb2JhbFN0YXRlU2VydmljZSxcbiAgKSB7fVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIHRoaXMuZ3MuZGF0YVJhbmdlVmFsdWVcbiAgICAgIC5waXBlKFxuICAgICAgICB0YWtlVW50aWwodGhpcy5kZXN0cm95JCksXG4gICAgICAgIGRlYm91bmNlVGltZSgyMDApLFxuICAgICAgICBmaWx0ZXIoKGRhdGEpID0+ICEhZGF0YSksXG4gICAgICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKCksXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKChkYXRhOiBhbnkpID0+IHtcbiAgICAgICAgaWYgKGRhdGE/LmNsaWVudCkge1xuICAgICAgICAgIHRoaXMuY2xpZW50SWQgPSBkYXRhLmNsaWVudDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBoZWFkZXJEYXRhID0gSlNPTi5wYXJzZShcbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdoZWFkZXItZmlsdGVycycpIGFzIHN0cmluZyxcbiAgICAgICAgICApO1xuICAgICAgICAgIHRoaXMuY2xpZW50SWQgPSBoZWFkZXJEYXRhPy5jbGllbnQ7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5sb2FkU3RvcmVzKCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIGxvYWRTdG9yZXMoKSB7XG4gICAgdGhpcy5zYnMuZ2V0U3RvcmVPckNsdXN0ZXJMaXN0KHsgc2VhcmNoTW9kZTogJ3N0b3JlJyB9KS5zdWJzY3JpYmUoKHJlczogYW55KSA9PiB7XG4gICAgICBpZiAocmVzPy5kYXRhKSB7XG4gICAgICAgIHRoaXMuc3RvcmVPcHRpb25zID0gcmVzLmRhdGE7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBvblN0b3Jlc1NlbGVjdGVkKHN0b3JlczogU3RvcmVPcHRpb25bXSkge1xuICAgIGNvbnN0IHN0b3JlID0gc3RvcmVzWzBdO1xuICAgIGNvbnN0IG5ld05hbWUgPSBzdG9yZSA/IHN0b3JlLmxhYmVsIDogJyc7XG4gICAgaWYgKG5ld05hbWUgPT09IHRoaXMuc2VsZWN0ZWRTdG9yZU5hbWUpIHJldHVybjtcblxuICAgIHRoaXMuc2VsZWN0ZWRTdG9yZU5hbWUgPSBuZXdOYW1lO1xuICAgIHRoaXMucmVzZXRBdWRpdCgpO1xuICAgIHRoaXMuZml4dHVyZXMgPSBbXTtcbiAgICBpZiAodGhpcy5zZWxlY3RlZFN0b3JlTmFtZSkge1xuICAgICAgdGhpcy5sb2FkRml4dHVyZXMoKTtcbiAgICB9XG4gIH1cblxuICBsb2FkRml4dHVyZXMoKSB7XG4gICAgaWYgKCF0aGlzLnNlbGVjdGVkU3RvcmVOYW1lKSByZXR1cm47XG4gICAgdGhpcy5pc0xvYWRpbmdGaXh0dXJlcyA9IHRydWU7XG4gICAgdGhpcy5zYnMubGlzdEZpeHR1cmVzRm9yQ29uZm9ybWFuY2UodGhpcy5zZWxlY3RlZFN0b3JlTmFtZSwgdGhpcy5jbGllbnRJZCkuc3Vic2NyaWJlKHtcbiAgICAgIG5leHQ6IChyZXM6IGFueSkgPT4ge1xuICAgICAgICB0aGlzLmZpeHR1cmVzID0gKHJlcz8uZGF0YSB8fCBbXSkubWFwKChmOiBhbnkpID0+ICh7XG4gICAgICAgICAgLi4uZixcbiAgICAgICAgICBzZWxlY3RlZDogZmFsc2UsXG4gICAgICAgIH0pKTtcbiAgICAgICAgdGhpcy5pc0xvYWRpbmdGaXh0dXJlcyA9IGZhbHNlO1xuICAgICAgfSxcbiAgICAgIGVycm9yOiAoKSA9PiB7XG4gICAgICAgIHRoaXMuZml4dHVyZXMgPSBbXTtcbiAgICAgICAgdGhpcy5pc0xvYWRpbmdGaXh0dXJlcyA9IGZhbHNlO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIC8vIC0tLS0gRml4dHVyZSBzZWxlY3Rpb24gLS0tLVxuICBnZXQgc2VsZWN0ZWRGaXh0dXJlSWRzKCk6IHN0cmluZ1tdIHtcbiAgICByZXR1cm4gdGhpcy5maXh0dXJlcy5maWx0ZXIoKGYpID0+IGYuc2VsZWN0ZWQpLm1hcCgoZikgPT4gZi5faWQpO1xuICB9XG5cbiAgZ2V0IGFsbEZpeHR1cmVzU2VsZWN0ZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZml4dHVyZXMubGVuZ3RoID4gMCAmJiB0aGlzLmZpeHR1cmVzLmV2ZXJ5KChmKSA9PiBmLnNlbGVjdGVkKTtcbiAgfVxuXG4gIHRvZ2dsZUFsbEZpeHR1cmVzKGNoZWNrZWQ6IGJvb2xlYW4pIHtcbiAgICB0aGlzLmZpeHR1cmVzLmZvckVhY2goKGYpID0+IChmLnNlbGVjdGVkID0gY2hlY2tlZCkpO1xuICB9XG5cbiAgLy8gLS0tLSBBdWRpdCAtLS0tXG4gIG9uQXVkaXQoKSB7XG4gICAgY29uc3QgaWRzID0gdGhpcy5zZWxlY3RlZEZpeHR1cmVJZHM7XG4gICAgaWYgKCFpZHMubGVuZ3RoIHx8IHRoaXMuaXNBdWRpdGluZykgcmV0dXJuO1xuXG4gICAgdGhpcy5pc0F1ZGl0aW5nID0gdHJ1ZTtcbiAgICB0aGlzLnJlc2V0QXVkaXQoKTtcbiAgICB0aGlzLnNicy5hdWRpdEZpeHR1cmVDb25mb3JtYW5jZSh7IGNsaWVudElkOiB0aGlzLmNsaWVudElkLCBmaXh0dXJlSWRzOiBpZHMgfSkuc3Vic2NyaWJlKHtcbiAgICAgIG5leHQ6IChyZXM6IGFueSkgPT4ge1xuICAgICAgICBjb25zdCByb3dzOiBBdWRpdFJvd1tdID0gKHJlcz8uZGF0YT8ucm93cyB8fCBbXSkubWFwKChyOiBhbnkpID0+ICh7XG4gICAgICAgICAgLi4ucixcbiAgICAgICAgICAvLyBQcmUtc2VsZWN0IHRoZSBmaXhhYmxlIHJvd3Mgc28gYXBwbHkgdGFyZ2V0cyB0aGVtIGJ5IGRlZmF1bHQuXG4gICAgICAgICAgc2VsZWN0ZWQ6ICEhci5maXhhYmxlLFxuICAgICAgICB9KSk7XG4gICAgICAgIHRoaXMuYXVkaXRSb3dzID0gcm93cztcbiAgICAgICAgdGhpcy5oYXNBdWRpdGVkID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5pc0F1ZGl0aW5nID0gZmFsc2U7XG4gICAgICB9LFxuICAgICAgZXJyb3I6IChlcnI6IGFueSkgPT4ge1xuICAgICAgICB0aGlzLmlzQXVkaXRpbmcgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5zaG93TWVzc2FnZShlcnI/LmVycm9yPy5tZXNzYWdlIHx8ICdGYWlsZWQgdG8gYXVkaXQgZml4dHVyZXMuJywgJ2Vycm9yJyk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgcmVzZXRBdWRpdCgpIHtcbiAgICB0aGlzLmF1ZGl0Um93cyA9IFtdO1xuICAgIHRoaXMuYXBwbHlSZXN1bHRzID0gW107XG4gICAgdGhpcy5oYXNBdWRpdGVkID0gZmFsc2U7XG4gIH1cblxuICAvLyAtLS0tIEF1ZGl0LXJvdyBzZWxlY3Rpb24gKG9ubHkgZml4YWJsZSByb3dzIGFyZSBzZWxlY3RhYmxlKSAtLS0tXG4gIGdldCBzZWxlY3RlZEF1ZGl0SWRzKCk6IHN0cmluZ1tdIHtcbiAgICByZXR1cm4gdGhpcy5hdWRpdFJvd3MuZmlsdGVyKChyKSA9PiByLmZpeGFibGUgJiYgci5zZWxlY3RlZCkubWFwKChyKSA9PiByLmZpeHR1cmVJZCk7XG4gIH1cblxuICBnZXQgZml4YWJsZVJvd3MoKTogQXVkaXRSb3dbXSB7XG4gICAgcmV0dXJuIHRoaXMuYXVkaXRSb3dzLmZpbHRlcigocikgPT4gci5maXhhYmxlKTtcbiAgfVxuXG4gIGdldCBhbGxGaXhhYmxlU2VsZWN0ZWQoKTogYm9vbGVhbiB7XG4gICAgY29uc3QgZml4YWJsZSA9IHRoaXMuZml4YWJsZVJvd3M7XG4gICAgcmV0dXJuIGZpeGFibGUubGVuZ3RoID4gMCAmJiBmaXhhYmxlLmV2ZXJ5KChyKSA9PiByLnNlbGVjdGVkKTtcbiAgfVxuXG4gIHRvZ2dsZUFsbEZpeGFibGUoY2hlY2tlZDogYm9vbGVhbikge1xuICAgIHRoaXMuYXVkaXRSb3dzLmZvckVhY2goKHIpID0+IHtcbiAgICAgIGlmIChyLmZpeGFibGUpIHIuc2VsZWN0ZWQgPSBjaGVja2VkO1xuICAgIH0pO1xuICB9XG5cbiAgLy8gLS0tLSBBcHBseSAtLS0tXG4gIG9uQXBwbHlGaXgoKSB7XG4gICAgY29uc3QgaWRzID0gdGhpcy5zZWxlY3RlZEF1ZGl0SWRzO1xuICAgIGlmICghaWRzLmxlbmd0aCB8fCB0aGlzLmlzQXBwbHlpbmcpIHJldHVybjtcblxuICAgIHRoaXMuaXNBcHBseWluZyA9IHRydWU7XG4gICAgdGhpcy5zYnNcbiAgICAgIC5hcHBseUZpeHR1cmVDb25mb3JtYW5jZSh7IGNsaWVudElkOiB0aGlzLmNsaWVudElkLCBmaXh0dXJlSWRzOiBpZHMsIHN5bmNDb21wbGlhbmNlOiB0cnVlIH0pXG4gICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKHJlczogYW55KSA9PiB7XG4gICAgICAgICAgdGhpcy5hcHBseVJlc3VsdHMgPSByZXM/LmRhdGE/LnJlc3VsdHMgfHwgW107XG4gICAgICAgICAgY29uc3QgcyA9IHJlcz8uZGF0YT8uc3VtbWFyeSB8fCB7fTtcbiAgICAgICAgICB0aGlzLnNob3dNZXNzYWdlKFxuICAgICAgICAgICAgYEFwcGxpZWQ6ICR7cy5maXh0dXJlc0NoYW5nZWQgPz8gMH0gZml4dHVyZShzKSB1cGRhdGVkLCAke3MuY29tcGxpYW5jZVN5bmNlZCA/PyAwfSBjb21wbGlhbmNlIHJvdyhzKSBzeW5jZWQke3Muc2tpcHBlZCA/IGAsICR7cy5za2lwcGVkfSBza2lwcGVkYCA6ICcnfS5gLFxuICAgICAgICAgICAgJ3N1Y2Nlc3MnLFxuICAgICAgICAgICk7XG4gICAgICAgICAgdGhpcy5pc0FwcGx5aW5nID0gZmFsc2U7XG4gICAgICAgICAgLy8gUmUtYXVkaXQgc28gdGhlIHRhYmxlIHJlZmxlY3RzIHRoZSBuZXcgKGNvbmZvcm1lZCkgc3RhdGUuXG4gICAgICAgICAgdGhpcy5vbkF1ZGl0KCk7XG4gICAgICAgIH0sXG4gICAgICAgIGVycm9yOiAoZXJyOiBhbnkpID0+IHtcbiAgICAgICAgICB0aGlzLmlzQXBwbHlpbmcgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLnNob3dNZXNzYWdlKGVycj8uZXJyb3I/Lm1lc3NhZ2UgfHwgJ0ZhaWxlZCB0byBhcHBseSBmaXhlcy4nLCAnZXJyb3InKTtcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICB9XG5cbiAgcmVhc29uTGFiZWwocmVhc29uOiBSZWFzb24pOiBzdHJpbmcge1xuICAgIHN3aXRjaCAocmVhc29uKSB7XG4gICAgICBjYXNlICdvayc6IHJldHVybiAnQ29uZm9ybXMnO1xuICAgICAgY2FzZSAnc2hlbGYtY291bnQtbWlzbWF0Y2gnOiByZXR1cm4gJ1NoZWxmIGNvdW50IG1pc21hdGNoJztcbiAgICAgIGNhc2UgJ3pvbmUtbWlzbWF0Y2gnOiByZXR1cm4gJ1pvbmUgbWlzbWF0Y2gnO1xuICAgICAgY2FzZSAndHlwZS1taXNtYXRjaCc6IHJldHVybiAnU2hlbGYgdHlwZSBtaXNtYXRjaCc7XG4gICAgICBjYXNlICduby1saWJyYXJ5JzogcmV0dXJuICdObyBsaWJyYXJ5IG1hdGNoJztcbiAgICAgIGNhc2UgJ2FtYmlndW91cyc6IHJldHVybiAnQW1iaWd1b3VzIGxpYnJhcnknO1xuICAgICAgY2FzZSAnbm8tc2hlbHZlcyc6IHJldHVybiAnTm8gc2hlbHZlcyc7XG4gICAgICBkZWZhdWx0OiByZXR1cm4gcmVhc29uO1xuICAgIH1cbiAgfVxuXG4gIHJlYXNvbkNsYXNzKHJlYXNvbjogUmVhc29uKTogc3RyaW5nIHtcbiAgICBzd2l0Y2ggKHJlYXNvbikge1xuICAgICAgY2FzZSAnb2snOiByZXR1cm4gJ2JhZGdlLW9rJztcbiAgICAgIGNhc2UgJ3NoZWxmLWNvdW50LW1pc21hdGNoJzpcbiAgICAgIGNhc2UgJ3pvbmUtbWlzbWF0Y2gnOlxuICAgICAgY2FzZSAndHlwZS1taXNtYXRjaCc6IHJldHVybiAnYmFkZ2Utd2Fybic7XG4gICAgICBjYXNlICduby1saWJyYXJ5JzpcbiAgICAgIGNhc2UgJ2FtYmlndW91cyc6XG4gICAgICBjYXNlICduby1zaGVsdmVzJzogcmV0dXJuICdiYWRnZS1tdXRlZCc7XG4gICAgICBkZWZhdWx0OiByZXR1cm4gJ2JhZGdlLW11dGVkJztcbiAgICB9XG4gIH1cblxuICBzaG93TWVzc2FnZShtc2c6IHN0cmluZywgdHlwZTogJ3N1Y2Nlc3MnIHwgJ2Vycm9yJykge1xuICAgIHRoaXMubWVzc2FnZSA9IG1zZztcbiAgICB0aGlzLm1lc3NhZ2VUeXBlID0gdHlwZTtcbiAgICBzZXRUaW1lb3V0KCgpID0+ICh0aGlzLm1lc3NhZ2UgPSAnJyksIDYwMDApO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XG4gICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICB9XG59XG4iLCI8c2VjdGlvbiBjbGFzcz1cImZpeHR1cmUtY29uZm9ybWFuY2UgcC0zXCI+XG4gIDxsaWItdG9vbC1wYWdlLWhlYWRlclxuICAgIHRpdGxlPVwiU3luYyBGaXh0dXJlcyB0byBMaWJyYXJ5XCJcbiAgICBkZXNjcmlwdGlvbj1cIkNoZWNrIGEgc3RvcmUncyBmaXh0dXJlcyBhZ2FpbnN0IHRoZSBsaWJyYXJ5LCB0aGVuIHN5bmMgc2hlbGYgY291bnQgYW5kIHpvbmVzIGZvciB0aGUgb25lcyB0aGF0IGRvbid0IG1hdGNoLlwiXG4gID48L2xpYi10b29sLXBhZ2UtaGVhZGVyPlxuXG4gIDxkaXYgY2xhc3M9XCJjYXJkIHAtNFwiPlxuICAgIDwhLS0gU3RvcmUgU2VsZWN0IC0tPlxuICAgIDxkaXYgY2xhc3M9XCJtYi0zXCIgc3R5bGU9XCJtYXgtd2lkdGg6IDYwMHB4O1wiPlxuICAgICAgPGxhYmVsIGNsYXNzPVwiZm9ybS1sYWJlbFwiPlN0b3JlPC9sYWJlbD5cbiAgICAgIDxsaWItc3RvcmUtc2VsZWN0XG4gICAgICAgIFtvcHRpb25zXT1cInN0b3JlT3B0aW9uc1wiXG4gICAgICAgIFtzaW5nbGVTZWxlY3RdPVwidHJ1ZVwiXG4gICAgICAgIChzZWxlY3RlZENoYW5nZSk9XCJvblN0b3Jlc1NlbGVjdGVkKCRldmVudClcIj5cbiAgICAgIDwvbGliLXN0b3JlLXNlbGVjdD5cbiAgICA8L2Rpdj5cblxuICAgIDwhLS0gRml4dHVyZSBMaXN0IC0tPlxuICAgIEBpZiAoc2VsZWN0ZWRTdG9yZU5hbWUpIHtcbiAgICAgIEBpZiAoaXNMb2FkaW5nRml4dHVyZXMpIHtcbiAgICAgICAgPGRpdiBjbGFzcz1cInRleHQtbXV0ZWRcIj5Mb2FkaW5nIGZpeHR1cmVzLi4uPC9kaXY+XG4gICAgICB9IEBlbHNlIGlmIChmaXh0dXJlcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgPGRpdiBjbGFzcz1cInRleHQtbXV0ZWRcIj5ObyBmaXh0dXJlcyBmb3VuZCBmb3IgdGhpcyBzdG9yZS48L2Rpdj5cbiAgICAgIH0gQGVsc2Uge1xuICAgICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNvbnRlbnQtYmV0d2VlbiBtYi0yXCI+XG4gICAgICAgICAgPGg2IGNsYXNzPVwic2VjdGlvbi1oZWFkaW5nIG1iLTBcIj5cbiAgICAgICAgICAgIEZpeHR1cmVzXG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtbXV0ZWQgc21hbGxcIj4oe3sgc2VsZWN0ZWRGaXh0dXJlSWRzLmxlbmd0aCB9fSBvZiB7eyBmaXh0dXJlcy5sZW5ndGggfX0gc2VsZWN0ZWQpPC9zcGFuPlxuICAgICAgICAgIDwvaDY+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLXByaW1hcnkgYnRuLXNtXCJcbiAgICAgICAgICAgIChjbGljayk9XCJvbkF1ZGl0KClcIlxuICAgICAgICAgICAgW2Rpc2FibGVkXT1cInNlbGVjdGVkRml4dHVyZUlkcy5sZW5ndGggPT09IDAgfHwgaXNBdWRpdGluZ1wiPlxuICAgICAgICAgICAge3sgaXNBdWRpdGluZyA/ICdBdWRpdGluZy4uLicgOiAnQXVkaXQgU2VsZWN0ZWQnIH19XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxkaXYgY2xhc3M9XCJ0YWJsZS1yZXNwb25zaXZlIG1iLTJcIj5cbiAgICAgICAgICA8dGFibGUgY2xhc3M9XCJ0YWJsZSB0YWJsZS1zbSBhbGlnbi1taWRkbGUgZmMtdGFibGVcIj5cbiAgICAgICAgICAgIDx0aGVhZD5cbiAgICAgICAgICAgICAgPHRyPlxuICAgICAgICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOiA0MnB4O1wiPlxuICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICAgICAgICAgIFtjaGVja2VkXT1cImFsbEZpeHR1cmVzU2VsZWN0ZWRcIlxuICAgICAgICAgICAgICAgICAgICAoY2hhbmdlKT1cInRvZ2dsZUFsbEZpeHR1cmVzKCRhbnkoJGV2ZW50LnRhcmdldCkuY2hlY2tlZClcIj5cbiAgICAgICAgICAgICAgICA8L3RoPlxuICAgICAgICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOiA5MHB4O1wiPkZpeHR1cmUgIzwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkhlYWRlcjwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkZpeHR1cmUgSUQ8L3RoPlxuICAgICAgICAgICAgICAgIDx0aD5DYXRlZ29yeTwvdGg+XG4gICAgICAgICAgICAgICAgPHRoIHN0eWxlPVwid2lkdGg6IDkwcHg7XCI+V2lkdGg8L3RoPlxuICAgICAgICAgICAgICA8L3RyPlxuICAgICAgICAgICAgPC90aGVhZD5cbiAgICAgICAgICAgIDx0Ym9keT5cbiAgICAgICAgICAgICAgQGZvciAoZiBvZiBmaXh0dXJlczsgdHJhY2sgZi5faWQpIHtcbiAgICAgICAgICAgICAgICA8dHIgKGNsaWNrKT1cImYuc2VsZWN0ZWQgPSAhZi5zZWxlY3RlZFwiIGNsYXNzPVwic2VsZWN0YWJsZS1yb3dcIj5cbiAgICAgICAgICAgICAgICAgIDx0ZCAoY2xpY2spPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCI+XG4gICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiY2hlY2tib3hcIiBbKG5nTW9kZWwpXT1cImYuc2VsZWN0ZWRcIj5cbiAgICAgICAgICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+e3sgZi5maXh0dXJlTnVtYmVyID8/ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZD57eyBmLmhlYWRlckxhYmVsIHx8ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cImZpeHR1cmUtaWRcIj57eyBmLl9pZCB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+e3sgZi5maXh0dXJlQ2F0ZWdvcnkgfHwgJ+KAlCcgfX08L3RkPlxuICAgICAgICAgICAgICAgICAgPHRkPnt7IGYud2lkdGggfHwgJ+KAlCcgfX08L3RkPlxuICAgICAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvdGJvZHk+XG4gICAgICAgICAgPC90YWJsZT5cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgfVxuXG4gICAgPCEtLSBBdWRpdCBSZXN1bHRzIC0tPlxuICAgIEBpZiAoaGFzQXVkaXRlZCkge1xuICAgICAgPGhyIGNsYXNzPVwibXktNFwiIC8+XG4gICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNvbnRlbnQtYmV0d2VlbiBtYi0yXCI+XG4gICAgICAgIDxoNiBjbGFzcz1cInNlY3Rpb24taGVhZGluZyBtYi0wXCI+XG4gICAgICAgICAgQXVkaXQgUmVzdWx0XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LW11dGVkIHNtYWxsXCI+KHt7IGZpeGFibGVSb3dzLmxlbmd0aCB9fSBmaXhhYmxlLCB7eyBzZWxlY3RlZEF1ZGl0SWRzLmxlbmd0aCB9fSBzZWxlY3RlZCk8L3NwYW4+XG4gICAgICAgIDwvaDY+XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICBjbGFzcz1cImJ0biBidG4tc3VjY2VzcyBidG4tc21cIlxuICAgICAgICAgIChjbGljayk9XCJvbkFwcGx5Rml4KClcIlxuICAgICAgICAgIFtkaXNhYmxlZF09XCJzZWxlY3RlZEF1ZGl0SWRzLmxlbmd0aCA9PT0gMCB8fCBpc0FwcGx5aW5nXCI+XG4gICAgICAgICAge3sgaXNBcHBseWluZyA/ICdBcHBseWluZy4uLicgOiAnQXBwbHkgRml4JyB9fVxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIDwvZGl2PlxuXG4gICAgICBAaWYgKGF1ZGl0Um93cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgPGRpdiBjbGFzcz1cInRleHQtbXV0ZWRcIj5ObyByZXN1bHRzLjwvZGl2PlxuICAgICAgfSBAZWxzZSB7XG4gICAgICAgIDxkaXYgY2xhc3M9XCJ0YWJsZS1yZXNwb25zaXZlXCI+XG4gICAgICAgICAgPHRhYmxlIGNsYXNzPVwidGFibGUgdGFibGUtc20gYWxpZ24tbWlkZGxlIGZjLXRhYmxlXCI+XG4gICAgICAgICAgICA8dGhlYWQ+XG4gICAgICAgICAgICAgIDx0cj5cbiAgICAgICAgICAgICAgICA8dGggc3R5bGU9XCJ3aWR0aDogNDJweDtcIj5cbiAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiY2hlY2tib3hcIlxuICAgICAgICAgICAgICAgICAgICBbY2hlY2tlZF09XCJhbGxGaXhhYmxlU2VsZWN0ZWRcIlxuICAgICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiZml4YWJsZVJvd3MubGVuZ3RoID09PSAwXCJcbiAgICAgICAgICAgICAgICAgICAgKGNoYW5nZSk9XCJ0b2dnbGVBbGxGaXhhYmxlKCRhbnkoJGV2ZW50LnRhcmdldCkuY2hlY2tlZClcIj5cbiAgICAgICAgICAgICAgICA8L3RoPlxuICAgICAgICAgICAgICAgIDx0aCBzdHlsZT1cIndpZHRoOiA5MHB4O1wiPkZpeHR1cmUgIzwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkhlYWRlcjwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkNhdGVnb3J5PC90aD5cbiAgICAgICAgICAgICAgICA8dGggc3R5bGU9XCJ3aWR0aDogNzBweDtcIj5XaWR0aDwvdGg+XG4gICAgICAgICAgICAgICAgPHRoIHN0eWxlPVwid2lkdGg6IDExMHB4O1wiPlNoZWx2ZXM8L3RoPlxuICAgICAgICAgICAgICAgIDx0aD5DdXJyZW50IFpvbmVzPC90aD5cbiAgICAgICAgICAgICAgICA8dGg+TGlicmFyeSBab25lczwvdGg+XG4gICAgICAgICAgICAgICAgPHRoPkN1cnJlbnQgVHlwZXM8L3RoPlxuICAgICAgICAgICAgICAgIDx0aD5MaWJyYXJ5IFR5cGVzPC90aD5cbiAgICAgICAgICAgICAgICA8dGggc3R5bGU9XCJ3aWR0aDogMTYwcHg7XCI+U3RhdHVzPC90aD5cbiAgICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICAgIDwvdGhlYWQ+XG4gICAgICAgICAgICA8dGJvZHk+XG4gICAgICAgICAgICAgIEBmb3IgKHIgb2YgYXVkaXRSb3dzOyB0cmFjayByLmZpeHR1cmVJZCkge1xuICAgICAgICAgICAgICAgIDx0ciBbY2xhc3Mucm93LWZpeGFibGVdPVwici5maXhhYmxlXCI+XG4gICAgICAgICAgICAgICAgICA8dGQ+XG4gICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiY2hlY2tib3hcIiBbKG5nTW9kZWwpXT1cInIuc2VsZWN0ZWRcIiBbZGlzYWJsZWRdPVwiIXIuZml4YWJsZVwiPlxuICAgICAgICAgICAgICAgICAgPC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZD57eyByLmZpeHR1cmVOdW1iZXIgPz8gJ+KAlCcgfX08L3RkPlxuICAgICAgICAgICAgICAgICAgPHRkPnt7IHIuaGVhZGVyIHx8ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZD57eyByLmNhdGVnb3J5IHx8ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZD57eyByLndpZHRoIHx8ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZD5cbiAgICAgICAgICAgICAgICAgICAgQGlmIChyLmxpYlNoZWxmQ291bnQgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIFtjbGFzcy50ZXh0LWRhbmdlcl09XCJyLmN1clNoZWxmQ291bnQgIT09IHIubGliU2hlbGZDb3VudFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAge3sgci5jdXJTaGVsZkNvdW50IH19IOKGkiB7eyByLmxpYlNoZWxmQ291bnQgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgIHt7IHIuY3VyU2hlbGZDb3VudCB9fVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICA8L3RkPlxuICAgICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwiem9uZXNcIj57eyByLmN1cnJlbnRab25lcyB8fCAn4oCUJyB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJ6b25lc1wiPnt7IHIubGliWm9uZXMgfHwgJ+KAlCcgfX08L3RkPlxuICAgICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwiem9uZXNcIiBbY2xhc3MudGV4dC1kYW5nZXJdPVwici5yZWFzb24gPT09ICd0eXBlLW1pc21hdGNoJ1wiPnt7IHIuY3VycmVudFR5cGVzIHx8ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInpvbmVzXCI+e3sgci5saWJUeXBlcyB8fCAn4oCUJyB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgICA8dGQ+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwic3RhdHVzLWJhZGdlXCIgW25nQ2xhc3NdPVwicmVhc29uQ2xhc3Moci5yZWFzb24pXCI+XG4gICAgICAgICAgICAgICAgICAgICAge3sgcmVhc29uTGFiZWwoci5yZWFzb24pIH19XG4gICAgICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC90Ym9keT5cbiAgICAgICAgICA8L3RhYmxlPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIH1cbiAgICB9XG5cbiAgICA8IS0tIEFwcGx5IHN1bW1hcnkgcGVyIGZpeHR1cmUgLS0+XG4gICAgQGlmIChhcHBseVJlc3VsdHMubGVuZ3RoID4gMCkge1xuICAgICAgPGhyIGNsYXNzPVwibXktNFwiIC8+XG4gICAgICA8aDYgY2xhc3M9XCJzZWN0aW9uLWhlYWRpbmcgbWItMlwiPkFwcGx5IFN1bW1hcnk8L2g2PlxuICAgICAgPGRpdiBjbGFzcz1cInRhYmxlLXJlc3BvbnNpdmVcIj5cbiAgICAgICAgPHRhYmxlIGNsYXNzPVwidGFibGUgdGFibGUtc20gYWxpZ24tbWlkZGxlIGZjLXRhYmxlXCI+XG4gICAgICAgICAgPHRoZWFkPlxuICAgICAgICAgICAgPHRyPlxuICAgICAgICAgICAgICA8dGggc3R5bGU9XCJ3aWR0aDogOTBweDtcIj5GaXh0dXJlICM8L3RoPlxuICAgICAgICAgICAgICA8dGggc3R5bGU9XCJ3aWR0aDogMTQwcHg7XCI+U3RhdHVzPC90aD5cbiAgICAgICAgICAgICAgPHRoPlNoZWx2ZXM8L3RoPlxuICAgICAgICAgICAgICA8dGg+Q29tcGxpYW5jZSBTeW5jZWQ8L3RoPlxuICAgICAgICAgICAgICA8dGg+Tm90ZTwvdGg+XG4gICAgICAgICAgICA8L3RyPlxuICAgICAgICAgIDwvdGhlYWQ+XG4gICAgICAgICAgPHRib2R5PlxuICAgICAgICAgICAgQGZvciAoYXIgb2YgYXBwbHlSZXN1bHRzOyB0cmFjayBhci5maXh0dXJlSWQpIHtcbiAgICAgICAgICAgICAgPHRyPlxuICAgICAgICAgICAgICAgIDx0ZD57eyBhci5maXh0dXJlTnVtYmVyID8/ICfigJQnIH19PC90ZD5cbiAgICAgICAgICAgICAgICA8dGQ+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInN0YXR1cy1iYWRnZVwiXG4gICAgICAgICAgICAgICAgICAgIFtjbGFzcy5iYWRnZS1va109XCJhci5zdGF0dXMgPT09ICdmaXhlZCcgfHwgYXIuc3RhdHVzID09PSAnYWxyZWFkeS1jb25mb3JtJ1wiXG4gICAgICAgICAgICAgICAgICAgIFtjbGFzcy5iYWRnZS13YXJuXT1cImFyLnN0YXR1cyA9PT0gJ3NraXBwZWQnXCJcbiAgICAgICAgICAgICAgICAgICAgW2NsYXNzLmJhZGdlLWRhbmdlcl09XCJhci5zdGF0dXMgPT09ICdmYWlsZWQnXCI+XG4gICAgICAgICAgICAgICAgICAgIHt7IGFyLnN0YXR1cyB9fVxuICAgICAgICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgICAgICAgPHRkPlxuICAgICAgICAgICAgICAgICAgQGlmIChhci5zaGVsdmVzRnJvbSAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHt7IGFyLnNoZWx2ZXNGcm9tIH19IOKGkiB7eyBhci5zaGVsdmVzVG8gfX1cbiAgICAgICAgICAgICAgICAgIH0gQGVsc2UgeyDigJQgfVxuICAgICAgICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgICAgICAgPHRkPnt7IGFyLmNvbXBsaWFuY2VTeW5jZWQgPz8gMCB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwidGV4dC1tdXRlZCBzbWFsbFwiPnt7IGFyLmVycm9yIHx8IGFyLnJlYXNvbiB8fCAnJyB9fTwvdGQ+XG4gICAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgPC90Ym9keT5cbiAgICAgICAgPC90YWJsZT5cbiAgICAgIDwvZGl2PlxuICAgIH1cblxuICAgIDwhLS0gTWVzc2FnZSAtLT5cbiAgICBAaWYgKG1lc3NhZ2UpIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJtdC0zIGFsZXJ0XCIgW2NsYXNzLmFsZXJ0LXN1Y2Nlc3NdPVwibWVzc2FnZVR5cGUgPT09ICdzdWNjZXNzJ1wiXG4gICAgICAgIFtjbGFzcy5hbGVydC1kYW5nZXJdPVwibWVzc2FnZVR5cGUgPT09ICdlcnJvcidcIj5cbiAgICAgICAge3sgbWVzc2FnZSB9fVxuICAgICAgPC9kaXY+XG4gICAgfVxuICA8L2Rpdj5cbjwvc2VjdGlvbj5cbiJdfQ==
@@ -5,7 +5,7 @@ import { CommonModule } from '@angular/common';
5
5
  import * as FileSaver from 'file-saver';
6
6
  import { firstValueFrom } from 'rxjs';
7
7
  import * as i1 from '@ng-bootstrap/ng-bootstrap';
8
- import { S as StoreBuilderService } from './tango-app-ui-store-builder-tango-app-ui-store-builder-UJrKxjTl.mjs';
8
+ import { S as StoreBuilderService } from './tango-app-ui-store-builder-tango-app-ui-store-builder-D1UC-7Qi.mjs';
9
9
  import * as i4 from 'tango-app-ui-shared';
10
10
  import '@angular/router';
11
11
  import 'tango-app-ui-global';
@@ -233,4 +233,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
233
233
  }] } });
234
234
 
235
235
  export { DxfSourceViewerComponent };
236
- //# sourceMappingURL=tango-app-ui-store-builder-dxf-source-viewer.component-B0ut7XVu.mjs.map
236
+ //# sourceMappingURL=tango-app-ui-store-builder-dxf-source-viewer.component-KfK9D7DB.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tango-app-ui-store-builder-dxf-source-viewer.component-B0ut7XVu.mjs","sources":["../../../projects/tango-store-builder/src/lib/components/dxf-source-viewer/dxf-source-viewer.component.ts","../../../projects/tango-store-builder/src/lib/components/dxf-source-viewer/dxf-source-viewer.component.html"],"sourcesContent":["import {\n AfterViewInit,\n Component,\n ElementRef,\n Input,\n OnDestroy,\n OnInit,\n ViewChild,\n} from \"@angular/core\";\nimport { CommonModule } from \"@angular/common\";\nimport { NgbActiveModal } from \"@ng-bootstrap/ng-bootstrap\";\nimport * as FileSaver from \"file-saver\";\nimport { firstValueFrom } from \"rxjs\";\nimport { ToastService } from \"tango-app-ui-shared\";\nimport { StoreBuilderService } from \"../../services/store-builder.service\";\n\n/**\n * Read-only viewer for a floor's SOURCE CAD/DXF files.\n *\n * Renders the floor's `cadFiles` (stored on the storelayout doc) as a timeline —\n * oldest first, latest last/highlighted — and shows the selected drawing in a 2D\n * `dxf-viewer` (WebGL, built-in pan/zoom + layer toggle). Each entry also has a\n * download button.\n *\n * `dxf-viewer` (and its heavy `three` peer) are dynamically imported here, and this\n * component itself is opened via a dynamic import (see CadSourceService), so the\n * whole thing lands in a lazy chunk and stays out of the main bundle.\n *\n * NOTE ON TEXT: dxf-viewer needs a font file to render TEXT/MTEXT entities. Drop a\n * .ttf into assets and list it in FONT_URLS to enable labels; with no font, geometry\n * renders but text is blank (no crash).\n */\n\n// Add a hosted TTF here (e.g. \"assets/fonts/Roboto-Regular.ttf\") to render DXF text.\nconst FONT_URLS: string[] = [];\n\ninterface CadFileEntry {\n key: string;\n fileName?: string;\n uploadedAt?: string | Date;\n inferred?: boolean;\n // resolved at runtime from listCadFiles:\n url?: string;\n size?: number;\n available?: boolean;\n}\n\n@Component({\n selector: \"lib-dxf-source-viewer\",\n standalone: true,\n imports: [CommonModule],\n templateUrl: \"./dxf-source-viewer.component.html\",\n styleUrl: \"./dxf-source-viewer.component.scss\",\n})\nexport class DxfSourceViewerComponent\n implements OnInit, AfterViewInit, OnDestroy\n{\n @Input() storeName = \"\";\n /** The floor's stored cadFiles array (oldest -> newest). */\n @Input() cadFiles: CadFileEntry[] = [];\n\n @ViewChild(\"viewerHost\") viewerHost!: ElementRef<HTMLDivElement>;\n\n entries: CadFileEntry[] = [];\n selected: CadFileEntry | null = null;\n\n isResolving = false;\n isRendering = false;\n loadError = \"\";\n\n layers: { name: string; displayName: string; visible: boolean }[] = [];\n\n private viewer: any;\n private viewReady = false;\n\n constructor(\n public activeModal: NgbActiveModal,\n private storeBuilderService: StoreBuilderService,\n private toastService: ToastService,\n ) {}\n\n async ngOnInit(): Promise<void> {\n await this.resolveUrls();\n }\n\n ngAfterViewInit(): void {\n this.viewReady = true;\n // If URLs resolved before the view was ready, render the default selection now.\n if (this.selected?.available) this.render(this.selected);\n }\n\n ngOnDestroy(): void {\n this.destroyViewer();\n }\n\n /** Match each stored cadFiles entry to a presigned URL from listCadFiles(storeName). */\n private async resolveUrls(): Promise<void> {\n this.isResolving = true;\n this.loadError = \"\";\n try {\n const res: any = await firstValueFrom(\n this.storeBuilderService.getCadFiles(this.storeName),\n );\n const s3Files: any[] = res?.data?.files ?? [];\n const byKey = new Map<string, any>(s3Files.map((f) => [f.key, f]));\n\n // Preserve stored order (oldest -> newest); render newest at top of timeline.\n this.entries = (this.cadFiles ?? []).map((c) => {\n const match = byKey.get(c.key);\n return {\n ...c,\n fileName: c.fileName ?? c.key?.split(\"/\").pop(),\n url: match?.url,\n size: match?.size,\n available: !!match?.url,\n };\n });\n\n // Default selection: latest available entry (last in stored order).\n const latestAvailable = [...this.entries]\n .reverse()\n .find((e) => e.available);\n if (latestAvailable) {\n this.select(latestAvailable);\n } else if (this.entries.length) {\n this.loadError =\n \"The source file(s) could not be found in storage. They may have been removed.\";\n }\n } catch (e: any) {\n this.loadError =\n e?.error?.error?.errMsg ??\n e?.error?.message ??\n \"Failed to load source CAD files.\";\n } finally {\n this.isResolving = false;\n }\n }\n\n /** Timeline shows newest first for scannability. */\n get timeline(): CadFileEntry[] {\n return [...this.entries].reverse();\n }\n\n isLatest(entry: CadFileEntry): boolean {\n return this.entries.length > 0 && entry === this.entries[this.entries.length - 1];\n }\n\n select(entry: CadFileEntry): void {\n if (!entry?.available || this.selected === entry) return;\n this.selected = entry;\n if (this.viewReady) this.render(entry);\n }\n\n private async render(entry: CadFileEntry): Promise<void> {\n if (!entry?.url) return;\n this.isRendering = true;\n this.loadError = \"\";\n this.layers = [];\n try {\n const { DxfViewer } = await import(\"dxf-viewer\");\n // three ships no type declarations; it's a runtime dep of dxf-viewer.\n // We only need Color here, so import it untyped rather than pulling @types/three.\n // @ts-ignore\n const THREE = await import(\"three\");\n\n if (!this.viewer) {\n this.viewer = new DxfViewer(this.viewerHost.nativeElement, {\n autoResize: true,\n // Correct entity colors for contrast against the (white) background so\n // light-coloured layers/text stay visible.\n colorCorrection: true,\n clearColor: new THREE.Color(0xffffff),\n clearAlpha: 1,\n });\n } else {\n this.viewer.Clear();\n }\n\n await this.viewer.Load({ url: entry.url, fonts: FONT_URLS });\n\n // Build the layer-toggle list.\n const layers = this.viewer.GetLayers?.() ?? [];\n this.layers = [...layers].map((l: any) => ({\n name: l.name,\n displayName: l.displayName ?? l.name,\n visible: true,\n }));\n } catch (e) {\n console.error(\"@@ ~ dxf render [ERR]:\", e);\n this.loadError = \"Failed to render this DXF drawing.\";\n } finally {\n this.isRendering = false;\n }\n }\n\n toggleLayer(layer: { name: string; visible: boolean }): void {\n layer.visible = !layer.visible;\n this.viewer?.ShowLayer?.(layer.name, layer.visible);\n }\n\n get allLayersVisible(): boolean {\n return this.layers.length > 0 && this.layers.every((l) => l.visible);\n }\n\n toggleAllLayers(event: Event): void {\n const checked = (event.target as HTMLInputElement).checked;\n for (const layer of this.layers) {\n layer.visible = checked;\n this.viewer?.ShowLayer?.(layer.name, checked);\n }\n }\n\n async download(entry: CadFileEntry, event: MouseEvent): Promise<void> {\n event.stopPropagation();\n if (!entry?.url) {\n this.toastService.getErrorToast(\"Download URL is not available.\");\n return;\n }\n try {\n const response = await fetch(entry.url);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const blob = await response.blob();\n FileSaver.saveAs(blob, entry.fileName ?? \"drawing.dxf\");\n } catch {\n this.toastService.getErrorToast(\n `Failed to download ${entry.fileName ?? \"file\"}.`,\n );\n }\n }\n\n private destroyViewer(): void {\n try {\n this.viewer?.Destroy?.();\n } catch {}\n this.viewer = null;\n }\n\n close(): void {\n this.activeModal.dismiss();\n }\n}\n","<div class=\"dxf-modal\">\n <div class=\"dxf-modal__header\">\n <div>\n <h3 class=\"dxf-modal__title\">Source CAD drawing</h3>\n <p class=\"dxf-modal__subtitle\">{{ storeName }}</p>\n </div>\n <button type=\"button\" class=\"dxf-icon-btn\" aria-label=\"Close\" (click)=\"close()\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 4L4 12M4 4l8 8\" stroke=\"#475467\" stroke-width=\"1.6\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"dxf-modal__body\">\n <!-- Timeline of source files -->\n <aside class=\"dxf-timeline\">\n <div class=\"dxf-timeline__head\">File history</div>\n\n <div *ngIf=\"isResolving\" class=\"dxf-muted\">Loading files…</div>\n\n <div *ngIf=\"!isResolving && !entries.length\" class=\"dxf-muted\">\n No source CAD files for this floor.\n </div>\n\n <ul class=\"dxf-timeline__list\" *ngIf=\"!isResolving && entries.length\">\n <li\n *ngFor=\"let entry of timeline\"\n class=\"dxf-timeline__item\"\n [class.is-selected]=\"entry === selected\"\n [class.is-disabled]=\"!entry.available\"\n (click)=\"select(entry)\"\n >\n <div class=\"dxf-timeline__dot\"></div>\n <div class=\"dxf-timeline__content\">\n <div class=\"dxf-timeline__name\" [title]=\"entry.fileName\">\n {{ entry.fileName }}\n <span *ngIf=\"isLatest(entry)\" class=\"dxf-badge dxf-badge--latest\">Latest</span>\n <span *ngIf=\"entry.inferred\" class=\"dxf-badge dxf-badge--inferred\" title=\"Matched from storage, not recorded at generation time\">Inferred</span>\n </div>\n <div class=\"dxf-timeline__meta\">\n <span *ngIf=\"entry.uploadedAt\">{{ entry.uploadedAt | date: \"medium\" }}</span>\n <span *ngIf=\"!entry.available\" class=\"dxf-unavailable\">Unavailable</span>\n </div>\n </div>\n <button\n type=\"button\"\n class=\"dxf-icon-btn dxf-icon-btn--sm\"\n [disabled]=\"!entry.available\"\n aria-label=\"Download\"\n (click)=\"download(entry, $event)\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M8 2v8m0 0L5 7m3 3l3-3M3 13h10\" stroke=\"#475467\" stroke-width=\"1.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </li>\n </ul>\n </aside>\n\n <!-- Viewer -->\n <section class=\"dxf-viewer\">\n <div class=\"dxf-viewer__toolbar\" *ngIf=\"layers.length\">\n <div class=\"dxf-layers\">\n <span class=\"dxf-layers__label\">Layers</span>\n <label class=\"dxf-layers__item dxf-layers__item--all\">\n <input type=\"checkbox\" [checked]=\"allLayersVisible\" (change)=\"toggleAllLayers($event)\" />\n All\n </label>\n <label class=\"dxf-layers__item\" *ngFor=\"let layer of layers\">\n <input type=\"checkbox\" [checked]=\"layer.visible\" (change)=\"toggleLayer(layer)\" />\n {{ layer.displayName }}\n </label>\n </div>\n </div>\n\n <div class=\"dxf-viewer__stage\">\n <div #viewerHost class=\"dxf-viewer__host\"></div>\n\n <div *ngIf=\"isRendering\" class=\"dxf-overlay\">\n <svg class=\"dxf-spinner\" viewBox=\"0 0 50 50\">\n <circle class=\"path\" cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\" stroke-width=\"4\"></circle>\n </svg>\n <span>Rendering drawing…</span>\n </div>\n\n <div *ngIf=\"loadError && !isRendering\" class=\"dxf-overlay dxf-overlay--error\">\n {{ loadError }}\n </div>\n\n <div *ngIf=\"!selected && !isResolving && !loadError\" class=\"dxf-overlay dxf-muted\">\n Select a file to view.\n </div>\n </div>\n\n <p class=\"dxf-viewer__hint\">Scroll to zoom · drag to pan</p>\n </section>\n </div>\n</div>\n"],"names":["i2.StoreBuilderService","i3","i4"],"mappings":"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"}
1
+ {"version":3,"file":"tango-app-ui-store-builder-dxf-source-viewer.component-KfK9D7DB.mjs","sources":["../../../projects/tango-store-builder/src/lib/components/dxf-source-viewer/dxf-source-viewer.component.ts","../../../projects/tango-store-builder/src/lib/components/dxf-source-viewer/dxf-source-viewer.component.html"],"sourcesContent":["import {\n AfterViewInit,\n Component,\n ElementRef,\n Input,\n OnDestroy,\n OnInit,\n ViewChild,\n} from \"@angular/core\";\nimport { CommonModule } from \"@angular/common\";\nimport { NgbActiveModal } from \"@ng-bootstrap/ng-bootstrap\";\nimport * as FileSaver from \"file-saver\";\nimport { firstValueFrom } from \"rxjs\";\nimport { ToastService } from \"tango-app-ui-shared\";\nimport { StoreBuilderService } from \"../../services/store-builder.service\";\n\n/**\n * Read-only viewer for a floor's SOURCE CAD/DXF files.\n *\n * Renders the floor's `cadFiles` (stored on the storelayout doc) as a timeline —\n * oldest first, latest last/highlighted — and shows the selected drawing in a 2D\n * `dxf-viewer` (WebGL, built-in pan/zoom + layer toggle). Each entry also has a\n * download button.\n *\n * `dxf-viewer` (and its heavy `three` peer) are dynamically imported here, and this\n * component itself is opened via a dynamic import (see CadSourceService), so the\n * whole thing lands in a lazy chunk and stays out of the main bundle.\n *\n * NOTE ON TEXT: dxf-viewer needs a font file to render TEXT/MTEXT entities. Drop a\n * .ttf into assets and list it in FONT_URLS to enable labels; with no font, geometry\n * renders but text is blank (no crash).\n */\n\n// Add a hosted TTF here (e.g. \"assets/fonts/Roboto-Regular.ttf\") to render DXF text.\nconst FONT_URLS: string[] = [];\n\ninterface CadFileEntry {\n key: string;\n fileName?: string;\n uploadedAt?: string | Date;\n inferred?: boolean;\n // resolved at runtime from listCadFiles:\n url?: string;\n size?: number;\n available?: boolean;\n}\n\n@Component({\n selector: \"lib-dxf-source-viewer\",\n standalone: true,\n imports: [CommonModule],\n templateUrl: \"./dxf-source-viewer.component.html\",\n styleUrl: \"./dxf-source-viewer.component.scss\",\n})\nexport class DxfSourceViewerComponent\n implements OnInit, AfterViewInit, OnDestroy\n{\n @Input() storeName = \"\";\n /** The floor's stored cadFiles array (oldest -> newest). */\n @Input() cadFiles: CadFileEntry[] = [];\n\n @ViewChild(\"viewerHost\") viewerHost!: ElementRef<HTMLDivElement>;\n\n entries: CadFileEntry[] = [];\n selected: CadFileEntry | null = null;\n\n isResolving = false;\n isRendering = false;\n loadError = \"\";\n\n layers: { name: string; displayName: string; visible: boolean }[] = [];\n\n private viewer: any;\n private viewReady = false;\n\n constructor(\n public activeModal: NgbActiveModal,\n private storeBuilderService: StoreBuilderService,\n private toastService: ToastService,\n ) {}\n\n async ngOnInit(): Promise<void> {\n await this.resolveUrls();\n }\n\n ngAfterViewInit(): void {\n this.viewReady = true;\n // If URLs resolved before the view was ready, render the default selection now.\n if (this.selected?.available) this.render(this.selected);\n }\n\n ngOnDestroy(): void {\n this.destroyViewer();\n }\n\n /** Match each stored cadFiles entry to a presigned URL from listCadFiles(storeName). */\n private async resolveUrls(): Promise<void> {\n this.isResolving = true;\n this.loadError = \"\";\n try {\n const res: any = await firstValueFrom(\n this.storeBuilderService.getCadFiles(this.storeName),\n );\n const s3Files: any[] = res?.data?.files ?? [];\n const byKey = new Map<string, any>(s3Files.map((f) => [f.key, f]));\n\n // Preserve stored order (oldest -> newest); render newest at top of timeline.\n this.entries = (this.cadFiles ?? []).map((c) => {\n const match = byKey.get(c.key);\n return {\n ...c,\n fileName: c.fileName ?? c.key?.split(\"/\").pop(),\n url: match?.url,\n size: match?.size,\n available: !!match?.url,\n };\n });\n\n // Default selection: latest available entry (last in stored order).\n const latestAvailable = [...this.entries]\n .reverse()\n .find((e) => e.available);\n if (latestAvailable) {\n this.select(latestAvailable);\n } else if (this.entries.length) {\n this.loadError =\n \"The source file(s) could not be found in storage. They may have been removed.\";\n }\n } catch (e: any) {\n this.loadError =\n e?.error?.error?.errMsg ??\n e?.error?.message ??\n \"Failed to load source CAD files.\";\n } finally {\n this.isResolving = false;\n }\n }\n\n /** Timeline shows newest first for scannability. */\n get timeline(): CadFileEntry[] {\n return [...this.entries].reverse();\n }\n\n isLatest(entry: CadFileEntry): boolean {\n return this.entries.length > 0 && entry === this.entries[this.entries.length - 1];\n }\n\n select(entry: CadFileEntry): void {\n if (!entry?.available || this.selected === entry) return;\n this.selected = entry;\n if (this.viewReady) this.render(entry);\n }\n\n private async render(entry: CadFileEntry): Promise<void> {\n if (!entry?.url) return;\n this.isRendering = true;\n this.loadError = \"\";\n this.layers = [];\n try {\n const { DxfViewer } = await import(\"dxf-viewer\");\n // three ships no type declarations; it's a runtime dep of dxf-viewer.\n // We only need Color here, so import it untyped rather than pulling @types/three.\n // @ts-ignore\n const THREE = await import(\"three\");\n\n if (!this.viewer) {\n this.viewer = new DxfViewer(this.viewerHost.nativeElement, {\n autoResize: true,\n // Correct entity colors for contrast against the (white) background so\n // light-coloured layers/text stay visible.\n colorCorrection: true,\n clearColor: new THREE.Color(0xffffff),\n clearAlpha: 1,\n });\n } else {\n this.viewer.Clear();\n }\n\n await this.viewer.Load({ url: entry.url, fonts: FONT_URLS });\n\n // Build the layer-toggle list.\n const layers = this.viewer.GetLayers?.() ?? [];\n this.layers = [...layers].map((l: any) => ({\n name: l.name,\n displayName: l.displayName ?? l.name,\n visible: true,\n }));\n } catch (e) {\n console.error(\"@@ ~ dxf render [ERR]:\", e);\n this.loadError = \"Failed to render this DXF drawing.\";\n } finally {\n this.isRendering = false;\n }\n }\n\n toggleLayer(layer: { name: string; visible: boolean }): void {\n layer.visible = !layer.visible;\n this.viewer?.ShowLayer?.(layer.name, layer.visible);\n }\n\n get allLayersVisible(): boolean {\n return this.layers.length > 0 && this.layers.every((l) => l.visible);\n }\n\n toggleAllLayers(event: Event): void {\n const checked = (event.target as HTMLInputElement).checked;\n for (const layer of this.layers) {\n layer.visible = checked;\n this.viewer?.ShowLayer?.(layer.name, checked);\n }\n }\n\n async download(entry: CadFileEntry, event: MouseEvent): Promise<void> {\n event.stopPropagation();\n if (!entry?.url) {\n this.toastService.getErrorToast(\"Download URL is not available.\");\n return;\n }\n try {\n const response = await fetch(entry.url);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const blob = await response.blob();\n FileSaver.saveAs(blob, entry.fileName ?? \"drawing.dxf\");\n } catch {\n this.toastService.getErrorToast(\n `Failed to download ${entry.fileName ?? \"file\"}.`,\n );\n }\n }\n\n private destroyViewer(): void {\n try {\n this.viewer?.Destroy?.();\n } catch {}\n this.viewer = null;\n }\n\n close(): void {\n this.activeModal.dismiss();\n }\n}\n","<div class=\"dxf-modal\">\n <div class=\"dxf-modal__header\">\n <div>\n <h3 class=\"dxf-modal__title\">Source CAD drawing</h3>\n <p class=\"dxf-modal__subtitle\">{{ storeName }}</p>\n </div>\n <button type=\"button\" class=\"dxf-icon-btn\" aria-label=\"Close\" (click)=\"close()\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 4L4 12M4 4l8 8\" stroke=\"#475467\" stroke-width=\"1.6\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"dxf-modal__body\">\n <!-- Timeline of source files -->\n <aside class=\"dxf-timeline\">\n <div class=\"dxf-timeline__head\">File history</div>\n\n <div *ngIf=\"isResolving\" class=\"dxf-muted\">Loading files…</div>\n\n <div *ngIf=\"!isResolving && !entries.length\" class=\"dxf-muted\">\n No source CAD files for this floor.\n </div>\n\n <ul class=\"dxf-timeline__list\" *ngIf=\"!isResolving && entries.length\">\n <li\n *ngFor=\"let entry of timeline\"\n class=\"dxf-timeline__item\"\n [class.is-selected]=\"entry === selected\"\n [class.is-disabled]=\"!entry.available\"\n (click)=\"select(entry)\"\n >\n <div class=\"dxf-timeline__dot\"></div>\n <div class=\"dxf-timeline__content\">\n <div class=\"dxf-timeline__name\" [title]=\"entry.fileName\">\n {{ entry.fileName }}\n <span *ngIf=\"isLatest(entry)\" class=\"dxf-badge dxf-badge--latest\">Latest</span>\n <span *ngIf=\"entry.inferred\" class=\"dxf-badge dxf-badge--inferred\" title=\"Matched from storage, not recorded at generation time\">Inferred</span>\n </div>\n <div class=\"dxf-timeline__meta\">\n <span *ngIf=\"entry.uploadedAt\">{{ entry.uploadedAt | date: \"medium\" }}</span>\n <span *ngIf=\"!entry.available\" class=\"dxf-unavailable\">Unavailable</span>\n </div>\n </div>\n <button\n type=\"button\"\n class=\"dxf-icon-btn dxf-icon-btn--sm\"\n [disabled]=\"!entry.available\"\n aria-label=\"Download\"\n (click)=\"download(entry, $event)\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M8 2v8m0 0L5 7m3 3l3-3M3 13h10\" stroke=\"#475467\" stroke-width=\"1.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </button>\n </li>\n </ul>\n </aside>\n\n <!-- Viewer -->\n <section class=\"dxf-viewer\">\n <div class=\"dxf-viewer__toolbar\" *ngIf=\"layers.length\">\n <div class=\"dxf-layers\">\n <span class=\"dxf-layers__label\">Layers</span>\n <label class=\"dxf-layers__item dxf-layers__item--all\">\n <input type=\"checkbox\" [checked]=\"allLayersVisible\" (change)=\"toggleAllLayers($event)\" />\n All\n </label>\n <label class=\"dxf-layers__item\" *ngFor=\"let layer of layers\">\n <input type=\"checkbox\" [checked]=\"layer.visible\" (change)=\"toggleLayer(layer)\" />\n {{ layer.displayName }}\n </label>\n </div>\n </div>\n\n <div class=\"dxf-viewer__stage\">\n <div #viewerHost class=\"dxf-viewer__host\"></div>\n\n <div *ngIf=\"isRendering\" class=\"dxf-overlay\">\n <svg class=\"dxf-spinner\" viewBox=\"0 0 50 50\">\n <circle class=\"path\" cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\" stroke-width=\"4\"></circle>\n </svg>\n <span>Rendering drawing…</span>\n </div>\n\n <div *ngIf=\"loadError && !isRendering\" class=\"dxf-overlay dxf-overlay--error\">\n {{ loadError }}\n </div>\n\n <div *ngIf=\"!selected && !isResolving && !loadError\" class=\"dxf-overlay dxf-muted\">\n Select a file to view.\n </div>\n </div>\n\n <p class=\"dxf-viewer__hint\">Scroll to zoom · drag to pan</p>\n </section>\n </div>\n</div>\n"],"names":["i2.StoreBuilderService","i3","i4"],"mappings":"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"}
@@ -7624,7 +7624,7 @@ class CadSourceService {
7624
7624
  async open(storeName, cadFiles) {
7625
7625
  if (!this.hasSource(cadFiles))
7626
7626
  return;
7627
- const { DxfSourceViewerComponent } = await import('./tango-app-ui-store-builder-dxf-source-viewer.component-B0ut7XVu.mjs');
7627
+ const { DxfSourceViewerComponent } = await import('./tango-app-ui-store-builder-dxf-source-viewer.component-KfK9D7DB.mjs');
7628
7628
  const ref = this.ngbModal.open(DxfSourceViewerComponent, {
7629
7629
  size: "xl",
7630
7630
  centered: true,
@@ -69087,6 +69087,7 @@ class FixtureConformanceComponent {
69087
69087
  case 'ok': return 'Conforms';
69088
69088
  case 'shelf-count-mismatch': return 'Shelf count mismatch';
69089
69089
  case 'zone-mismatch': return 'Zone mismatch';
69090
+ case 'type-mismatch': return 'Shelf type mismatch';
69090
69091
  case 'no-library': return 'No library match';
69091
69092
  case 'ambiguous': return 'Ambiguous library';
69092
69093
  case 'no-shelves': return 'No shelves';
@@ -69097,7 +69098,8 @@ class FixtureConformanceComponent {
69097
69098
  switch (reason) {
69098
69099
  case 'ok': return 'badge-ok';
69099
69100
  case 'shelf-count-mismatch':
69100
- case 'zone-mismatch': return 'badge-warn';
69101
+ case 'zone-mismatch':
69102
+ case 'type-mismatch': return 'badge-warn';
69101
69103
  case 'no-library':
69102
69104
  case 'ambiguous':
69103
69105
  case 'no-shelves': return 'badge-muted';
@@ -69114,11 +69116,11 @@ class FixtureConformanceComponent {
69114
69116
  this.destroy$.complete();
69115
69117
  }
69116
69118
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FixtureConformanceComponent, deps: [{ token: StoreBuilderService }, { token: i2$1.GlobalStateService }], target: i0.ɵɵFactoryTarget.Component });
69117
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FixtureConformanceComponent, selector: "lib-fixture-conformance", ngImport: i0, template: "<section class=\"fixture-conformance p-3\">\n <lib-tool-page-header\n title=\"Sync Fixtures to Library\"\n description=\"Check a store's fixtures against the library, then sync shelf count and zones for the ones that don't match.\"\n ></lib-tool-page-header>\n\n <div class=\"card p-4\">\n <!-- Store Select -->\n <div class=\"mb-3\" style=\"max-width: 600px;\">\n <label class=\"form-label\">Store</label>\n <lib-store-select\n [options]=\"storeOptions\"\n [singleSelect]=\"true\"\n (selectedChange)=\"onStoresSelected($event)\">\n </lib-store-select>\n </div>\n\n <!-- Fixture List -->\n @if (selectedStoreName) {\n @if (isLoadingFixtures) {\n <div class=\"text-muted\">Loading fixtures...</div>\n } @else if (fixtures.length === 0) {\n <div class=\"text-muted\">No fixtures found for this store.</div>\n } @else {\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Fixtures\n <span class=\"text-muted small\">({{ selectedFixtureIds.length }} of {{ fixtures.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-primary btn-sm\"\n (click)=\"onAudit()\"\n [disabled]=\"selectedFixtureIds.length === 0 || isAuditing\">\n {{ isAuditing ? 'Auditing...' : 'Audit Selected' }}\n </button>\n </div>\n\n <div class=\"table-responsive mb-2\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixturesSelected\"\n (change)=\"toggleAllFixtures($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Fixture ID</th>\n <th>Category</th>\n <th style=\"width: 90px;\">Width</th>\n </tr>\n </thead>\n <tbody>\n @for (f of fixtures; track f._id) {\n <tr (click)=\"f.selected = !f.selected\" class=\"selectable-row\">\n <td (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" [(ngModel)]=\"f.selected\">\n </td>\n <td>{{ f.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ f.headerLabel || '\u2014' }}</td>\n <td class=\"fixture-id\">{{ f._id }}</td>\n <td>{{ f.fixtureCategory || '\u2014' }}</td>\n <td>{{ f.width || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Audit Results -->\n @if (hasAudited) {\n <hr class=\"my-4\" />\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Audit Result\n <span class=\"text-muted small\">({{ fixableRows.length }} fixable, {{ selectedAuditIds.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-success btn-sm\"\n (click)=\"onApplyFix()\"\n [disabled]=\"selectedAuditIds.length === 0 || isApplying\">\n {{ isApplying ? 'Applying...' : 'Apply Fix' }}\n </button>\n </div>\n\n @if (auditRows.length === 0) {\n <div class=\"text-muted\">No results.</div>\n } @else {\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixableSelected\"\n [disabled]=\"fixableRows.length === 0\"\n (change)=\"toggleAllFixable($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Category</th>\n <th style=\"width: 70px;\">Width</th>\n <th style=\"width: 110px;\">Shelves</th>\n <th>Current Zones</th>\n <th>Library Zones</th>\n <th style=\"width: 160px;\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (r of auditRows; track r.fixtureId) {\n <tr [class.row-fixable]=\"r.fixable\">\n <td>\n <input type=\"checkbox\" [(ngModel)]=\"r.selected\" [disabled]=\"!r.fixable\">\n </td>\n <td>{{ r.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ r.header || '\u2014' }}</td>\n <td>{{ r.category || '\u2014' }}</td>\n <td>{{ r.width || '\u2014' }}</td>\n <td>\n @if (r.libShelfCount != null) {\n <span [class.text-danger]=\"r.curShelfCount !== r.libShelfCount\">\n {{ r.curShelfCount }} \u2192 {{ r.libShelfCount }}\n </span>\n } @else {\n {{ r.curShelfCount }}\n }\n </td>\n <td class=\"zones\">{{ r.currentZones || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libZones || '\u2014' }}</td>\n <td>\n <span class=\"status-badge\" [ngClass]=\"reasonClass(r.reason)\">\n {{ reasonLabel(r.reason) }}\n </span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Apply summary per fixture -->\n @if (applyResults.length > 0) {\n <hr class=\"my-4\" />\n <h6 class=\"section-heading mb-2\">Apply Summary</h6>\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 90px;\">Fixture #</th>\n <th style=\"width: 140px;\">Status</th>\n <th>Shelves</th>\n <th>Compliance Synced</th>\n <th>Note</th>\n </tr>\n </thead>\n <tbody>\n @for (ar of applyResults; track ar.fixtureId) {\n <tr>\n <td>{{ ar.fixtureNumber ?? '\u2014' }}</td>\n <td>\n <span class=\"status-badge\"\n [class.badge-ok]=\"ar.status === 'fixed' || ar.status === 'already-conform'\"\n [class.badge-warn]=\"ar.status === 'skipped'\"\n [class.badge-danger]=\"ar.status === 'failed'\">\n {{ ar.status }}\n </span>\n </td>\n <td>\n @if (ar.shelvesFrom != null) {\n {{ ar.shelvesFrom }} \u2192 {{ ar.shelvesTo }}\n } @else { \u2014 }\n </td>\n <td>{{ ar.complianceSynced ?? 0 }}</td>\n <td class=\"text-muted small\">{{ ar.error || ar.reason || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Message -->\n @if (message) {\n <div class=\"mt-3 alert\" [class.alert-success]=\"messageType === 'success'\"\n [class.alert-danger]=\"messageType === 'error'\">\n {{ message }}\n </div>\n }\n </div>\n</section>\n", styles: [".fixture-conformance .section-heading{color:#344054;font-weight:600}.fixture-conformance .fc-table th{background-color:#f9fafb;font-weight:600;font-size:13px;color:#475467;white-space:nowrap}.fixture-conformance .fc-table td{font-size:13px;vertical-align:middle}.fixture-conformance .fc-table .zones{font-family:Courier New,monospace;font-size:12px;color:#475467;word-break:break-word}.fixture-conformance .fc-table .fixture-id{font-family:Courier New,monospace;font-size:12px;color:#667085}.fixture-conformance .fc-table .selectable-row{cursor:pointer}.fixture-conformance .fc-table .selectable-row:hover{background-color:#f9fafb}.fixture-conformance .fc-table .row-fixable{background-color:#fffaf0}.fixture-conformance .status-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fixture-conformance .status-badge.badge-ok{background-color:#ecfdf3;color:#027a48}.fixture-conformance .status-badge.badge-warn{background-color:#fffaeb;color:#b54708}.fixture-conformance .status-badge.badge-muted{background-color:#f2f4f7;color:#475467}.fixture-conformance .status-badge.badge-danger{background-color:#fef3f2;color:#b42318}\n"], dependencies: [{ kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: StoreSelectComponent, selector: "lib-store-select", inputs: ["options", "initialSelected", "singleSelect"], outputs: ["selectedChange"] }, { kind: "component", type: ToolPageHeaderComponent, selector: "lib-tool-page-header", inputs: ["title", "description", "backLink"] }] });
69119
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FixtureConformanceComponent, selector: "lib-fixture-conformance", ngImport: i0, template: "<section class=\"fixture-conformance p-3\">\n <lib-tool-page-header\n title=\"Sync Fixtures to Library\"\n description=\"Check a store's fixtures against the library, then sync shelf count and zones for the ones that don't match.\"\n ></lib-tool-page-header>\n\n <div class=\"card p-4\">\n <!-- Store Select -->\n <div class=\"mb-3\" style=\"max-width: 600px;\">\n <label class=\"form-label\">Store</label>\n <lib-store-select\n [options]=\"storeOptions\"\n [singleSelect]=\"true\"\n (selectedChange)=\"onStoresSelected($event)\">\n </lib-store-select>\n </div>\n\n <!-- Fixture List -->\n @if (selectedStoreName) {\n @if (isLoadingFixtures) {\n <div class=\"text-muted\">Loading fixtures...</div>\n } @else if (fixtures.length === 0) {\n <div class=\"text-muted\">No fixtures found for this store.</div>\n } @else {\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Fixtures\n <span class=\"text-muted small\">({{ selectedFixtureIds.length }} of {{ fixtures.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-primary btn-sm\"\n (click)=\"onAudit()\"\n [disabled]=\"selectedFixtureIds.length === 0 || isAuditing\">\n {{ isAuditing ? 'Auditing...' : 'Audit Selected' }}\n </button>\n </div>\n\n <div class=\"table-responsive mb-2\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixturesSelected\"\n (change)=\"toggleAllFixtures($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Fixture ID</th>\n <th>Category</th>\n <th style=\"width: 90px;\">Width</th>\n </tr>\n </thead>\n <tbody>\n @for (f of fixtures; track f._id) {\n <tr (click)=\"f.selected = !f.selected\" class=\"selectable-row\">\n <td (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" [(ngModel)]=\"f.selected\">\n </td>\n <td>{{ f.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ f.headerLabel || '\u2014' }}</td>\n <td class=\"fixture-id\">{{ f._id }}</td>\n <td>{{ f.fixtureCategory || '\u2014' }}</td>\n <td>{{ f.width || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Audit Results -->\n @if (hasAudited) {\n <hr class=\"my-4\" />\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Audit Result\n <span class=\"text-muted small\">({{ fixableRows.length }} fixable, {{ selectedAuditIds.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-success btn-sm\"\n (click)=\"onApplyFix()\"\n [disabled]=\"selectedAuditIds.length === 0 || isApplying\">\n {{ isApplying ? 'Applying...' : 'Apply Fix' }}\n </button>\n </div>\n\n @if (auditRows.length === 0) {\n <div class=\"text-muted\">No results.</div>\n } @else {\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixableSelected\"\n [disabled]=\"fixableRows.length === 0\"\n (change)=\"toggleAllFixable($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Category</th>\n <th style=\"width: 70px;\">Width</th>\n <th style=\"width: 110px;\">Shelves</th>\n <th>Current Zones</th>\n <th>Library Zones</th>\n <th>Current Types</th>\n <th>Library Types</th>\n <th style=\"width: 160px;\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (r of auditRows; track r.fixtureId) {\n <tr [class.row-fixable]=\"r.fixable\">\n <td>\n <input type=\"checkbox\" [(ngModel)]=\"r.selected\" [disabled]=\"!r.fixable\">\n </td>\n <td>{{ r.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ r.header || '\u2014' }}</td>\n <td>{{ r.category || '\u2014' }}</td>\n <td>{{ r.width || '\u2014' }}</td>\n <td>\n @if (r.libShelfCount != null) {\n <span [class.text-danger]=\"r.curShelfCount !== r.libShelfCount\">\n {{ r.curShelfCount }} \u2192 {{ r.libShelfCount }}\n </span>\n } @else {\n {{ r.curShelfCount }}\n }\n </td>\n <td class=\"zones\">{{ r.currentZones || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libZones || '\u2014' }}</td>\n <td class=\"zones\" [class.text-danger]=\"r.reason === 'type-mismatch'\">{{ r.currentTypes || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libTypes || '\u2014' }}</td>\n <td>\n <span class=\"status-badge\" [ngClass]=\"reasonClass(r.reason)\">\n {{ reasonLabel(r.reason) }}\n </span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Apply summary per fixture -->\n @if (applyResults.length > 0) {\n <hr class=\"my-4\" />\n <h6 class=\"section-heading mb-2\">Apply Summary</h6>\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 90px;\">Fixture #</th>\n <th style=\"width: 140px;\">Status</th>\n <th>Shelves</th>\n <th>Compliance Synced</th>\n <th>Note</th>\n </tr>\n </thead>\n <tbody>\n @for (ar of applyResults; track ar.fixtureId) {\n <tr>\n <td>{{ ar.fixtureNumber ?? '\u2014' }}</td>\n <td>\n <span class=\"status-badge\"\n [class.badge-ok]=\"ar.status === 'fixed' || ar.status === 'already-conform'\"\n [class.badge-warn]=\"ar.status === 'skipped'\"\n [class.badge-danger]=\"ar.status === 'failed'\">\n {{ ar.status }}\n </span>\n </td>\n <td>\n @if (ar.shelvesFrom != null) {\n {{ ar.shelvesFrom }} \u2192 {{ ar.shelvesTo }}\n } @else { \u2014 }\n </td>\n <td>{{ ar.complianceSynced ?? 0 }}</td>\n <td class=\"text-muted small\">{{ ar.error || ar.reason || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Message -->\n @if (message) {\n <div class=\"mt-3 alert\" [class.alert-success]=\"messageType === 'success'\"\n [class.alert-danger]=\"messageType === 'error'\">\n {{ message }}\n </div>\n }\n </div>\n</section>\n", styles: [".fixture-conformance .section-heading{color:#344054;font-weight:600}.fixture-conformance .fc-table th{background-color:#f9fafb;font-weight:600;font-size:13px;color:#475467;white-space:nowrap}.fixture-conformance .fc-table td{font-size:13px;vertical-align:middle}.fixture-conformance .fc-table .zones{font-family:Courier New,monospace;font-size:12px;color:#475467;word-break:break-word}.fixture-conformance .fc-table .fixture-id{font-family:Courier New,monospace;font-size:12px;color:#667085}.fixture-conformance .fc-table .selectable-row{cursor:pointer}.fixture-conformance .fc-table .selectable-row:hover{background-color:#f9fafb}.fixture-conformance .fc-table .row-fixable{background-color:#fffaf0}.fixture-conformance .status-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fixture-conformance .status-badge.badge-ok{background-color:#ecfdf3;color:#027a48}.fixture-conformance .status-badge.badge-warn{background-color:#fffaeb;color:#b54708}.fixture-conformance .status-badge.badge-muted{background-color:#f2f4f7;color:#475467}.fixture-conformance .status-badge.badge-danger{background-color:#fef3f2;color:#b42318}\n"], dependencies: [{ kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: StoreSelectComponent, selector: "lib-store-select", inputs: ["options", "initialSelected", "singleSelect"], outputs: ["selectedChange"] }, { kind: "component", type: ToolPageHeaderComponent, selector: "lib-tool-page-header", inputs: ["title", "description", "backLink"] }] });
69118
69120
  }
69119
69121
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FixtureConformanceComponent, decorators: [{
69120
69122
  type: Component,
69121
- args: [{ selector: 'lib-fixture-conformance', template: "<section class=\"fixture-conformance p-3\">\n <lib-tool-page-header\n title=\"Sync Fixtures to Library\"\n description=\"Check a store's fixtures against the library, then sync shelf count and zones for the ones that don't match.\"\n ></lib-tool-page-header>\n\n <div class=\"card p-4\">\n <!-- Store Select -->\n <div class=\"mb-3\" style=\"max-width: 600px;\">\n <label class=\"form-label\">Store</label>\n <lib-store-select\n [options]=\"storeOptions\"\n [singleSelect]=\"true\"\n (selectedChange)=\"onStoresSelected($event)\">\n </lib-store-select>\n </div>\n\n <!-- Fixture List -->\n @if (selectedStoreName) {\n @if (isLoadingFixtures) {\n <div class=\"text-muted\">Loading fixtures...</div>\n } @else if (fixtures.length === 0) {\n <div class=\"text-muted\">No fixtures found for this store.</div>\n } @else {\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Fixtures\n <span class=\"text-muted small\">({{ selectedFixtureIds.length }} of {{ fixtures.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-primary btn-sm\"\n (click)=\"onAudit()\"\n [disabled]=\"selectedFixtureIds.length === 0 || isAuditing\">\n {{ isAuditing ? 'Auditing...' : 'Audit Selected' }}\n </button>\n </div>\n\n <div class=\"table-responsive mb-2\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixturesSelected\"\n (change)=\"toggleAllFixtures($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Fixture ID</th>\n <th>Category</th>\n <th style=\"width: 90px;\">Width</th>\n </tr>\n </thead>\n <tbody>\n @for (f of fixtures; track f._id) {\n <tr (click)=\"f.selected = !f.selected\" class=\"selectable-row\">\n <td (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" [(ngModel)]=\"f.selected\">\n </td>\n <td>{{ f.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ f.headerLabel || '\u2014' }}</td>\n <td class=\"fixture-id\">{{ f._id }}</td>\n <td>{{ f.fixtureCategory || '\u2014' }}</td>\n <td>{{ f.width || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Audit Results -->\n @if (hasAudited) {\n <hr class=\"my-4\" />\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Audit Result\n <span class=\"text-muted small\">({{ fixableRows.length }} fixable, {{ selectedAuditIds.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-success btn-sm\"\n (click)=\"onApplyFix()\"\n [disabled]=\"selectedAuditIds.length === 0 || isApplying\">\n {{ isApplying ? 'Applying...' : 'Apply Fix' }}\n </button>\n </div>\n\n @if (auditRows.length === 0) {\n <div class=\"text-muted\">No results.</div>\n } @else {\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixableSelected\"\n [disabled]=\"fixableRows.length === 0\"\n (change)=\"toggleAllFixable($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Category</th>\n <th style=\"width: 70px;\">Width</th>\n <th style=\"width: 110px;\">Shelves</th>\n <th>Current Zones</th>\n <th>Library Zones</th>\n <th style=\"width: 160px;\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (r of auditRows; track r.fixtureId) {\n <tr [class.row-fixable]=\"r.fixable\">\n <td>\n <input type=\"checkbox\" [(ngModel)]=\"r.selected\" [disabled]=\"!r.fixable\">\n </td>\n <td>{{ r.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ r.header || '\u2014' }}</td>\n <td>{{ r.category || '\u2014' }}</td>\n <td>{{ r.width || '\u2014' }}</td>\n <td>\n @if (r.libShelfCount != null) {\n <span [class.text-danger]=\"r.curShelfCount !== r.libShelfCount\">\n {{ r.curShelfCount }} \u2192 {{ r.libShelfCount }}\n </span>\n } @else {\n {{ r.curShelfCount }}\n }\n </td>\n <td class=\"zones\">{{ r.currentZones || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libZones || '\u2014' }}</td>\n <td>\n <span class=\"status-badge\" [ngClass]=\"reasonClass(r.reason)\">\n {{ reasonLabel(r.reason) }}\n </span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Apply summary per fixture -->\n @if (applyResults.length > 0) {\n <hr class=\"my-4\" />\n <h6 class=\"section-heading mb-2\">Apply Summary</h6>\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 90px;\">Fixture #</th>\n <th style=\"width: 140px;\">Status</th>\n <th>Shelves</th>\n <th>Compliance Synced</th>\n <th>Note</th>\n </tr>\n </thead>\n <tbody>\n @for (ar of applyResults; track ar.fixtureId) {\n <tr>\n <td>{{ ar.fixtureNumber ?? '\u2014' }}</td>\n <td>\n <span class=\"status-badge\"\n [class.badge-ok]=\"ar.status === 'fixed' || ar.status === 'already-conform'\"\n [class.badge-warn]=\"ar.status === 'skipped'\"\n [class.badge-danger]=\"ar.status === 'failed'\">\n {{ ar.status }}\n </span>\n </td>\n <td>\n @if (ar.shelvesFrom != null) {\n {{ ar.shelvesFrom }} \u2192 {{ ar.shelvesTo }}\n } @else { \u2014 }\n </td>\n <td>{{ ar.complianceSynced ?? 0 }}</td>\n <td class=\"text-muted small\">{{ ar.error || ar.reason || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Message -->\n @if (message) {\n <div class=\"mt-3 alert\" [class.alert-success]=\"messageType === 'success'\"\n [class.alert-danger]=\"messageType === 'error'\">\n {{ message }}\n </div>\n }\n </div>\n</section>\n", styles: [".fixture-conformance .section-heading{color:#344054;font-weight:600}.fixture-conformance .fc-table th{background-color:#f9fafb;font-weight:600;font-size:13px;color:#475467;white-space:nowrap}.fixture-conformance .fc-table td{font-size:13px;vertical-align:middle}.fixture-conformance .fc-table .zones{font-family:Courier New,monospace;font-size:12px;color:#475467;word-break:break-word}.fixture-conformance .fc-table .fixture-id{font-family:Courier New,monospace;font-size:12px;color:#667085}.fixture-conformance .fc-table .selectable-row{cursor:pointer}.fixture-conformance .fc-table .selectable-row:hover{background-color:#f9fafb}.fixture-conformance .fc-table .row-fixable{background-color:#fffaf0}.fixture-conformance .status-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fixture-conformance .status-badge.badge-ok{background-color:#ecfdf3;color:#027a48}.fixture-conformance .status-badge.badge-warn{background-color:#fffaeb;color:#b54708}.fixture-conformance .status-badge.badge-muted{background-color:#f2f4f7;color:#475467}.fixture-conformance .status-badge.badge-danger{background-color:#fef3f2;color:#b42318}\n"] }]
69123
+ args: [{ selector: 'lib-fixture-conformance', template: "<section class=\"fixture-conformance p-3\">\n <lib-tool-page-header\n title=\"Sync Fixtures to Library\"\n description=\"Check a store's fixtures against the library, then sync shelf count and zones for the ones that don't match.\"\n ></lib-tool-page-header>\n\n <div class=\"card p-4\">\n <!-- Store Select -->\n <div class=\"mb-3\" style=\"max-width: 600px;\">\n <label class=\"form-label\">Store</label>\n <lib-store-select\n [options]=\"storeOptions\"\n [singleSelect]=\"true\"\n (selectedChange)=\"onStoresSelected($event)\">\n </lib-store-select>\n </div>\n\n <!-- Fixture List -->\n @if (selectedStoreName) {\n @if (isLoadingFixtures) {\n <div class=\"text-muted\">Loading fixtures...</div>\n } @else if (fixtures.length === 0) {\n <div class=\"text-muted\">No fixtures found for this store.</div>\n } @else {\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Fixtures\n <span class=\"text-muted small\">({{ selectedFixtureIds.length }} of {{ fixtures.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-primary btn-sm\"\n (click)=\"onAudit()\"\n [disabled]=\"selectedFixtureIds.length === 0 || isAuditing\">\n {{ isAuditing ? 'Auditing...' : 'Audit Selected' }}\n </button>\n </div>\n\n <div class=\"table-responsive mb-2\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixturesSelected\"\n (change)=\"toggleAllFixtures($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Fixture ID</th>\n <th>Category</th>\n <th style=\"width: 90px;\">Width</th>\n </tr>\n </thead>\n <tbody>\n @for (f of fixtures; track f._id) {\n <tr (click)=\"f.selected = !f.selected\" class=\"selectable-row\">\n <td (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" [(ngModel)]=\"f.selected\">\n </td>\n <td>{{ f.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ f.headerLabel || '\u2014' }}</td>\n <td class=\"fixture-id\">{{ f._id }}</td>\n <td>{{ f.fixtureCategory || '\u2014' }}</td>\n <td>{{ f.width || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Audit Results -->\n @if (hasAudited) {\n <hr class=\"my-4\" />\n <div class=\"d-flex align-items-center justify-content-between mb-2\">\n <h6 class=\"section-heading mb-0\">\n Audit Result\n <span class=\"text-muted small\">({{ fixableRows.length }} fixable, {{ selectedAuditIds.length }} selected)</span>\n </h6>\n <button\n class=\"btn btn-success btn-sm\"\n (click)=\"onApplyFix()\"\n [disabled]=\"selectedAuditIds.length === 0 || isApplying\">\n {{ isApplying ? 'Applying...' : 'Apply Fix' }}\n </button>\n </div>\n\n @if (auditRows.length === 0) {\n <div class=\"text-muted\">No results.</div>\n } @else {\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 42px;\">\n <input type=\"checkbox\"\n [checked]=\"allFixableSelected\"\n [disabled]=\"fixableRows.length === 0\"\n (change)=\"toggleAllFixable($any($event.target).checked)\">\n </th>\n <th style=\"width: 90px;\">Fixture #</th>\n <th>Header</th>\n <th>Category</th>\n <th style=\"width: 70px;\">Width</th>\n <th style=\"width: 110px;\">Shelves</th>\n <th>Current Zones</th>\n <th>Library Zones</th>\n <th>Current Types</th>\n <th>Library Types</th>\n <th style=\"width: 160px;\">Status</th>\n </tr>\n </thead>\n <tbody>\n @for (r of auditRows; track r.fixtureId) {\n <tr [class.row-fixable]=\"r.fixable\">\n <td>\n <input type=\"checkbox\" [(ngModel)]=\"r.selected\" [disabled]=\"!r.fixable\">\n </td>\n <td>{{ r.fixtureNumber ?? '\u2014' }}</td>\n <td>{{ r.header || '\u2014' }}</td>\n <td>{{ r.category || '\u2014' }}</td>\n <td>{{ r.width || '\u2014' }}</td>\n <td>\n @if (r.libShelfCount != null) {\n <span [class.text-danger]=\"r.curShelfCount !== r.libShelfCount\">\n {{ r.curShelfCount }} \u2192 {{ r.libShelfCount }}\n </span>\n } @else {\n {{ r.curShelfCount }}\n }\n </td>\n <td class=\"zones\">{{ r.currentZones || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libZones || '\u2014' }}</td>\n <td class=\"zones\" [class.text-danger]=\"r.reason === 'type-mismatch'\">{{ r.currentTypes || '\u2014' }}</td>\n <td class=\"zones\">{{ r.libTypes || '\u2014' }}</td>\n <td>\n <span class=\"status-badge\" [ngClass]=\"reasonClass(r.reason)\">\n {{ reasonLabel(r.reason) }}\n </span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n\n <!-- Apply summary per fixture -->\n @if (applyResults.length > 0) {\n <hr class=\"my-4\" />\n <h6 class=\"section-heading mb-2\">Apply Summary</h6>\n <div class=\"table-responsive\">\n <table class=\"table table-sm align-middle fc-table\">\n <thead>\n <tr>\n <th style=\"width: 90px;\">Fixture #</th>\n <th style=\"width: 140px;\">Status</th>\n <th>Shelves</th>\n <th>Compliance Synced</th>\n <th>Note</th>\n </tr>\n </thead>\n <tbody>\n @for (ar of applyResults; track ar.fixtureId) {\n <tr>\n <td>{{ ar.fixtureNumber ?? '\u2014' }}</td>\n <td>\n <span class=\"status-badge\"\n [class.badge-ok]=\"ar.status === 'fixed' || ar.status === 'already-conform'\"\n [class.badge-warn]=\"ar.status === 'skipped'\"\n [class.badge-danger]=\"ar.status === 'failed'\">\n {{ ar.status }}\n </span>\n </td>\n <td>\n @if (ar.shelvesFrom != null) {\n {{ ar.shelvesFrom }} \u2192 {{ ar.shelvesTo }}\n } @else { \u2014 }\n </td>\n <td>{{ ar.complianceSynced ?? 0 }}</td>\n <td class=\"text-muted small\">{{ ar.error || ar.reason || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Message -->\n @if (message) {\n <div class=\"mt-3 alert\" [class.alert-success]=\"messageType === 'success'\"\n [class.alert-danger]=\"messageType === 'error'\">\n {{ message }}\n </div>\n }\n </div>\n</section>\n", styles: [".fixture-conformance .section-heading{color:#344054;font-weight:600}.fixture-conformance .fc-table th{background-color:#f9fafb;font-weight:600;font-size:13px;color:#475467;white-space:nowrap}.fixture-conformance .fc-table td{font-size:13px;vertical-align:middle}.fixture-conformance .fc-table .zones{font-family:Courier New,monospace;font-size:12px;color:#475467;word-break:break-word}.fixture-conformance .fc-table .fixture-id{font-family:Courier New,monospace;font-size:12px;color:#667085}.fixture-conformance .fc-table .selectable-row{cursor:pointer}.fixture-conformance .fc-table .selectable-row:hover{background-color:#f9fafb}.fixture-conformance .fc-table .row-fixable{background-color:#fffaf0}.fixture-conformance .status-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fixture-conformance .status-badge.badge-ok{background-color:#ecfdf3;color:#027a48}.fixture-conformance .status-badge.badge-warn{background-color:#fffaeb;color:#b54708}.fixture-conformance .status-badge.badge-muted{background-color:#f2f4f7;color:#475467}.fixture-conformance .status-badge.badge-danger{background-color:#fef3f2;color:#b42318}\n"] }]
69122
69124
  }], ctorParameters: () => [{ type: StoreBuilderService }, { type: i2$1.GlobalStateService }] });
69123
69125
 
69124
69126
  class SwapTemplateComponent {
@@ -80271,4 +80273,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
80271
80273
  */
80272
80274
 
80273
80275
  export { StoreBuilderService as S, TangoStoreBuilderModule as T, TangoStorePlanoModule as a };
80274
- //# sourceMappingURL=tango-app-ui-store-builder-tango-app-ui-store-builder-UJrKxjTl.mjs.map
80276
+ //# sourceMappingURL=tango-app-ui-store-builder-tango-app-ui-store-builder-D1UC-7Qi.mjs.map