@memberjunction/ng-dashboards 5.21.0 → 5.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +364 -362
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +2 -2
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +313 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +2792 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts +382 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +2683 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -0
- package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +191 -197
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/models/model-management.component.js +9 -8
- package/dist/AI/components/models/model-management.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-management.component.js +305 -299
- package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
- package/dist/AI/components/system/system-configuration.component.js +319 -313
- package/dist/AI/components/system/system-configuration.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +240 -0
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -0
- package/dist/AI/components/vectors/vector-management-resource.component.js +1767 -0
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -0
- package/dist/AI/index.d.ts +3 -0
- package/dist/AI/index.d.ts.map +1 -1
- package/dist/AI/index.js +6 -0
- package/dist/AI/index.js.map +1 -1
- package/dist/AI/services/ai-instrumentation.service.d.ts +50 -7
- package/dist/AI/services/ai-instrumentation.service.d.ts.map +1 -1
- package/dist/AI/services/ai-instrumentation.service.js +161 -193
- package/dist/AI/services/ai-instrumentation.service.js.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.js +10 -12
- package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-create-dialog.component.js +13 -19
- package/dist/APIKeys/api-key-create-dialog.component.js.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js +12 -14
- package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
- package/dist/APIKeys/api-scopes-panel.component.js +61 -68
- package/dist/APIKeys/api-scopes-panel.component.js.map +1 -1
- package/dist/APIKeys/api-usage-panel.component.js +10 -11
- package/dist/APIKeys/api-usage-panel.component.js.map +1 -1
- package/dist/Actions/components/actions-list-view.component.js +82 -96
- package/dist/Actions/components/actions-list-view.component.js.map +1 -1
- package/dist/Actions/components/actions-overview.component.js +130 -134
- package/dist/Actions/components/actions-overview.component.js.map +1 -1
- package/dist/Actions/components/categories-list-view.component.d.ts.map +1 -1
- package/dist/Actions/components/categories-list-view.component.js +40 -46
- package/dist/Actions/components/categories-list-view.component.js.map +1 -1
- package/dist/Actions/components/code-management.component.js +2 -2
- package/dist/Actions/components/code-management.component.js.map +1 -1
- package/dist/Actions/components/entity-integration.component.js +2 -2
- package/dist/Actions/components/entity-integration.component.js.map +1 -1
- package/dist/Actions/components/execution-monitoring.component.js +127 -132
- package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
- package/dist/Actions/components/executions-list-view.component.js +2 -2
- package/dist/Actions/components/executions-list-view.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-card.component.js +11 -17
- package/dist/Actions/components/explorer/action-card.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-explorer.component.js +5 -11
- package/dist/Actions/components/explorer/action-explorer.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-list-item.component.js +8 -10
- package/dist/Actions/components/explorer/action-list-item.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-toolbar.component.js +112 -133
- package/dist/Actions/components/explorer/action-toolbar.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-tree-panel.component.js +63 -83
- package/dist/Actions/components/explorer/action-tree-panel.component.js.map +1 -1
- package/dist/Actions/components/explorer/new-action-panel.component.js +17 -21
- package/dist/Actions/components/explorer/new-action-panel.component.js.map +1 -1
- package/dist/Actions/components/explorer/new-category-panel.component.js +17 -21
- package/dist/Actions/components/explorer/new-category-panel.component.js.map +1 -1
- package/dist/Actions/components/scheduled-actions.component.js +2 -2
- package/dist/Actions/components/scheduled-actions.component.js.map +1 -1
- package/dist/Actions/components/security-permissions.component.js +2 -2
- package/dist/Actions/components/security-permissions.component.js.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.d.ts +13 -5
- package/dist/ComponentStudio/component-studio-dashboard.component.d.ts.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.js +168 -145
- package/dist/ComponentStudio/component-studio-dashboard.component.js.map +1 -1
- package/dist/ComponentStudio/components/artifact-load-dialog.component.d.ts +4 -5
- package/dist/ComponentStudio/components/artifact-load-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/artifact-load-dialog.component.js +197 -200
- package/dist/ComponentStudio/components/artifact-load-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.d.ts +5 -7
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +142 -148
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/browser/component-browser.component.js +153 -166
- package/dist/ComponentStudio/components/browser/component-browser.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/code-editor-panel.component.js +15 -20
- package/dist/ComponentStudio/components/editors/code-editor-panel.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js +16 -21
- package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/requirements-editor.component.js +18 -23
- package/dist/ComponentStudio/components/editors/requirements-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/spec-editor.component.js +25 -30
- package/dist/ComponentStudio/components/editors/spec-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js +10 -11
- package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js +24 -35
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/text-import-dialog.component.js +15 -17
- package/dist/ComponentStudio/components/text-import-dialog.component.js.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.js +7 -6
- package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +6 -5
- package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-types-resource.component.js +7 -6
- package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts +11 -0
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js +57 -0
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js +9 -9
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js.map +1 -1
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +10 -2
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +35 -11
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-resource.component.d.ts +1 -0
- package/dist/DataExplorer/data-explorer-resource.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-resource.component.js +8 -4
- package/dist/DataExplorer/data-explorer-resource.component.js.map +1 -1
- package/dist/Home/home-dashboard.component.d.ts +181 -1
- package/dist/Home/home-dashboard.component.d.ts.map +1 -1
- package/dist/Home/home-dashboard.component.js +1704 -182
- package/dist/Home/home-dashboard.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +4 -4
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +246 -259
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -1
- package/dist/Integration/components/widgets/integration-card.component.js +7 -9
- package/dist/Integration/components/widgets/integration-card.component.js.map +1 -1
- package/dist/Integration/integration.module.d.ts +6 -10
- package/dist/Integration/integration.module.d.ts.map +1 -1
- package/dist/Integration/integration.module.js +12 -20
- package/dist/Integration/integration.module.js.map +1 -1
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts +106 -0
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js +607 -0
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts +126 -0
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +1086 -0
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/components/results-detail/search-result-detail.component.d.ts +56 -0
- package/dist/KnowledgeHub/components/results-detail/search-result-detail.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/results-detail/search-result-detail.component.js +291 -0
- package/dist/KnowledgeHub/components/results-detail/search-result-detail.component.js.map +1 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.d.ts +85 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.js +461 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/index.d.ts +5 -0
- package/dist/KnowledgeHub/index.d.ts.map +1 -0
- package/dist/KnowledgeHub/index.js +6 -0
- package/dist/KnowledgeHub/index.js.map +1 -0
- package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +9 -7
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +5 -4
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +10 -9
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/MCP/components/mcp-connection-dialog.component.js +141 -132
- package/dist/MCP/components/mcp-connection-dialog.component.js.map +1 -1
- package/dist/MCP/components/mcp-log-detail-panel.component.js +4 -4
- package/dist/MCP/components/mcp-log-detail-panel.component.js.map +1 -1
- package/dist/MCP/components/mcp-server-dialog.component.js +141 -128
- package/dist/MCP/components/mcp-server-dialog.component.js.map +1 -1
- package/dist/MCP/components/mcp-test-tool-dialog.component.js +210 -218
- package/dist/MCP/components/mcp-test-tool-dialog.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +2 -2
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/MCP/mcp.module.d.ts +6 -9
- package/dist/MCP/mcp.module.d.ts.map +1 -1
- package/dist/MCP/mcp.module.js +20 -22
- package/dist/MCP/mcp.module.js.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +5 -1
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-activity.component.js +5 -4
- package/dist/Scheduling/components/scheduling-activity.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-jobs.component.js +6 -5
- package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-overview.component.js +93 -92
- package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
- package/dist/SystemDiagnostics/system-diagnostics.component.d.ts.map +1 -1
- package/dist/SystemDiagnostics/system-diagnostics.component.js +1 -0
- package/dist/SystemDiagnostics/system-diagnostics.component.js.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab-resource.component.d.ts +7 -1
- package/dist/Testing/components/testing-dashboard-tab-resource.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab-resource.component.js +63 -8
- package/dist/Testing/components/testing-dashboard-tab-resource.component.js.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab.component.d.ts +9 -1
- package/dist/Testing/components/testing-dashboard-tab.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab.component.js +109 -62
- package/dist/Testing/components/testing-dashboard-tab.component.js.map +1 -1
- package/dist/Testing/components/testing-explorer.component.d.ts +2 -1
- package/dist/Testing/components/testing-explorer.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-explorer.component.js +241 -200
- package/dist/Testing/components/testing-explorer.component.js.map +1 -1
- package/dist/Testing/components/testing-runs-resource.component.d.ts +7 -1
- package/dist/Testing/components/testing-runs-resource.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-runs-resource.component.js +63 -8
- package/dist/Testing/components/testing-runs-resource.component.js.map +1 -1
- package/dist/Testing/components/testing-runs.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-runs.component.js +7 -5
- package/dist/Testing/components/testing-runs.component.js.map +1 -1
- package/dist/Testing/testing-dashboard.component.d.ts +9 -1
- package/dist/Testing/testing-dashboard.component.d.ts.map +1 -1
- package/dist/Testing/testing-dashboard.component.js +122 -54
- package/dist/Testing/testing-dashboard.component.js.map +1 -1
- package/dist/actions-dashboards.module.d.ts +8 -13
- package/dist/actions-dashboards.module.d.ts.map +1 -1
- package/dist/actions-dashboards.module.js +6 -27
- package/dist/actions-dashboards.module.js.map +1 -1
- package/dist/ai-dashboards.module.d.ts +14 -11
- package/dist/ai-dashboards.module.d.ts.map +1 -1
- package/dist/ai-dashboards.module.js +58 -44
- package/dist/ai-dashboards.module.js.map +1 -1
- package/dist/communication-dashboards.module.d.ts +4 -8
- package/dist/communication-dashboards.module.d.ts.map +1 -1
- package/dist/communication-dashboards.module.js +0 -19
- package/dist/communication-dashboards.module.js.map +1 -1
- package/dist/component-studio-dashboards.module.d.ts +7 -11
- package/dist/component-studio-dashboards.module.d.ts.map +1 -1
- package/dist/component-studio-dashboards.module.js +22 -34
- package/dist/component-studio-dashboards.module.js.map +1 -1
- package/dist/core-dashboards.module.d.ts +13 -18
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +18 -31
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/credentials-dashboards.module.d.ts +5 -8
- package/dist/credentials-dashboards.module.d.ts.map +1 -1
- package/dist/credentials-dashboards.module.js +3 -19
- package/dist/credentials-dashboards.module.js.map +1 -1
- package/dist/data-explorer-dashboards.module.d.ts +7 -13
- package/dist/data-explorer-dashboards.module.d.ts.map +1 -1
- package/dist/data-explorer-dashboards.module.js +0 -27
- package/dist/data-explorer-dashboards.module.js.map +1 -1
- package/dist/lists-dashboards.module.d.ts +5 -8
- package/dist/lists-dashboards.module.d.ts.map +1 -1
- package/dist/lists-dashboards.module.js +3 -19
- package/dist/lists-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +5 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -1
- package/dist/public-api.js.map +1 -1
- package/dist/scheduling-dashboards.module.d.ts +6 -10
- package/dist/scheduling-dashboards.module.d.ts.map +1 -1
- package/dist/scheduling-dashboards.module.js +3 -23
- package/dist/scheduling-dashboards.module.js.map +1 -1
- package/dist/testing-dashboards.module.d.ts +7 -12
- package/dist/testing-dashboards.module.d.ts.map +1 -1
- package/dist/testing-dashboards.module.js +4 -27
- package/dist/testing-dashboards.module.js.map +1 -1
- package/package.json +47 -53
|
@@ -0,0 +1,2683 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview Duplicate Detection Kanban Board Resource Component
|
|
9
|
+
*
|
|
10
|
+
* Dashboard resource for reviewing duplicate detection results in a Kanban-style
|
|
11
|
+
* board with three columns: Pending Review, Approved, and Rejected.
|
|
12
|
+
* Supports triggering new detection runs with real-time progress via
|
|
13
|
+
* GraphQL subscriptions.
|
|
14
|
+
*/
|
|
15
|
+
import { Component, ChangeDetectorRef, Input, inject, ViewEncapsulation } from '@angular/core';
|
|
16
|
+
import { Subject } from 'rxjs';
|
|
17
|
+
import { debounceTime, takeUntil } from 'rxjs/operators';
|
|
18
|
+
import { CompositeKey, Metadata, RecordMergeRequest, RunView } from '@memberjunction/core';
|
|
19
|
+
import { MJNotificationService } from '@memberjunction/ng-notifications';
|
|
20
|
+
import { KnowledgeHubMetadataEngine } from '@memberjunction/core-entities';
|
|
21
|
+
import { RegisterClass, UUIDsEqual } from '@memberjunction/global';
|
|
22
|
+
import { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';
|
|
23
|
+
import * as i0 from "@angular/core";
|
|
24
|
+
import * as i1 from "@angular/forms";
|
|
25
|
+
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
26
|
+
const _forTrack0 = ($index, $item) => $item.ID;
|
|
27
|
+
const _forTrack1 = ($index, $item) => $item.DetailId;
|
|
28
|
+
const _forTrack2 = ($index, $item) => $item.Name;
|
|
29
|
+
const _forTrack3 = ($index, $item) => $item.Match.ID;
|
|
30
|
+
const _forTrack4 = ($index, $item) => $item.FieldName;
|
|
31
|
+
const _forTrack5 = ($index, $item) => $item.Entity;
|
|
32
|
+
const _forTrack6 = ($index, $item) => $item.ColumnIndex;
|
|
33
|
+
function DuplicateDetectionResourceComponent_For_12_Template(rf, ctx) { if (rf & 1) {
|
|
34
|
+
i0.ɵɵelementStart(0, "option", 9);
|
|
35
|
+
i0.ɵɵtext(1);
|
|
36
|
+
i0.ɵɵelementEnd();
|
|
37
|
+
} if (rf & 2) {
|
|
38
|
+
const doc_r1 = ctx.$implicit;
|
|
39
|
+
i0.ɵɵproperty("value", doc_r1.ID);
|
|
40
|
+
i0.ɵɵadvance();
|
|
41
|
+
i0.ɵɵtextInterpolate2("", doc_r1.Name, " (", doc_r1.EntityName, ")");
|
|
42
|
+
} }
|
|
43
|
+
function DuplicateDetectionResourceComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
44
|
+
i0.ɵɵelement(0, "i", 33);
|
|
45
|
+
i0.ɵɵtext(1, " Detecting... ");
|
|
46
|
+
} }
|
|
47
|
+
function DuplicateDetectionResourceComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
48
|
+
i0.ɵɵelement(0, "i", 34);
|
|
49
|
+
i0.ɵɵtext(1, " Run Detection ");
|
|
50
|
+
} }
|
|
51
|
+
function DuplicateDetectionResourceComponent_Conditional_16_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
52
|
+
i0.ɵɵelementStart(0, "span", 40);
|
|
53
|
+
i0.ɵɵtext(1);
|
|
54
|
+
i0.ɵɵelementEnd();
|
|
55
|
+
} if (rf & 2) {
|
|
56
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
57
|
+
i0.ɵɵadvance();
|
|
58
|
+
i0.ɵɵtextInterpolate(ctx_r1.DetectionCurrentItem);
|
|
59
|
+
} }
|
|
60
|
+
function DuplicateDetectionResourceComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
61
|
+
i0.ɵɵelementStart(0, "div", 11)(1, "div", 35)(2, "span", 36);
|
|
62
|
+
i0.ɵɵelement(3, "i", 33);
|
|
63
|
+
i0.ɵɵtext(4);
|
|
64
|
+
i0.ɵɵelementEnd();
|
|
65
|
+
i0.ɵɵelementStart(5, "span", 37);
|
|
66
|
+
i0.ɵɵtext(6);
|
|
67
|
+
i0.ɵɵelementEnd()();
|
|
68
|
+
i0.ɵɵelementStart(7, "div", 38);
|
|
69
|
+
i0.ɵɵelement(8, "div", 39);
|
|
70
|
+
i0.ɵɵelementEnd();
|
|
71
|
+
i0.ɵɵconditionalCreate(9, DuplicateDetectionResourceComponent_Conditional_16_Conditional_9_Template, 2, 1, "span", 40);
|
|
72
|
+
i0.ɵɵelementEnd();
|
|
73
|
+
} if (rf & 2) {
|
|
74
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
75
|
+
i0.ɵɵadvance(4);
|
|
76
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.DetectionStage, " ");
|
|
77
|
+
i0.ɵɵadvance(2);
|
|
78
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.DetectionProgress, "%");
|
|
79
|
+
i0.ɵɵadvance(2);
|
|
80
|
+
i0.ɵɵstyleProp("width", ctx_r1.DetectionProgress, "%");
|
|
81
|
+
i0.ɵɵadvance();
|
|
82
|
+
i0.ɵɵconditional(ctx_r1.DetectionCurrentItem ? 9 : -1);
|
|
83
|
+
} }
|
|
84
|
+
function DuplicateDetectionResourceComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
85
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
86
|
+
i0.ɵɵelementStart(0, "div", 12)(1, "div", 41)(2, "label", 42);
|
|
87
|
+
i0.ɵɵelement(3, "i", 43);
|
|
88
|
+
i0.ɵɵtext(4, " Potential Match ");
|
|
89
|
+
i0.ɵɵelementStart(5, "span", 44);
|
|
90
|
+
i0.ɵɵtext(6);
|
|
91
|
+
i0.ɵɵelementEnd()();
|
|
92
|
+
i0.ɵɵelementStart(7, "input", 45);
|
|
93
|
+
i0.ɵɵlistener("input", function DuplicateDetectionResourceComponent_Conditional_17_Template_input_input_7_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnPotentialThresholdChanged($event.target.value / 100)); });
|
|
94
|
+
i0.ɵɵelementEnd();
|
|
95
|
+
i0.ɵɵelementStart(8, "span", 46);
|
|
96
|
+
i0.ɵɵtext(9, "Score above which duplicates are flagged for review");
|
|
97
|
+
i0.ɵɵelementEnd()();
|
|
98
|
+
i0.ɵɵelementStart(10, "div", 41)(11, "label", 42);
|
|
99
|
+
i0.ɵɵelement(12, "i", 47);
|
|
100
|
+
i0.ɵɵtext(13, " Absolute Match ");
|
|
101
|
+
i0.ɵɵelementStart(14, "span", 44);
|
|
102
|
+
i0.ɵɵtext(15);
|
|
103
|
+
i0.ɵɵelementEnd()();
|
|
104
|
+
i0.ɵɵelementStart(16, "input", 45);
|
|
105
|
+
i0.ɵɵlistener("input", function DuplicateDetectionResourceComponent_Conditional_17_Template_input_input_16_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnAbsoluteThresholdChanged($event.target.value / 100)); });
|
|
106
|
+
i0.ɵɵelementEnd();
|
|
107
|
+
i0.ɵɵelementStart(17, "span", 46);
|
|
108
|
+
i0.ɵɵtext(18, "Score above which duplicates are auto-confirmed");
|
|
109
|
+
i0.ɵɵelementEnd()()();
|
|
110
|
+
} if (rf & 2) {
|
|
111
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
112
|
+
i0.ɵɵadvance(6);
|
|
113
|
+
i0.ɵɵtextInterpolate1("", (ctx_r1.RunPotentialThreshold * 100).toFixed(0), "%");
|
|
114
|
+
i0.ɵɵadvance();
|
|
115
|
+
i0.ɵɵproperty("min", 30)("max", 99)("step", 1)("value", ctx_r1.RunPotentialThreshold * 100)("disabled", ctx_r1.IsDetecting);
|
|
116
|
+
i0.ɵɵadvance(8);
|
|
117
|
+
i0.ɵɵtextInterpolate1("", (ctx_r1.RunAbsoluteThreshold * 100).toFixed(0), "%");
|
|
118
|
+
i0.ɵɵadvance();
|
|
119
|
+
i0.ɵɵproperty("min", 50)("max", 100)("step", 1)("value", ctx_r1.RunAbsoluteThreshold * 100)("disabled", ctx_r1.IsDetecting);
|
|
120
|
+
} }
|
|
121
|
+
function DuplicateDetectionResourceComponent_Conditional_42_Template(rf, ctx) { if (rf & 1) {
|
|
122
|
+
i0.ɵɵelementStart(0, "option", 8);
|
|
123
|
+
i0.ɵɵtext(1, "All Entities");
|
|
124
|
+
i0.ɵɵelementEnd();
|
|
125
|
+
} }
|
|
126
|
+
function DuplicateDetectionResourceComponent_For_44_Template(rf, ctx) { if (rf & 1) {
|
|
127
|
+
i0.ɵɵelementStart(0, "option", 9);
|
|
128
|
+
i0.ɵɵtext(1);
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
} if (rf & 2) {
|
|
131
|
+
const name_r4 = ctx.$implicit;
|
|
132
|
+
i0.ɵɵproperty("value", name_r4);
|
|
133
|
+
i0.ɵɵadvance();
|
|
134
|
+
i0.ɵɵtextInterpolate(name_r4);
|
|
135
|
+
} }
|
|
136
|
+
function DuplicateDetectionResourceComponent_Conditional_61_Template(rf, ctx) { if (rf & 1) {
|
|
137
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
138
|
+
i0.ɵɵelementStart(0, "button", 48);
|
|
139
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_61_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ClearFilters()); });
|
|
140
|
+
i0.ɵɵelement(1, "i", 49);
|
|
141
|
+
i0.ɵɵtext(2, " Clear Filters ");
|
|
142
|
+
i0.ɵɵelementEnd();
|
|
143
|
+
} }
|
|
144
|
+
function DuplicateDetectionResourceComponent_Conditional_62_Template(rf, ctx) { if (rf & 1) {
|
|
145
|
+
i0.ɵɵelementStart(0, "div", 28);
|
|
146
|
+
i0.ɵɵelement(1, "mj-loading", 50);
|
|
147
|
+
i0.ɵɵelementEnd();
|
|
148
|
+
} }
|
|
149
|
+
function DuplicateDetectionResourceComponent_Conditional_63_Template(rf, ctx) { if (rf & 1) {
|
|
150
|
+
i0.ɵɵelementStart(0, "div", 29);
|
|
151
|
+
i0.ɵɵelement(1, "i", 51);
|
|
152
|
+
i0.ɵɵelementStart(2, "p", 52);
|
|
153
|
+
i0.ɵɵtext(3, "No duplicate detection results found.");
|
|
154
|
+
i0.ɵɵelementEnd();
|
|
155
|
+
i0.ɵɵelementStart(4, "p", 53);
|
|
156
|
+
i0.ɵɵtext(5, "Select an entity document and click \"Run Detection\" to start.");
|
|
157
|
+
i0.ɵɵelementEnd()();
|
|
158
|
+
} }
|
|
159
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_10_Conditional_13_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
160
|
+
i0.ɵɵelementStart(0, "div", 84)(1, "span", 86);
|
|
161
|
+
i0.ɵɵtext(2);
|
|
162
|
+
i0.ɵɵelementEnd();
|
|
163
|
+
i0.ɵɵelementStart(3, "span", 87);
|
|
164
|
+
i0.ɵɵtext(4);
|
|
165
|
+
i0.ɵɵelementEnd()();
|
|
166
|
+
} if (rf & 2) {
|
|
167
|
+
const ms_r9 = ctx.$implicit;
|
|
168
|
+
i0.ɵɵadvance(2);
|
|
169
|
+
i0.ɵɵtextInterpolate1("", (ms_r9.Score * 100).toFixed(0), "%");
|
|
170
|
+
i0.ɵɵadvance(2);
|
|
171
|
+
i0.ɵɵtextInterpolate(ms_r9.Name);
|
|
172
|
+
} }
|
|
173
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_10_Conditional_13_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
174
|
+
i0.ɵɵelementStart(0, "div", 85);
|
|
175
|
+
i0.ɵɵtext(1);
|
|
176
|
+
i0.ɵɵelementEnd();
|
|
177
|
+
} if (rf & 2) {
|
|
178
|
+
const group_r8 = i0.ɵɵnextContext(2).$implicit;
|
|
179
|
+
i0.ɵɵadvance();
|
|
180
|
+
i0.ɵɵtextInterpolate1("+", group_r8.MatchCount - group_r8.TopMatchSummaries.length, " more");
|
|
181
|
+
} }
|
|
182
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_10_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
183
|
+
i0.ɵɵelementStart(0, "div", 75);
|
|
184
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_64_For_10_Conditional_13_For_2_Template, 5, 2, "div", 84, _forTrack2);
|
|
185
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_64_For_10_Conditional_13_Conditional_3_Template, 2, 1, "div", 85);
|
|
186
|
+
i0.ɵɵelementEnd();
|
|
187
|
+
} if (rf & 2) {
|
|
188
|
+
const group_r8 = i0.ɵɵnextContext().$implicit;
|
|
189
|
+
i0.ɵɵadvance();
|
|
190
|
+
i0.ɵɵrepeater(group_r8.TopMatchSummaries);
|
|
191
|
+
i0.ɵɵadvance(2);
|
|
192
|
+
i0.ɵɵconditional(group_r8.MatchCount > group_r8.TopMatchSummaries.length ? 3 : -1);
|
|
193
|
+
} }
|
|
194
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_10_Template(rf, ctx) { if (rf & 1) {
|
|
195
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
196
|
+
i0.ɵɵelementStart(0, "div", 66);
|
|
197
|
+
i0.ɵɵlistener("dragstart", function DuplicateDetectionResourceComponent_Conditional_64_For_10_Template_div_dragstart_0_listener($event) { const group_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnDragStart($event, group_r8)); })("dragend", function DuplicateDetectionResourceComponent_Conditional_64_For_10_Template_div_dragend_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnDragEnd()); })("click", function DuplicateDetectionResourceComponent_Conditional_64_For_10_Template_div_click_0_listener() { const group_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OpenComparison(group_r8)); });
|
|
198
|
+
i0.ɵɵelementStart(1, "div", 67)(2, "div", 68)(3, "div", 69);
|
|
199
|
+
i0.ɵɵelement(4, "i");
|
|
200
|
+
i0.ɵɵelementEnd();
|
|
201
|
+
i0.ɵɵelementStart(5, "div", 70)(6, "div", 71);
|
|
202
|
+
i0.ɵɵtext(7);
|
|
203
|
+
i0.ɵɵelementEnd();
|
|
204
|
+
i0.ɵɵelementStart(8, "span", 72);
|
|
205
|
+
i0.ɵɵtext(9);
|
|
206
|
+
i0.ɵɵelementEnd()()();
|
|
207
|
+
i0.ɵɵelementStart(10, "span", 73);
|
|
208
|
+
i0.ɵɵtext(11);
|
|
209
|
+
i0.ɵɵelementEnd()();
|
|
210
|
+
i0.ɵɵelementStart(12, "div", 74);
|
|
211
|
+
i0.ɵɵconditionalCreate(13, DuplicateDetectionResourceComponent_Conditional_64_For_10_Conditional_13_Template, 4, 1, "div", 75);
|
|
212
|
+
i0.ɵɵelementStart(14, "div", 76)(15, "span", 77);
|
|
213
|
+
i0.ɵɵelement(16, "i", 78);
|
|
214
|
+
i0.ɵɵtext(17);
|
|
215
|
+
i0.ɵɵelementEnd();
|
|
216
|
+
i0.ɵɵelementStart(18, "span", 77);
|
|
217
|
+
i0.ɵɵelement(19, "i", 79);
|
|
218
|
+
i0.ɵɵtext(20);
|
|
219
|
+
i0.ɵɵelementEnd()()();
|
|
220
|
+
i0.ɵɵelementStart(21, "div", 80)(22, "button", 81);
|
|
221
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_64_For_10_Template_button_click_22_listener($event) { const group_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); ctx_r1.ApproveMatch(group_r8); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
222
|
+
i0.ɵɵelement(23, "i", 82);
|
|
223
|
+
i0.ɵɵtext(24, " Approve ");
|
|
224
|
+
i0.ɵɵelementEnd();
|
|
225
|
+
i0.ɵɵelementStart(25, "button", 83);
|
|
226
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_64_For_10_Template_button_click_25_listener($event) { const group_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); ctx_r1.RejectMatch(group_r8); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
227
|
+
i0.ɵɵelement(26, "i", 49);
|
|
228
|
+
i0.ɵɵtext(27, " Reject ");
|
|
229
|
+
i0.ɵɵelementEnd()()();
|
|
230
|
+
} if (rf & 2) {
|
|
231
|
+
const group_r8 = ctx.$implicit;
|
|
232
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
233
|
+
i0.ɵɵadvance(4);
|
|
234
|
+
i0.ɵɵclassMap(group_r8.EntityIcon);
|
|
235
|
+
i0.ɵɵadvance(2);
|
|
236
|
+
i0.ɵɵproperty("title", group_r8.RecordName);
|
|
237
|
+
i0.ɵɵadvance();
|
|
238
|
+
i0.ɵɵtextInterpolate(group_r8.RecordName);
|
|
239
|
+
i0.ɵɵadvance(2);
|
|
240
|
+
i0.ɵɵtextInterpolate(group_r8.EntityName);
|
|
241
|
+
i0.ɵɵadvance();
|
|
242
|
+
i0.ɵɵclassMap(ctx_r1.GetScoreClass(group_r8.HighestScore));
|
|
243
|
+
i0.ɵɵadvance();
|
|
244
|
+
i0.ɵɵtextInterpolate1(" ", (group_r8.HighestScore * 100).toFixed(0), "% ");
|
|
245
|
+
i0.ɵɵadvance(2);
|
|
246
|
+
i0.ɵɵconditional(group_r8.TopMatchSummaries.length > 0 ? 13 : -1);
|
|
247
|
+
i0.ɵɵadvance(4);
|
|
248
|
+
i0.ɵɵtextInterpolate2(" ", group_r8.MatchCount, " match", group_r8.MatchCount !== 1 ? "es" : "", " ");
|
|
249
|
+
i0.ɵɵadvance(3);
|
|
250
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatDate(group_r8.MatchedAt), " ");
|
|
251
|
+
i0.ɵɵadvance(2);
|
|
252
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving);
|
|
253
|
+
i0.ɵɵadvance(3);
|
|
254
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving);
|
|
255
|
+
} }
|
|
256
|
+
function DuplicateDetectionResourceComponent_Conditional_64_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
257
|
+
i0.ɵɵelementStart(0, "div", 61);
|
|
258
|
+
i0.ɵɵelement(1, "i", 63);
|
|
259
|
+
i0.ɵɵelementStart(2, "span");
|
|
260
|
+
i0.ɵɵtext(3, "No pending items");
|
|
261
|
+
i0.ɵɵelementEnd()();
|
|
262
|
+
} }
|
|
263
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_21_Conditional_13_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
264
|
+
i0.ɵɵelementStart(0, "div", 84)(1, "span", 86);
|
|
265
|
+
i0.ɵɵtext(2);
|
|
266
|
+
i0.ɵɵelementEnd();
|
|
267
|
+
i0.ɵɵelementStart(3, "span", 87);
|
|
268
|
+
i0.ɵɵtext(4);
|
|
269
|
+
i0.ɵɵelementEnd()();
|
|
270
|
+
} if (rf & 2) {
|
|
271
|
+
const ms_r12 = ctx.$implicit;
|
|
272
|
+
i0.ɵɵadvance(2);
|
|
273
|
+
i0.ɵɵtextInterpolate1("", (ms_r12.Score * 100).toFixed(0), "%");
|
|
274
|
+
i0.ɵɵadvance(2);
|
|
275
|
+
i0.ɵɵtextInterpolate(ms_r12.Name);
|
|
276
|
+
} }
|
|
277
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_21_Conditional_13_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
278
|
+
i0.ɵɵelementStart(0, "div", 85);
|
|
279
|
+
i0.ɵɵtext(1);
|
|
280
|
+
i0.ɵɵelementEnd();
|
|
281
|
+
} if (rf & 2) {
|
|
282
|
+
const group_r11 = i0.ɵɵnextContext(2).$implicit;
|
|
283
|
+
i0.ɵɵadvance();
|
|
284
|
+
i0.ɵɵtextInterpolate1("+", group_r11.MatchCount - group_r11.TopMatchSummaries.length, " more");
|
|
285
|
+
} }
|
|
286
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_21_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
287
|
+
i0.ɵɵelementStart(0, "div", 75);
|
|
288
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_64_For_21_Conditional_13_For_2_Template, 5, 2, "div", 84, _forTrack2);
|
|
289
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_64_For_21_Conditional_13_Conditional_3_Template, 2, 1, "div", 85);
|
|
290
|
+
i0.ɵɵelementEnd();
|
|
291
|
+
} if (rf & 2) {
|
|
292
|
+
const group_r11 = i0.ɵɵnextContext().$implicit;
|
|
293
|
+
i0.ɵɵadvance();
|
|
294
|
+
i0.ɵɵrepeater(group_r11.TopMatchSummaries);
|
|
295
|
+
i0.ɵɵadvance(2);
|
|
296
|
+
i0.ɵɵconditional(group_r11.MatchCount > group_r11.TopMatchSummaries.length ? 3 : -1);
|
|
297
|
+
} }
|
|
298
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_21_Template(rf, ctx) { if (rf & 1) {
|
|
299
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
300
|
+
i0.ɵɵelementStart(0, "div", 66);
|
|
301
|
+
i0.ɵɵlistener("dragstart", function DuplicateDetectionResourceComponent_Conditional_64_For_21_Template_div_dragstart_0_listener($event) { const group_r11 = i0.ɵɵrestoreView(_r10).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnDragStart($event, group_r11)); })("dragend", function DuplicateDetectionResourceComponent_Conditional_64_For_21_Template_div_dragend_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnDragEnd()); })("click", function DuplicateDetectionResourceComponent_Conditional_64_For_21_Template_div_click_0_listener() { const group_r11 = i0.ɵɵrestoreView(_r10).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OpenComparison(group_r11)); });
|
|
302
|
+
i0.ɵɵelementStart(1, "div", 67)(2, "div", 68)(3, "div", 69);
|
|
303
|
+
i0.ɵɵelement(4, "i");
|
|
304
|
+
i0.ɵɵelementEnd();
|
|
305
|
+
i0.ɵɵelementStart(5, "div", 70)(6, "div", 71);
|
|
306
|
+
i0.ɵɵtext(7);
|
|
307
|
+
i0.ɵɵelementEnd();
|
|
308
|
+
i0.ɵɵelementStart(8, "span", 72);
|
|
309
|
+
i0.ɵɵtext(9);
|
|
310
|
+
i0.ɵɵelementEnd()()();
|
|
311
|
+
i0.ɵɵelementStart(10, "span", 73);
|
|
312
|
+
i0.ɵɵtext(11);
|
|
313
|
+
i0.ɵɵelementEnd()();
|
|
314
|
+
i0.ɵɵelementStart(12, "div", 74);
|
|
315
|
+
i0.ɵɵconditionalCreate(13, DuplicateDetectionResourceComponent_Conditional_64_For_21_Conditional_13_Template, 4, 1, "div", 75);
|
|
316
|
+
i0.ɵɵelementStart(14, "div", 76)(15, "span", 77);
|
|
317
|
+
i0.ɵɵelement(16, "i", 78);
|
|
318
|
+
i0.ɵɵtext(17);
|
|
319
|
+
i0.ɵɵelementEnd();
|
|
320
|
+
i0.ɵɵelementStart(18, "span", 77);
|
|
321
|
+
i0.ɵɵelement(19, "i", 79);
|
|
322
|
+
i0.ɵɵtext(20);
|
|
323
|
+
i0.ɵɵelementEnd()()()();
|
|
324
|
+
} if (rf & 2) {
|
|
325
|
+
const group_r11 = ctx.$implicit;
|
|
326
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
327
|
+
i0.ɵɵadvance(4);
|
|
328
|
+
i0.ɵɵclassMap(group_r11.EntityIcon);
|
|
329
|
+
i0.ɵɵadvance(2);
|
|
330
|
+
i0.ɵɵproperty("title", group_r11.RecordName);
|
|
331
|
+
i0.ɵɵadvance();
|
|
332
|
+
i0.ɵɵtextInterpolate(group_r11.RecordName);
|
|
333
|
+
i0.ɵɵadvance(2);
|
|
334
|
+
i0.ɵɵtextInterpolate(group_r11.EntityName);
|
|
335
|
+
i0.ɵɵadvance();
|
|
336
|
+
i0.ɵɵclassMap(ctx_r1.GetScoreClass(group_r11.HighestScore));
|
|
337
|
+
i0.ɵɵadvance();
|
|
338
|
+
i0.ɵɵtextInterpolate1(" ", (group_r11.HighestScore * 100).toFixed(0), "% ");
|
|
339
|
+
i0.ɵɵadvance(2);
|
|
340
|
+
i0.ɵɵconditional(group_r11.TopMatchSummaries.length > 0 ? 13 : -1);
|
|
341
|
+
i0.ɵɵadvance(4);
|
|
342
|
+
i0.ɵɵtextInterpolate2(" ", group_r11.MatchCount, " match", group_r11.MatchCount !== 1 ? "es" : "", " ");
|
|
343
|
+
i0.ɵɵadvance(3);
|
|
344
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatDate(group_r11.MatchedAt), " ");
|
|
345
|
+
} }
|
|
346
|
+
function DuplicateDetectionResourceComponent_Conditional_64_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
347
|
+
i0.ɵɵelementStart(0, "div", 61);
|
|
348
|
+
i0.ɵɵelement(1, "i", 88);
|
|
349
|
+
i0.ɵɵelementStart(2, "span");
|
|
350
|
+
i0.ɵɵtext(3, "No approved items");
|
|
351
|
+
i0.ɵɵelementEnd()();
|
|
352
|
+
} }
|
|
353
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_32_Conditional_13_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
354
|
+
i0.ɵɵelementStart(0, "div", 84)(1, "span", 86);
|
|
355
|
+
i0.ɵɵtext(2);
|
|
356
|
+
i0.ɵɵelementEnd();
|
|
357
|
+
i0.ɵɵelementStart(3, "span", 87);
|
|
358
|
+
i0.ɵɵtext(4);
|
|
359
|
+
i0.ɵɵelementEnd()();
|
|
360
|
+
} if (rf & 2) {
|
|
361
|
+
const ms_r15 = ctx.$implicit;
|
|
362
|
+
i0.ɵɵadvance(2);
|
|
363
|
+
i0.ɵɵtextInterpolate1("", (ms_r15.Score * 100).toFixed(0), "%");
|
|
364
|
+
i0.ɵɵadvance(2);
|
|
365
|
+
i0.ɵɵtextInterpolate(ms_r15.Name);
|
|
366
|
+
} }
|
|
367
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_32_Conditional_13_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
368
|
+
i0.ɵɵelementStart(0, "div", 85);
|
|
369
|
+
i0.ɵɵtext(1);
|
|
370
|
+
i0.ɵɵelementEnd();
|
|
371
|
+
} if (rf & 2) {
|
|
372
|
+
const group_r14 = i0.ɵɵnextContext(2).$implicit;
|
|
373
|
+
i0.ɵɵadvance();
|
|
374
|
+
i0.ɵɵtextInterpolate1("+", group_r14.MatchCount - group_r14.TopMatchSummaries.length, " more");
|
|
375
|
+
} }
|
|
376
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_32_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
377
|
+
i0.ɵɵelementStart(0, "div", 75);
|
|
378
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_64_For_32_Conditional_13_For_2_Template, 5, 2, "div", 84, _forTrack2);
|
|
379
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_64_For_32_Conditional_13_Conditional_3_Template, 2, 1, "div", 85);
|
|
380
|
+
i0.ɵɵelementEnd();
|
|
381
|
+
} if (rf & 2) {
|
|
382
|
+
const group_r14 = i0.ɵɵnextContext().$implicit;
|
|
383
|
+
i0.ɵɵadvance();
|
|
384
|
+
i0.ɵɵrepeater(group_r14.TopMatchSummaries);
|
|
385
|
+
i0.ɵɵadvance(2);
|
|
386
|
+
i0.ɵɵconditional(group_r14.MatchCount > group_r14.TopMatchSummaries.length ? 3 : -1);
|
|
387
|
+
} }
|
|
388
|
+
function DuplicateDetectionResourceComponent_Conditional_64_For_32_Template(rf, ctx) { if (rf & 1) {
|
|
389
|
+
const _r13 = i0.ɵɵgetCurrentView();
|
|
390
|
+
i0.ɵɵelementStart(0, "div", 66);
|
|
391
|
+
i0.ɵɵlistener("dragstart", function DuplicateDetectionResourceComponent_Conditional_64_For_32_Template_div_dragstart_0_listener($event) { const group_r14 = i0.ɵɵrestoreView(_r13).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnDragStart($event, group_r14)); })("dragend", function DuplicateDetectionResourceComponent_Conditional_64_For_32_Template_div_dragend_0_listener() { i0.ɵɵrestoreView(_r13); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnDragEnd()); })("click", function DuplicateDetectionResourceComponent_Conditional_64_For_32_Template_div_click_0_listener() { const group_r14 = i0.ɵɵrestoreView(_r13).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OpenComparison(group_r14)); });
|
|
392
|
+
i0.ɵɵelementStart(1, "div", 67)(2, "div", 68)(3, "div", 69);
|
|
393
|
+
i0.ɵɵelement(4, "i");
|
|
394
|
+
i0.ɵɵelementEnd();
|
|
395
|
+
i0.ɵɵelementStart(5, "div", 70)(6, "div", 71);
|
|
396
|
+
i0.ɵɵtext(7);
|
|
397
|
+
i0.ɵɵelementEnd();
|
|
398
|
+
i0.ɵɵelementStart(8, "span", 72);
|
|
399
|
+
i0.ɵɵtext(9);
|
|
400
|
+
i0.ɵɵelementEnd()()();
|
|
401
|
+
i0.ɵɵelementStart(10, "span", 73);
|
|
402
|
+
i0.ɵɵtext(11);
|
|
403
|
+
i0.ɵɵelementEnd()();
|
|
404
|
+
i0.ɵɵelementStart(12, "div", 74);
|
|
405
|
+
i0.ɵɵconditionalCreate(13, DuplicateDetectionResourceComponent_Conditional_64_For_32_Conditional_13_Template, 4, 1, "div", 75);
|
|
406
|
+
i0.ɵɵelementStart(14, "div", 76)(15, "span", 77);
|
|
407
|
+
i0.ɵɵelement(16, "i", 78);
|
|
408
|
+
i0.ɵɵtext(17);
|
|
409
|
+
i0.ɵɵelementEnd();
|
|
410
|
+
i0.ɵɵelementStart(18, "span", 77);
|
|
411
|
+
i0.ɵɵelement(19, "i", 79);
|
|
412
|
+
i0.ɵɵtext(20);
|
|
413
|
+
i0.ɵɵelementEnd()()()();
|
|
414
|
+
} if (rf & 2) {
|
|
415
|
+
const group_r14 = ctx.$implicit;
|
|
416
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
417
|
+
i0.ɵɵadvance(4);
|
|
418
|
+
i0.ɵɵclassMap(group_r14.EntityIcon);
|
|
419
|
+
i0.ɵɵadvance(2);
|
|
420
|
+
i0.ɵɵproperty("title", group_r14.RecordName);
|
|
421
|
+
i0.ɵɵadvance();
|
|
422
|
+
i0.ɵɵtextInterpolate(group_r14.RecordName);
|
|
423
|
+
i0.ɵɵadvance(2);
|
|
424
|
+
i0.ɵɵtextInterpolate(group_r14.EntityName);
|
|
425
|
+
i0.ɵɵadvance();
|
|
426
|
+
i0.ɵɵclassMap(ctx_r1.GetScoreClass(group_r14.HighestScore));
|
|
427
|
+
i0.ɵɵadvance();
|
|
428
|
+
i0.ɵɵtextInterpolate1(" ", (group_r14.HighestScore * 100).toFixed(0), "% ");
|
|
429
|
+
i0.ɵɵadvance(2);
|
|
430
|
+
i0.ɵɵconditional(group_r14.TopMatchSummaries.length > 0 ? 13 : -1);
|
|
431
|
+
i0.ɵɵadvance(4);
|
|
432
|
+
i0.ɵɵtextInterpolate2(" ", group_r14.MatchCount, " match", group_r14.MatchCount !== 1 ? "es" : "", " ");
|
|
433
|
+
i0.ɵɵadvance(3);
|
|
434
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatDate(group_r14.MatchedAt), " ");
|
|
435
|
+
} }
|
|
436
|
+
function DuplicateDetectionResourceComponent_Conditional_64_Conditional_33_Template(rf, ctx) { if (rf & 1) {
|
|
437
|
+
i0.ɵɵelementStart(0, "div", 61);
|
|
438
|
+
i0.ɵɵelement(1, "i", 88);
|
|
439
|
+
i0.ɵɵelementStart(2, "span");
|
|
440
|
+
i0.ɵɵtext(3, "No rejected items");
|
|
441
|
+
i0.ɵɵelementEnd()();
|
|
442
|
+
} }
|
|
443
|
+
function DuplicateDetectionResourceComponent_Conditional_64_Template(rf, ctx) { if (rf & 1) {
|
|
444
|
+
const _r6 = i0.ɵɵgetCurrentView();
|
|
445
|
+
i0.ɵɵelementStart(0, "div", 30)(1, "div", 54)(2, "div", 55);
|
|
446
|
+
i0.ɵɵelement(3, "i", 56);
|
|
447
|
+
i0.ɵɵelementStart(4, "span", 57);
|
|
448
|
+
i0.ɵɵtext(5, "Pending Review");
|
|
449
|
+
i0.ɵɵelementEnd();
|
|
450
|
+
i0.ɵɵelementStart(6, "span", 58);
|
|
451
|
+
i0.ɵɵtext(7);
|
|
452
|
+
i0.ɵɵelementEnd()();
|
|
453
|
+
i0.ɵɵelementStart(8, "div", 59);
|
|
454
|
+
i0.ɵɵlistener("dragover", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_dragover_8_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDragOver($event, "Pending")); })("dragleave", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_dragleave_8_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDragLeave($event, "Pending")); })("drop", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_drop_8_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDrop($event, "Pending")); });
|
|
455
|
+
i0.ɵɵrepeaterCreate(9, DuplicateDetectionResourceComponent_Conditional_64_For_10_Template, 28, 14, "div", 60, _forTrack1);
|
|
456
|
+
i0.ɵɵconditionalCreate(11, DuplicateDetectionResourceComponent_Conditional_64_Conditional_11_Template, 4, 0, "div", 61);
|
|
457
|
+
i0.ɵɵelementEnd()();
|
|
458
|
+
i0.ɵɵelementStart(12, "div", 54)(13, "div", 62);
|
|
459
|
+
i0.ɵɵelement(14, "i", 63);
|
|
460
|
+
i0.ɵɵelementStart(15, "span", 57);
|
|
461
|
+
i0.ɵɵtext(16, "Approved");
|
|
462
|
+
i0.ɵɵelementEnd();
|
|
463
|
+
i0.ɵɵelementStart(17, "span", 58);
|
|
464
|
+
i0.ɵɵtext(18);
|
|
465
|
+
i0.ɵɵelementEnd()();
|
|
466
|
+
i0.ɵɵelementStart(19, "div", 59);
|
|
467
|
+
i0.ɵɵlistener("dragover", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_dragover_19_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDragOver($event, "Approved")); })("dragleave", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_dragleave_19_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDragLeave($event, "Approved")); })("drop", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_drop_19_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDrop($event, "Approved")); });
|
|
468
|
+
i0.ɵɵrepeaterCreate(20, DuplicateDetectionResourceComponent_Conditional_64_For_21_Template, 21, 12, "div", 60, _forTrack1);
|
|
469
|
+
i0.ɵɵconditionalCreate(22, DuplicateDetectionResourceComponent_Conditional_64_Conditional_22_Template, 4, 0, "div", 61);
|
|
470
|
+
i0.ɵɵelementEnd()();
|
|
471
|
+
i0.ɵɵelementStart(23, "div", 54)(24, "div", 64);
|
|
472
|
+
i0.ɵɵelement(25, "i", 65);
|
|
473
|
+
i0.ɵɵelementStart(26, "span", 57);
|
|
474
|
+
i0.ɵɵtext(27, "Rejected");
|
|
475
|
+
i0.ɵɵelementEnd();
|
|
476
|
+
i0.ɵɵelementStart(28, "span", 58);
|
|
477
|
+
i0.ɵɵtext(29);
|
|
478
|
+
i0.ɵɵelementEnd()();
|
|
479
|
+
i0.ɵɵelementStart(30, "div", 59);
|
|
480
|
+
i0.ɵɵlistener("dragover", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_dragover_30_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDragOver($event, "Rejected")); })("dragleave", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_dragleave_30_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDragLeave($event, "Rejected")); })("drop", function DuplicateDetectionResourceComponent_Conditional_64_Template_div_drop_30_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDrop($event, "Rejected")); });
|
|
481
|
+
i0.ɵɵrepeaterCreate(31, DuplicateDetectionResourceComponent_Conditional_64_For_32_Template, 21, 12, "div", 60, _forTrack1);
|
|
482
|
+
i0.ɵɵconditionalCreate(33, DuplicateDetectionResourceComponent_Conditional_64_Conditional_33_Template, 4, 0, "div", 61);
|
|
483
|
+
i0.ɵɵelementEnd()()();
|
|
484
|
+
} if (rf & 2) {
|
|
485
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
486
|
+
i0.ɵɵadvance();
|
|
487
|
+
i0.ɵɵclassProp("drop-target-active", ctx_r1.DragOverColumn === "Pending" && (ctx_r1.DraggedGroup == null ? null : ctx_r1.DraggedGroup.ApprovalStatus) !== "Pending");
|
|
488
|
+
i0.ɵɵadvance(6);
|
|
489
|
+
i0.ɵɵtextInterpolate(ctx_r1.PendingCount);
|
|
490
|
+
i0.ɵɵadvance(2);
|
|
491
|
+
i0.ɵɵrepeater(ctx_r1.PendingGroups);
|
|
492
|
+
i0.ɵɵadvance(2);
|
|
493
|
+
i0.ɵɵconditional(ctx_r1.PendingGroups.length === 0 ? 11 : -1);
|
|
494
|
+
i0.ɵɵadvance();
|
|
495
|
+
i0.ɵɵclassProp("drop-target-active", ctx_r1.DragOverColumn === "Approved" && (ctx_r1.DraggedGroup == null ? null : ctx_r1.DraggedGroup.ApprovalStatus) !== "Approved");
|
|
496
|
+
i0.ɵɵadvance(6);
|
|
497
|
+
i0.ɵɵtextInterpolate(ctx_r1.ApprovedCount);
|
|
498
|
+
i0.ɵɵadvance(2);
|
|
499
|
+
i0.ɵɵrepeater(ctx_r1.ApprovedGroups);
|
|
500
|
+
i0.ɵɵadvance(2);
|
|
501
|
+
i0.ɵɵconditional(ctx_r1.ApprovedGroups.length === 0 ? 22 : -1);
|
|
502
|
+
i0.ɵɵadvance();
|
|
503
|
+
i0.ɵɵclassProp("drop-target-active", ctx_r1.DragOverColumn === "Rejected" && (ctx_r1.DraggedGroup == null ? null : ctx_r1.DraggedGroup.ApprovalStatus) !== "Rejected");
|
|
504
|
+
i0.ɵɵadvance(6);
|
|
505
|
+
i0.ɵɵtextInterpolate(ctx_r1.RejectedCount);
|
|
506
|
+
i0.ɵɵadvance(2);
|
|
507
|
+
i0.ɵɵrepeater(ctx_r1.RejectedGroups);
|
|
508
|
+
i0.ɵɵadvance(2);
|
|
509
|
+
i0.ɵɵconditional(ctx_r1.RejectedGroups.length === 0 ? 33 : -1);
|
|
510
|
+
} }
|
|
511
|
+
function DuplicateDetectionResourceComponent_Conditional_65_Template(rf, ctx) { if (rf & 1) {
|
|
512
|
+
i0.ɵɵelementStart(0, "div", 31);
|
|
513
|
+
i0.ɵɵelement(1, "mj-loading", 89);
|
|
514
|
+
i0.ɵɵelementEnd();
|
|
515
|
+
} }
|
|
516
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
517
|
+
i0.ɵɵelementStart(0, "div", 102);
|
|
518
|
+
i0.ɵɵelement(1, "mj-loading", 127);
|
|
519
|
+
i0.ɵɵelementEnd();
|
|
520
|
+
} }
|
|
521
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_44_Template(rf, ctx) { if (rf & 1) {
|
|
522
|
+
i0.ɵɵelementStart(0, "span", 118);
|
|
523
|
+
i0.ɵɵelement(1, "i", 128);
|
|
524
|
+
i0.ɵɵtext(2, " Most deps");
|
|
525
|
+
i0.ɵɵelementEnd();
|
|
526
|
+
} }
|
|
527
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Conditional_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
528
|
+
i0.ɵɵelementStart(0, "div", 137);
|
|
529
|
+
i0.ɵɵelement(1, "i", 33);
|
|
530
|
+
i0.ɵɵtext(2, " Loading...");
|
|
531
|
+
i0.ɵɵelementEnd();
|
|
532
|
+
} }
|
|
533
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Conditional_7_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
534
|
+
const _r20 = i0.ɵɵgetCurrentView();
|
|
535
|
+
i0.ɵɵelementStart(0, "div", 139);
|
|
536
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Conditional_7_For_3_Template_div_click_0_listener($event) { const record_r21 = i0.ɵɵrestoreView(_r20).$implicit; const ctx_r1 = i0.ɵɵnextContext(6); ctx_r1.OpenDepRecord(record_r21); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
537
|
+
i0.ɵɵelement(1, "i", 140);
|
|
538
|
+
i0.ɵɵelementStart(2, "span", 141);
|
|
539
|
+
i0.ɵɵtext(3);
|
|
540
|
+
i0.ɵɵelementEnd()();
|
|
541
|
+
} if (rf & 2) {
|
|
542
|
+
const record_r21 = ctx.$implicit;
|
|
543
|
+
i0.ɵɵadvance(3);
|
|
544
|
+
i0.ɵɵtextInterpolate(record_r21.Name);
|
|
545
|
+
} }
|
|
546
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
547
|
+
i0.ɵɵelementStart(0, "div", 136);
|
|
548
|
+
i0.ɵɵconditionalCreate(1, DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Conditional_7_Conditional_1_Template, 3, 0, "div", 137);
|
|
549
|
+
i0.ɵɵrepeaterCreate(2, DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Conditional_7_For_3_Template, 4, 1, "div", 138, i0.ɵɵrepeaterTrackByIndex);
|
|
550
|
+
i0.ɵɵelementEnd();
|
|
551
|
+
} if (rf & 2) {
|
|
552
|
+
const dep_r19 = i0.ɵɵnextContext().$implicit;
|
|
553
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
554
|
+
i0.ɵɵadvance();
|
|
555
|
+
i0.ɵɵconditional(ctx_r1.IsDepRecordsLoading(0, dep_r19.Entity) ? 1 : -1);
|
|
556
|
+
i0.ɵɵadvance();
|
|
557
|
+
i0.ɵɵrepeater(ctx_r1.GetDepRecords(0, dep_r19.Entity));
|
|
558
|
+
} }
|
|
559
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
560
|
+
const _r18 = i0.ɵɵgetCurrentView();
|
|
561
|
+
i0.ɵɵelementStart(0, "div", 131)(1, "div", 132);
|
|
562
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Template_div_click_1_listener($event) { const dep_r19 = i0.ɵɵrestoreView(_r18).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); ctx_r1.ToggleDepEntityGroup(0, dep_r19.Entity); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
563
|
+
i0.ɵɵelementStart(2, "span", 133);
|
|
564
|
+
i0.ɵɵelement(3, "i", 134);
|
|
565
|
+
i0.ɵɵtext(4);
|
|
566
|
+
i0.ɵɵelementEnd();
|
|
567
|
+
i0.ɵɵelementStart(5, "span", 135);
|
|
568
|
+
i0.ɵɵtext(6);
|
|
569
|
+
i0.ɵɵelementEnd()();
|
|
570
|
+
i0.ɵɵconditionalCreate(7, DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Conditional_7_Template, 4, 1, "div", 136);
|
|
571
|
+
i0.ɵɵelementEnd();
|
|
572
|
+
} if (rf & 2) {
|
|
573
|
+
const dep_r19 = ctx.$implicit;
|
|
574
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
575
|
+
i0.ɵɵadvance(3);
|
|
576
|
+
i0.ɵɵclassProp("fa-chevron-right", !ctx_r1.IsDepEntityGroupExpanded(0, dep_r19.Entity))("fa-chevron-down", ctx_r1.IsDepEntityGroupExpanded(0, dep_r19.Entity));
|
|
577
|
+
i0.ɵɵadvance();
|
|
578
|
+
i0.ɵɵtextInterpolate1(" ", dep_r19.Entity, " ");
|
|
579
|
+
i0.ɵɵadvance(2);
|
|
580
|
+
i0.ɵɵtextInterpolate(dep_r19.Count);
|
|
581
|
+
i0.ɵɵadvance();
|
|
582
|
+
i0.ɵɵconditional(ctx_r1.IsDepEntityGroupExpanded(0, dep_r19.Entity) ? 7 : -1);
|
|
583
|
+
} }
|
|
584
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
585
|
+
i0.ɵɵelementStart(0, "div", 130);
|
|
586
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_For_2_Template, 8, 7, "div", 131, _forTrack5);
|
|
587
|
+
i0.ɵɵelementEnd();
|
|
588
|
+
} if (rf & 2) {
|
|
589
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
590
|
+
i0.ɵɵadvance();
|
|
591
|
+
i0.ɵɵrepeater(ctx_r1.GetGroupedDeps(0));
|
|
592
|
+
} }
|
|
593
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Template(rf, ctx) { if (rf & 1) {
|
|
594
|
+
const _r17 = i0.ɵɵgetCurrentView();
|
|
595
|
+
i0.ɵɵelementStart(0, "div", 129);
|
|
596
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleDepsExpanded(0)); });
|
|
597
|
+
i0.ɵɵelementStart(1, "span");
|
|
598
|
+
i0.ɵɵtext(2);
|
|
599
|
+
i0.ɵɵelementEnd();
|
|
600
|
+
i0.ɵɵelement(3, "i", 113);
|
|
601
|
+
i0.ɵɵelementEnd();
|
|
602
|
+
i0.ɵɵconditionalCreate(4, DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Conditional_4_Template, 3, 0, "div", 130);
|
|
603
|
+
} if (rf & 2) {
|
|
604
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
605
|
+
i0.ɵɵadvance(2);
|
|
606
|
+
i0.ɵɵtextInterpolate(ctx_r1.IsDepsExpanded(0) ? "Hide details" : "Show details");
|
|
607
|
+
i0.ɵɵadvance();
|
|
608
|
+
i0.ɵɵclassProp("fa-chevron-down", !ctx_r1.IsDepsExpanded(0))("fa-chevron-up", ctx_r1.IsDepsExpanded(0));
|
|
609
|
+
i0.ɵɵadvance();
|
|
610
|
+
i0.ɵɵconditional(ctx_r1.IsDepsExpanded(0) ? 4 : -1);
|
|
611
|
+
} }
|
|
612
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
613
|
+
i0.ɵɵelementStart(0, "span", 118);
|
|
614
|
+
i0.ɵɵelement(1, "i", 128);
|
|
615
|
+
i0.ɵɵtext(2, " Most deps");
|
|
616
|
+
i0.ɵɵelementEnd();
|
|
617
|
+
} }
|
|
618
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
619
|
+
i0.ɵɵelementStart(0, "div", 137);
|
|
620
|
+
i0.ɵɵelement(1, "i", 33);
|
|
621
|
+
i0.ɵɵtext(2, " Loading...");
|
|
622
|
+
i0.ɵɵelementEnd();
|
|
623
|
+
} }
|
|
624
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
625
|
+
const _r27 = i0.ɵɵgetCurrentView();
|
|
626
|
+
i0.ɵɵelementStart(0, "div", 139);
|
|
627
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_For_3_Template_div_click_0_listener($event) { const record_r28 = i0.ɵɵrestoreView(_r27).$implicit; const ctx_r1 = i0.ɵɵnextContext(7); ctx_r1.OpenDepRecord(record_r28); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
628
|
+
i0.ɵɵelement(1, "i", 140);
|
|
629
|
+
i0.ɵɵelementStart(2, "span", 141);
|
|
630
|
+
i0.ɵɵtext(3);
|
|
631
|
+
i0.ɵɵelementEnd()();
|
|
632
|
+
} if (rf & 2) {
|
|
633
|
+
const record_r28 = ctx.$implicit;
|
|
634
|
+
i0.ɵɵadvance(3);
|
|
635
|
+
i0.ɵɵtextInterpolate(record_r28.Name);
|
|
636
|
+
} }
|
|
637
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
638
|
+
i0.ɵɵelementStart(0, "div", 136);
|
|
639
|
+
i0.ɵɵconditionalCreate(1, DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_Conditional_1_Template, 3, 0, "div", 137);
|
|
640
|
+
i0.ɵɵrepeaterCreate(2, DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_For_3_Template, 4, 1, "div", 138, i0.ɵɵrepeaterTrackByIndex);
|
|
641
|
+
i0.ɵɵelementEnd();
|
|
642
|
+
} if (rf & 2) {
|
|
643
|
+
const dep_r26 = i0.ɵɵnextContext().$implicit;
|
|
644
|
+
const ɵ$index_556_r23 = i0.ɵɵnextContext(3).$index;
|
|
645
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
646
|
+
i0.ɵɵadvance();
|
|
647
|
+
i0.ɵɵconditional(ctx_r1.IsDepRecordsLoading(ɵ$index_556_r23 + 1, dep_r26.Entity) ? 1 : -1);
|
|
648
|
+
i0.ɵɵadvance();
|
|
649
|
+
i0.ɵɵrepeater(ctx_r1.GetDepRecords(ɵ$index_556_r23 + 1, dep_r26.Entity));
|
|
650
|
+
} }
|
|
651
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
652
|
+
const _r25 = i0.ɵɵgetCurrentView();
|
|
653
|
+
i0.ɵɵelementStart(0, "div", 131)(1, "div", 132);
|
|
654
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Template_div_click_1_listener($event) { const dep_r26 = i0.ɵɵrestoreView(_r25).$implicit; const ɵ$index_556_r23 = i0.ɵɵnextContext(3).$index; const ctx_r1 = i0.ɵɵnextContext(2); ctx_r1.ToggleDepEntityGroup(ɵ$index_556_r23 + 1, dep_r26.Entity); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
655
|
+
i0.ɵɵelementStart(2, "span", 133);
|
|
656
|
+
i0.ɵɵelement(3, "i", 134);
|
|
657
|
+
i0.ɵɵtext(4);
|
|
658
|
+
i0.ɵɵelementEnd();
|
|
659
|
+
i0.ɵɵelementStart(5, "span", 135);
|
|
660
|
+
i0.ɵɵtext(6);
|
|
661
|
+
i0.ɵɵelementEnd()();
|
|
662
|
+
i0.ɵɵconditionalCreate(7, DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_Template, 4, 1, "div", 136);
|
|
663
|
+
i0.ɵɵelementEnd();
|
|
664
|
+
} if (rf & 2) {
|
|
665
|
+
const dep_r26 = ctx.$implicit;
|
|
666
|
+
const ɵ$index_556_r23 = i0.ɵɵnextContext(3).$index;
|
|
667
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
668
|
+
i0.ɵɵadvance(3);
|
|
669
|
+
i0.ɵɵclassProp("fa-chevron-right", !ctx_r1.IsDepEntityGroupExpanded(ɵ$index_556_r23 + 1, dep_r26.Entity))("fa-chevron-down", ctx_r1.IsDepEntityGroupExpanded(ɵ$index_556_r23 + 1, dep_r26.Entity));
|
|
670
|
+
i0.ɵɵadvance();
|
|
671
|
+
i0.ɵɵtextInterpolate1(" ", dep_r26.Entity, " ");
|
|
672
|
+
i0.ɵɵadvance(2);
|
|
673
|
+
i0.ɵɵtextInterpolate(dep_r26.Count);
|
|
674
|
+
i0.ɵɵadvance();
|
|
675
|
+
i0.ɵɵconditional(ctx_r1.IsDepEntityGroupExpanded(ɵ$index_556_r23 + 1, dep_r26.Entity) ? 7 : -1);
|
|
676
|
+
} }
|
|
677
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
678
|
+
i0.ɵɵelementStart(0, "div", 130);
|
|
679
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_For_2_Template, 8, 7, "div", 131, _forTrack5);
|
|
680
|
+
i0.ɵɵelementEnd();
|
|
681
|
+
} if (rf & 2) {
|
|
682
|
+
const ɵ$index_556_r23 = i0.ɵɵnextContext(2).$index;
|
|
683
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
684
|
+
i0.ɵɵadvance();
|
|
685
|
+
i0.ɵɵrepeater(ctx_r1.GetGroupedDeps(ɵ$index_556_r23 + 1));
|
|
686
|
+
} }
|
|
687
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
688
|
+
const _r24 = i0.ɵɵgetCurrentView();
|
|
689
|
+
i0.ɵɵelementStart(0, "div", 129);
|
|
690
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r24); const ɵ$index_556_r23 = i0.ɵɵnextContext().$index; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleDepsExpanded(ɵ$index_556_r23 + 1)); });
|
|
691
|
+
i0.ɵɵelementStart(1, "span");
|
|
692
|
+
i0.ɵɵtext(2);
|
|
693
|
+
i0.ɵɵelementEnd();
|
|
694
|
+
i0.ɵɵelement(3, "i", 113);
|
|
695
|
+
i0.ɵɵelementEnd();
|
|
696
|
+
i0.ɵɵconditionalCreate(4, DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Conditional_4_Template, 3, 0, "div", 130);
|
|
697
|
+
} if (rf & 2) {
|
|
698
|
+
const ɵ$index_556_r23 = i0.ɵɵnextContext().$index;
|
|
699
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
700
|
+
i0.ɵɵadvance(2);
|
|
701
|
+
i0.ɵɵtextInterpolate(ctx_r1.IsDepsExpanded(ɵ$index_556_r23 + 1) ? "Hide details" : "Show details");
|
|
702
|
+
i0.ɵɵadvance();
|
|
703
|
+
i0.ɵɵclassProp("fa-chevron-down", !ctx_r1.IsDepsExpanded(ɵ$index_556_r23 + 1))("fa-chevron-up", ctx_r1.IsDepsExpanded(ɵ$index_556_r23 + 1));
|
|
704
|
+
i0.ɵɵadvance();
|
|
705
|
+
i0.ɵɵconditional(ctx_r1.IsDepsExpanded(ɵ$index_556_r23 + 1) ? 4 : -1);
|
|
706
|
+
} }
|
|
707
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
708
|
+
i0.ɵɵelementStart(0, "span", 146);
|
|
709
|
+
i0.ɵɵelement(1, "i", 113);
|
|
710
|
+
i0.ɵɵtext(2);
|
|
711
|
+
i0.ɵɵelementEnd();
|
|
712
|
+
} if (rf & 2) {
|
|
713
|
+
const match_r29 = i0.ɵɵnextContext().$implicit;
|
|
714
|
+
i0.ɵɵclassProp("status-approved", match_r29.Match.ApprovalStatus === "Approved")("status-rejected", match_r29.Match.ApprovalStatus === "Rejected");
|
|
715
|
+
i0.ɵɵadvance();
|
|
716
|
+
i0.ɵɵclassProp("fa-check", match_r29.Match.ApprovalStatus === "Approved")("fa-times", match_r29.Match.ApprovalStatus === "Rejected");
|
|
717
|
+
i0.ɵɵadvance();
|
|
718
|
+
i0.ɵɵtextInterpolate1(" ", match_r29.Match.ApprovalStatus, " ");
|
|
719
|
+
} }
|
|
720
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_47_Template(rf, ctx) { if (rf & 1) {
|
|
721
|
+
const _r22 = i0.ɵɵgetCurrentView();
|
|
722
|
+
i0.ɵɵelementStart(0, "div", 142)(1, "div", 143)(2, "span", 108);
|
|
723
|
+
i0.ɵɵtext(3);
|
|
724
|
+
i0.ɵɵelementEnd();
|
|
725
|
+
i0.ɵɵelementStart(4, "span", 73);
|
|
726
|
+
i0.ɵɵtext(5);
|
|
727
|
+
i0.ɵɵelementEnd()();
|
|
728
|
+
i0.ɵɵelementStart(6, "span", 144);
|
|
729
|
+
i0.ɵɵtext(7);
|
|
730
|
+
i0.ɵɵelementEnd();
|
|
731
|
+
i0.ɵɵelementStart(8, "div", 109)(9, "input", 110);
|
|
732
|
+
i0.ɵɵlistener("change", function DuplicateDetectionResourceComponent_Conditional_66_For_47_Template_input_change_9_listener() { const ɵ$index_556_r23 = i0.ɵɵrestoreView(_r22).$index; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SetSurvivor(ɵ$index_556_r23 + 1)); });
|
|
733
|
+
i0.ɵɵelementEnd();
|
|
734
|
+
i0.ɵɵelementStart(10, "span", 111);
|
|
735
|
+
i0.ɵɵtext(11);
|
|
736
|
+
i0.ɵɵelementEnd()();
|
|
737
|
+
i0.ɵɵelementStart(12, "button", 112);
|
|
738
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_For_47_Template_button_click_12_listener() { const ɵ$index_556_r23 = i0.ɵɵrestoreView(_r22).$index; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.UseAllFieldsFrom(ɵ$index_556_r23 + 1)); });
|
|
739
|
+
i0.ɵɵelement(13, "i", 113);
|
|
740
|
+
i0.ɵɵtext(14);
|
|
741
|
+
i0.ɵɵelementEnd();
|
|
742
|
+
i0.ɵɵelementStart(15, "div", 114)(16, "div", 115);
|
|
743
|
+
i0.ɵɵelement(17, "i", 116);
|
|
744
|
+
i0.ɵɵelementStart(18, "span", 117);
|
|
745
|
+
i0.ɵɵtext(19);
|
|
746
|
+
i0.ɵɵelementEnd();
|
|
747
|
+
i0.ɵɵtext(20);
|
|
748
|
+
i0.ɵɵconditionalCreate(21, DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_21_Template, 3, 0, "span", 118);
|
|
749
|
+
i0.ɵɵelementEnd();
|
|
750
|
+
i0.ɵɵconditionalCreate(22, DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_22_Template, 5, 6);
|
|
751
|
+
i0.ɵɵelementEnd();
|
|
752
|
+
i0.ɵɵconditionalCreate(23, DuplicateDetectionResourceComponent_Conditional_66_For_47_Conditional_23_Template, 3, 9, "span", 145);
|
|
753
|
+
i0.ɵɵelementEnd();
|
|
754
|
+
} if (rf & 2) {
|
|
755
|
+
const match_r29 = ctx.$implicit;
|
|
756
|
+
const ɵ$index_556_r23 = ctx.$index;
|
|
757
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
758
|
+
i0.ɵɵclassProp("match-approved", match_r29.Match.ApprovalStatus === "Approved")("match-rejected", match_r29.Match.ApprovalStatus === "Rejected");
|
|
759
|
+
i0.ɵɵadvance(3);
|
|
760
|
+
i0.ɵɵtextInterpolate(match_r29.Name);
|
|
761
|
+
i0.ɵɵadvance();
|
|
762
|
+
i0.ɵɵclassMap(ctx_r1.GetScoreClass(match_r29.Score));
|
|
763
|
+
i0.ɵɵadvance();
|
|
764
|
+
i0.ɵɵtextInterpolate1(" ", (match_r29.Score * 100).toFixed(0), "% ");
|
|
765
|
+
i0.ɵɵadvance(2);
|
|
766
|
+
i0.ɵɵtextInterpolate2("", match_r29.DiffCount, " difference", match_r29.DiffCount !== 1 ? "s" : "");
|
|
767
|
+
i0.ɵɵadvance(2);
|
|
768
|
+
i0.ɵɵproperty("checked", ctx_r1.SurvivorColumnIndex === ɵ$index_556_r23 + 1);
|
|
769
|
+
i0.ɵɵadvance();
|
|
770
|
+
i0.ɵɵclassProp("is-survivor", ctx_r1.SurvivorColumnIndex === ɵ$index_556_r23 + 1);
|
|
771
|
+
i0.ɵɵadvance();
|
|
772
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.SurvivorColumnIndex === ɵ$index_556_r23 + 1 ? "Surviving Record" : "Set as survivor", " ");
|
|
773
|
+
i0.ɵɵadvance();
|
|
774
|
+
i0.ɵɵclassProp("all-selected", ctx_r1.AllFieldsSelectedFrom(ɵ$index_556_r23 + 1));
|
|
775
|
+
i0.ɵɵadvance();
|
|
776
|
+
i0.ɵɵclassProp("fa-check-double", ctx_r1.AllFieldsSelectedFrom(ɵ$index_556_r23 + 1))("fa-clone", !ctx_r1.AllFieldsSelectedFrom(ɵ$index_556_r23 + 1));
|
|
777
|
+
i0.ɵɵadvance();
|
|
778
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.AllFieldsSelectedFrom(ɵ$index_556_r23 + 1) ? "Using all fields" : "Use all fields", " ");
|
|
779
|
+
i0.ɵɵadvance(5);
|
|
780
|
+
i0.ɵɵtextInterpolate(ctx_r1.GetTotalDeps(ɵ$index_556_r23 + 1));
|
|
781
|
+
i0.ɵɵadvance();
|
|
782
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.GetTotalDeps(ɵ$index_556_r23 + 1) === 1 ? "dependency" : "dependencies", " ");
|
|
783
|
+
i0.ɵɵadvance();
|
|
784
|
+
i0.ɵɵconditional(ctx_r1.GetMaxDepsColumnIndex() === ɵ$index_556_r23 + 1 && ctx_r1.GetTotalDeps(ɵ$index_556_r23 + 1) > 0 ? 21 : -1);
|
|
785
|
+
i0.ɵɵadvance();
|
|
786
|
+
i0.ɵɵconditional(ctx_r1.GetGroupedDeps(ɵ$index_556_r23 + 1).length > 0 ? 22 : -1);
|
|
787
|
+
i0.ɵɵadvance();
|
|
788
|
+
i0.ɵɵconditional(match_r29.Match.ApprovalStatus !== "Pending" ? 23 : -1);
|
|
789
|
+
} }
|
|
790
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_49_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
791
|
+
i0.ɵɵtext(0);
|
|
792
|
+
} if (rf & 2) {
|
|
793
|
+
const field_r31 = i0.ɵɵnextContext().$implicit;
|
|
794
|
+
i0.ɵɵtextInterpolate1(" ", field_r31.SourceValue, " ");
|
|
795
|
+
} }
|
|
796
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_49_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
797
|
+
i0.ɵɵelementStart(0, "span", 149);
|
|
798
|
+
i0.ɵɵtext(1, "(not available)");
|
|
799
|
+
i0.ɵɵelementEnd();
|
|
800
|
+
} }
|
|
801
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_49_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
802
|
+
const _r32 = i0.ɵɵgetCurrentView();
|
|
803
|
+
i0.ɵɵelementStart(0, "input", 152);
|
|
804
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_For_49_Conditional_5_Template_input_click_0_listener($event) { i0.ɵɵrestoreView(_r32); return i0.ɵɵresetView($event.stopPropagation()); })("change", function DuplicateDetectionResourceComponent_Conditional_66_For_49_Conditional_5_Template_input_change_0_listener() { i0.ɵɵrestoreView(_r32); const field_r31 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectFieldValue(field_r31, 0)); });
|
|
805
|
+
i0.ɵɵelementEnd();
|
|
806
|
+
} if (rf & 2) {
|
|
807
|
+
const field_r31 = i0.ɵɵnextContext().$implicit;
|
|
808
|
+
i0.ɵɵproperty("name", "field-" + field_r31.FieldName)("checked", field_r31.SelectedColumnIndex === 0);
|
|
809
|
+
} }
|
|
810
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
811
|
+
const _r37 = i0.ɵɵgetCurrentView();
|
|
812
|
+
i0.ɵɵelementStart(0, "input", 152);
|
|
813
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Conditional_1_Conditional_1_Template_input_click_0_listener($event) { i0.ɵɵrestoreView(_r37); return i0.ɵɵresetView($event.stopPropagation()); })("change", function DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Conditional_1_Conditional_1_Template_input_change_0_listener() { i0.ɵɵrestoreView(_r37); const ɵ$index_660_r36 = i0.ɵɵnextContext(2).$index; const field_r31 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectFieldValue(field_r31, ɵ$index_660_r36 + 1)); });
|
|
814
|
+
i0.ɵɵelementEnd();
|
|
815
|
+
} if (rf & 2) {
|
|
816
|
+
const ɵ$index_660_r36 = i0.ɵɵnextContext(2).$index;
|
|
817
|
+
const field_r31 = i0.ɵɵnextContext().$implicit;
|
|
818
|
+
i0.ɵɵproperty("name", "field-" + field_r31.FieldName)("checked", field_r31.SelectedColumnIndex === ɵ$index_660_r36 + 1);
|
|
819
|
+
} }
|
|
820
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
821
|
+
i0.ɵɵtext(0);
|
|
822
|
+
i0.ɵɵconditionalCreate(1, DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Conditional_1_Conditional_1_Template, 1, 2, "input", 150);
|
|
823
|
+
} if (rf & 2) {
|
|
824
|
+
const ctx_r37 = i0.ɵɵnextContext();
|
|
825
|
+
const matchVal_r35 = ctx_r37.$implicit;
|
|
826
|
+
const ɵ$index_660_r36 = ctx_r37.$index;
|
|
827
|
+
const field_r31 = i0.ɵɵnextContext().$implicit;
|
|
828
|
+
i0.ɵɵtextInterpolate1(" ", matchVal_r35, " ");
|
|
829
|
+
i0.ɵɵadvance();
|
|
830
|
+
i0.ɵɵconditional(field_r31.HasDifference || field_r31.SelectedColumnIndex === ɵ$index_660_r36 + 1 ? 1 : -1);
|
|
831
|
+
} }
|
|
832
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
833
|
+
i0.ɵɵelementStart(0, "span", 149);
|
|
834
|
+
i0.ɵɵtext(1, "(not available)");
|
|
835
|
+
i0.ɵɵelementEnd();
|
|
836
|
+
} }
|
|
837
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Template(rf, ctx) { if (rf & 1) {
|
|
838
|
+
const _r33 = i0.ɵɵgetCurrentView();
|
|
839
|
+
i0.ɵɵelementStart(0, "div", 153);
|
|
840
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Template_div_click_0_listener() { const ctx_r33 = i0.ɵɵrestoreView(_r33); const matchVal_r35 = ctx_r33.$implicit; const ɵ$index_660_r36 = ctx_r33.$index; const field_r31 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(matchVal_r35 != null ? ctx_r1.SelectFieldValue(field_r31, ɵ$index_660_r36 + 1) : null); });
|
|
841
|
+
i0.ɵɵconditionalCreate(1, DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Conditional_1_Template, 2, 2)(2, DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Conditional_2_Template, 2, 0, "span", 149);
|
|
842
|
+
i0.ɵɵelementEnd();
|
|
843
|
+
} if (rf & 2) {
|
|
844
|
+
const matchVal_r35 = ctx.$implicit;
|
|
845
|
+
const ɵ$index_660_r36 = ctx.$index;
|
|
846
|
+
const ctx_r38 = i0.ɵɵnextContext();
|
|
847
|
+
const field_r31 = ctx_r38.$implicit;
|
|
848
|
+
const ɵ$index_645_r40 = ctx_r38.$index;
|
|
849
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
850
|
+
i0.ɵɵclassProp("grid-row-odd", ɵ$index_645_r40 % 2 !== 0)("value-same", ctx_r1.AreValuesEqual(field_r31.SourceValue, matchVal_r35))("value-different", matchVal_r35 != null && field_r31.SourceValue != null && !ctx_r1.AreValuesEqual(field_r31.SourceValue, matchVal_r35))("field-selected", field_r31.SelectedColumnIndex === ɵ$index_660_r36 + 1);
|
|
851
|
+
i0.ɵɵadvance();
|
|
852
|
+
i0.ɵɵconditional(matchVal_r35 != null ? 1 : 2);
|
|
853
|
+
} }
|
|
854
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_49_Template(rf, ctx) { if (rf & 1) {
|
|
855
|
+
const _r30 = i0.ɵɵgetCurrentView();
|
|
856
|
+
i0.ɵɵelementStart(0, "div", 147);
|
|
857
|
+
i0.ɵɵtext(1);
|
|
858
|
+
i0.ɵɵelementEnd();
|
|
859
|
+
i0.ɵɵelementStart(2, "div", 148);
|
|
860
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_For_49_Template_div_click_2_listener() { const field_r31 = i0.ɵɵrestoreView(_r30).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectFieldValue(field_r31, 0)); });
|
|
861
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_66_For_49_Conditional_3_Template, 1, 1)(4, DuplicateDetectionResourceComponent_Conditional_66_For_49_Conditional_4_Template, 2, 0, "span", 149);
|
|
862
|
+
i0.ɵɵconditionalCreate(5, DuplicateDetectionResourceComponent_Conditional_66_For_49_Conditional_5_Template, 1, 2, "input", 150);
|
|
863
|
+
i0.ɵɵelementEnd();
|
|
864
|
+
i0.ɵɵrepeaterCreate(6, DuplicateDetectionResourceComponent_Conditional_66_For_49_For_7_Template, 3, 9, "div", 151, i0.ɵɵrepeaterTrackByIndex);
|
|
865
|
+
} if (rf & 2) {
|
|
866
|
+
const field_r31 = ctx.$implicit;
|
|
867
|
+
const ɵ$index_645_r40 = ctx.$index;
|
|
868
|
+
i0.ɵɵclassProp("grid-row-odd", ɵ$index_645_r40 % 2 !== 0);
|
|
869
|
+
i0.ɵɵadvance();
|
|
870
|
+
i0.ɵɵtextInterpolate1(" ", field_r31.DisplayName, " ");
|
|
871
|
+
i0.ɵɵadvance();
|
|
872
|
+
i0.ɵɵclassProp("grid-row-odd", ɵ$index_645_r40 % 2 !== 0)("has-diff-in-row", field_r31.HasDifference)("field-selected", field_r31.SelectedColumnIndex === 0);
|
|
873
|
+
i0.ɵɵadvance();
|
|
874
|
+
i0.ɵɵconditional(field_r31.SourceValue != null ? 3 : 4);
|
|
875
|
+
i0.ɵɵadvance(2);
|
|
876
|
+
i0.ɵɵconditional(field_r31.HasDifference || field_r31.SelectedColumnIndex === 0 ? 5 : -1);
|
|
877
|
+
i0.ɵɵadvance();
|
|
878
|
+
i0.ɵɵrepeater(field_r31.MatchValues);
|
|
879
|
+
} }
|
|
880
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_59_Template(rf, ctx) { if (rf & 1) {
|
|
881
|
+
i0.ɵɵtext(0);
|
|
882
|
+
} if (rf & 2) {
|
|
883
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
884
|
+
i0.ɵɵtextInterpolate2(" \u00B7 ", ctx_r1.CherryPickedCount(), " field", ctx_r1.CherryPickedCount() !== 1 ? "s" : "", " cherry-picked ");
|
|
885
|
+
} }
|
|
886
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Template(rf, ctx) { if (rf & 1) {
|
|
887
|
+
const _r16 = i0.ɵɵgetCurrentView();
|
|
888
|
+
i0.ɵɵelementStart(0, "div", 90);
|
|
889
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseComparison()); });
|
|
890
|
+
i0.ɵɵelementEnd();
|
|
891
|
+
i0.ɵɵelementStart(1, "div", 91);
|
|
892
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r16); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
893
|
+
i0.ɵɵelementStart(2, "div", 92)(3, "div", 93)(4, "div", 94);
|
|
894
|
+
i0.ɵɵelement(5, "i");
|
|
895
|
+
i0.ɵɵelementEnd();
|
|
896
|
+
i0.ɵɵelementStart(6, "div")(7, "div", 95);
|
|
897
|
+
i0.ɵɵtext(8);
|
|
898
|
+
i0.ɵɵelementEnd();
|
|
899
|
+
i0.ɵɵelementStart(9, "span", 96);
|
|
900
|
+
i0.ɵɵtext(10);
|
|
901
|
+
i0.ɵɵelementEnd();
|
|
902
|
+
i0.ɵɵelementStart(11, "span", 97);
|
|
903
|
+
i0.ɵɵtext(12);
|
|
904
|
+
i0.ɵɵelementEnd()()();
|
|
905
|
+
i0.ɵɵelementStart(13, "div", 98)(14, "div", 99)(15, "button", 100);
|
|
906
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ComparisonShowAllFields = true); });
|
|
907
|
+
i0.ɵɵtext(16, "All Fields");
|
|
908
|
+
i0.ɵɵelementEnd();
|
|
909
|
+
i0.ɵɵelementStart(17, "button", 100);
|
|
910
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Template_button_click_17_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ComparisonShowAllFields = false); });
|
|
911
|
+
i0.ɵɵtext(18, "Differences Only");
|
|
912
|
+
i0.ɵɵelementEnd()();
|
|
913
|
+
i0.ɵɵelementStart(19, "button", 101);
|
|
914
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Template_button_click_19_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseComparison()); });
|
|
915
|
+
i0.ɵɵelement(20, "i", 49);
|
|
916
|
+
i0.ɵɵelementEnd()()();
|
|
917
|
+
i0.ɵɵconditionalCreate(21, DuplicateDetectionResourceComponent_Conditional_66_Conditional_21_Template, 2, 0, "div", 102);
|
|
918
|
+
i0.ɵɵelementStart(22, "div", 103)(23, "div", 104)(24, "div", 105);
|
|
919
|
+
i0.ɵɵtext(25, "Field");
|
|
920
|
+
i0.ɵɵelementEnd();
|
|
921
|
+
i0.ɵɵelementStart(26, "div", 106)(27, "span", 107);
|
|
922
|
+
i0.ɵɵtext(28, "Source");
|
|
923
|
+
i0.ɵɵelementEnd();
|
|
924
|
+
i0.ɵɵelementStart(29, "span", 108);
|
|
925
|
+
i0.ɵɵtext(30);
|
|
926
|
+
i0.ɵɵelementEnd();
|
|
927
|
+
i0.ɵɵelementStart(31, "div", 109)(32, "input", 110);
|
|
928
|
+
i0.ɵɵlistener("change", function DuplicateDetectionResourceComponent_Conditional_66_Template_input_change_32_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetSurvivor(0)); });
|
|
929
|
+
i0.ɵɵelementEnd();
|
|
930
|
+
i0.ɵɵelementStart(33, "span", 111);
|
|
931
|
+
i0.ɵɵtext(34);
|
|
932
|
+
i0.ɵɵelementEnd()();
|
|
933
|
+
i0.ɵɵelementStart(35, "button", 112);
|
|
934
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Template_button_click_35_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.UseAllFieldsFrom(0)); });
|
|
935
|
+
i0.ɵɵelement(36, "i", 113);
|
|
936
|
+
i0.ɵɵtext(37);
|
|
937
|
+
i0.ɵɵelementEnd();
|
|
938
|
+
i0.ɵɵelementStart(38, "div", 114)(39, "div", 115);
|
|
939
|
+
i0.ɵɵelement(40, "i", 116);
|
|
940
|
+
i0.ɵɵelementStart(41, "span", 117);
|
|
941
|
+
i0.ɵɵtext(42);
|
|
942
|
+
i0.ɵɵelementEnd();
|
|
943
|
+
i0.ɵɵtext(43);
|
|
944
|
+
i0.ɵɵconditionalCreate(44, DuplicateDetectionResourceComponent_Conditional_66_Conditional_44_Template, 3, 0, "span", 118);
|
|
945
|
+
i0.ɵɵelementEnd();
|
|
946
|
+
i0.ɵɵconditionalCreate(45, DuplicateDetectionResourceComponent_Conditional_66_Conditional_45_Template, 5, 6);
|
|
947
|
+
i0.ɵɵelementEnd()();
|
|
948
|
+
i0.ɵɵrepeaterCreate(46, DuplicateDetectionResourceComponent_Conditional_66_For_47_Template, 24, 26, "div", 119, _forTrack3);
|
|
949
|
+
i0.ɵɵrepeaterCreate(48, DuplicateDetectionResourceComponent_Conditional_66_For_49_Template, 8, 11, null, null, _forTrack4);
|
|
950
|
+
i0.ɵɵelementEnd()();
|
|
951
|
+
i0.ɵɵelementStart(50, "div", 120)(51, "div", 121)(52, "span", 122);
|
|
952
|
+
i0.ɵɵtext(53);
|
|
953
|
+
i0.ɵɵelementEnd();
|
|
954
|
+
i0.ɵɵelementStart(54, "span", 123);
|
|
955
|
+
i0.ɵɵelement(55, "i", 124);
|
|
956
|
+
i0.ɵɵtext(56, " Surviving: ");
|
|
957
|
+
i0.ɵɵelementStart(57, "strong");
|
|
958
|
+
i0.ɵɵtext(58);
|
|
959
|
+
i0.ɵɵelementEnd();
|
|
960
|
+
i0.ɵɵconditionalCreate(59, DuplicateDetectionResourceComponent_Conditional_66_Conditional_59_Template, 1, 2);
|
|
961
|
+
i0.ɵɵelementEnd()();
|
|
962
|
+
i0.ɵɵelementStart(60, "div", 125)(61, "button", 83);
|
|
963
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Template_button_click_61_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); ctx_r1.RejectMatch(ctx_r1.ComparisonGroup); return i0.ɵɵresetView(ctx_r1.CloseComparison()); });
|
|
964
|
+
i0.ɵɵelement(62, "i", 49);
|
|
965
|
+
i0.ɵɵtext(63, " Reject All ");
|
|
966
|
+
i0.ɵɵelementEnd();
|
|
967
|
+
i0.ɵɵelementStart(64, "button", 126);
|
|
968
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_Template_button_click_64_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OpenMergeConfirm()); });
|
|
969
|
+
i0.ɵɵelement(65, "i", 124);
|
|
970
|
+
i0.ɵɵtext(66, " Merge Records ");
|
|
971
|
+
i0.ɵɵelementEnd()()()();
|
|
972
|
+
} if (rf & 2) {
|
|
973
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
974
|
+
i0.ɵɵclassProp("comparison-closing", ctx_r1.ComparisonClosing);
|
|
975
|
+
i0.ɵɵadvance();
|
|
976
|
+
i0.ɵɵclassProp("comparison-closing", ctx_r1.ComparisonClosing);
|
|
977
|
+
i0.ɵɵadvance(4);
|
|
978
|
+
i0.ɵɵclassMap(ctx_r1.ComparisonGroup.EntityIcon);
|
|
979
|
+
i0.ɵɵadvance(3);
|
|
980
|
+
i0.ɵɵtextInterpolate(ctx_r1.ComparisonGroup.RecordName);
|
|
981
|
+
i0.ɵɵadvance(2);
|
|
982
|
+
i0.ɵɵtextInterpolate(ctx_r1.ComparisonGroup.EntityName);
|
|
983
|
+
i0.ɵɵadvance(2);
|
|
984
|
+
i0.ɵɵtextInterpolate2(" ", ctx_r1.ComparisonGroup.MatchCount, " potential duplicate", ctx_r1.ComparisonGroup.MatchCount !== 1 ? "s" : "", " ");
|
|
985
|
+
i0.ɵɵadvance(3);
|
|
986
|
+
i0.ɵɵclassProp("toggle-active", ctx_r1.ComparisonShowAllFields);
|
|
987
|
+
i0.ɵɵadvance(2);
|
|
988
|
+
i0.ɵɵclassProp("toggle-active", !ctx_r1.ComparisonShowAllFields);
|
|
989
|
+
i0.ɵɵadvance(4);
|
|
990
|
+
i0.ɵɵconditional(ctx_r1.ComparisonLoading ? 21 : -1);
|
|
991
|
+
i0.ɵɵadvance();
|
|
992
|
+
i0.ɵɵproperty("hidden", ctx_r1.ComparisonLoading);
|
|
993
|
+
i0.ɵɵadvance();
|
|
994
|
+
i0.ɵɵstyleProp("grid-template-columns", "160px repeat(" + (1 + ctx_r1.ComparisonMatches.length) + ", minmax(180px, 1fr))");
|
|
995
|
+
i0.ɵɵadvance(7);
|
|
996
|
+
i0.ɵɵtextInterpolate(ctx_r1.ComparisonGroup.RecordName);
|
|
997
|
+
i0.ɵɵadvance(2);
|
|
998
|
+
i0.ɵɵproperty("checked", ctx_r1.SurvivorColumnIndex === 0);
|
|
999
|
+
i0.ɵɵadvance();
|
|
1000
|
+
i0.ɵɵclassProp("is-survivor", ctx_r1.SurvivorColumnIndex === 0);
|
|
1001
|
+
i0.ɵɵadvance();
|
|
1002
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.SurvivorColumnIndex === 0 ? "Surviving Record" : "Set as survivor", " ");
|
|
1003
|
+
i0.ɵɵadvance();
|
|
1004
|
+
i0.ɵɵclassProp("all-selected", ctx_r1.AllFieldsSelectedFrom(0));
|
|
1005
|
+
i0.ɵɵadvance();
|
|
1006
|
+
i0.ɵɵclassProp("fa-check-double", ctx_r1.AllFieldsSelectedFrom(0))("fa-clone", !ctx_r1.AllFieldsSelectedFrom(0));
|
|
1007
|
+
i0.ɵɵadvance();
|
|
1008
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.AllFieldsSelectedFrom(0) ? "Using all fields" : "Use all fields", " ");
|
|
1009
|
+
i0.ɵɵadvance(5);
|
|
1010
|
+
i0.ɵɵtextInterpolate(ctx_r1.GetTotalDeps(0));
|
|
1011
|
+
i0.ɵɵadvance();
|
|
1012
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.GetTotalDeps(0) === 1 ? "dependency" : "dependencies", " ");
|
|
1013
|
+
i0.ɵɵadvance();
|
|
1014
|
+
i0.ɵɵconditional(ctx_r1.GetMaxDepsColumnIndex() === 0 && ctx_r1.GetTotalDeps(0) > 0 ? 44 : -1);
|
|
1015
|
+
i0.ɵɵadvance();
|
|
1016
|
+
i0.ɵɵconditional(ctx_r1.GetGroupedDeps(0).length > 0 ? 45 : -1);
|
|
1017
|
+
i0.ɵɵadvance();
|
|
1018
|
+
i0.ɵɵrepeater(ctx_r1.ComparisonMatches);
|
|
1019
|
+
i0.ɵɵadvance(2);
|
|
1020
|
+
i0.ɵɵrepeater(ctx_r1.GetVisibleFields());
|
|
1021
|
+
i0.ɵɵadvance(5);
|
|
1022
|
+
i0.ɵɵtextInterpolate2(" Showing ", ctx_r1.GetVisibleFields().length, " of ", ctx_r1.ComparisonFields.length, " fields ");
|
|
1023
|
+
i0.ɵɵadvance(5);
|
|
1024
|
+
i0.ɵɵtextInterpolate(ctx_r1.SurvivorName());
|
|
1025
|
+
i0.ɵɵadvance();
|
|
1026
|
+
i0.ɵɵconditional(ctx_r1.CherryPickedCount() > 0 ? 59 : -1);
|
|
1027
|
+
i0.ɵɵadvance(2);
|
|
1028
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving);
|
|
1029
|
+
i0.ɵɵadvance(3);
|
|
1030
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving);
|
|
1031
|
+
} }
|
|
1032
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Conditional_22_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
1033
|
+
i0.ɵɵelementStart(0, "div", 178)(1, "span", 179);
|
|
1034
|
+
i0.ɵɵtext(2);
|
|
1035
|
+
i0.ɵɵelementEnd();
|
|
1036
|
+
i0.ɵɵelementStart(3, "span", 180);
|
|
1037
|
+
i0.ɵɵtext(4);
|
|
1038
|
+
i0.ɵɵelementEnd();
|
|
1039
|
+
i0.ɵɵelementStart(5, "span", 181);
|
|
1040
|
+
i0.ɵɵtext(6);
|
|
1041
|
+
i0.ɵɵelementEnd()();
|
|
1042
|
+
} if (rf & 2) {
|
|
1043
|
+
const field_r42 = ctx.$implicit;
|
|
1044
|
+
i0.ɵɵadvance(2);
|
|
1045
|
+
i0.ɵɵtextInterpolate(field_r42.DisplayName);
|
|
1046
|
+
i0.ɵɵadvance(2);
|
|
1047
|
+
i0.ɵɵtextInterpolate1("\"", field_r42.Value, "\"");
|
|
1048
|
+
i0.ɵɵadvance(2);
|
|
1049
|
+
i0.ɵɵtextInterpolate1("from ", field_r42.SourceName);
|
|
1050
|
+
} }
|
|
1051
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
1052
|
+
i0.ɵɵelementStart(0, "div")(1, "div", 177);
|
|
1053
|
+
i0.ɵɵtext(2);
|
|
1054
|
+
i0.ɵɵelementEnd();
|
|
1055
|
+
i0.ɵɵrepeaterCreate(3, DuplicateDetectionResourceComponent_Conditional_67_Conditional_22_For_4_Template, 7, 3, "div", 178, _forTrack4);
|
|
1056
|
+
i0.ɵɵelementEnd();
|
|
1057
|
+
} if (rf & 2) {
|
|
1058
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
1059
|
+
i0.ɵɵadvance(2);
|
|
1060
|
+
i0.ɵɵtextInterpolate2("Field Value Overrides (", ctx_r1.GetCherryPickedFields().length, " field", ctx_r1.GetCherryPickedFields().length !== 1 ? "s" : "", ")");
|
|
1061
|
+
i0.ɵɵadvance();
|
|
1062
|
+
i0.ɵɵrepeater(ctx_r1.GetCherryPickedFields());
|
|
1063
|
+
} }
|
|
1064
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Conditional_23_For_5_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
1065
|
+
i0.ɵɵelementStart(0, "strong");
|
|
1066
|
+
i0.ɵɵtext(1);
|
|
1067
|
+
i0.ɵɵelementEnd();
|
|
1068
|
+
i0.ɵɵtext(2);
|
|
1069
|
+
i0.ɵɵelementStart(3, "strong");
|
|
1070
|
+
i0.ɵɵtext(4);
|
|
1071
|
+
i0.ɵɵelementEnd();
|
|
1072
|
+
i0.ɵɵtext(5);
|
|
1073
|
+
} if (rf & 2) {
|
|
1074
|
+
const col_r43 = i0.ɵɵnextContext().$implicit;
|
|
1075
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
1076
|
+
i0.ɵɵadvance();
|
|
1077
|
+
i0.ɵɵtextInterpolate(col_r43.DepCount);
|
|
1078
|
+
i0.ɵɵadvance();
|
|
1079
|
+
i0.ɵɵtextInterpolate1(" ", col_r43.DepCount === 1 ? "dependency" : "dependencies", " from ");
|
|
1080
|
+
i0.ɵɵadvance(2);
|
|
1081
|
+
i0.ɵɵtextInterpolate(col_r43.Name);
|
|
1082
|
+
i0.ɵɵadvance();
|
|
1083
|
+
i0.ɵɵtextInterpolate1(" \u2192 ", ctx_r1.SurvivorName(), " ");
|
|
1084
|
+
} }
|
|
1085
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Conditional_23_For_5_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
1086
|
+
i0.ɵɵelementStart(0, "strong");
|
|
1087
|
+
i0.ɵɵtext(1, "0");
|
|
1088
|
+
i0.ɵɵelementEnd();
|
|
1089
|
+
i0.ɵɵtext(2, " dependencies from ");
|
|
1090
|
+
i0.ɵɵelementStart(3, "strong");
|
|
1091
|
+
i0.ɵɵtext(4);
|
|
1092
|
+
i0.ɵɵelementEnd();
|
|
1093
|
+
} if (rf & 2) {
|
|
1094
|
+
const col_r43 = i0.ɵɵnextContext().$implicit;
|
|
1095
|
+
i0.ɵɵadvance(4);
|
|
1096
|
+
i0.ɵɵtextInterpolate(col_r43.Name);
|
|
1097
|
+
} }
|
|
1098
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Conditional_23_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
1099
|
+
i0.ɵɵelementStart(0, "div", 184);
|
|
1100
|
+
i0.ɵɵelement(1, "i", 185);
|
|
1101
|
+
i0.ɵɵelementStart(2, "span");
|
|
1102
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_67_Conditional_23_For_5_Conditional_3_Template, 6, 4)(4, DuplicateDetectionResourceComponent_Conditional_67_Conditional_23_For_5_Conditional_4_Template, 5, 1);
|
|
1103
|
+
i0.ɵɵelementEnd()();
|
|
1104
|
+
} if (rf & 2) {
|
|
1105
|
+
const col_r43 = ctx.$implicit;
|
|
1106
|
+
i0.ɵɵadvance(3);
|
|
1107
|
+
i0.ɵɵconditional(col_r43.DepCount > 0 ? 3 : 4);
|
|
1108
|
+
} }
|
|
1109
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
1110
|
+
i0.ɵɵelementStart(0, "div", 168)(1, "div", 182);
|
|
1111
|
+
i0.ɵɵelement(2, "i", 183);
|
|
1112
|
+
i0.ɵɵtext(3, " Dependencies to Transfer");
|
|
1113
|
+
i0.ɵɵelementEnd();
|
|
1114
|
+
i0.ɵɵrepeaterCreate(4, DuplicateDetectionResourceComponent_Conditional_67_Conditional_23_For_5_Template, 5, 1, "div", 184, _forTrack6);
|
|
1115
|
+
i0.ɵɵelementEnd();
|
|
1116
|
+
} if (rf & 2) {
|
|
1117
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
1118
|
+
i0.ɵɵadvance(4);
|
|
1119
|
+
i0.ɵɵrepeater(ctx_r1.GetNonSurvivorColumns());
|
|
1120
|
+
} }
|
|
1121
|
+
function DuplicateDetectionResourceComponent_Conditional_67_For_29_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
1122
|
+
i0.ɵɵtext(0);
|
|
1123
|
+
} if (rf & 2) {
|
|
1124
|
+
const col_r44 = i0.ɵɵnextContext().$implicit;
|
|
1125
|
+
i0.ɵɵtextInterpolate2(" ", col_r44.DepCount, " dep", col_r44.DepCount !== 1 ? "s" : "", " transferring ");
|
|
1126
|
+
} }
|
|
1127
|
+
function DuplicateDetectionResourceComponent_Conditional_67_For_29_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
1128
|
+
i0.ɵɵtext(0, " no deps ");
|
|
1129
|
+
} }
|
|
1130
|
+
function DuplicateDetectionResourceComponent_Conditional_67_For_29_Template(rf, ctx) { if (rf & 1) {
|
|
1131
|
+
i0.ɵɵelementStart(0, "div", 172)(1, "span", 186);
|
|
1132
|
+
i0.ɵɵtext(2);
|
|
1133
|
+
i0.ɵɵelementEnd();
|
|
1134
|
+
i0.ɵɵelementStart(3, "span", 187);
|
|
1135
|
+
i0.ɵɵconditionalCreate(4, DuplicateDetectionResourceComponent_Conditional_67_For_29_Conditional_4_Template, 1, 2)(5, DuplicateDetectionResourceComponent_Conditional_67_For_29_Conditional_5_Template, 1, 0);
|
|
1136
|
+
i0.ɵɵelementEnd()();
|
|
1137
|
+
} if (rf & 2) {
|
|
1138
|
+
const col_r44 = ctx.$implicit;
|
|
1139
|
+
i0.ɵɵadvance(2);
|
|
1140
|
+
i0.ɵɵtextInterpolate(col_r44.Name);
|
|
1141
|
+
i0.ɵɵadvance(2);
|
|
1142
|
+
i0.ɵɵconditional(col_r44.DepCount > 0 ? 4 : 5);
|
|
1143
|
+
} }
|
|
1144
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Conditional_35_Template(rf, ctx) { if (rf & 1) {
|
|
1145
|
+
i0.ɵɵelement(0, "i", 33);
|
|
1146
|
+
i0.ɵɵtext(1, " Merging... ");
|
|
1147
|
+
} }
|
|
1148
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Conditional_36_Template(rf, ctx) { if (rf & 1) {
|
|
1149
|
+
i0.ɵɵelement(0, "i", 124);
|
|
1150
|
+
i0.ɵɵtext(1);
|
|
1151
|
+
} if (rf & 2) {
|
|
1152
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
1153
|
+
i0.ɵɵadvance();
|
|
1154
|
+
i0.ɵɵtextInterpolate1(" Confirm Merge (", ctx_r1.GetNonSurvivorColumns().length + 1, " records \u2192 1) ");
|
|
1155
|
+
} }
|
|
1156
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Template(rf, ctx) { if (rf & 1) {
|
|
1157
|
+
const _r41 = i0.ɵɵgetCurrentView();
|
|
1158
|
+
i0.ɵɵelementStart(0, "div", 154);
|
|
1159
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_67_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r41); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseMergeConfirm()); });
|
|
1160
|
+
i0.ɵɵelementStart(1, "div", 155);
|
|
1161
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_67_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r41); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
1162
|
+
i0.ɵɵelementStart(2, "div", 156)(3, "div", 157);
|
|
1163
|
+
i0.ɵɵelement(4, "i", 124);
|
|
1164
|
+
i0.ɵɵelementEnd();
|
|
1165
|
+
i0.ɵɵelementStart(5, "div")(6, "div", 158);
|
|
1166
|
+
i0.ɵɵtext(7, "Confirm Record Merge");
|
|
1167
|
+
i0.ɵɵelementEnd();
|
|
1168
|
+
i0.ɵɵelementStart(8, "div", 159);
|
|
1169
|
+
i0.ɵɵtext(9, "This action cannot be undone. Please review carefully.");
|
|
1170
|
+
i0.ɵɵelementEnd()()();
|
|
1171
|
+
i0.ɵɵelementStart(10, "div", 160)(11, "div", 161)(12, "div", 162);
|
|
1172
|
+
i0.ɵɵelement(13, "i", 163);
|
|
1173
|
+
i0.ɵɵtext(14, " Surviving Record");
|
|
1174
|
+
i0.ɵɵelementEnd();
|
|
1175
|
+
i0.ɵɵelementStart(15, "div", 164);
|
|
1176
|
+
i0.ɵɵtext(16);
|
|
1177
|
+
i0.ɵɵelementEnd();
|
|
1178
|
+
i0.ɵɵelementStart(17, "div", 165);
|
|
1179
|
+
i0.ɵɵelement(18, "i", 166);
|
|
1180
|
+
i0.ɵɵtext(19);
|
|
1181
|
+
i0.ɵɵelementEnd();
|
|
1182
|
+
i0.ɵɵelementStart(20, "div", 167);
|
|
1183
|
+
i0.ɵɵtext(21, "This record's ID will be retained. All dependencies from merged records will be transferred here.");
|
|
1184
|
+
i0.ɵɵelementEnd()();
|
|
1185
|
+
i0.ɵɵconditionalCreate(22, DuplicateDetectionResourceComponent_Conditional_67_Conditional_22_Template, 5, 2, "div");
|
|
1186
|
+
i0.ɵɵconditionalCreate(23, DuplicateDetectionResourceComponent_Conditional_67_Conditional_23_Template, 6, 0, "div", 168);
|
|
1187
|
+
i0.ɵɵelementStart(24, "div", 169)(25, "div", 170);
|
|
1188
|
+
i0.ɵɵelement(26, "i", 171);
|
|
1189
|
+
i0.ɵɵtext(27, " Records to Delete After Merge");
|
|
1190
|
+
i0.ɵɵelementEnd();
|
|
1191
|
+
i0.ɵɵrepeaterCreate(28, DuplicateDetectionResourceComponent_Conditional_67_For_29_Template, 6, 2, "div", 172, _forTrack6);
|
|
1192
|
+
i0.ɵɵelementEnd()();
|
|
1193
|
+
i0.ɵɵelementStart(30, "div", 173)(31, "button", 174);
|
|
1194
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_67_Template_button_click_31_listener() { i0.ɵɵrestoreView(_r41); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseMergeConfirm()); });
|
|
1195
|
+
i0.ɵɵelement(32, "i", 175);
|
|
1196
|
+
i0.ɵɵtext(33, " Back ");
|
|
1197
|
+
i0.ɵɵelementEnd();
|
|
1198
|
+
i0.ɵɵelementStart(34, "button", 176);
|
|
1199
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_67_Template_button_click_34_listener() { i0.ɵɵrestoreView(_r41); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ExecuteMerge()); });
|
|
1200
|
+
i0.ɵɵconditionalCreate(35, DuplicateDetectionResourceComponent_Conditional_67_Conditional_35_Template, 2, 0)(36, DuplicateDetectionResourceComponent_Conditional_67_Conditional_36_Template, 2, 1);
|
|
1201
|
+
i0.ɵɵelementEnd()()()();
|
|
1202
|
+
} if (rf & 2) {
|
|
1203
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
1204
|
+
i0.ɵɵadvance(16);
|
|
1205
|
+
i0.ɵɵtextInterpolate(ctx_r1.SurvivorName());
|
|
1206
|
+
i0.ɵɵadvance(3);
|
|
1207
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.SurvivorKeyDisplay(), " ");
|
|
1208
|
+
i0.ɵɵadvance(3);
|
|
1209
|
+
i0.ɵɵconditional(ctx_r1.GetCherryPickedFields().length > 0 ? 22 : -1);
|
|
1210
|
+
i0.ɵɵadvance();
|
|
1211
|
+
i0.ɵɵconditional(ctx_r1.GetNonSurvivorColumns().length > 0 ? 23 : -1);
|
|
1212
|
+
i0.ɵɵadvance(5);
|
|
1213
|
+
i0.ɵɵrepeater(ctx_r1.GetNonSurvivorColumns());
|
|
1214
|
+
i0.ɵɵadvance(3);
|
|
1215
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsMerging);
|
|
1216
|
+
i0.ɵɵadvance(3);
|
|
1217
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsMerging);
|
|
1218
|
+
i0.ɵɵadvance();
|
|
1219
|
+
i0.ɵɵconditional(ctx_r1.IsMerging ? 35 : 36);
|
|
1220
|
+
} }
|
|
1221
|
+
let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceComponent extends BaseResourceComponent {
|
|
1222
|
+
cdr = inject(ChangeDetectorRef);
|
|
1223
|
+
navigationService = inject(NavigationService);
|
|
1224
|
+
destroy$ = new Subject();
|
|
1225
|
+
filterSubject = new Subject();
|
|
1226
|
+
// Loading state
|
|
1227
|
+
IsLoading = false;
|
|
1228
|
+
IsSaving = false;
|
|
1229
|
+
// ── Comparison Panel State ──
|
|
1230
|
+
/** The group being compared (null = panel closed) */
|
|
1231
|
+
ComparisonGroup = null;
|
|
1232
|
+
/** Whether the comparison panel is loading entity records */
|
|
1233
|
+
ComparisonLoading = false;
|
|
1234
|
+
/** Whether to show all fields or only differences */
|
|
1235
|
+
ComparisonShowAllFields = true;
|
|
1236
|
+
/** Precomputed field rows for the comparison grid */
|
|
1237
|
+
ComparisonFields = [];
|
|
1238
|
+
/** Parsed match info for comparison columns */
|
|
1239
|
+
ComparisonMatches = [];
|
|
1240
|
+
/** Index of the surviving record column (0 = source, 1+ = match index) */
|
|
1241
|
+
SurvivorColumnIndex = 0;
|
|
1242
|
+
/** Whether the panel is animating closed */
|
|
1243
|
+
ComparisonClosing = false;
|
|
1244
|
+
/** Loaded entity records keyed by record ID (populated on panel open via RunView) */
|
|
1245
|
+
comparisonRecords = new Map();
|
|
1246
|
+
// ── Dependencies State ──
|
|
1247
|
+
/** Dependencies per record, keyed by composite key string */
|
|
1248
|
+
ComparisonDependencies = new Map();
|
|
1249
|
+
/** Which column indices have expanded dependency details */
|
|
1250
|
+
DepsExpandedColumns = new Set();
|
|
1251
|
+
/** Tracks which entity groups within deps are expanded (key: "columnIndex::entityName") */
|
|
1252
|
+
depsEntityGroupExpanded = new Set();
|
|
1253
|
+
// ── Merge Confirmation State ──
|
|
1254
|
+
/** Whether the merge confirmation panel is visible */
|
|
1255
|
+
ShowMergeConfirm = false;
|
|
1256
|
+
/** Whether the merge is currently executing */
|
|
1257
|
+
IsMerging = false;
|
|
1258
|
+
// Raw data
|
|
1259
|
+
Runs = [];
|
|
1260
|
+
Details = [];
|
|
1261
|
+
Matches = [];
|
|
1262
|
+
// Aggregated groups for display
|
|
1263
|
+
AllGroups = [];
|
|
1264
|
+
PendingGroups = [];
|
|
1265
|
+
ApprovedGroups = [];
|
|
1266
|
+
RejectedGroups = [];
|
|
1267
|
+
// Filter state
|
|
1268
|
+
Filters = {
|
|
1269
|
+
EntityName: '',
|
|
1270
|
+
MinScore: 0,
|
|
1271
|
+
MaxScore: 1,
|
|
1272
|
+
DateFrom: '',
|
|
1273
|
+
DateTo: ''
|
|
1274
|
+
};
|
|
1275
|
+
// Available entity names from loaded runs
|
|
1276
|
+
EntityNames = [];
|
|
1277
|
+
// Detection run state
|
|
1278
|
+
IsDetecting = false;
|
|
1279
|
+
DetectionProgress = 0;
|
|
1280
|
+
DetectionStage = '';
|
|
1281
|
+
/** Runtime threshold overrides — initialized from entity doc, adjustable via sliders */
|
|
1282
|
+
RunPotentialThreshold = 0.70;
|
|
1283
|
+
RunAbsoluteThreshold = 0.95;
|
|
1284
|
+
DetectionCurrentItem = '';
|
|
1285
|
+
// Entity document picker
|
|
1286
|
+
EntityDocuments = [];
|
|
1287
|
+
_selectedEntityDocumentID = '';
|
|
1288
|
+
get SelectedEntityDocumentID() { return this._selectedEntityDocumentID; }
|
|
1289
|
+
set SelectedEntityDocumentID(value) {
|
|
1290
|
+
this._selectedEntityDocumentID = value;
|
|
1291
|
+
// Sync threshold sliders from selected entity document
|
|
1292
|
+
const doc = this.EntityDocuments.find(d => UUIDsEqual(d.ID, value));
|
|
1293
|
+
if (doc) {
|
|
1294
|
+
this.RunPotentialThreshold = doc.PotentialMatchThreshold;
|
|
1295
|
+
this.RunAbsoluteThreshold = doc.AbsoluteMatchThreshold;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
/** Whether this component is embedded inside the Knowledge Hub shell */
|
|
1299
|
+
EmbeddedMode = false;
|
|
1300
|
+
/** View mode: 'kanban' (card board) or 'table' (paged grid) */
|
|
1301
|
+
DisplayMode = 'kanban';
|
|
1302
|
+
/** Page size for table view */
|
|
1303
|
+
PageSize = 50;
|
|
1304
|
+
/** Current page (0-based) */
|
|
1305
|
+
CurrentPage = 0;
|
|
1306
|
+
/** Selected entity filter */
|
|
1307
|
+
SelectedEntityFilter = '';
|
|
1308
|
+
/** Currently selected group for merge panel */
|
|
1309
|
+
SelectedMergeGroup = null;
|
|
1310
|
+
/** Whether the merge panel is visible */
|
|
1311
|
+
ShowMergePanel = false;
|
|
1312
|
+
/** Auto-switch to table mode when groups exceed threshold */
|
|
1313
|
+
kanbanThreshold = 50;
|
|
1314
|
+
/** Toggle display mode */
|
|
1315
|
+
ToggleDisplayMode() {
|
|
1316
|
+
this.DisplayMode = this.DisplayMode === 'kanban' ? 'table' : 'kanban';
|
|
1317
|
+
this.CurrentPage = 0;
|
|
1318
|
+
this.cdr.detectChanges();
|
|
1319
|
+
}
|
|
1320
|
+
/** Filter by entity name */
|
|
1321
|
+
FilterByEntity(entityName) {
|
|
1322
|
+
this.SelectedEntityFilter = entityName;
|
|
1323
|
+
this.Filters.EntityName = entityName;
|
|
1324
|
+
this.autoSelectDisplayMode();
|
|
1325
|
+
this.cdr.detectChanges();
|
|
1326
|
+
}
|
|
1327
|
+
/** Get paged groups for table view */
|
|
1328
|
+
GetPagedGroups() {
|
|
1329
|
+
const start = this.CurrentPage * this.PageSize;
|
|
1330
|
+
return this.AllGroups.slice(start, start + this.PageSize);
|
|
1331
|
+
}
|
|
1332
|
+
/** Get total page count */
|
|
1333
|
+
get TotalPages() {
|
|
1334
|
+
return Math.ceil(this.AllGroups.length / this.PageSize);
|
|
1335
|
+
}
|
|
1336
|
+
/** Navigate to next page */
|
|
1337
|
+
NextPage() {
|
|
1338
|
+
if (this.CurrentPage < this.TotalPages - 1) {
|
|
1339
|
+
this.CurrentPage++;
|
|
1340
|
+
this.cdr.detectChanges();
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
/** Navigate to previous page */
|
|
1344
|
+
PrevPage() {
|
|
1345
|
+
if (this.CurrentPage > 0) {
|
|
1346
|
+
this.CurrentPage--;
|
|
1347
|
+
this.cdr.detectChanges();
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
/** Open the merge panel for a duplicate group */
|
|
1351
|
+
OpenMergePanel(group) {
|
|
1352
|
+
this.SelectedMergeGroup = group;
|
|
1353
|
+
this.ShowMergePanel = true;
|
|
1354
|
+
this.cdr.detectChanges();
|
|
1355
|
+
}
|
|
1356
|
+
/** Close the merge panel */
|
|
1357
|
+
CloseMergePanel() {
|
|
1358
|
+
this.SelectedMergeGroup = null;
|
|
1359
|
+
this.ShowMergePanel = false;
|
|
1360
|
+
this.cdr.detectChanges();
|
|
1361
|
+
}
|
|
1362
|
+
/** Auto-select display mode based on group count */
|
|
1363
|
+
autoSelectDisplayMode() {
|
|
1364
|
+
this.DisplayMode = this.AllGroups.length > this.kanbanThreshold ? 'table' : 'kanban';
|
|
1365
|
+
}
|
|
1366
|
+
// KPI values
|
|
1367
|
+
get TotalGroupCount() {
|
|
1368
|
+
return this.AllGroups.length;
|
|
1369
|
+
}
|
|
1370
|
+
get PendingCount() {
|
|
1371
|
+
return this.PendingGroups.length;
|
|
1372
|
+
}
|
|
1373
|
+
get ApprovedCount() {
|
|
1374
|
+
return this.ApprovedGroups.length;
|
|
1375
|
+
}
|
|
1376
|
+
get RejectedCount() {
|
|
1377
|
+
return this.RejectedGroups.length;
|
|
1378
|
+
}
|
|
1379
|
+
/** Get selected entity document threshold info */
|
|
1380
|
+
get SelectedDocumentThresholds() {
|
|
1381
|
+
if (!this.SelectedEntityDocumentID)
|
|
1382
|
+
return null;
|
|
1383
|
+
return this.EntityDocuments.find(d => UUIDsEqual(d.ID, this.SelectedEntityDocumentID)) ?? null;
|
|
1384
|
+
}
|
|
1385
|
+
async ngAfterViewInit() {
|
|
1386
|
+
this.setupFilterDebounce();
|
|
1387
|
+
await this.LoadData();
|
|
1388
|
+
this.NotifyLoadComplete();
|
|
1389
|
+
}
|
|
1390
|
+
ngOnDestroy() {
|
|
1391
|
+
this.destroy$.next();
|
|
1392
|
+
this.destroy$.complete();
|
|
1393
|
+
}
|
|
1394
|
+
async GetResourceDisplayName(_data) {
|
|
1395
|
+
return 'Duplicate Detection';
|
|
1396
|
+
}
|
|
1397
|
+
async GetResourceIconClass(_data) {
|
|
1398
|
+
return 'fa-solid fa-clone';
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Loads all duplicate run data and builds the Kanban groups.
|
|
1402
|
+
*/
|
|
1403
|
+
async LoadData() {
|
|
1404
|
+
this.IsLoading = true;
|
|
1405
|
+
this.cdr.detectChanges();
|
|
1406
|
+
try {
|
|
1407
|
+
// Use KnowledgeHubMetadataEngine for cached entity document data
|
|
1408
|
+
const engine = KnowledgeHubMetadataEngine.Instance;
|
|
1409
|
+
await engine.Config(false);
|
|
1410
|
+
const rv = new RunView();
|
|
1411
|
+
const [runsResult, detailsResult, matchesResult] = await rv.RunViews([
|
|
1412
|
+
{
|
|
1413
|
+
EntityName: 'MJ: Duplicate Runs',
|
|
1414
|
+
ExtraFilter: "ProcessingStatus IN ('Complete', 'Failed', 'In Progress')",
|
|
1415
|
+
OrderBy: 'StartedAt DESC',
|
|
1416
|
+
ResultType: 'entity_object'
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
EntityName: 'MJ: Duplicate Run Details',
|
|
1420
|
+
ExtraFilter: "MatchStatus = 'Complete'",
|
|
1421
|
+
OrderBy: '__mj_CreatedAt DESC',
|
|
1422
|
+
ResultType: 'entity_object'
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
EntityName: 'MJ: Duplicate Run Detail Matches',
|
|
1426
|
+
OrderBy: 'MatchProbability DESC',
|
|
1427
|
+
ResultType: 'entity_object'
|
|
1428
|
+
}
|
|
1429
|
+
]);
|
|
1430
|
+
if (runsResult.Success) {
|
|
1431
|
+
this.Runs = runsResult.Results;
|
|
1432
|
+
}
|
|
1433
|
+
if (detailsResult.Success) {
|
|
1434
|
+
this.Details = detailsResult.Results;
|
|
1435
|
+
}
|
|
1436
|
+
if (matchesResult.Success) {
|
|
1437
|
+
this.Matches = matchesResult.Results;
|
|
1438
|
+
}
|
|
1439
|
+
// Build entity document options from cached active documents
|
|
1440
|
+
this.buildEntityDocumentOptionsFromEngine(engine.GetActiveEntityDocuments());
|
|
1441
|
+
this.buildGroups();
|
|
1442
|
+
this.extractEntityNames();
|
|
1443
|
+
this.computeDataRanges();
|
|
1444
|
+
this.applyFilters();
|
|
1445
|
+
}
|
|
1446
|
+
catch (error) {
|
|
1447
|
+
console.error('Error loading duplicate detection data:', error);
|
|
1448
|
+
}
|
|
1449
|
+
finally {
|
|
1450
|
+
this.IsLoading = false;
|
|
1451
|
+
this.cdr.detectChanges();
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Trigger a new duplicate detection run by creating a DuplicateRun entity.
|
|
1456
|
+
* The server hook auto-triggers detection when a run is saved with EndedAt === null.
|
|
1457
|
+
*/
|
|
1458
|
+
async RunDetection() {
|
|
1459
|
+
if (this.IsDetecting || !this.SelectedEntityDocumentID)
|
|
1460
|
+
return;
|
|
1461
|
+
const selectedDoc = this.EntityDocuments.find(d => UUIDsEqual(d.ID, this.SelectedEntityDocumentID));
|
|
1462
|
+
if (!selectedDoc)
|
|
1463
|
+
return;
|
|
1464
|
+
this.IsDetecting = true;
|
|
1465
|
+
this.DetectionProgress = 0;
|
|
1466
|
+
this.DetectionStage = 'Initializing...';
|
|
1467
|
+
this.DetectionCurrentItem = '';
|
|
1468
|
+
this.cdr.detectChanges();
|
|
1469
|
+
try {
|
|
1470
|
+
const md = new Metadata();
|
|
1471
|
+
const dupeRun = await md.GetEntityObject('MJ: Duplicate Runs');
|
|
1472
|
+
dupeRun.NewRecord();
|
|
1473
|
+
// Look up the EntityID from the entity document's entity name
|
|
1474
|
+
const entityInfo = md.Entities.find(e => e.Name === selectedDoc.EntityName);
|
|
1475
|
+
if (entityInfo) {
|
|
1476
|
+
dupeRun.EntityID = entityInfo.ID;
|
|
1477
|
+
}
|
|
1478
|
+
dupeRun.StartedByUserID = new Metadata().CurrentUser.ID;
|
|
1479
|
+
dupeRun.StartedAt = new Date();
|
|
1480
|
+
dupeRun.ProcessingStatus = 'In Progress';
|
|
1481
|
+
dupeRun.ApprovalStatus = 'Pending';
|
|
1482
|
+
const saved = await dupeRun.Save();
|
|
1483
|
+
if (!saved) {
|
|
1484
|
+
console.error('Failed to create duplicate run:', dupeRun.LatestResult?.Message || 'unknown error');
|
|
1485
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Failed to start detection: ${dupeRun.LatestResult?.Message || 'unknown error'}`, 'error', 5000);
|
|
1486
|
+
this.IsDetecting = false;
|
|
1487
|
+
this.DetectionStage = '';
|
|
1488
|
+
this.cdr.detectChanges();
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
// Subscribe to progress using the run ID as PipelineRunID
|
|
1492
|
+
this.subscribeToPipelineProgress(dupeRun.ID);
|
|
1493
|
+
}
|
|
1494
|
+
catch (error) {
|
|
1495
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1496
|
+
console.error('[DuplicateDetection] Error starting detection:', msg);
|
|
1497
|
+
this.IsDetecting = false;
|
|
1498
|
+
this.DetectionStage = '';
|
|
1499
|
+
this.cdr.detectChanges();
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Approves all matches within a duplicate group by updating each match's ApprovalStatus.
|
|
1504
|
+
*/
|
|
1505
|
+
async ApproveMatch(group) {
|
|
1506
|
+
await this.updateGroupApprovalStatus(group, 'Approved');
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Rejects all matches within a duplicate group by updating each match's ApprovalStatus.
|
|
1510
|
+
*/
|
|
1511
|
+
async RejectMatch(group) {
|
|
1512
|
+
await this.updateGroupApprovalStatus(group, 'Rejected');
|
|
1513
|
+
}
|
|
1514
|
+
// ════════════════════════════════════════════
|
|
1515
|
+
// Drag and Drop
|
|
1516
|
+
// ════════════════════════════════════════════
|
|
1517
|
+
/** The group currently being dragged */
|
|
1518
|
+
DraggedGroup = null;
|
|
1519
|
+
/** Which column is being dragged over (for highlight) */
|
|
1520
|
+
DragOverColumn = null;
|
|
1521
|
+
OnDragStart(event, group) {
|
|
1522
|
+
this.DraggedGroup = group;
|
|
1523
|
+
if (event.dataTransfer) {
|
|
1524
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
1525
|
+
event.dataTransfer.setData('text/plain', group.DetailId);
|
|
1526
|
+
}
|
|
1527
|
+
// Add a slight delay so the drag ghost renders before we add the dragging class
|
|
1528
|
+
setTimeout(() => this.cdr.detectChanges(), 0);
|
|
1529
|
+
}
|
|
1530
|
+
OnDragEnd() {
|
|
1531
|
+
this.DraggedGroup = null;
|
|
1532
|
+
this.DragOverColumn = null;
|
|
1533
|
+
this.cdr.detectChanges();
|
|
1534
|
+
}
|
|
1535
|
+
OnDragOver(event, column) {
|
|
1536
|
+
event.preventDefault(); // Required to allow drop
|
|
1537
|
+
if (event.dataTransfer) {
|
|
1538
|
+
event.dataTransfer.dropEffect = 'move';
|
|
1539
|
+
}
|
|
1540
|
+
if (this.DragOverColumn !== column) {
|
|
1541
|
+
this.DragOverColumn = column;
|
|
1542
|
+
this.cdr.detectChanges();
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
OnDragLeave(event, column) {
|
|
1546
|
+
// Only clear if leaving the column (not entering a child element)
|
|
1547
|
+
const related = event.relatedTarget;
|
|
1548
|
+
const columnEl = event.currentTarget;
|
|
1549
|
+
if (!columnEl.contains(related)) {
|
|
1550
|
+
if (this.DragOverColumn === column) {
|
|
1551
|
+
this.DragOverColumn = null;
|
|
1552
|
+
this.cdr.detectChanges();
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
async OnDrop(event, targetStatus) {
|
|
1557
|
+
event.preventDefault();
|
|
1558
|
+
this.DragOverColumn = null;
|
|
1559
|
+
if (!this.DraggedGroup)
|
|
1560
|
+
return;
|
|
1561
|
+
if (this.DraggedGroup.ApprovalStatus === targetStatus) {
|
|
1562
|
+
this.DraggedGroup = null;
|
|
1563
|
+
this.cdr.detectChanges();
|
|
1564
|
+
return; // Already in this column
|
|
1565
|
+
}
|
|
1566
|
+
const group = this.DraggedGroup;
|
|
1567
|
+
this.DraggedGroup = null;
|
|
1568
|
+
await this.updateGroupApprovalStatus(group, targetStatus);
|
|
1569
|
+
}
|
|
1570
|
+
/** Whether a card is currently being dragged */
|
|
1571
|
+
get IsDragging() {
|
|
1572
|
+
return this.DraggedGroup !== null;
|
|
1573
|
+
}
|
|
1574
|
+
/** Handle filter changes with debounce */
|
|
1575
|
+
OnFilterChange() {
|
|
1576
|
+
this.filterSubject.next();
|
|
1577
|
+
}
|
|
1578
|
+
/** Computed range bounds from actual data — used as min/max/placeholder for filter inputs */
|
|
1579
|
+
DataMinScore = 0;
|
|
1580
|
+
DataMaxScore = 1;
|
|
1581
|
+
DataMinDate = '';
|
|
1582
|
+
DataMaxDate = '';
|
|
1583
|
+
/** Compute the actual data ranges from AllGroups */
|
|
1584
|
+
computeDataRanges() {
|
|
1585
|
+
if (this.AllGroups.length === 0) {
|
|
1586
|
+
this.DataMinScore = 0;
|
|
1587
|
+
this.DataMaxScore = 1;
|
|
1588
|
+
this.DataMinDate = '';
|
|
1589
|
+
this.DataMaxDate = '';
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
let minScore = 1, maxScore = 0;
|
|
1593
|
+
let minDate = null, maxDate = null;
|
|
1594
|
+
for (const group of this.AllGroups) {
|
|
1595
|
+
if (group.HighestScore < minScore)
|
|
1596
|
+
minScore = group.HighestScore;
|
|
1597
|
+
if (group.HighestScore > maxScore)
|
|
1598
|
+
maxScore = group.HighestScore;
|
|
1599
|
+
const d = new Date(group.MatchedAt);
|
|
1600
|
+
if (!isNaN(d.getTime())) {
|
|
1601
|
+
if (!minDate || d < minDate)
|
|
1602
|
+
minDate = d;
|
|
1603
|
+
if (!maxDate || d > maxDate)
|
|
1604
|
+
maxDate = d;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
this.DataMinScore = Math.floor(minScore * 100) / 100;
|
|
1608
|
+
this.DataMaxScore = Math.ceil(maxScore * 100) / 100;
|
|
1609
|
+
this.DataMinDate = minDate ? this.toInputDate(minDate) : '';
|
|
1610
|
+
this.DataMaxDate = maxDate ? this.toInputDate(maxDate) : '';
|
|
1611
|
+
// Leave filters empty by default — no filtering until user explicitly sets values
|
|
1612
|
+
this.Filters.MinScore = 0;
|
|
1613
|
+
this.Filters.MaxScore = 1;
|
|
1614
|
+
this.Filters.DateFrom = '';
|
|
1615
|
+
this.Filters.DateTo = '';
|
|
1616
|
+
}
|
|
1617
|
+
/** Format a Date to YYYY-MM-DD for input[type=date] using local time */
|
|
1618
|
+
toInputDate(d) {
|
|
1619
|
+
const year = d.getFullYear();
|
|
1620
|
+
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
1621
|
+
const day = String(d.getDate()).padStart(2, '0');
|
|
1622
|
+
return `${year}-${month}-${day}`;
|
|
1623
|
+
}
|
|
1624
|
+
/** Clear all filters */
|
|
1625
|
+
ClearFilters() {
|
|
1626
|
+
this.Filters = {
|
|
1627
|
+
EntityName: this.EntityNames.length === 1 ? this.EntityNames[0] : '',
|
|
1628
|
+
MinScore: 0,
|
|
1629
|
+
MaxScore: 1,
|
|
1630
|
+
DateFrom: '',
|
|
1631
|
+
DateTo: '',
|
|
1632
|
+
};
|
|
1633
|
+
this.applyFilters();
|
|
1634
|
+
}
|
|
1635
|
+
/** Returns the CSS class for a match score indicator */
|
|
1636
|
+
GetScoreClass(score) {
|
|
1637
|
+
if (score > 0.9)
|
|
1638
|
+
return 'score-high';
|
|
1639
|
+
if (score >= 0.7)
|
|
1640
|
+
return 'score-medium';
|
|
1641
|
+
return 'score-low';
|
|
1642
|
+
}
|
|
1643
|
+
/** Returns a human-readable label for a match score */
|
|
1644
|
+
GetScoreLabel(score) {
|
|
1645
|
+
if (score > 0.9)
|
|
1646
|
+
return 'High';
|
|
1647
|
+
if (score >= 0.7)
|
|
1648
|
+
return 'Medium';
|
|
1649
|
+
return 'Low';
|
|
1650
|
+
}
|
|
1651
|
+
/** Format a date for display */
|
|
1652
|
+
FormatDate(date) {
|
|
1653
|
+
if (!date)
|
|
1654
|
+
return '';
|
|
1655
|
+
const d = new Date(date);
|
|
1656
|
+
return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
1657
|
+
}
|
|
1658
|
+
/** Whether any filters are active */
|
|
1659
|
+
get HasActiveFilters() {
|
|
1660
|
+
return (this.EntityNames.length > 1 && this.Filters.EntityName !== '') ||
|
|
1661
|
+
this.Filters.MinScore > 0 ||
|
|
1662
|
+
(this.Filters.MaxScore > 0 && this.Filters.MaxScore < 1) ||
|
|
1663
|
+
this.Filters.DateFrom !== '' ||
|
|
1664
|
+
this.Filters.DateTo !== '';
|
|
1665
|
+
}
|
|
1666
|
+
// ---- Private Methods ----
|
|
1667
|
+
setupFilterDebounce() {
|
|
1668
|
+
this.filterSubject.pipe(debounceTime(300), takeUntil(this.destroy$)).subscribe(() => {
|
|
1669
|
+
this.applyFilters();
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
/** Build entity document options from KnowledgeHubMetadataEngine cached entities */
|
|
1673
|
+
buildEntityDocumentOptionsFromEngine(docs) {
|
|
1674
|
+
this.EntityDocuments = docs.map(d => ({
|
|
1675
|
+
ID: d.ID,
|
|
1676
|
+
Name: d.Name ?? 'Unnamed',
|
|
1677
|
+
EntityName: d.Entity ?? '',
|
|
1678
|
+
PotentialMatchThreshold: this.normalizeDupeThreshold(d.PotentialMatchThreshold, 0.70),
|
|
1679
|
+
AbsoluteMatchThreshold: this.normalizeDupeThreshold(d.AbsoluteMatchThreshold, 0.95)
|
|
1680
|
+
}));
|
|
1681
|
+
// Auto-select the first entity document if available
|
|
1682
|
+
if (this.EntityDocuments.length > 0 && !this.SelectedEntityDocumentID) {
|
|
1683
|
+
this.SelectedEntityDocumentID = this.EntityDocuments[0].ID;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
/** Subscribe to PipelineProgress for a specific detection run */
|
|
1687
|
+
/**
|
|
1688
|
+
* Normalizes a duplicate threshold value — treats null, undefined, 0, and 1.0
|
|
1689
|
+
* as "not configured" and falls back to a sensible default.
|
|
1690
|
+
* Thresholds of exactly 1.0 mean "100% match only" which is effectively useless
|
|
1691
|
+
* for real-world duplicate detection.
|
|
1692
|
+
*/
|
|
1693
|
+
normalizeDupeThreshold(value, fallback) {
|
|
1694
|
+
if (value == null || value <= 0 || value >= 1.0) {
|
|
1695
|
+
return fallback;
|
|
1696
|
+
}
|
|
1697
|
+
return value;
|
|
1698
|
+
}
|
|
1699
|
+
/** Handle potential threshold slider change */
|
|
1700
|
+
OnPotentialThresholdChanged(value) {
|
|
1701
|
+
this.RunPotentialThreshold = value;
|
|
1702
|
+
}
|
|
1703
|
+
/** Handle absolute threshold slider change */
|
|
1704
|
+
OnAbsoluteThresholdChanged(value) {
|
|
1705
|
+
this.RunAbsoluteThreshold = value;
|
|
1706
|
+
}
|
|
1707
|
+
subscribeToPipelineProgress(pipelineRunID) {
|
|
1708
|
+
const provider = Metadata.Provider;
|
|
1709
|
+
const subscriptionQuery = `
|
|
1710
|
+
subscription PipelineProgress($pipelineRunID: String!) {
|
|
1711
|
+
PipelineProgress(pipelineRunID: $pipelineRunID) {
|
|
1712
|
+
PipelineRunID
|
|
1713
|
+
Stage
|
|
1714
|
+
TotalItems
|
|
1715
|
+
ProcessedItems
|
|
1716
|
+
CurrentItem
|
|
1717
|
+
PercentComplete
|
|
1718
|
+
ElapsedMs
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
`;
|
|
1722
|
+
let idleTimer = null;
|
|
1723
|
+
const finishDetection = (success) => {
|
|
1724
|
+
if (idleTimer)
|
|
1725
|
+
clearTimeout(idleTimer);
|
|
1726
|
+
rxSub?.unsubscribe();
|
|
1727
|
+
Promise.resolve().then(async () => {
|
|
1728
|
+
this.IsDetecting = false;
|
|
1729
|
+
this.DetectionStage = success ? 'Complete' : 'Error';
|
|
1730
|
+
this.DetectionProgress = success ? 100 : 0;
|
|
1731
|
+
if (success) {
|
|
1732
|
+
await this.LoadData();
|
|
1733
|
+
}
|
|
1734
|
+
this.cdr.detectChanges();
|
|
1735
|
+
});
|
|
1736
|
+
};
|
|
1737
|
+
const resetIdleTimer = () => {
|
|
1738
|
+
if (idleTimer)
|
|
1739
|
+
clearTimeout(idleTimer);
|
|
1740
|
+
idleTimer = setTimeout(() => {
|
|
1741
|
+
if (this.IsDetecting) {
|
|
1742
|
+
finishDetection(true);
|
|
1743
|
+
}
|
|
1744
|
+
}, 60000); // 60s timeout for duplicate detection (can be very long)
|
|
1745
|
+
};
|
|
1746
|
+
resetIdleTimer();
|
|
1747
|
+
const sub = provider.subscribe(subscriptionQuery, { pipelineRunID });
|
|
1748
|
+
const rxSub = sub.pipe(takeUntil(this.destroy$)).subscribe({
|
|
1749
|
+
next: (data) => {
|
|
1750
|
+
const progress = data['PipelineProgress'];
|
|
1751
|
+
if (!progress)
|
|
1752
|
+
return;
|
|
1753
|
+
const stage = progress['Stage'];
|
|
1754
|
+
const pct = progress['PercentComplete'];
|
|
1755
|
+
const currentItem = progress['CurrentItem'];
|
|
1756
|
+
this.DetectionProgress = pct;
|
|
1757
|
+
this.DetectionStage = this.formatDetectionStage(stage);
|
|
1758
|
+
this.DetectionCurrentItem = currentItem ?? '';
|
|
1759
|
+
this.cdr.detectChanges();
|
|
1760
|
+
if (stage === 'complete') {
|
|
1761
|
+
finishDetection(true);
|
|
1762
|
+
}
|
|
1763
|
+
else if (stage === 'error') {
|
|
1764
|
+
finishDetection(false);
|
|
1765
|
+
}
|
|
1766
|
+
else {
|
|
1767
|
+
resetIdleTimer();
|
|
1768
|
+
}
|
|
1769
|
+
},
|
|
1770
|
+
error: (err) => {
|
|
1771
|
+
console.error('[DuplicateDetection] Pipeline subscription error:', err);
|
|
1772
|
+
finishDetection(false);
|
|
1773
|
+
}
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
/** Format detection stage names for display */
|
|
1777
|
+
formatDetectionStage(stage) {
|
|
1778
|
+
const stageMap = {
|
|
1779
|
+
'vectorize': 'Vectorizing records',
|
|
1780
|
+
'autotag': 'Analyzing matches',
|
|
1781
|
+
'extract': 'Querying vector database',
|
|
1782
|
+
'complete': 'Complete',
|
|
1783
|
+
'error': 'Error'
|
|
1784
|
+
};
|
|
1785
|
+
return stageMap[stage] ?? stage;
|
|
1786
|
+
}
|
|
1787
|
+
/** Extract unique entity names from loaded runs */
|
|
1788
|
+
extractEntityNames() {
|
|
1789
|
+
const nameSet = new Set();
|
|
1790
|
+
// Extract from runs first
|
|
1791
|
+
for (const run of this.Runs) {
|
|
1792
|
+
if (run.Entity) {
|
|
1793
|
+
nameSet.add(run.Entity);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
// Also extract from groups (covers cases where runs failed but details/matches exist)
|
|
1797
|
+
for (const group of this.AllGroups) {
|
|
1798
|
+
if (group.EntityName && group.EntityName !== 'Unknown') {
|
|
1799
|
+
nameSet.add(group.EntityName);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
this.EntityNames = Array.from(nameSet).sort();
|
|
1803
|
+
// Auto-select if only one entity — no point showing "All Entities" for a single option
|
|
1804
|
+
if (this.EntityNames.length === 1) {
|
|
1805
|
+
this.Filters.EntityName = this.EntityNames[0];
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Build DuplicateGroup objects by joining details to their matches,
|
|
1810
|
+
* and resolving the entity name from the parent run.
|
|
1811
|
+
*/
|
|
1812
|
+
buildGroups() {
|
|
1813
|
+
const runMap = new Map();
|
|
1814
|
+
for (const run of this.Runs) {
|
|
1815
|
+
runMap.set(run.ID, run);
|
|
1816
|
+
}
|
|
1817
|
+
const matchesByDetail = new Map();
|
|
1818
|
+
for (const match of this.Matches) {
|
|
1819
|
+
const key = match.DuplicateRunDetailID;
|
|
1820
|
+
const existing = matchesByDetail.get(key);
|
|
1821
|
+
if (existing) {
|
|
1822
|
+
existing.push(match);
|
|
1823
|
+
}
|
|
1824
|
+
else {
|
|
1825
|
+
matchesByDetail.set(key, [match]);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
this.AllGroups = [];
|
|
1829
|
+
for (const detail of this.Details) {
|
|
1830
|
+
const detailMatches = matchesByDetail.get(detail.ID);
|
|
1831
|
+
if (!detailMatches || detailMatches.length === 0) {
|
|
1832
|
+
continue; // Skip details with no matches
|
|
1833
|
+
}
|
|
1834
|
+
const run = runMap.get(detail.DuplicateRunID);
|
|
1835
|
+
const highestScore = this.computeHighestScore(detailMatches);
|
|
1836
|
+
const dominantStatus = this.computeDominantApprovalStatus(detailMatches);
|
|
1837
|
+
const latestMatchDate = this.computeLatestMatchDate(detailMatches);
|
|
1838
|
+
// Parse source record metadata (stored as JSON by the detector)
|
|
1839
|
+
const metadata = this.parseRecordMetadata(detail.RecordMetadata);
|
|
1840
|
+
const entityName = metadata.Entity || run?.Entity || 'Unknown';
|
|
1841
|
+
const entityIcon = metadata.EntityIcon || 'fa-solid fa-database';
|
|
1842
|
+
const recordName = metadata.Name || detail.RecordID;
|
|
1843
|
+
// Build top match summaries from match metadata
|
|
1844
|
+
const topMatchSummaries = this.buildTopMatchSummaries(detailMatches, 3);
|
|
1845
|
+
this.AllGroups.push({
|
|
1846
|
+
DetailId: detail.ID,
|
|
1847
|
+
RunId: detail.DuplicateRunID,
|
|
1848
|
+
RecordId: detail.RecordID,
|
|
1849
|
+
EntityName: entityName,
|
|
1850
|
+
EntityIcon: entityIcon,
|
|
1851
|
+
RecordName: recordName,
|
|
1852
|
+
ApprovalStatus: dominantStatus,
|
|
1853
|
+
MatchCount: detailMatches.length,
|
|
1854
|
+
HighestScore: highestScore,
|
|
1855
|
+
Matches: detailMatches,
|
|
1856
|
+
MatchedAt: latestMatchDate,
|
|
1857
|
+
Metadata: metadata,
|
|
1858
|
+
TopMatchSummaries: topMatchSummaries,
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
/** Find the highest match probability across a set of matches */
|
|
1863
|
+
computeHighestScore(matches) {
|
|
1864
|
+
let max = 0;
|
|
1865
|
+
for (const m of matches) {
|
|
1866
|
+
if (m.MatchProbability > max) {
|
|
1867
|
+
max = m.MatchProbability;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
return max;
|
|
1871
|
+
}
|
|
1872
|
+
// ════════════════════════════════════════════
|
|
1873
|
+
// Comparison Panel
|
|
1874
|
+
// ════════════════════════════════════════════
|
|
1875
|
+
/** Open the comparison slide-in panel for a group — loads real entity records */
|
|
1876
|
+
async OpenComparison(group) {
|
|
1877
|
+
this.ComparisonGroup = group;
|
|
1878
|
+
this.ComparisonShowAllFields = true;
|
|
1879
|
+
this.SurvivorColumnIndex = 0;
|
|
1880
|
+
this.ComparisonClosing = false;
|
|
1881
|
+
this.ComparisonLoading = true;
|
|
1882
|
+
this.ComparisonFields = [];
|
|
1883
|
+
this.ComparisonMatches = [];
|
|
1884
|
+
this.comparisonRecords.clear();
|
|
1885
|
+
this.ComparisonDependencies.clear();
|
|
1886
|
+
this.DepsExpandedColumns.clear();
|
|
1887
|
+
this.depsEntityGroupExpanded.clear();
|
|
1888
|
+
this.ShowMergeConfirm = false;
|
|
1889
|
+
this.cdr.detectChanges();
|
|
1890
|
+
// Load actual entity records and dependencies in parallel
|
|
1891
|
+
await Promise.all([
|
|
1892
|
+
this.loadComparisonRecords(group),
|
|
1893
|
+
this.loadComparisonDependencies(group)
|
|
1894
|
+
]);
|
|
1895
|
+
this.buildComparisonData();
|
|
1896
|
+
this.ComparisonLoading = false;
|
|
1897
|
+
this.cdr.detectChanges();
|
|
1898
|
+
}
|
|
1899
|
+
/** Close the comparison panel with slide-out animation */
|
|
1900
|
+
CloseComparison() {
|
|
1901
|
+
this.ShowMergeConfirm = false;
|
|
1902
|
+
this.ComparisonClosing = true;
|
|
1903
|
+
this.cdr.detectChanges();
|
|
1904
|
+
setTimeout(() => {
|
|
1905
|
+
this.ComparisonGroup = null;
|
|
1906
|
+
this.ComparisonClosing = false;
|
|
1907
|
+
this.ComparisonFields = [];
|
|
1908
|
+
this.ComparisonMatches = [];
|
|
1909
|
+
this.ComparisonDependencies.clear();
|
|
1910
|
+
this.DepsExpandedColumns.clear();
|
|
1911
|
+
this.depsEntityGroupExpanded.clear();
|
|
1912
|
+
this.cdr.detectChanges();
|
|
1913
|
+
}, 250);
|
|
1914
|
+
}
|
|
1915
|
+
/** Get visible fields based on the toggle state */
|
|
1916
|
+
GetVisibleFields() {
|
|
1917
|
+
return this.ComparisonShowAllFields
|
|
1918
|
+
? this.ComparisonFields
|
|
1919
|
+
: this.ComparisonFields.filter(f => f.HasDifference);
|
|
1920
|
+
}
|
|
1921
|
+
/** Case-insensitive, trimmed comparison of two field values */
|
|
1922
|
+
AreValuesEqual(a, b) {
|
|
1923
|
+
if (a == null && b == null)
|
|
1924
|
+
return true;
|
|
1925
|
+
if (a == null || b == null)
|
|
1926
|
+
return false;
|
|
1927
|
+
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
1928
|
+
}
|
|
1929
|
+
/** Set the surviving record column index */
|
|
1930
|
+
SetSurvivor(columnIndex) {
|
|
1931
|
+
this.SurvivorColumnIndex = columnIndex;
|
|
1932
|
+
// When switching survivor, reset all field selections to the new survivor
|
|
1933
|
+
for (const field of this.ComparisonFields) {
|
|
1934
|
+
field.SelectedColumnIndex = columnIndex;
|
|
1935
|
+
}
|
|
1936
|
+
this.cdr.detectChanges();
|
|
1937
|
+
}
|
|
1938
|
+
/** Select all field values from a specific column */
|
|
1939
|
+
UseAllFieldsFrom(columnIndex) {
|
|
1940
|
+
for (const field of this.ComparisonFields) {
|
|
1941
|
+
// Only select if the column has a value for this field
|
|
1942
|
+
const val = columnIndex === 0 ? field.SourceValue : field.MatchValues[columnIndex - 1];
|
|
1943
|
+
if (val != null) {
|
|
1944
|
+
field.SelectedColumnIndex = columnIndex;
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
this.cdr.detectChanges();
|
|
1948
|
+
}
|
|
1949
|
+
/** Select a specific field value from a column */
|
|
1950
|
+
SelectFieldValue(field, columnIndex) {
|
|
1951
|
+
field.SelectedColumnIndex = columnIndex;
|
|
1952
|
+
this.cdr.detectChanges();
|
|
1953
|
+
}
|
|
1954
|
+
/** Check if all fields are selected from a given column */
|
|
1955
|
+
AllFieldsSelectedFrom(columnIndex) {
|
|
1956
|
+
return this.ComparisonFields.every(f => f.SelectedColumnIndex === columnIndex);
|
|
1957
|
+
}
|
|
1958
|
+
/** Count how many fields are cherry-picked from non-survivor columns */
|
|
1959
|
+
CherryPickedCount() {
|
|
1960
|
+
return this.ComparisonFields.filter(f => f.SelectedColumnIndex !== this.SurvivorColumnIndex).length;
|
|
1961
|
+
}
|
|
1962
|
+
/** Get the name of the surviving record */
|
|
1963
|
+
SurvivorName() {
|
|
1964
|
+
if (!this.ComparisonGroup)
|
|
1965
|
+
return '';
|
|
1966
|
+
if (this.SurvivorColumnIndex === 0)
|
|
1967
|
+
return this.ComparisonGroup.RecordName;
|
|
1968
|
+
const match = this.ComparisonMatches[this.SurvivorColumnIndex - 1];
|
|
1969
|
+
return match?.Name || 'Unknown';
|
|
1970
|
+
}
|
|
1971
|
+
/** Get the primary key display string for the surviving record */
|
|
1972
|
+
SurvivorKeyDisplay() {
|
|
1973
|
+
if (!this.ComparisonGroup)
|
|
1974
|
+
return '';
|
|
1975
|
+
const keyStr = this.getCompositeKeyStringForColumn(this.SurvivorColumnIndex);
|
|
1976
|
+
if (!keyStr)
|
|
1977
|
+
return '';
|
|
1978
|
+
const ck = new CompositeKey();
|
|
1979
|
+
ck.LoadFromConcatenatedString(keyStr);
|
|
1980
|
+
return ck.KeyValuePairs.map(kv => `${kv.FieldName}: ${kv.Value}`).join(', ');
|
|
1981
|
+
}
|
|
1982
|
+
// ════════════════════════════════════════════
|
|
1983
|
+
// Dependencies
|
|
1984
|
+
// ════════════════════════════════════════════
|
|
1985
|
+
/** Get dependencies for a column (0 = source, 1+ = match index) */
|
|
1986
|
+
GetDepsForColumn(columnIndex) {
|
|
1987
|
+
const keyStr = this.getCompositeKeyStringForColumn(columnIndex);
|
|
1988
|
+
return keyStr ? (this.ComparisonDependencies.get(keyStr) ?? []) : [];
|
|
1989
|
+
}
|
|
1990
|
+
/** Get deps grouped by related entity for a column */
|
|
1991
|
+
GetGroupedDeps(columnIndex) {
|
|
1992
|
+
const deps = this.GetDepsForColumn(columnIndex);
|
|
1993
|
+
const grouped = new Map();
|
|
1994
|
+
for (const dep of deps) {
|
|
1995
|
+
const name = dep.RelatedEntityName;
|
|
1996
|
+
grouped.set(name, (grouped.get(name) ?? 0) + 1);
|
|
1997
|
+
}
|
|
1998
|
+
return Array.from(grouped.entries())
|
|
1999
|
+
.map(([Entity, Count]) => ({ Entity, Count }))
|
|
2000
|
+
.sort((a, b) => b.Count - a.Count);
|
|
2001
|
+
}
|
|
2002
|
+
/** Cached dependent records loaded on demand, keyed by "columnIndex::entityName" */
|
|
2003
|
+
depRecordsCache = new Map();
|
|
2004
|
+
/** Tracks which entity groups are currently loading */
|
|
2005
|
+
depRecordsLoading = new Set();
|
|
2006
|
+
/** Get cached dependent records for an entity group (empty until expanded and loaded) */
|
|
2007
|
+
GetDepRecords(columnIndex, entityName) {
|
|
2008
|
+
return this.depRecordsCache.get(`${columnIndex}::${entityName}`) ?? [];
|
|
2009
|
+
}
|
|
2010
|
+
/** Check if dep records are loading for an entity group */
|
|
2011
|
+
IsDepRecordsLoading(columnIndex, entityName) {
|
|
2012
|
+
return this.depRecordsLoading.has(`${columnIndex}::${entityName}`);
|
|
2013
|
+
}
|
|
2014
|
+
/** Navigate to a dependent record */
|
|
2015
|
+
OpenDepRecord(record) {
|
|
2016
|
+
if (record.PrimaryKey) {
|
|
2017
|
+
this.navigationService.OpenEntityRecord(record.EntityName, record.PrimaryKey);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
/** Toggle expanded state for a specific entity group — lazy-loads records on first expand */
|
|
2021
|
+
async ToggleDepEntityGroup(columnIndex, entityName) {
|
|
2022
|
+
const key = `${columnIndex}::${entityName}`;
|
|
2023
|
+
if (this.depsEntityGroupExpanded.has(key)) {
|
|
2024
|
+
this.depsEntityGroupExpanded.delete(key);
|
|
2025
|
+
this.cdr.detectChanges();
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
this.depsEntityGroupExpanded.add(key);
|
|
2029
|
+
this.cdr.detectChanges();
|
|
2030
|
+
// Lazy-load actual dependent records on first expand
|
|
2031
|
+
if (!this.depRecordsCache.has(key)) {
|
|
2032
|
+
await this.loadDepRecordsForGroup(columnIndex, entityName);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
/** Load dependent records for an entity group via RunView */
|
|
2036
|
+
async loadDepRecordsForGroup(columnIndex, relatedEntityName) {
|
|
2037
|
+
const key = `${columnIndex}::${relatedEntityName}`;
|
|
2038
|
+
this.depRecordsLoading.add(key);
|
|
2039
|
+
this.cdr.detectChanges();
|
|
2040
|
+
try {
|
|
2041
|
+
// Get the FK field name from the dependency info
|
|
2042
|
+
const deps = this.GetDepsForColumn(columnIndex)
|
|
2043
|
+
.filter(d => d.RelatedEntityName === relatedEntityName);
|
|
2044
|
+
if (deps.length === 0)
|
|
2045
|
+
return;
|
|
2046
|
+
const dep = deps[0];
|
|
2047
|
+
const fkFieldName = dep.FieldName;
|
|
2048
|
+
// Get the parent record's primary key value from the column's composite key string
|
|
2049
|
+
const keyStr = this.getCompositeKeyStringForColumn(columnIndex);
|
|
2050
|
+
if (!keyStr)
|
|
2051
|
+
return;
|
|
2052
|
+
const parentCK = new CompositeKey();
|
|
2053
|
+
parentCK.LoadFromConcatenatedString(keyStr);
|
|
2054
|
+
const parentKeyValue = parentCK.KeyValuePairs[0]?.Value;
|
|
2055
|
+
if (!parentKeyValue)
|
|
2056
|
+
return;
|
|
2057
|
+
// Query the related entity for records pointing at this parent via the FK field
|
|
2058
|
+
const rv = new RunView();
|
|
2059
|
+
const md = new Metadata();
|
|
2060
|
+
const relatedEntityInfo = md.Entities.find(e => e.Name === relatedEntityName);
|
|
2061
|
+
const nameField = relatedEntityInfo?.NameField;
|
|
2062
|
+
const pkFieldName = relatedEntityInfo?.FirstPrimaryKey?.Name || 'ID';
|
|
2063
|
+
const result = await rv.RunView({
|
|
2064
|
+
EntityName: relatedEntityName,
|
|
2065
|
+
ExtraFilter: `${fkFieldName}='${parentKeyValue}'`,
|
|
2066
|
+
Fields: nameField ? [pkFieldName, nameField.Name] : [pkFieldName],
|
|
2067
|
+
MaxRows: 50,
|
|
2068
|
+
ResultType: 'simple',
|
|
2069
|
+
});
|
|
2070
|
+
const records = [];
|
|
2071
|
+
if (result.Success && result.Results) {
|
|
2072
|
+
for (const row of result.Results) {
|
|
2073
|
+
const pk = new CompositeKey([{ FieldName: pkFieldName, Value: String(row[pkFieldName] || '') }]);
|
|
2074
|
+
const name = nameField ? String(row[nameField.Name] || '') : String(row[pkFieldName] || '');
|
|
2075
|
+
records.push({ Name: name || pk.Values(), PrimaryKey: pk, EntityName: relatedEntityName });
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
this.depRecordsCache.set(key, records);
|
|
2079
|
+
}
|
|
2080
|
+
catch (error) {
|
|
2081
|
+
console.warn('[DuplicateDetection] Error loading dep records:', error);
|
|
2082
|
+
this.depRecordsCache.set(key, []);
|
|
2083
|
+
}
|
|
2084
|
+
finally {
|
|
2085
|
+
this.depRecordsLoading.delete(key);
|
|
2086
|
+
this.cdr.detectChanges();
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
/** Check if a specific entity group is expanded */
|
|
2090
|
+
IsDepEntityGroupExpanded(columnIndex, entityName) {
|
|
2091
|
+
return this.depsEntityGroupExpanded.has(`${columnIndex}::${entityName}`);
|
|
2092
|
+
}
|
|
2093
|
+
/** Get total dependency count for a column */
|
|
2094
|
+
GetTotalDeps(columnIndex) {
|
|
2095
|
+
return this.GetDepsForColumn(columnIndex).length;
|
|
2096
|
+
}
|
|
2097
|
+
/** Toggle expanded state for dependency details in a column */
|
|
2098
|
+
ToggleDepsExpanded(columnIndex) {
|
|
2099
|
+
if (this.DepsExpandedColumns.has(columnIndex)) {
|
|
2100
|
+
this.DepsExpandedColumns.delete(columnIndex);
|
|
2101
|
+
}
|
|
2102
|
+
else {
|
|
2103
|
+
this.DepsExpandedColumns.add(columnIndex);
|
|
2104
|
+
}
|
|
2105
|
+
this.cdr.detectChanges();
|
|
2106
|
+
}
|
|
2107
|
+
/** Check if dependency details are expanded for a column */
|
|
2108
|
+
IsDepsExpanded(columnIndex) {
|
|
2109
|
+
return this.DepsExpandedColumns.has(columnIndex);
|
|
2110
|
+
}
|
|
2111
|
+
/** Get the column index with the most dependencies */
|
|
2112
|
+
GetMaxDepsColumnIndex() {
|
|
2113
|
+
let maxDeps = -1;
|
|
2114
|
+
let maxIndex = 0;
|
|
2115
|
+
const totalColumns = 1 + this.ComparisonMatches.length;
|
|
2116
|
+
for (let i = 0; i < totalColumns; i++) {
|
|
2117
|
+
const count = this.GetTotalDeps(i);
|
|
2118
|
+
if (count > maxDeps) {
|
|
2119
|
+
maxDeps = count;
|
|
2120
|
+
maxIndex = i;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
return maxIndex;
|
|
2124
|
+
}
|
|
2125
|
+
/** Get the composite key string for a column index */
|
|
2126
|
+
getCompositeKeyStringForColumn(columnIndex) {
|
|
2127
|
+
if (!this.ComparisonGroup)
|
|
2128
|
+
return null;
|
|
2129
|
+
if (columnIndex === 0)
|
|
2130
|
+
return this.ComparisonGroup.RecordId;
|
|
2131
|
+
const match = this.ComparisonMatches[columnIndex - 1];
|
|
2132
|
+
return match?.Match.MatchRecordID ?? null;
|
|
2133
|
+
}
|
|
2134
|
+
/** Get the name for a column index */
|
|
2135
|
+
GetColumnName(columnIndex) {
|
|
2136
|
+
if (!this.ComparisonGroup)
|
|
2137
|
+
return '';
|
|
2138
|
+
if (columnIndex === 0)
|
|
2139
|
+
return this.ComparisonGroup.RecordName;
|
|
2140
|
+
const match = this.ComparisonMatches[columnIndex - 1];
|
|
2141
|
+
return match?.Name ?? 'Unknown';
|
|
2142
|
+
}
|
|
2143
|
+
// ════════════════════════════════════════════
|
|
2144
|
+
// Merge Confirmation
|
|
2145
|
+
// ════════════════════════════════════════════
|
|
2146
|
+
/** Open the merge confirmation panel */
|
|
2147
|
+
OpenMergeConfirm() {
|
|
2148
|
+
this.ShowMergeConfirm = true;
|
|
2149
|
+
this.cdr.detectChanges();
|
|
2150
|
+
}
|
|
2151
|
+
/** Close the merge confirmation panel */
|
|
2152
|
+
CloseMergeConfirm() {
|
|
2153
|
+
this.ShowMergeConfirm = false;
|
|
2154
|
+
this.cdr.detectChanges();
|
|
2155
|
+
}
|
|
2156
|
+
/** Get the list of cherry-picked field overrides (fields picked from non-survivor columns) */
|
|
2157
|
+
GetCherryPickedFields() {
|
|
2158
|
+
return this.ComparisonFields
|
|
2159
|
+
.filter(f => f.SelectedColumnIndex !== this.SurvivorColumnIndex)
|
|
2160
|
+
.map(f => {
|
|
2161
|
+
const value = f.SelectedColumnIndex === 0
|
|
2162
|
+
? f.SourceValue
|
|
2163
|
+
: f.MatchValues[f.SelectedColumnIndex - 1];
|
|
2164
|
+
return {
|
|
2165
|
+
FieldName: f.FieldName,
|
|
2166
|
+
DisplayName: f.DisplayName,
|
|
2167
|
+
Value: value ?? '(empty)',
|
|
2168
|
+
SourceName: this.GetColumnName(f.SelectedColumnIndex)
|
|
2169
|
+
};
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
/** Get non-surviving columns with their dependency counts */
|
|
2173
|
+
GetNonSurvivorColumns() {
|
|
2174
|
+
const result = [];
|
|
2175
|
+
const totalColumns = 1 + this.ComparisonMatches.length;
|
|
2176
|
+
for (let i = 0; i < totalColumns; i++) {
|
|
2177
|
+
if (i === this.SurvivorColumnIndex)
|
|
2178
|
+
continue;
|
|
2179
|
+
result.push({
|
|
2180
|
+
ColumnIndex: i,
|
|
2181
|
+
Name: this.GetColumnName(i),
|
|
2182
|
+
DepCount: this.GetTotalDeps(i)
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
return result;
|
|
2186
|
+
}
|
|
2187
|
+
/** Execute the merge operation */
|
|
2188
|
+
async ExecuteMerge() {
|
|
2189
|
+
if (!this.ComparisonGroup || this.IsMerging)
|
|
2190
|
+
return;
|
|
2191
|
+
this.IsMerging = true;
|
|
2192
|
+
this.cdr.detectChanges();
|
|
2193
|
+
try {
|
|
2194
|
+
const request = new RecordMergeRequest();
|
|
2195
|
+
request.EntityName = this.ComparisonGroup.EntityName;
|
|
2196
|
+
// Build surviving record composite key
|
|
2197
|
+
const survivorKeyStr = this.getCompositeKeyStringForColumn(this.SurvivorColumnIndex);
|
|
2198
|
+
if (!survivorKeyStr)
|
|
2199
|
+
return;
|
|
2200
|
+
const survivorKey = new CompositeKey();
|
|
2201
|
+
survivorKey.SimpleLoadFromURLSegment(survivorKeyStr);
|
|
2202
|
+
request.SurvivingRecordCompositeKey = survivorKey;
|
|
2203
|
+
// Build records to merge (non-survivors)
|
|
2204
|
+
request.RecordsToMerge = this.buildNonSurvivorKeys();
|
|
2205
|
+
// Build field map for cherry-picked fields
|
|
2206
|
+
const cherryPicked = this.GetCherryPickedFields();
|
|
2207
|
+
if (cherryPicked.length > 0) {
|
|
2208
|
+
request.FieldMap = cherryPicked.map(f => ({
|
|
2209
|
+
FieldName: f.FieldName,
|
|
2210
|
+
Value: f.Value
|
|
2211
|
+
}));
|
|
2212
|
+
}
|
|
2213
|
+
const result = await Metadata.Provider.MergeRecords(request);
|
|
2214
|
+
if (result.Success) {
|
|
2215
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Successfully merged ${request.RecordsToMerge.length + 1} records into one.`, 'success', 5000);
|
|
2216
|
+
// Close both panels and reload
|
|
2217
|
+
this.ShowMergeConfirm = false;
|
|
2218
|
+
this.ComparisonGroup = null;
|
|
2219
|
+
this.ComparisonFields = [];
|
|
2220
|
+
this.ComparisonMatches = [];
|
|
2221
|
+
this.ComparisonDependencies.clear();
|
|
2222
|
+
this.DepsExpandedColumns.clear();
|
|
2223
|
+
this.depsEntityGroupExpanded.clear();
|
|
2224
|
+
await this.LoadData();
|
|
2225
|
+
}
|
|
2226
|
+
else {
|
|
2227
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Merge failed: ${result.OverallStatus}`, 'error', 5000);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
catch (error) {
|
|
2231
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2232
|
+
console.error('[DuplicateDetection] Merge error:', msg);
|
|
2233
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Merge error: ${msg}`, 'error', 5000);
|
|
2234
|
+
}
|
|
2235
|
+
finally {
|
|
2236
|
+
this.IsMerging = false;
|
|
2237
|
+
this.cdr.detectChanges();
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
/** Build CompositeKey array for non-surviving records */
|
|
2241
|
+
buildNonSurvivorKeys() {
|
|
2242
|
+
const keys = [];
|
|
2243
|
+
const totalColumns = 1 + this.ComparisonMatches.length;
|
|
2244
|
+
for (let i = 0; i < totalColumns; i++) {
|
|
2245
|
+
if (i === this.SurvivorColumnIndex)
|
|
2246
|
+
continue;
|
|
2247
|
+
const keyStr = this.getCompositeKeyStringForColumn(i);
|
|
2248
|
+
if (keyStr) {
|
|
2249
|
+
const ck = new CompositeKey();
|
|
2250
|
+
ck.SimpleLoadFromURLSegment(keyStr);
|
|
2251
|
+
keys.push(ck);
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
return keys;
|
|
2255
|
+
}
|
|
2256
|
+
/** Approve an individual match */
|
|
2257
|
+
async ApproveIndividualMatch(matchInfo) {
|
|
2258
|
+
this.IsSaving = true;
|
|
2259
|
+
matchInfo.Match.ApprovalStatus = 'Approved';
|
|
2260
|
+
await matchInfo.Match.Save();
|
|
2261
|
+
this.IsSaving = false;
|
|
2262
|
+
this.cdr.detectChanges();
|
|
2263
|
+
}
|
|
2264
|
+
/** Reject an individual match */
|
|
2265
|
+
async RejectIndividualMatch(matchInfo) {
|
|
2266
|
+
this.IsSaving = true;
|
|
2267
|
+
matchInfo.Match.ApprovalStatus = 'Rejected';
|
|
2268
|
+
await matchInfo.Match.Save();
|
|
2269
|
+
this.IsSaving = false;
|
|
2270
|
+
this.cdr.detectChanges();
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Load the actual entity records for the source + all matches in one RunView call.
|
|
2274
|
+
* Record IDs are stored as composite key strings (e.g., "ID|uuid") — we parse each
|
|
2275
|
+
* into a CompositeKey, generate WHERE clauses, and OR them together for one query.
|
|
2276
|
+
* Results stored in comparisonRecords map keyed by the composite key string.
|
|
2277
|
+
*/
|
|
2278
|
+
async loadComparisonRecords(group) {
|
|
2279
|
+
this.comparisonRecords.clear();
|
|
2280
|
+
// Collect all composite key strings (source + matches)
|
|
2281
|
+
const keyStrings = [group.RecordId];
|
|
2282
|
+
for (const m of group.Matches) {
|
|
2283
|
+
if (m.MatchRecordID) {
|
|
2284
|
+
keyStrings.push(m.MatchRecordID);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
// Parse each into a CompositeKey and build WHERE clauses
|
|
2288
|
+
const whereClauses = [];
|
|
2289
|
+
for (const keyStr of keyStrings) {
|
|
2290
|
+
const ck = new CompositeKey();
|
|
2291
|
+
ck.SimpleLoadFromURLSegment(keyStr);
|
|
2292
|
+
if (ck.KeyValuePairs.length > 0) {
|
|
2293
|
+
whereClauses.push(`(${ck.ToWhereClause()})`);
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
if (whereClauses.length === 0)
|
|
2297
|
+
return;
|
|
2298
|
+
// Single RunView with all records OR'd together
|
|
2299
|
+
const rv = new RunView();
|
|
2300
|
+
const result = await rv.RunView({
|
|
2301
|
+
EntityName: group.EntityName,
|
|
2302
|
+
ExtraFilter: whereClauses.join(' OR '),
|
|
2303
|
+
ResultType: 'simple',
|
|
2304
|
+
});
|
|
2305
|
+
if (result.Success && result.Results) {
|
|
2306
|
+
// Get entity info to know primary key field name
|
|
2307
|
+
const md = new Metadata();
|
|
2308
|
+
const entityInfo = md.Entities.find(e => e.Name === group.EntityName);
|
|
2309
|
+
const pkFieldName = entityInfo?.FirstPrimaryKey?.Name || 'ID';
|
|
2310
|
+
for (const record of result.Results) {
|
|
2311
|
+
const pkValue = String(record[pkFieldName] || '');
|
|
2312
|
+
// Store keyed by both raw PK value and the composite key string format
|
|
2313
|
+
this.comparisonRecords.set(pkValue, record);
|
|
2314
|
+
this.comparisonRecords.set(`${pkFieldName}|${pkValue}`, record);
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
/**
|
|
2319
|
+
* Load dependencies for all records (source + matches) in parallel.
|
|
2320
|
+
* Each record's deps are stored in ComparisonDependencies keyed by composite key string.
|
|
2321
|
+
*/
|
|
2322
|
+
async loadComparisonDependencies(group) {
|
|
2323
|
+
const provider = Metadata.Provider;
|
|
2324
|
+
const keyStrings = [group.RecordId];
|
|
2325
|
+
for (const m of group.Matches) {
|
|
2326
|
+
if (m.MatchRecordID) {
|
|
2327
|
+
keyStrings.push(m.MatchRecordID);
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
const promises = keyStrings.map(async (keyStr) => {
|
|
2331
|
+
const ck = new CompositeKey();
|
|
2332
|
+
ck.SimpleLoadFromURLSegment(keyStr);
|
|
2333
|
+
if (ck.KeyValuePairs.length === 0)
|
|
2334
|
+
return;
|
|
2335
|
+
try {
|
|
2336
|
+
const deps = await provider.GetRecordDependencies(group.EntityName, ck);
|
|
2337
|
+
this.ComparisonDependencies.set(keyStr, deps);
|
|
2338
|
+
}
|
|
2339
|
+
catch (error) {
|
|
2340
|
+
console.warn(`[DuplicateDetection] Failed to load deps for ${keyStr}:`, error);
|
|
2341
|
+
this.ComparisonDependencies.set(keyStr, []);
|
|
2342
|
+
}
|
|
2343
|
+
});
|
|
2344
|
+
await Promise.all(promises);
|
|
2345
|
+
}
|
|
2346
|
+
/** Get the field value for a record from the loaded entity records map */
|
|
2347
|
+
getRecordFieldValue(recordId, fieldName) {
|
|
2348
|
+
// Try the composite key string directly (e.g., "ID|uuid")
|
|
2349
|
+
let record = this.comparisonRecords.get(recordId);
|
|
2350
|
+
if (!record) {
|
|
2351
|
+
// Try extracting just the value from "FieldName|Value" format
|
|
2352
|
+
const parts = recordId.split('|');
|
|
2353
|
+
const id = parts.length >= 2 ? parts[1] : parts[0];
|
|
2354
|
+
record = this.comparisonRecords.get(id);
|
|
2355
|
+
}
|
|
2356
|
+
if (!record) {
|
|
2357
|
+
// Case-insensitive search as a fallback (UUIDs may differ in case)
|
|
2358
|
+
const lower = recordId.toLowerCase();
|
|
2359
|
+
for (const [key, val] of this.comparisonRecords.entries()) {
|
|
2360
|
+
if (key.toLowerCase() === lower || key.toLowerCase().endsWith(lower)) {
|
|
2361
|
+
record = val;
|
|
2362
|
+
break;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
if (!record)
|
|
2367
|
+
return undefined;
|
|
2368
|
+
const val = record[fieldName];
|
|
2369
|
+
return val != null ? String(val) : undefined;
|
|
2370
|
+
}
|
|
2371
|
+
/** Build comparison data from loaded entity records */
|
|
2372
|
+
buildComparisonData() {
|
|
2373
|
+
if (!this.ComparisonGroup)
|
|
2374
|
+
return;
|
|
2375
|
+
// Build match infos (use loaded record data for names)
|
|
2376
|
+
this.ComparisonMatches = this.ComparisonGroup.Matches
|
|
2377
|
+
.sort((a, b) => b.MatchProbability - a.MatchProbability)
|
|
2378
|
+
.map(m => {
|
|
2379
|
+
const meta = this.parseRecordMetadata(m.RecordMetadata);
|
|
2380
|
+
// Try to get name from loaded entity record, fall back to vector metadata
|
|
2381
|
+
const nameFromRecord = this.getRecordFieldValue(m.MatchRecordID, 'Name');
|
|
2382
|
+
return {
|
|
2383
|
+
Match: m,
|
|
2384
|
+
Name: nameFromRecord || meta.Name || m.MatchRecordID?.substring(0, 16) + '...',
|
|
2385
|
+
Score: m.MatchProbability,
|
|
2386
|
+
Metadata: meta,
|
|
2387
|
+
DiffCount: 0,
|
|
2388
|
+
};
|
|
2389
|
+
});
|
|
2390
|
+
// Get entity field info for display names and ordering
|
|
2391
|
+
const md = new Metadata();
|
|
2392
|
+
const entityInfo = md.Entities.find(e => e.Name === this.ComparisonGroup.EntityName);
|
|
2393
|
+
const entityFields = entityInfo?.Fields ?? [];
|
|
2394
|
+
const skip = new Set(['ID', '__mj_CreatedAt', '__mj_UpdatedAt']);
|
|
2395
|
+
const rows = [];
|
|
2396
|
+
// Use entity fields in sequence order — values come from loaded entity records
|
|
2397
|
+
const sourceId = this.ComparisonGroup.RecordId;
|
|
2398
|
+
const matchRecordIds = this.ComparisonGroup.Matches
|
|
2399
|
+
.sort((a, b) => b.MatchProbability - a.MatchProbability)
|
|
2400
|
+
.map(m => m.MatchRecordID);
|
|
2401
|
+
for (const field of entityFields.sort((a, b) => a.Sequence - b.Sequence)) {
|
|
2402
|
+
if (skip.has(field.Name) || field.IsPrimaryKey)
|
|
2403
|
+
continue;
|
|
2404
|
+
const sourceVal = this.getRecordFieldValue(sourceId, field.Name);
|
|
2405
|
+
const matchVals = matchRecordIds.map(rid => this.getRecordFieldValue(rid, field.Name));
|
|
2406
|
+
const hasDiff = matchVals.some(mv => !this.AreValuesEqual(sourceVal, mv));
|
|
2407
|
+
const hasData = sourceVal != null || matchVals.some(v => v != null);
|
|
2408
|
+
if (!hasData)
|
|
2409
|
+
continue;
|
|
2410
|
+
rows.push({
|
|
2411
|
+
FieldName: field.Name,
|
|
2412
|
+
DisplayName: field.DisplayName || field.Name,
|
|
2413
|
+
Category: field.Category || null,
|
|
2414
|
+
SourceValue: sourceVal,
|
|
2415
|
+
MatchValues: matchVals,
|
|
2416
|
+
HasDifference: hasDiff,
|
|
2417
|
+
SelectedColumnIndex: 0,
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
this.ComparisonFields = rows;
|
|
2421
|
+
// Compute diff counts per match
|
|
2422
|
+
for (let mi = 0; mi < this.ComparisonMatches.length; mi++) {
|
|
2423
|
+
this.ComparisonMatches[mi].DiffCount = rows.filter(r => !this.AreValuesEqual(r.SourceValue, r.MatchValues[mi])).length;
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
/** Parse RecordMetadata JSON from a detail or match entity */
|
|
2427
|
+
parseRecordMetadata(json) {
|
|
2428
|
+
if (!json)
|
|
2429
|
+
return {};
|
|
2430
|
+
try {
|
|
2431
|
+
return JSON.parse(json);
|
|
2432
|
+
}
|
|
2433
|
+
catch {
|
|
2434
|
+
return {};
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
/** Build top N match summaries with parsed names and scores */
|
|
2438
|
+
buildTopMatchSummaries(matches, limit) {
|
|
2439
|
+
return matches
|
|
2440
|
+
.slice(0, limit)
|
|
2441
|
+
.map(m => {
|
|
2442
|
+
const meta = this.parseRecordMetadata(m.RecordMetadata);
|
|
2443
|
+
return {
|
|
2444
|
+
Name: meta.Name || m.MatchRecordID?.substring(0, 12) + '...',
|
|
2445
|
+
Score: m.MatchProbability,
|
|
2446
|
+
};
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Determine the dominant approval status for a group of matches.
|
|
2451
|
+
* If any match is Pending, the group is Pending.
|
|
2452
|
+
* Otherwise, if all are Approved, the group is Approved.
|
|
2453
|
+
* If all are Rejected, the group is Rejected.
|
|
2454
|
+
* Mixed Approved/Rejected defaults to Pending.
|
|
2455
|
+
*/
|
|
2456
|
+
computeDominantApprovalStatus(matches) {
|
|
2457
|
+
let hasApproved = false;
|
|
2458
|
+
let hasRejected = false;
|
|
2459
|
+
for (const m of matches) {
|
|
2460
|
+
if (m.ApprovalStatus === 'Pending')
|
|
2461
|
+
return 'Pending';
|
|
2462
|
+
if (m.ApprovalStatus === 'Approved')
|
|
2463
|
+
hasApproved = true;
|
|
2464
|
+
if (m.ApprovalStatus === 'Rejected')
|
|
2465
|
+
hasRejected = true;
|
|
2466
|
+
}
|
|
2467
|
+
if (hasApproved && !hasRejected)
|
|
2468
|
+
return 'Approved';
|
|
2469
|
+
if (hasRejected && !hasApproved)
|
|
2470
|
+
return 'Rejected';
|
|
2471
|
+
return 'Pending'; // mixed
|
|
2472
|
+
}
|
|
2473
|
+
/** Find the latest MatchedAt date across a set of matches */
|
|
2474
|
+
computeLatestMatchDate(matches) {
|
|
2475
|
+
let latest = new Date(0);
|
|
2476
|
+
for (const m of matches) {
|
|
2477
|
+
const d = new Date(m.MatchedAt);
|
|
2478
|
+
if (d > latest) {
|
|
2479
|
+
latest = d;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
return latest;
|
|
2483
|
+
}
|
|
2484
|
+
/** Apply filters and distribute groups into Kanban columns */
|
|
2485
|
+
applyFilters() {
|
|
2486
|
+
let filtered = [...this.AllGroups];
|
|
2487
|
+
if (this.Filters.EntityName) {
|
|
2488
|
+
filtered = filtered.filter(g => g.EntityName === this.Filters.EntityName);
|
|
2489
|
+
}
|
|
2490
|
+
if (this.Filters.MinScore > 0) {
|
|
2491
|
+
filtered = filtered.filter(g => g.HighestScore >= this.Filters.MinScore);
|
|
2492
|
+
}
|
|
2493
|
+
if (this.Filters.MaxScore > 0 && this.Filters.MaxScore < 1) {
|
|
2494
|
+
filtered = filtered.filter(g => g.HighestScore <= this.Filters.MaxScore);
|
|
2495
|
+
}
|
|
2496
|
+
if (this.Filters.DateFrom) {
|
|
2497
|
+
const parts = this.Filters.DateFrom.split('-');
|
|
2498
|
+
const from = new Date(+parts[0], +parts[1] - 1, +parts[2], 0, 0, 0, 0);
|
|
2499
|
+
filtered = filtered.filter(g => new Date(g.MatchedAt) >= from);
|
|
2500
|
+
}
|
|
2501
|
+
if (this.Filters.DateTo) {
|
|
2502
|
+
const parts = this.Filters.DateTo.split('-');
|
|
2503
|
+
const to = new Date(+parts[0], +parts[1] - 1, +parts[2], 23, 59, 59, 999);
|
|
2504
|
+
filtered = filtered.filter(g => new Date(g.MatchedAt) <= to);
|
|
2505
|
+
}
|
|
2506
|
+
this.PendingGroups = filtered.filter(g => g.ApprovalStatus === 'Pending');
|
|
2507
|
+
this.ApprovedGroups = filtered.filter(g => g.ApprovalStatus === 'Approved');
|
|
2508
|
+
this.RejectedGroups = filtered.filter(g => g.ApprovalStatus === 'Rejected');
|
|
2509
|
+
this.cdr.detectChanges();
|
|
2510
|
+
}
|
|
2511
|
+
/** Update the ApprovalStatus of all matches within a group and re-sort. */
|
|
2512
|
+
async updateGroupApprovalStatus(group, status) {
|
|
2513
|
+
this.IsSaving = true;
|
|
2514
|
+
this.cdr.detectChanges();
|
|
2515
|
+
try {
|
|
2516
|
+
for (const match of group.Matches) {
|
|
2517
|
+
match.ApprovalStatus = status;
|
|
2518
|
+
await match.Save();
|
|
2519
|
+
}
|
|
2520
|
+
// Update the local group state
|
|
2521
|
+
group.ApprovalStatus = status;
|
|
2522
|
+
this.applyFilters();
|
|
2523
|
+
}
|
|
2524
|
+
catch (error) {
|
|
2525
|
+
console.error(`Error updating match approval status to ${status}:`, error);
|
|
2526
|
+
}
|
|
2527
|
+
finally {
|
|
2528
|
+
this.IsSaving = false;
|
|
2529
|
+
this.cdr.detectChanges();
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵDuplicateDetectionResourceComponent_BaseFactory; return function DuplicateDetectionResourceComponent_Factory(__ngFactoryType__) { return (ɵDuplicateDetectionResourceComponent_BaseFactory || (ɵDuplicateDetectionResourceComponent_BaseFactory = i0.ɵɵgetInheritedFactory(DuplicateDetectionResourceComponent)))(__ngFactoryType__ || DuplicateDetectionResourceComponent); }; })();
|
|
2533
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DuplicateDetectionResourceComponent, selectors: [["app-duplicate-detection-resource"]], inputs: { EmbeddedMode: "EmbeddedMode" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 68, vars: 28, consts: [[1, "duplicate-detection-container"], [1, "page-header"], [1, "header-left"], [1, "page-title"], [1, "fa-solid", "fa-clone"], [1, "header-actions"], [1, "run-detection-controls"], [1, "entity-doc-select", 3, "ngModelChange", "ngModel", "disabled"], ["value", ""], [3, "value"], [1, "run-detection-btn", 3, "click", "disabled"], [1, "detection-progress-section"], [1, "threshold-controls"], [1, "kpi-strip"], [1, "kpi-card"], [1, "kpi-value"], [1, "kpi-label"], [1, "kpi-card", "kpi-pending"], [1, "kpi-card", "kpi-approved"], [1, "kpi-card", "kpi-rejected"], [1, "filter-bar"], [1, "filter-group"], [1, "filter-select", 3, "ngModelChange", "ngModel", "disabled"], [1, "filter-range"], [1, "filter-label"], ["type", "number", "min", "0", "max", "1", "step", "0.05", 1, "filter-input", "filter-input-score", 3, "ngModelChange", "placeholder", "ngModel"], ["type", "date", 1, "filter-input", "filter-input-date", 3, "ngModelChange", "min", "max", "ngModel"], [1, "clear-filters-btn"], [1, "loading-container"], [1, "empty-state"], [1, "kanban-board"], [1, "saving-overlay"], [1, "merge-confirm-backdrop"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-magnifying-glass"], [1, "progress-header"], [1, "progress-stage"], [1, "progress-percent"], [1, "progress-bar-track"], [1, "progress-bar-fill"], [1, "progress-current-item"], [1, "threshold-slider-group"], [1, "threshold-label"], [1, "fa-solid", "fa-adjust"], [1, "threshold-value"], ["type", "range", 1, "threshold-slider", 3, "input", "min", "max", "step", "value", "disabled"], [1, "threshold-hint"], [1, "fa-solid", "fa-bullseye"], [1, "clear-filters-btn", 3, "click"], [1, "fa-solid", "fa-times"], ["text", "Loading duplicate detection results..."], [1, "fa-solid", "fa-clone", "empty-icon"], [1, "empty-text"], [1, "empty-subtext"], [1, "kanban-column"], [1, "column-header", "column-header-pending"], [1, "fa-solid", "fa-clock"], [1, "column-title"], [1, "column-count"], [1, "column-body", 3, "dragover", "dragleave", "drop"], ["draggable", "true", 1, "kanban-card"], [1, "column-empty"], [1, "column-header", "column-header-approved"], [1, "fa-solid", "fa-check-circle"], [1, "column-header", "column-header-rejected"], [1, "fa-solid", "fa-ban"], ["draggable", "true", 1, "kanban-card", 3, "dragstart", "dragend", "click"], [1, "card-header"], [1, "card-header-left"], [1, "card-icon"], [1, "card-title-block"], [1, "card-record-name", 3, "title"], [1, "entity-badge"], [1, "score-indicator"], [1, "card-body"], [1, "match-summaries"], [1, "card-meta-row"], [1, "card-meta-item"], [1, "fa-solid", "fa-layer-group"], [1, "fa-solid", "fa-calendar"], [1, "card-actions"], [1, "action-btn", "approve-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-check"], [1, "action-btn", "reject-btn", 3, "click", "disabled"], [1, "match-summary-row"], [1, "match-summary-more"], [1, "match-score"], [1, "match-name"], [1, "fa-solid", "fa-inbox"], ["text", "Saving...", "size", "small"], [1, "slide-backdrop", 3, "click"], [1, "comparison-panel", 3, "click"], [1, "comparison-header"], [1, "comparison-header-left"], [1, "comparison-entity-icon"], [1, "comparison-title"], [1, "comparison-entity-badge"], [1, "comparison-match-count"], [1, "comparison-header-right"], [1, "comparison-toggle"], [1, "toggle-btn", 3, "click"], ["title", "Close (Esc)", 1, "comparison-close-btn", 3, "click"], [1, "comparison-loading"], [1, "comparison-grid-wrapper", 3, "hidden"], [1, "comparison-grid"], [1, "grid-corner-cell"], [1, "grid-col-header", "grid-col-source"], [1, "col-header-label"], [1, "col-header-name"], [1, "surviving-selector"], ["type", "radio", "name", "survivor", 1, "surviving-radio", 3, "change", "checked"], [1, "surviving-label"], [1, "use-all-btn", 3, "click"], [1, "fa-solid"], [1, "deps-summary"], [1, "deps-total"], [1, "fa-solid", "fa-link"], [1, "deps-total-number"], [1, "deps-total-recommended"], [1, "grid-col-header", 3, "match-approved", "match-rejected"], [1, "comparison-footer"], [1, "comparison-footer-left"], [1, "comparison-summary"], [1, "merge-summary"], [1, "fa-solid", "fa-code-merge"], [1, "comparison-footer-right"], [1, "action-btn", "merge-btn", 3, "click", "disabled"], ["text", "Loading records for comparison...", "size", "medium"], [1, "fa-solid", "fa-star"], [1, "deps-header", 3, "click"], [1, "deps-detail-list"], [1, "deps-detail-group"], [1, "deps-detail-row", 3, "click"], [1, "deps-detail-entity"], [1, "fa-solid", "deps-expand-icon"], [1, "deps-detail-count"], [1, "deps-records-list"], [1, "deps-record-loading"], [1, "deps-record-row"], [1, "deps-record-row", 3, "click"], [1, "fa-solid", "fa-arrow-up-right-from-square", "deps-record-icon"], [1, "deps-record-name"], [1, "grid-col-header"], [1, "col-header-top"], [1, "col-header-diff-count"], [1, "match-status-badge", 3, "status-approved", "status-rejected"], [1, "match-status-badge"], [1, "grid-label-cell"], [1, "grid-value-cell", "grid-source-cell", 3, "click"], [1, "field-not-available"], ["type", "radio", 1, "field-select-radio", 3, "name", "checked"], [1, "grid-value-cell", 3, "grid-row-odd", "value-same", "value-different", "field-selected"], ["type", "radio", 1, "field-select-radio", 3, "click", "change", "name", "checked"], [1, "grid-value-cell", 3, "click"], [1, "merge-confirm-backdrop", 3, "click"], [1, "merge-confirm-panel", 3, "click"], [1, "merge-confirm-header"], [1, "merge-confirm-icon"], [1, "merge-confirm-title"], [1, "merge-confirm-subtitle"], [1, "merge-confirm-body"], [1, "merge-survivor-card"], [1, "merge-survivor-label"], [1, "fa-solid", "fa-shield-halved"], [1, "merge-survivor-name"], [1, "merge-survivor-pk"], [1, "fa-solid", "fa-key"], [1, "merge-survivor-detail"], [1, "merge-deps-transfer"], [1, "merge-delete-card"], [1, "merge-delete-label"], [1, "fa-solid", "fa-trash"], [1, "merge-delete-item"], [1, "merge-confirm-footer"], [1, "action-btn", "cancel-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-left"], [1, "action-btn", "confirm-merge-btn", 3, "click", "disabled"], [1, "merge-section-label"], [1, "merge-field-override"], [1, "merge-field-name"], [1, "merge-field-value"], [1, "merge-field-source"], [1, "merge-deps-transfer-label"], [1, "fa-solid", "fa-arrow-right-arrow-left"], [1, "merge-deps-transfer-row"], [1, "fa-solid", "fa-arrow-right"], [1, "merge-delete-name"], [1, "merge-delete-deps"]], template: function DuplicateDetectionResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
2534
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "h2", 3);
|
|
2535
|
+
i0.ɵɵelement(4, "i", 4);
|
|
2536
|
+
i0.ɵɵtext(5, " Duplicate Detection ");
|
|
2537
|
+
i0.ɵɵelementEnd()();
|
|
2538
|
+
i0.ɵɵelementStart(6, "div", 5)(7, "div", 6)(8, "select", 7);
|
|
2539
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_select_ngModelChange_8_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.SelectedEntityDocumentID, $event) || (ctx.SelectedEntityDocumentID = $event); return $event; });
|
|
2540
|
+
i0.ɵɵelementStart(9, "option", 8);
|
|
2541
|
+
i0.ɵɵtext(10, "Select entity document...");
|
|
2542
|
+
i0.ɵɵelementEnd();
|
|
2543
|
+
i0.ɵɵrepeaterCreate(11, DuplicateDetectionResourceComponent_For_12_Template, 2, 3, "option", 9, _forTrack0);
|
|
2544
|
+
i0.ɵɵelementEnd();
|
|
2545
|
+
i0.ɵɵelementStart(13, "button", 10);
|
|
2546
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Template_button_click_13_listener() { return ctx.RunDetection(); });
|
|
2547
|
+
i0.ɵɵconditionalCreate(14, DuplicateDetectionResourceComponent_Conditional_14_Template, 2, 0)(15, DuplicateDetectionResourceComponent_Conditional_15_Template, 2, 0);
|
|
2548
|
+
i0.ɵɵelementEnd()()()();
|
|
2549
|
+
i0.ɵɵconditionalCreate(16, DuplicateDetectionResourceComponent_Conditional_16_Template, 10, 5, "div", 11);
|
|
2550
|
+
i0.ɵɵconditionalCreate(17, DuplicateDetectionResourceComponent_Conditional_17_Template, 19, 12, "div", 12);
|
|
2551
|
+
i0.ɵɵelementStart(18, "div", 13)(19, "div", 14)(20, "div", 15);
|
|
2552
|
+
i0.ɵɵtext(21);
|
|
2553
|
+
i0.ɵɵelementEnd();
|
|
2554
|
+
i0.ɵɵelementStart(22, "div", 16);
|
|
2555
|
+
i0.ɵɵtext(23, "Total Groups");
|
|
2556
|
+
i0.ɵɵelementEnd()();
|
|
2557
|
+
i0.ɵɵelementStart(24, "div", 17)(25, "div", 15);
|
|
2558
|
+
i0.ɵɵtext(26);
|
|
2559
|
+
i0.ɵɵelementEnd();
|
|
2560
|
+
i0.ɵɵelementStart(27, "div", 16);
|
|
2561
|
+
i0.ɵɵtext(28, "Pending");
|
|
2562
|
+
i0.ɵɵelementEnd()();
|
|
2563
|
+
i0.ɵɵelementStart(29, "div", 18)(30, "div", 15);
|
|
2564
|
+
i0.ɵɵtext(31);
|
|
2565
|
+
i0.ɵɵelementEnd();
|
|
2566
|
+
i0.ɵɵelementStart(32, "div", 16);
|
|
2567
|
+
i0.ɵɵtext(33, "Approved");
|
|
2568
|
+
i0.ɵɵelementEnd()();
|
|
2569
|
+
i0.ɵɵelementStart(34, "div", 19)(35, "div", 15);
|
|
2570
|
+
i0.ɵɵtext(36);
|
|
2571
|
+
i0.ɵɵelementEnd();
|
|
2572
|
+
i0.ɵɵelementStart(37, "div", 16);
|
|
2573
|
+
i0.ɵɵtext(38, "Rejected");
|
|
2574
|
+
i0.ɵɵelementEnd()()();
|
|
2575
|
+
i0.ɵɵelementStart(39, "div", 20)(40, "div", 21)(41, "select", 22);
|
|
2576
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_select_ngModelChange_41_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.EntityName, $event) || (ctx.Filters.EntityName = $event); return $event; });
|
|
2577
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_select_ngModelChange_41_listener() { return ctx.OnFilterChange(); });
|
|
2578
|
+
i0.ɵɵconditionalCreate(42, DuplicateDetectionResourceComponent_Conditional_42_Template, 2, 0, "option", 8);
|
|
2579
|
+
i0.ɵɵrepeaterCreate(43, DuplicateDetectionResourceComponent_For_44_Template, 2, 2, "option", 9, i0.ɵɵrepeaterTrackByIdentity);
|
|
2580
|
+
i0.ɵɵelementEnd();
|
|
2581
|
+
i0.ɵɵelementStart(45, "div", 23)(46, "label", 24);
|
|
2582
|
+
i0.ɵɵtext(47, "Min Score");
|
|
2583
|
+
i0.ɵɵelementEnd();
|
|
2584
|
+
i0.ɵɵelementStart(48, "input", 25);
|
|
2585
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_48_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.MinScore, $event) || (ctx.Filters.MinScore = $event); return $event; });
|
|
2586
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_48_listener() { return ctx.OnFilterChange(); });
|
|
2587
|
+
i0.ɵɵelementEnd()();
|
|
2588
|
+
i0.ɵɵelementStart(49, "div", 23)(50, "label", 24);
|
|
2589
|
+
i0.ɵɵtext(51, "Max Score");
|
|
2590
|
+
i0.ɵɵelementEnd();
|
|
2591
|
+
i0.ɵɵelementStart(52, "input", 25);
|
|
2592
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_52_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.MaxScore, $event) || (ctx.Filters.MaxScore = $event); return $event; });
|
|
2593
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_52_listener() { return ctx.OnFilterChange(); });
|
|
2594
|
+
i0.ɵɵelementEnd()();
|
|
2595
|
+
i0.ɵɵelementStart(53, "div", 23)(54, "label", 24);
|
|
2596
|
+
i0.ɵɵtext(55, "From");
|
|
2597
|
+
i0.ɵɵelementEnd();
|
|
2598
|
+
i0.ɵɵelementStart(56, "input", 26);
|
|
2599
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_56_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.DateFrom, $event) || (ctx.Filters.DateFrom = $event); return $event; });
|
|
2600
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_56_listener() { return ctx.OnFilterChange(); });
|
|
2601
|
+
i0.ɵɵelementEnd()();
|
|
2602
|
+
i0.ɵɵelementStart(57, "div", 23)(58, "label", 24);
|
|
2603
|
+
i0.ɵɵtext(59, "To");
|
|
2604
|
+
i0.ɵɵelementEnd();
|
|
2605
|
+
i0.ɵɵelementStart(60, "input", 26);
|
|
2606
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_60_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.DateTo, $event) || (ctx.Filters.DateTo = $event); return $event; });
|
|
2607
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_60_listener() { return ctx.OnFilterChange(); });
|
|
2608
|
+
i0.ɵɵelementEnd()()();
|
|
2609
|
+
i0.ɵɵconditionalCreate(61, DuplicateDetectionResourceComponent_Conditional_61_Template, 3, 0, "button", 27);
|
|
2610
|
+
i0.ɵɵelementEnd();
|
|
2611
|
+
i0.ɵɵconditionalCreate(62, DuplicateDetectionResourceComponent_Conditional_62_Template, 2, 0, "div", 28)(63, DuplicateDetectionResourceComponent_Conditional_63_Template, 6, 0, "div", 29)(64, DuplicateDetectionResourceComponent_Conditional_64_Template, 34, 12, "div", 30);
|
|
2612
|
+
i0.ɵɵconditionalCreate(65, DuplicateDetectionResourceComponent_Conditional_65_Template, 2, 0, "div", 31);
|
|
2613
|
+
i0.ɵɵconditionalCreate(66, DuplicateDetectionResourceComponent_Conditional_66_Template, 67, 40);
|
|
2614
|
+
i0.ɵɵconditionalCreate(67, DuplicateDetectionResourceComponent_Conditional_67_Template, 37, 7, "div", 32);
|
|
2615
|
+
i0.ɵɵelementEnd();
|
|
2616
|
+
} if (rf & 2) {
|
|
2617
|
+
i0.ɵɵadvance(8);
|
|
2618
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.SelectedEntityDocumentID);
|
|
2619
|
+
i0.ɵɵproperty("disabled", ctx.IsDetecting);
|
|
2620
|
+
i0.ɵɵadvance(3);
|
|
2621
|
+
i0.ɵɵrepeater(ctx.EntityDocuments);
|
|
2622
|
+
i0.ɵɵadvance(2);
|
|
2623
|
+
i0.ɵɵproperty("disabled", ctx.IsDetecting || !ctx.SelectedEntityDocumentID);
|
|
2624
|
+
i0.ɵɵadvance();
|
|
2625
|
+
i0.ɵɵconditional(ctx.IsDetecting ? 14 : 15);
|
|
2626
|
+
i0.ɵɵadvance(2);
|
|
2627
|
+
i0.ɵɵconditional(ctx.IsDetecting ? 16 : -1);
|
|
2628
|
+
i0.ɵɵadvance();
|
|
2629
|
+
i0.ɵɵconditional(ctx.SelectedDocumentThresholds ? 17 : -1);
|
|
2630
|
+
i0.ɵɵadvance(4);
|
|
2631
|
+
i0.ɵɵtextInterpolate(ctx.TotalGroupCount);
|
|
2632
|
+
i0.ɵɵadvance(5);
|
|
2633
|
+
i0.ɵɵtextInterpolate(ctx.PendingCount);
|
|
2634
|
+
i0.ɵɵadvance(5);
|
|
2635
|
+
i0.ɵɵtextInterpolate(ctx.ApprovedCount);
|
|
2636
|
+
i0.ɵɵadvance(5);
|
|
2637
|
+
i0.ɵɵtextInterpolate(ctx.RejectedCount);
|
|
2638
|
+
i0.ɵɵadvance(5);
|
|
2639
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.EntityName);
|
|
2640
|
+
i0.ɵɵproperty("disabled", ctx.EntityNames.length <= 1);
|
|
2641
|
+
i0.ɵɵadvance();
|
|
2642
|
+
i0.ɵɵconditional(ctx.EntityNames.length > 1 ? 42 : -1);
|
|
2643
|
+
i0.ɵɵadvance();
|
|
2644
|
+
i0.ɵɵrepeater(ctx.EntityNames);
|
|
2645
|
+
i0.ɵɵadvance(5);
|
|
2646
|
+
i0.ɵɵproperty("placeholder", ctx.DataMinScore);
|
|
2647
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.MinScore);
|
|
2648
|
+
i0.ɵɵadvance(4);
|
|
2649
|
+
i0.ɵɵproperty("placeholder", ctx.DataMaxScore);
|
|
2650
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.MaxScore);
|
|
2651
|
+
i0.ɵɵadvance(4);
|
|
2652
|
+
i0.ɵɵproperty("min", ctx.DataMinDate)("max", ctx.DataMaxDate);
|
|
2653
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.DateFrom);
|
|
2654
|
+
i0.ɵɵadvance(4);
|
|
2655
|
+
i0.ɵɵproperty("min", ctx.DataMinDate)("max", ctx.DataMaxDate);
|
|
2656
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.DateTo);
|
|
2657
|
+
i0.ɵɵadvance();
|
|
2658
|
+
i0.ɵɵconditional(ctx.HasActiveFilters ? 61 : -1);
|
|
2659
|
+
i0.ɵɵadvance();
|
|
2660
|
+
i0.ɵɵconditional(ctx.IsLoading ? 62 : ctx.TotalGroupCount === 0 ? 63 : 64);
|
|
2661
|
+
i0.ɵɵadvance(3);
|
|
2662
|
+
i0.ɵɵconditional(ctx.IsSaving ? 65 : -1);
|
|
2663
|
+
i0.ɵɵadvance();
|
|
2664
|
+
i0.ɵɵconditional(ctx.ComparisonGroup ? 66 : -1);
|
|
2665
|
+
i0.ɵɵadvance();
|
|
2666
|
+
i0.ɵɵconditional(ctx.ShowMergeConfirm && ctx.ComparisonGroup ? 67 : -1);
|
|
2667
|
+
} }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.NumberValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.MinValidator, i1.MaxValidator, i1.NgModel, i2.LoadingComponent], styles: ["/* ============================================================\n Duplicate Detection Kanban Board - Resource Component Styles\n All colors use MJ design tokens (--mj-*) exclusively.\n ============================================================ */\n\napp-duplicate-detection-resource {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n}\n\n.duplicate-detection-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n padding: 16px 20px;\n background: var(--mj-bg-page);\n position: relative;\n overflow: hidden;\n}\n\n/* ---- Page Header ---- */\n\n.page-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.page-title {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.page-title i {\n color: var(--mj-brand-primary);\n}\n\n/* ---- Header Actions ---- */\n\n.header-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.run-detection-controls {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.entity-doc-select {\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 13px;\n min-width: 200px;\n}\n\n.entity-doc-select:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.run-detection-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n font-size: 13px;\n font-weight: 600;\n transition: background 0.15s;\n white-space: nowrap;\n}\n\n.run-detection-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.run-detection-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* ---- Detection Progress ---- */\n\n.detection-progress-section {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-brand-primary);\n border-radius: 8px;\n padding: 14px 18px;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.progress-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n\n.progress-stage {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.progress-percent {\n font-size: 13px;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.progress-bar-track {\n height: 6px;\n border-radius: 3px;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.progress-bar-fill {\n height: 100%;\n border-radius: 3px;\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n}\n\n.progress-current-item {\n display: block;\n margin-top: 6px;\n font-size: 11px;\n color: var(--mj-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ---- Threshold Controls ---- */\n\n.threshold-controls {\n display: flex;\n gap: 24px;\n padding: 12px 16px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--mj-status-info) 6%, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-subtle);\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.threshold-slider-group {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.threshold-label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.threshold-value {\n margin-left: auto;\n font-weight: 700;\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n}\n\n.threshold-slider {\n width: 100%;\n height: 4px;\n appearance: none;\n -webkit-appearance: none;\n background: var(--mj-border-default);\n border-radius: 2px;\n outline: none;\n cursor: pointer;\n}\n\n.threshold-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n border: 2px solid var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n cursor: pointer;\n}\n\n.threshold-slider::-moz-range-thumb {\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n border: 2px solid var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n cursor: pointer;\n}\n\n.threshold-slider:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.threshold-hint {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n/* ---- Threshold Info (legacy) ---- */\n\n.threshold-info {\n display: flex;\n gap: 16px;\n padding: 8px 14px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-status-info) 8%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-info-border);\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.threshold-item {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-status-info-text);\n font-weight: 500;\n}\n\n.threshold-item i {\n font-size: 11px;\n}\n\n/* ---- KPI Strip ---- */\n\n.kpi-strip {\n display: flex;\n gap: 12px;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.kpi-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n}\n\n.kpi-value {\n font-size: 28px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.kpi-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 2px;\n}\n\n.kpi-pending .kpi-value {\n color: var(--mj-status-warning);\n}\n\n.kpi-approved .kpi-value {\n color: var(--mj-status-success);\n}\n\n.kpi-rejected .kpi-value {\n color: var(--mj-status-error);\n}\n\n/* ---- Filter Bar ---- */\n\n.filter-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.filter-group {\n display: flex;\n align-items: flex-end;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.filter-select {\n height: 34px;\n padding: 4px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 13px;\n outline: none;\n min-width: 160px;\n}\n\n.filter-select:focus {\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.filter-range {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.filter-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.filter-input {\n height: 34px;\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 13px;\n outline: none;\n width: 100px;\n}\n\n.filter-input-score {\n width: 80px;\n}\n\n.filter-input-date {\n width: 140px;\n}\n\n.filter-input:focus {\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.clear-filters-btn {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 34px;\n padding: 0 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 13px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.clear-filters-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* ---- Loading & Empty States ---- */\n\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n min-height: 200px;\n}\n\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n flex: 1;\n min-height: 200px;\n gap: 8px;\n}\n\n.empty-icon {\n font-size: 48px;\n color: var(--mj-text-disabled);\n}\n\n.empty-text {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.empty-subtext {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin: 0;\n}\n\n/* ---- Kanban Board ---- */\n\n.kanban-board {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n overflow-x: auto;\n}\n\n.kanban-column {\n flex: 1;\n min-width: 280px;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.column-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n border-bottom: 1px solid var(--mj-border-subtle);\n flex-shrink: 0;\n}\n\n.column-header-pending {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n}\n\n.column-header-pending i {\n color: var(--mj-status-warning);\n}\n\n.column-header-approved {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n}\n\n.column-header-approved i {\n color: var(--mj-status-success);\n}\n\n.column-header-rejected {\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n}\n\n.column-header-rejected i {\n color: var(--mj-status-error);\n}\n\n.column-title {\n flex: 1;\n}\n\n.column-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 6px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.column-body {\n flex: 1;\n overflow-y: auto;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.column-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 32px 16px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n}\n\n.column-empty i {\n font-size: 24px;\n}\n\n/* ---- Kanban Cards ---- */\n\n.kanban-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n transition: box-shadow 0.15s ease, border-color 0.15s ease;\n flex-shrink: 0; /* Prevent cards from shrinking \u2014 column scrolls instead */\n}\n\n.kanban-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n}\n\n/* Drag and Drop */\n.kanban-card[draggable=\"true\"] {\n cursor: grab;\n}\n\n.kanban-card[draggable=\"true\"]:active {\n cursor: grabbing;\n}\n\n.drop-target-active {\n outline: 2px dashed var(--mj-brand-primary);\n outline-offset: -2px;\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface-card)) !important;\n}\n\n.drop-target-active .column-body {\n min-height: 100px;\n}\n\n.card-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n gap: 8px;\n}\n\n.card-header-left {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n flex: 1;\n}\n\n.card-icon {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n}\n\n.card-title-block {\n min-width: 0;\n flex: 1;\n}\n\n.card-record-name {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-bottom: 2px;\n}\n\n.entity-badge {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border-radius: 3px;\n font-size: 10px;\n font-weight: 500;\n color: var(--mj-text-muted);\n letter-spacing: 0.2px;\n}\n\n/* Score indicator colors */\n\n.score-indicator {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 40px;\n padding: 2px 8px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 700;\n}\n\n.score-high {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.score-medium {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n.score-low {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.card-body {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.match-summaries {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.match-summary-row {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n}\n\n.match-score {\n flex-shrink: 0;\n font-weight: 600;\n font-size: 11px;\n color: var(--mj-text-muted);\n min-width: 30px;\n}\n\n.match-name {\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.match-summary-more {\n font-size: 11px;\n color: var(--mj-text-muted);\n font-style: italic;\n padding-left: 38px;\n}\n\n.card-meta-row {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 11px;\n color: var(--mj-text-muted);\n border-top: 1px solid var(--mj-border-subtle);\n padding-top: 6px;\n}\n\n.card-meta-item {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.card-meta-item i {\n font-size: 10px;\n}\n font-size: 12px;\n max-width: 180px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Comparison Slide-In Panel\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.slide-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 9999;\n animation: fadeIn 0.2s ease;\n}\n\n.slide-backdrop.comparison-closing {\n animation: fadeOut 0.25s ease forwards;\n}\n\n@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }\n@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }\n\n.comparison-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 82vw;\n max-width: 1300px;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -8px 0 40px rgba(0,0,0,0.4);\n z-index: 10000;\n display: flex;\n flex-direction: column;\n animation: slideIn 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n}\n\n.comparison-panel.comparison-closing {\n animation: slideOut 0.25s cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n@keyframes slideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }\n@keyframes slideOut { from { transform: translateX(0); } to { transform: translateX(100%); } }\n\n/* Header */\n.comparison-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n gap: 12px;\n}\n\n.comparison-header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n}\n\n.comparison-entity-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 1rem;\n flex-shrink: 0;\n}\n\n.comparison-title {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.comparison-entity-badge {\n display: inline-block;\n padding: 1px 7px;\n border-radius: 10px;\n font-size: 0.65rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n margin-right: 6px;\n}\n\n.comparison-match-count {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.comparison-header-right {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-shrink: 0;\n}\n\n/* Toggle */\n.comparison-toggle {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.toggle-btn {\n padding: 5px 12px;\n font-size: 0.7rem;\n font-weight: 500;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n}\n\n.toggle-btn:first-child {\n border-right: 1px solid var(--mj-border-default);\n}\n\n.toggle-btn.toggle-active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.comparison-close-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n transition: all 0.15s ease;\n}\n\n.comparison-close-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Loading state */\n.comparison-loading {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Grid wrapper */\n.comparison-grid-wrapper {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* Grid */\n.comparison-grid {\n display: grid;\n min-width: 100%;\n}\n\n.comparison-grid > div {\n padding: 8px 12px;\n font-size: 0.78rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n border-right: 1px solid var(--mj-border-subtle);\n}\n\n/* Corner cell */\n.grid-corner-cell {\n position: sticky;\n top: 0;\n left: 0;\n z-index: 4;\n background: var(--mj-bg-surface-elevated);\n font-size: 0.65rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: flex-end;\n padding-bottom: 6px;\n}\n\n/* Column headers */\n.grid-col-header {\n position: sticky;\n top: 0;\n z-index: 3;\n background: var(--mj-bg-surface-elevated);\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.grid-col-source {\n border-left: 3px solid var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface-elevated));\n}\n\n.col-header-label {\n font-size: 0.6rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-brand-primary);\n}\n\n.col-header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n}\n\n.col-header-name {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.col-header-diff-count {\n font-size: 0.65rem;\n color: var(--mj-status-warning-text);\n}\n\n/* Surviving record selector */\n.surviving-selector {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n}\n\n.surviving-radio {\n appearance: none;\n width: 14px;\n height: 14px;\n border: 2px solid var(--mj-border-strong);\n border-radius: 50%;\n cursor: pointer;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n\n.surviving-radio:checked {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary);\n box-shadow: inset 0 0 0 2px var(--mj-bg-surface-elevated);\n}\n\n.surviving-label {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n}\n\n.surviving-label.is-survivor {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n/* Use all fields button */\n.use-all-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.62rem;\n cursor: pointer;\n transition: all 0.12s ease;\n margin-top: 2px;\n}\n\n.use-all-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.use-all-btn.all-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n/* Per-match actions */\n.comparison-match-actions {\n display: flex;\n gap: 4px;\n margin-top: 3px;\n}\n\n.action-btn-sm {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px solid;\n border-radius: 5px;\n cursor: pointer;\n font-size: 0.65rem;\n transition: all 0.15s ease;\n}\n\n.approve-btn-sm {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border-color: var(--mj-status-success-border);\n}\n.approve-btn-sm:hover { background: rgba(34,197,94,0.25); }\n\n.reject-btn-sm {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border-color: var(--mj-status-error-border);\n}\n.reject-btn-sm:hover { background: rgba(239,68,68,0.25); }\n\n.match-status-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.68rem;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-approved {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.status-rejected {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.match-approved {\n border-top: 3px solid var(--mj-status-success);\n}\n\n.match-rejected {\n border-top: 3px solid var(--mj-status-error);\n opacity: 0.6;\n}\n\n/* Label cells */\n.grid-label-cell {\n position: sticky;\n left: 0;\n z-index: 2;\n background: var(--mj-bg-surface);\n font-size: 0.72rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n}\n\n.grid-row-odd {\n background: color-mix(in srgb, var(--mj-bg-surface-sunken) 50%, var(--mj-bg-surface));\n}\n\n.grid-label-cell.grid-row-odd {\n background: color-mix(in srgb, var(--mj-bg-surface-sunken) 50%, var(--mj-bg-surface));\n}\n\n/* Value cells */\n.grid-value-cell {\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n line-height: 1.45;\n word-break: break-word;\n position: relative;\n cursor: pointer;\n}\n\n.grid-source-cell {\n background: color-mix(in srgb, var(--mj-brand-primary) 3%, var(--mj-bg-surface));\n border-left: 3px solid transparent;\n}\n\n.grid-source-cell.grid-row-odd {\n background: color-mix(in srgb, var(--mj-brand-primary) 3%, color-mix(in srgb, var(--mj-bg-surface-sunken) 50%, var(--mj-bg-surface)));\n}\n\n.grid-source-cell.has-diff-in-row {\n font-weight: 600;\n}\n\n/* Diff highlighting */\n.value-same {\n color: var(--mj-text-muted);\n}\n\n.value-different {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, transparent) !important;\n border-left: 3px solid var(--mj-status-warning);\n color: var(--mj-text-primary);\n}\n\n.field-not-available {\n font-style: italic;\n font-size: 0.72rem;\n color: var(--mj-text-disabled);\n}\n\n/* Field selection radio */\n.field-select-radio {\n position: absolute;\n top: 50%;\n right: 8px;\n transform: translateY(-50%);\n appearance: none;\n width: 16px;\n height: 16px;\n border: 2px solid var(--mj-border-strong);\n border-radius: 50%;\n cursor: pointer;\n transition: all 0.12s ease;\n opacity: 0.4;\n}\n\n.field-select-radio:hover {\n opacity: 0.8;\n}\n\n.field-select-radio:checked {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary);\n box-shadow: inset 0 0 0 2.5px var(--mj-bg-surface);\n opacity: 1;\n}\n\n.grid-value-cell.field-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)) !important;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.grid-value-cell.field-selected .field-select-radio {\n opacity: 1;\n}\n\n/* Footer */\n.comparison-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 20px;\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.comparison-footer-left {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.comparison-summary {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.merge-summary {\n font-size: 0.72rem;\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.merge-summary i {\n font-size: 0.65rem;\n}\n\n.comparison-footer-right {\n display: flex;\n gap: 8px;\n}\n\n.comparison-footer-right .action-btn {\n flex: none;\n padding: 7px 14px;\n}\n\n.merge-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.merge-btn:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.card-actions {\n display: flex;\n gap: 8px;\n padding: 10px 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.action-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 32px;\n padding: 0 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n\n.action-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.approve-btn {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success-text);\n border-color: var(--mj-status-success-border);\n}\n\n.approve-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-success) 20%, var(--mj-bg-surface));\n}\n\n.reject-btn {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error-text);\n border-color: var(--mj-status-error-border);\n}\n\n.reject-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-bg-surface));\n}\n\n/* ---- Responsive ---- */\n\n@media (max-width: 768px) {\n .kanban-board {\n flex-direction: column;\n }\n\n .kanban-column {\n min-width: auto;\n max-height: 400px;\n }\n}\n\n@media (max-width: 480px) {\n .kpi-strip {\n flex-direction: column;\n }\n\n .filter-bar {\n flex-wrap: wrap;\n }\n\n .filter-group {\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n }\n\n .filter-select {\n min-width: auto;\n width: 100%;\n }\n\n .filter-input {\n width: 100%;\n }\n\n .card-actions {\n flex-direction: column;\n }\n\n .action-btn {\n width: 100%;\n }\n}\n\n/* ---- Saving Overlay ---- */\n\n.saving-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-overlay);\n z-index: 10;\n border-radius: 8px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Dependencies Summary (in column headers)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.deps-summary {\n margin-top: 6px;\n padding: 6px 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n font-size: 0.68rem;\n}\n\n.deps-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-weight: 600;\n}\n\n.deps-header i {\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.deps-header:hover {\n color: var(--mj-text-primary);\n}\n\n.deps-total {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.72rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin-bottom: 2px;\n}\n\n.deps-total i {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n.deps-total-number {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 0 6px;\n border-radius: 8px;\n font-size: 0.7rem;\n font-weight: 700;\n}\n\n.deps-total-recommended {\n font-size: 0.58rem;\n color: var(--mj-status-success-text);\n font-weight: 600;\n margin-left: 4px;\n}\n\n.deps-total-recommended i {\n color: var(--mj-status-success-text);\n}\n\n.deps-detail-list {\n margin-top: 4px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.deps-detail-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 3px 0;\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n border-radius: 3px;\n transition: background 0.1s ease;\n}\n\n.deps-detail-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.deps-detail-entity {\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.deps-expand-icon {\n font-size: 0.5rem;\n width: 10px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n\n/* Individual dependency records list */\n.deps-records-list {\n padding-left: 14px;\n margin-bottom: 2px;\n}\n\n.deps-record-row {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 2px 4px;\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n border-radius: 3px;\n transition: color 0.1s ease, background 0.1s ease;\n}\n\n.deps-record-row:hover {\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n\n.deps-record-icon {\n font-size: 0.5rem;\n flex-shrink: 0;\n}\n\n.deps-record-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 0.6rem;\n}\n\n.deps-record-loading {\n padding: 3px 4px;\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.deps-record-loading i {\n margin-right: 4px;\n}\n\n.deps-detail-count {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Merge Confirmation Panel\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.merge-confirm-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.3);\n z-index: 20000;\n animation: fadeIn 0.2s ease;\n}\n\n.merge-confirm-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 520px;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -8px 0 40px rgba(0, 0, 0, 0.4);\n z-index: 20001;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideIn 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.merge-confirm-header {\n padding: 18px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.merge-confirm-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text);\n font-size: 1.1rem;\n flex-shrink: 0;\n}\n\n.merge-confirm-title {\n font-size: 1.05rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.merge-confirm-subtitle {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.merge-confirm-body {\n padding: 20px 24px;\n overflow-y: auto;\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n/* Surviving record card */\n\n.merge-survivor-card {\n padding: 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n border: 1px solid var(--mj-brand-primary);\n border-radius: 8px;\n}\n\n.merge-survivor-label {\n font-size: 0.65rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-brand-primary);\n margin-bottom: 4px;\n}\n\n.merge-survivor-label i {\n margin-right: 4px;\n}\n\n.merge-survivor-name {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.merge-survivor-pk {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n padding: 4px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-radius: 4px;\n font-size: 0.68rem;\n font-family: monospace;\n color: var(--mj-text-secondary);\n}\n\n.merge-survivor-pk i {\n font-size: 0.6rem;\n color: var(--mj-brand-primary);\n}\n\n.merge-survivor-detail {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n/* Cherry-picked fields section */\n\n.merge-section-label {\n font-size: 0.7rem;\n font-weight: 700;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 6px;\n}\n\n.merge-field-override {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 6px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.merge-field-name {\n font-weight: 600;\n color: var(--mj-text-secondary);\n min-width: 110px;\n flex-shrink: 0;\n}\n\n.merge-field-value {\n color: var(--mj-text-primary);\n}\n\n.merge-field-source {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n margin-left: auto;\n white-space: nowrap;\n}\n\n/* Dependency transfer summary */\n\n.merge-deps-transfer {\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n}\n\n.merge-deps-transfer-label {\n font-size: 0.65rem;\n font-weight: 700;\n text-transform: uppercase;\n color: var(--mj-brand-primary);\n margin-bottom: 6px;\n}\n\n.merge-deps-transfer-label i {\n margin-right: 4px;\n}\n\n.merge-deps-transfer-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 3px 0;\n font-size: 0.75rem;\n color: var(--mj-text-secondary);\n}\n\n.merge-deps-transfer-row i {\n color: var(--mj-brand-primary);\n font-size: 0.65rem;\n width: 14px;\n text-align: center;\n}\n\n/* Records to delete */\n\n.merge-delete-card {\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n}\n\n.merge-delete-label {\n font-size: 0.65rem;\n font-weight: 700;\n text-transform: uppercase;\n color: var(--mj-status-error-text);\n margin-bottom: 6px;\n}\n\n.merge-delete-label i {\n margin-right: 4px;\n}\n\n.merge-delete-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 5px 0;\n font-size: 0.78rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.merge-delete-item:last-child {\n border-bottom: none;\n}\n\n.merge-delete-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.merge-delete-deps {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n/* Confirm footer */\n\n.merge-confirm-footer {\n padding: 14px 24px;\n border-top: 1px solid var(--mj-border-default);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 10px;\n}\n\n.cancel-btn {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n}\n\n.cancel-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.confirm-merge-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.confirm-merge-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.confirm-merge-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* ---- Responsive: Merge Confirm ---- */\n\n@media (max-width: 600px) {\n .merge-confirm-panel {\n width: 100%;\n }\n}\n"], encapsulation: 2 });
|
|
2668
|
+
};
|
|
2669
|
+
DuplicateDetectionResourceComponent = __decorate([
|
|
2670
|
+
RegisterClass(BaseResourceComponent, 'DuplicateDetectionResource')
|
|
2671
|
+
], DuplicateDetectionResourceComponent);
|
|
2672
|
+
export { DuplicateDetectionResourceComponent };
|
|
2673
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DuplicateDetectionResourceComponent, [{
|
|
2674
|
+
type: Component,
|
|
2675
|
+
args: [{ standalone: false, selector: 'app-duplicate-detection-resource', encapsulation: ViewEncapsulation.None, template: "<div class=\"duplicate-detection-container\">\n <!-- Header -->\n <div class=\"page-header\">\n <div class=\"header-left\">\n <h2 class=\"page-title\">\n <i class=\"fa-solid fa-clone\"></i> Duplicate Detection\n </h2>\n </div>\n <div class=\"header-actions\">\n <div class=\"run-detection-controls\">\n <select class=\"entity-doc-select\"\n [(ngModel)]=\"SelectedEntityDocumentID\"\n [disabled]=\"IsDetecting\">\n <option value=\"\">Select entity document...</option>\n @for (doc of EntityDocuments; track doc.ID) {\n <option [value]=\"doc.ID\">{{ doc.Name }} ({{ doc.EntityName }})</option>\n }\n </select>\n <button class=\"run-detection-btn\"\n (click)=\"RunDetection()\"\n [disabled]=\"IsDetecting || !SelectedEntityDocumentID\">\n @if (IsDetecting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Detecting...\n } @else {\n <i class=\"fa-solid fa-magnifying-glass\"></i> Run Detection\n }\n </button>\n </div>\n </div>\n </div>\n\n <!-- Detection Progress (visible during run) -->\n @if (IsDetecting) {\n <div class=\"detection-progress-section\">\n <div class=\"progress-header\">\n <span class=\"progress-stage\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n {{ DetectionStage }}\n </span>\n <span class=\"progress-percent\">{{ DetectionProgress }}%</span>\n </div>\n <div class=\"progress-bar-track\">\n <div class=\"progress-bar-fill\" [style.width.%]=\"DetectionProgress\"></div>\n </div>\n @if (DetectionCurrentItem) {\n <span class=\"progress-current-item\">{{ DetectionCurrentItem }}</span>\n }\n </div>\n }\n\n <!-- Threshold Controls (when entity doc selected) -->\n @if (SelectedDocumentThresholds) {\n <div class=\"threshold-controls\">\n <div class=\"threshold-slider-group\">\n <label class=\"threshold-label\">\n <i class=\"fa-solid fa-adjust\"></i>\n Potential Match\n <span class=\"threshold-value\">{{ (RunPotentialThreshold * 100).toFixed(0) }}%</span>\n </label>\n <input type=\"range\" class=\"threshold-slider\"\n [min]=\"30\" [max]=\"99\" [step]=\"1\"\n [value]=\"RunPotentialThreshold * 100\"\n (input)=\"OnPotentialThresholdChanged($any($event.target).value / 100)\"\n [disabled]=\"IsDetecting\" />\n <span class=\"threshold-hint\">Score above which duplicates are flagged for review</span>\n </div>\n <div class=\"threshold-slider-group\">\n <label class=\"threshold-label\">\n <i class=\"fa-solid fa-bullseye\"></i>\n Absolute Match\n <span class=\"threshold-value\">{{ (RunAbsoluteThreshold * 100).toFixed(0) }}%</span>\n </label>\n <input type=\"range\" class=\"threshold-slider\"\n [min]=\"50\" [max]=\"100\" [step]=\"1\"\n [value]=\"RunAbsoluteThreshold * 100\"\n (input)=\"OnAbsoluteThresholdChanged($any($event.target).value / 100)\"\n [disabled]=\"IsDetecting\" />\n <span class=\"threshold-hint\">Score above which duplicates are auto-confirmed</span>\n </div>\n </div>\n }\n\n <!-- KPI Strip -->\n <div class=\"kpi-strip\">\n <div class=\"kpi-card\">\n <div class=\"kpi-value\">{{ TotalGroupCount }}</div>\n <div class=\"kpi-label\">Total Groups</div>\n </div>\n <div class=\"kpi-card kpi-pending\">\n <div class=\"kpi-value\">{{ PendingCount }}</div>\n <div class=\"kpi-label\">Pending</div>\n </div>\n <div class=\"kpi-card kpi-approved\">\n <div class=\"kpi-value\">{{ ApprovedCount }}</div>\n <div class=\"kpi-label\">Approved</div>\n </div>\n <div class=\"kpi-card kpi-rejected\">\n <div class=\"kpi-value\">{{ RejectedCount }}</div>\n <div class=\"kpi-label\">Rejected</div>\n </div>\n </div>\n\n <!-- Filter Bar -->\n <div class=\"filter-bar\">\n <div class=\"filter-group\">\n <select class=\"filter-select\"\n [(ngModel)]=\"Filters.EntityName\"\n (ngModelChange)=\"OnFilterChange()\"\n [disabled]=\"EntityNames.length <= 1\">\n @if (EntityNames.length > 1) {\n <option value=\"\">All Entities</option>\n }\n @for (name of EntityNames; track name) {\n <option [value]=\"name\">{{ name }}</option>\n }\n </select>\n\n <div class=\"filter-range\">\n <label class=\"filter-label\">Min Score</label>\n <input type=\"number\"\n class=\"filter-input filter-input-score\"\n min=\"0\" max=\"1\" step=\"0.05\"\n [placeholder]=\"DataMinScore\"\n [(ngModel)]=\"Filters.MinScore\"\n (ngModelChange)=\"OnFilterChange()\">\n </div>\n\n <div class=\"filter-range\">\n <label class=\"filter-label\">Max Score</label>\n <input type=\"number\"\n class=\"filter-input filter-input-score\"\n min=\"0\" max=\"1\" step=\"0.05\"\n [placeholder]=\"DataMaxScore\"\n [(ngModel)]=\"Filters.MaxScore\"\n (ngModelChange)=\"OnFilterChange()\">\n </div>\n\n <div class=\"filter-range\">\n <label class=\"filter-label\">From</label>\n <input type=\"date\"\n class=\"filter-input filter-input-date\"\n [min]=\"DataMinDate\" [max]=\"DataMaxDate\"\n [(ngModel)]=\"Filters.DateFrom\"\n (ngModelChange)=\"OnFilterChange()\">\n </div>\n\n <div class=\"filter-range\">\n <label class=\"filter-label\">To</label>\n <input type=\"date\"\n class=\"filter-input filter-input-date\"\n [min]=\"DataMinDate\" [max]=\"DataMaxDate\"\n [(ngModel)]=\"Filters.DateTo\"\n (ngModelChange)=\"OnFilterChange()\">\n </div>\n </div>\n\n @if (HasActiveFilters) {\n <button class=\"clear-filters-btn\" (click)=\"ClearFilters()\">\n <i class=\"fa-solid fa-times\"></i> Clear Filters\n </button>\n }\n </div>\n\n <!-- Content -->\n @if (IsLoading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading duplicate detection results...\"></mj-loading>\n </div>\n } @else if (TotalGroupCount === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-clone empty-icon\"></i>\n <p class=\"empty-text\">No duplicate detection results found.</p>\n <p class=\"empty-subtext\">Select an entity document and click \"Run Detection\" to start.</p>\n </div>\n } @else {\n <!-- Kanban Board -->\n <div class=\"kanban-board\">\n <!-- Pending Column -->\n <div class=\"kanban-column\" [class.drop-target-active]=\"DragOverColumn === 'Pending' && DraggedGroup?.ApprovalStatus !== 'Pending'\">\n <div class=\"column-header column-header-pending\">\n <i class=\"fa-solid fa-clock\"></i>\n <span class=\"column-title\">Pending Review</span>\n <span class=\"column-count\">{{ PendingCount }}</span>\n </div>\n <div class=\"column-body\"\n (dragover)=\"OnDragOver($event, 'Pending')\"\n (dragleave)=\"OnDragLeave($event, 'Pending')\"\n (drop)=\"OnDrop($event, 'Pending')\">\n @for (group of PendingGroups; track group.DetailId) {\n <div class=\"kanban-card\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event, group)\"\n (dragend)=\"OnDragEnd()\"\n (click)=\"OpenComparison(group)\">\n <div class=\"card-header\">\n <div class=\"card-header-left\">\n <div class=\"card-icon\">\n <i [class]=\"group.EntityIcon\"></i>\n </div>\n <div class=\"card-title-block\">\n <div class=\"card-record-name\" [title]=\"group.RecordName\">{{ group.RecordName }}</div>\n <span class=\"entity-badge\">{{ group.EntityName }}</span>\n </div>\n </div>\n <span class=\"score-indicator\" [class]=\"GetScoreClass(group.HighestScore)\">\n {{ (group.HighestScore * 100).toFixed(0) }}%\n </span>\n </div>\n <div class=\"card-body\">\n @if (group.TopMatchSummaries.length > 0) {\n <div class=\"match-summaries\">\n @for (ms of group.TopMatchSummaries; track ms.Name) {\n <div class=\"match-summary-row\">\n <span class=\"match-score\">{{ (ms.Score * 100).toFixed(0) }}%</span>\n <span class=\"match-name\">{{ ms.Name }}</span>\n </div>\n }\n @if (group.MatchCount > group.TopMatchSummaries.length) {\n <div class=\"match-summary-more\">+{{ group.MatchCount - group.TopMatchSummaries.length }} more</div>\n }\n </div>\n }\n <div class=\"card-meta-row\">\n <span class=\"card-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n {{ group.MatchCount }} match{{ group.MatchCount !== 1 ? 'es' : '' }}\n </span>\n <span class=\"card-meta-item\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatDate(group.MatchedAt) }}\n </span>\n </div>\n </div>\n <div class=\"card-actions\">\n <button class=\"action-btn approve-btn\"\n [disabled]=\"IsSaving\"\n (click)=\"ApproveMatch(group); $event.stopPropagation()\">\n <i class=\"fa-solid fa-check\"></i> Approve\n </button>\n <button class=\"action-btn reject-btn\"\n [disabled]=\"IsSaving\"\n (click)=\"RejectMatch(group); $event.stopPropagation()\">\n <i class=\"fa-solid fa-times\"></i> Reject\n </button>\n </div>\n </div>\n }\n @if (PendingGroups.length === 0) {\n <div class=\"column-empty\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <span>No pending items</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Approved Column -->\n <div class=\"kanban-column\" [class.drop-target-active]=\"DragOverColumn === 'Approved' && DraggedGroup?.ApprovalStatus !== 'Approved'\">\n <div class=\"column-header column-header-approved\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <span class=\"column-title\">Approved</span>\n <span class=\"column-count\">{{ ApprovedCount }}</span>\n </div>\n <div class=\"column-body\"\n (dragover)=\"OnDragOver($event, 'Approved')\"\n (dragleave)=\"OnDragLeave($event, 'Approved')\"\n (drop)=\"OnDrop($event, 'Approved')\">\n @for (group of ApprovedGroups; track group.DetailId) {\n <div class=\"kanban-card\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event, group)\"\n (dragend)=\"OnDragEnd()\"\n (click)=\"OpenComparison(group)\">\n <div class=\"card-header\">\n <div class=\"card-header-left\">\n <div class=\"card-icon\">\n <i [class]=\"group.EntityIcon\"></i>\n </div>\n <div class=\"card-title-block\">\n <div class=\"card-record-name\" [title]=\"group.RecordName\">{{ group.RecordName }}</div>\n <span class=\"entity-badge\">{{ group.EntityName }}</span>\n </div>\n </div>\n <span class=\"score-indicator\" [class]=\"GetScoreClass(group.HighestScore)\">\n {{ (group.HighestScore * 100).toFixed(0) }}%\n </span>\n </div>\n <div class=\"card-body\">\n @if (group.TopMatchSummaries.length > 0) {\n <div class=\"match-summaries\">\n @for (ms of group.TopMatchSummaries; track ms.Name) {\n <div class=\"match-summary-row\">\n <span class=\"match-score\">{{ (ms.Score * 100).toFixed(0) }}%</span>\n <span class=\"match-name\">{{ ms.Name }}</span>\n </div>\n }\n @if (group.MatchCount > group.TopMatchSummaries.length) {\n <div class=\"match-summary-more\">+{{ group.MatchCount - group.TopMatchSummaries.length }} more</div>\n }\n </div>\n }\n <div class=\"card-meta-row\">\n <span class=\"card-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n {{ group.MatchCount }} match{{ group.MatchCount !== 1 ? 'es' : '' }}\n </span>\n <span class=\"card-meta-item\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatDate(group.MatchedAt) }}\n </span>\n </div>\n </div>\n </div>\n }\n @if (ApprovedGroups.length === 0) {\n <div class=\"column-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <span>No approved items</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Rejected Column -->\n <div class=\"kanban-column\" [class.drop-target-active]=\"DragOverColumn === 'Rejected' && DraggedGroup?.ApprovalStatus !== 'Rejected'\">\n <div class=\"column-header column-header-rejected\">\n <i class=\"fa-solid fa-ban\"></i>\n <span class=\"column-title\">Rejected</span>\n <span class=\"column-count\">{{ RejectedCount }}</span>\n </div>\n <div class=\"column-body\"\n (dragover)=\"OnDragOver($event, 'Rejected')\"\n (dragleave)=\"OnDragLeave($event, 'Rejected')\"\n (drop)=\"OnDrop($event, 'Rejected')\">\n @for (group of RejectedGroups; track group.DetailId) {\n <div class=\"kanban-card\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event, group)\"\n (dragend)=\"OnDragEnd()\"\n (click)=\"OpenComparison(group)\">\n <div class=\"card-header\">\n <div class=\"card-header-left\">\n <div class=\"card-icon\">\n <i [class]=\"group.EntityIcon\"></i>\n </div>\n <div class=\"card-title-block\">\n <div class=\"card-record-name\" [title]=\"group.RecordName\">{{ group.RecordName }}</div>\n <span class=\"entity-badge\">{{ group.EntityName }}</span>\n </div>\n </div>\n <span class=\"score-indicator\" [class]=\"GetScoreClass(group.HighestScore)\">\n {{ (group.HighestScore * 100).toFixed(0) }}%\n </span>\n </div>\n <div class=\"card-body\">\n @if (group.TopMatchSummaries.length > 0) {\n <div class=\"match-summaries\">\n @for (ms of group.TopMatchSummaries; track ms.Name) {\n <div class=\"match-summary-row\">\n <span class=\"match-score\">{{ (ms.Score * 100).toFixed(0) }}%</span>\n <span class=\"match-name\">{{ ms.Name }}</span>\n </div>\n }\n @if (group.MatchCount > group.TopMatchSummaries.length) {\n <div class=\"match-summary-more\">+{{ group.MatchCount - group.TopMatchSummaries.length }} more</div>\n }\n </div>\n }\n <div class=\"card-meta-row\">\n <span class=\"card-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n {{ group.MatchCount }} match{{ group.MatchCount !== 1 ? 'es' : '' }}\n </span>\n <span class=\"card-meta-item\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatDate(group.MatchedAt) }}\n </span>\n </div>\n </div>\n </div>\n }\n @if (RejectedGroups.length === 0) {\n <div class=\"column-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <span>No rejected items</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Saving overlay -->\n @if (IsSaving) {\n <div class=\"saving-overlay\">\n <mj-loading text=\"Saving...\" size=\"small\"></mj-loading>\n </div>\n }\n\n <!-- \u2550\u2550\u2550 Comparison Slide-In Panel \u2550\u2550\u2550 -->\n @if (ComparisonGroup) {\n <div class=\"slide-backdrop\" [class.comparison-closing]=\"ComparisonClosing\" (click)=\"CloseComparison()\"></div>\n <div class=\"comparison-panel\" [class.comparison-closing]=\"ComparisonClosing\" (click)=\"$event.stopPropagation()\">\n <!-- Header -->\n <div class=\"comparison-header\">\n <div class=\"comparison-header-left\">\n <div class=\"comparison-entity-icon\">\n <i [class]=\"ComparisonGroup.EntityIcon\"></i>\n </div>\n <div>\n <div class=\"comparison-title\">{{ ComparisonGroup.RecordName }}</div>\n <span class=\"comparison-entity-badge\">{{ ComparisonGroup.EntityName }}</span>\n <span class=\"comparison-match-count\">\n {{ ComparisonGroup.MatchCount }} potential duplicate{{ ComparisonGroup.MatchCount !== 1 ? 's' : '' }}\n </span>\n </div>\n </div>\n <div class=\"comparison-header-right\">\n <div class=\"comparison-toggle\">\n <button class=\"toggle-btn\" [class.toggle-active]=\"ComparisonShowAllFields\"\n (click)=\"ComparisonShowAllFields = true\">All Fields</button>\n <button class=\"toggle-btn\" [class.toggle-active]=\"!ComparisonShowAllFields\"\n (click)=\"ComparisonShowAllFields = false\">Differences Only</button>\n </div>\n <button class=\"comparison-close-btn\" (click)=\"CloseComparison()\" title=\"Close (Esc)\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n </div>\n\n <!-- Grid -->\n @if (ComparisonLoading) {\n <div class=\"comparison-loading\">\n <mj-loading text=\"Loading records for comparison...\" size=\"medium\"></mj-loading>\n </div>\n }\n <div class=\"comparison-grid-wrapper\" [hidden]=\"ComparisonLoading\">\n <div class=\"comparison-grid\"\n [style.grid-template-columns]=\"'160px repeat(' + (1 + ComparisonMatches.length) + ', minmax(180px, 1fr))'\">\n\n <!-- Corner cell -->\n <div class=\"grid-corner-cell\">Field</div>\n\n <!-- Source column header -->\n <div class=\"grid-col-header grid-col-source\">\n <span class=\"col-header-label\">Source</span>\n <span class=\"col-header-name\">{{ ComparisonGroup.RecordName }}</span>\n <div class=\"surviving-selector\">\n <input type=\"radio\" name=\"survivor\" class=\"surviving-radio\"\n [checked]=\"SurvivorColumnIndex === 0\"\n (change)=\"SetSurvivor(0)\">\n <span class=\"surviving-label\" [class.is-survivor]=\"SurvivorColumnIndex === 0\">\n {{ SurvivorColumnIndex === 0 ? 'Surviving Record' : 'Set as survivor' }}\n </span>\n </div>\n <button class=\"use-all-btn\" [class.all-selected]=\"AllFieldsSelectedFrom(0)\"\n (click)=\"UseAllFieldsFrom(0)\">\n <i class=\"fa-solid\" [class.fa-check-double]=\"AllFieldsSelectedFrom(0)\" [class.fa-clone]=\"!AllFieldsSelectedFrom(0)\"></i>\n {{ AllFieldsSelectedFrom(0) ? 'Using all fields' : 'Use all fields' }}\n </button>\n <!-- Dependencies Summary -->\n <div class=\"deps-summary\">\n <div class=\"deps-total\">\n <i class=\"fa-solid fa-link\"></i>\n <span class=\"deps-total-number\">{{ GetTotalDeps(0) }}</span>\n {{ GetTotalDeps(0) === 1 ? 'dependency' : 'dependencies' }}\n @if (GetMaxDepsColumnIndex() === 0 && GetTotalDeps(0) > 0) {\n <span class=\"deps-total-recommended\"><i class=\"fa-solid fa-star\"></i> Most deps</span>\n }\n </div>\n @if (GetGroupedDeps(0).length > 0) {\n <div class=\"deps-header\" (click)=\"ToggleDepsExpanded(0)\">\n <span>{{ IsDepsExpanded(0) ? 'Hide details' : 'Show details' }}</span>\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsDepsExpanded(0)\" [class.fa-chevron-up]=\"IsDepsExpanded(0)\"></i>\n </div>\n @if (IsDepsExpanded(0)) {\n <div class=\"deps-detail-list\">\n @for (dep of GetGroupedDeps(0); track dep.Entity) {\n <div class=\"deps-detail-group\">\n <div class=\"deps-detail-row\" (click)=\"ToggleDepEntityGroup(0, dep.Entity); $event.stopPropagation()\">\n <span class=\"deps-detail-entity\">\n <i class=\"fa-solid deps-expand-icon\"\n [class.fa-chevron-right]=\"!IsDepEntityGroupExpanded(0, dep.Entity)\"\n [class.fa-chevron-down]=\"IsDepEntityGroupExpanded(0, dep.Entity)\"></i>\n {{ dep.Entity }}\n </span>\n <span class=\"deps-detail-count\">{{ dep.Count }}</span>\n </div>\n @if (IsDepEntityGroupExpanded(0, dep.Entity)) {\n <div class=\"deps-records-list\">\n @if (IsDepRecordsLoading(0, dep.Entity)) {\n <div class=\"deps-record-loading\"><i class=\"fa-solid fa-spinner fa-spin\"></i> Loading...</div>\n }\n @for (record of GetDepRecords(0, dep.Entity); track $index) {\n <div class=\"deps-record-row\" (click)=\"OpenDepRecord(record); $event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square deps-record-icon\"></i>\n <span class=\"deps-record-name\">{{ record.Name }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n </div>\n\n <!-- Match column headers -->\n @for (match of ComparisonMatches; track match.Match.ID; let mi = $index) {\n <div class=\"grid-col-header\"\n [class.match-approved]=\"match.Match.ApprovalStatus === 'Approved'\"\n [class.match-rejected]=\"match.Match.ApprovalStatus === 'Rejected'\">\n <div class=\"col-header-top\">\n <span class=\"col-header-name\">{{ match.Name }}</span>\n <span class=\"score-indicator\" [class]=\"GetScoreClass(match.Score)\">\n {{ (match.Score * 100).toFixed(0) }}%\n </span>\n </div>\n <span class=\"col-header-diff-count\">{{ match.DiffCount }} difference{{ match.DiffCount !== 1 ? 's' : '' }}</span>\n <div class=\"surviving-selector\">\n <input type=\"radio\" name=\"survivor\" class=\"surviving-radio\"\n [checked]=\"SurvivorColumnIndex === mi + 1\"\n (change)=\"SetSurvivor(mi + 1)\">\n <span class=\"surviving-label\" [class.is-survivor]=\"SurvivorColumnIndex === mi + 1\">\n {{ SurvivorColumnIndex === mi + 1 ? 'Surviving Record' : 'Set as survivor' }}\n </span>\n </div>\n <button class=\"use-all-btn\" [class.all-selected]=\"AllFieldsSelectedFrom(mi + 1)\"\n (click)=\"UseAllFieldsFrom(mi + 1)\">\n <i class=\"fa-solid\" [class.fa-check-double]=\"AllFieldsSelectedFrom(mi + 1)\" [class.fa-clone]=\"!AllFieldsSelectedFrom(mi + 1)\"></i>\n {{ AllFieldsSelectedFrom(mi + 1) ? 'Using all fields' : 'Use all fields' }}\n </button>\n <!-- Dependencies Summary -->\n <div class=\"deps-summary\">\n <div class=\"deps-total\">\n <i class=\"fa-solid fa-link\"></i>\n <span class=\"deps-total-number\">{{ GetTotalDeps(mi + 1) }}</span>\n {{ GetTotalDeps(mi + 1) === 1 ? 'dependency' : 'dependencies' }}\n @if (GetMaxDepsColumnIndex() === mi + 1 && GetTotalDeps(mi + 1) > 0) {\n <span class=\"deps-total-recommended\"><i class=\"fa-solid fa-star\"></i> Most deps</span>\n }\n </div>\n @if (GetGroupedDeps(mi + 1).length > 0) {\n <div class=\"deps-header\" (click)=\"ToggleDepsExpanded(mi + 1)\">\n <span>{{ IsDepsExpanded(mi + 1) ? 'Hide details' : 'Show details' }}</span>\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsDepsExpanded(mi + 1)\" [class.fa-chevron-up]=\"IsDepsExpanded(mi + 1)\"></i>\n </div>\n @if (IsDepsExpanded(mi + 1)) {\n <div class=\"deps-detail-list\">\n @for (dep of GetGroupedDeps(mi + 1); track dep.Entity) {\n <div class=\"deps-detail-group\">\n <div class=\"deps-detail-row\" (click)=\"ToggleDepEntityGroup(mi + 1, dep.Entity); $event.stopPropagation()\">\n <span class=\"deps-detail-entity\">\n <i class=\"fa-solid deps-expand-icon\"\n [class.fa-chevron-right]=\"!IsDepEntityGroupExpanded(mi + 1, dep.Entity)\"\n [class.fa-chevron-down]=\"IsDepEntityGroupExpanded(mi + 1, dep.Entity)\"></i>\n {{ dep.Entity }}\n </span>\n <span class=\"deps-detail-count\">{{ dep.Count }}</span>\n </div>\n @if (IsDepEntityGroupExpanded(mi + 1, dep.Entity)) {\n <div class=\"deps-records-list\">\n @if (IsDepRecordsLoading(mi + 1, dep.Entity)) {\n <div class=\"deps-record-loading\"><i class=\"fa-solid fa-spinner fa-spin\"></i> Loading...</div>\n }\n @for (record of GetDepRecords(mi + 1, dep.Entity); track $index) {\n <div class=\"deps-record-row\" (click)=\"OpenDepRecord(record); $event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square deps-record-icon\"></i>\n <span class=\"deps-record-name\">{{ record.Name }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n @if (match.Match.ApprovalStatus !== 'Pending') {\n <span class=\"match-status-badge\"\n [class.status-approved]=\"match.Match.ApprovalStatus === 'Approved'\"\n [class.status-rejected]=\"match.Match.ApprovalStatus === 'Rejected'\">\n <i class=\"fa-solid\" [class.fa-check]=\"match.Match.ApprovalStatus === 'Approved'\"\n [class.fa-times]=\"match.Match.ApprovalStatus === 'Rejected'\"></i>\n {{ match.Match.ApprovalStatus }}\n </span>\n }\n </div>\n }\n\n <!-- Field rows -->\n @for (field of GetVisibleFields(); track field.FieldName; let odd = $odd) {\n <!-- Label cell -->\n <div class=\"grid-label-cell\" [class.grid-row-odd]=\"odd\">\n {{ field.DisplayName }}\n </div>\n <!-- Source value cell -->\n <div class=\"grid-value-cell grid-source-cell\"\n [class.grid-row-odd]=\"odd\"\n [class.has-diff-in-row]=\"field.HasDifference\"\n [class.field-selected]=\"field.SelectedColumnIndex === 0\"\n (click)=\"SelectFieldValue(field, 0)\">\n @if (field.SourceValue != null) {\n {{ field.SourceValue }}\n } @else {\n <span class=\"field-not-available\">(not available)</span>\n }\n @if (field.HasDifference || field.SelectedColumnIndex === 0) {\n <input type=\"radio\" [name]=\"'field-' + field.FieldName\" class=\"field-select-radio\"\n [checked]=\"field.SelectedColumnIndex === 0\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"SelectFieldValue(field, 0)\">\n }\n </div>\n <!-- Match value cells -->\n @for (matchVal of field.MatchValues; track $index; let mi = $index) {\n <div class=\"grid-value-cell\"\n [class.grid-row-odd]=\"odd\"\n [class.value-same]=\"AreValuesEqual(field.SourceValue, matchVal)\"\n [class.value-different]=\"matchVal != null && field.SourceValue != null && !AreValuesEqual(field.SourceValue, matchVal)\"\n [class.field-selected]=\"field.SelectedColumnIndex === mi + 1\"\n (click)=\"matchVal != null ? SelectFieldValue(field, mi + 1) : null\">\n @if (matchVal != null) {\n {{ matchVal }}\n @if (field.HasDifference || field.SelectedColumnIndex === mi + 1) {\n <input type=\"radio\" [name]=\"'field-' + field.FieldName\" class=\"field-select-radio\"\n [checked]=\"field.SelectedColumnIndex === mi + 1\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"SelectFieldValue(field, mi + 1)\">\n }\n } @else {\n <span class=\"field-not-available\">(not available)</span>\n }\n </div>\n }\n }\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"comparison-footer\">\n <div class=\"comparison-footer-left\">\n <span class=\"comparison-summary\">\n Showing {{ GetVisibleFields().length }} of {{ ComparisonFields.length }} fields\n </span>\n <span class=\"merge-summary\">\n <i class=\"fa-solid fa-code-merge\"></i>\n Surviving: <strong>{{ SurvivorName() }}</strong>\n @if (CherryPickedCount() > 0) {\n · {{ CherryPickedCount() }} field{{ CherryPickedCount() !== 1 ? 's' : '' }} cherry-picked\n }\n </span>\n </div>\n <div class=\"comparison-footer-right\">\n <button class=\"action-btn reject-btn\" [disabled]=\"IsSaving\"\n (click)=\"RejectMatch(ComparisonGroup!); CloseComparison()\">\n <i class=\"fa-solid fa-times\"></i> Reject All\n </button>\n <button class=\"action-btn merge-btn\" [disabled]=\"IsSaving\"\n (click)=\"OpenMergeConfirm()\">\n <i class=\"fa-solid fa-code-merge\"></i> Merge Records\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550 Merge Confirmation Panel \u2550\u2550\u2550 -->\n @if (ShowMergeConfirm && ComparisonGroup) {\n <div class=\"merge-confirm-backdrop\" (click)=\"CloseMergeConfirm()\">\n <div class=\"merge-confirm-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"merge-confirm-header\">\n <div class=\"merge-confirm-icon\"><i class=\"fa-solid fa-code-merge\"></i></div>\n <div>\n <div class=\"merge-confirm-title\">Confirm Record Merge</div>\n <div class=\"merge-confirm-subtitle\">This action cannot be undone. Please review carefully.</div>\n </div>\n </div>\n\n <div class=\"merge-confirm-body\">\n <!-- Surviving Record -->\n <div class=\"merge-survivor-card\">\n <div class=\"merge-survivor-label\"><i class=\"fa-solid fa-shield-halved\"></i> Surviving Record</div>\n <div class=\"merge-survivor-name\">{{ SurvivorName() }}</div>\n <div class=\"merge-survivor-pk\">\n <i class=\"fa-solid fa-key\"></i>\n {{ SurvivorKeyDisplay() }}\n </div>\n <div class=\"merge-survivor-detail\">This record's ID will be retained. All dependencies from merged records will be transferred here.</div>\n </div>\n\n <!-- Cherry-picked field overrides -->\n @if (GetCherryPickedFields().length > 0) {\n <div>\n <div class=\"merge-section-label\">Field Value Overrides ({{ GetCherryPickedFields().length }} field{{ GetCherryPickedFields().length !== 1 ? 's' : '' }})</div>\n @for (field of GetCherryPickedFields(); track field.FieldName) {\n <div class=\"merge-field-override\">\n <span class=\"merge-field-name\">{{ field.DisplayName }}</span>\n <span class=\"merge-field-value\">\"{{ field.Value }}\"</span>\n <span class=\"merge-field-source\">from {{ field.SourceName }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Dependency transfer -->\n @if (GetNonSurvivorColumns().length > 0) {\n <div class=\"merge-deps-transfer\">\n <div class=\"merge-deps-transfer-label\"><i class=\"fa-solid fa-arrow-right-arrow-left\"></i> Dependencies to Transfer</div>\n @for (col of GetNonSurvivorColumns(); track col.ColumnIndex) {\n <div class=\"merge-deps-transfer-row\">\n <i class=\"fa-solid fa-arrow-right\"></i>\n <span>\n @if (col.DepCount > 0) {\n <strong>{{ col.DepCount }}</strong> {{ col.DepCount === 1 ? 'dependency' : 'dependencies' }} from <strong>{{ col.Name }}</strong> → {{ SurvivorName() }}\n } @else {\n <strong>0</strong> dependencies from <strong>{{ col.Name }}</strong>\n }\n </span>\n </div>\n }\n </div>\n }\n\n <!-- Records to be deleted -->\n <div class=\"merge-delete-card\">\n <div class=\"merge-delete-label\"><i class=\"fa-solid fa-trash\"></i> Records to Delete After Merge</div>\n @for (col of GetNonSurvivorColumns(); track col.ColumnIndex) {\n <div class=\"merge-delete-item\">\n <span class=\"merge-delete-name\">{{ col.Name }}</span>\n <span class=\"merge-delete-deps\">\n @if (col.DepCount > 0) {\n {{ col.DepCount }} dep{{ col.DepCount !== 1 ? 's' : '' }} transferring\n } @else {\n no deps\n }\n </span>\n </div>\n }\n </div>\n </div>\n\n <div class=\"merge-confirm-footer\">\n <button class=\"action-btn cancel-btn\" (click)=\"CloseMergeConfirm()\" [disabled]=\"IsMerging\">\n <i class=\"fa-solid fa-arrow-left\"></i> Back\n </button>\n <button class=\"action-btn confirm-merge-btn\" (click)=\"ExecuteMerge()\" [disabled]=\"IsMerging\">\n @if (IsMerging) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Merging...\n } @else {\n <i class=\"fa-solid fa-code-merge\"></i> Confirm Merge ({{ GetNonSurvivorColumns().length + 1 }} records → 1)\n }\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Duplicate Detection Kanban Board - Resource Component Styles\n All colors use MJ design tokens (--mj-*) exclusively.\n ============================================================ */\n\napp-duplicate-detection-resource {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n}\n\n.duplicate-detection-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n padding: 16px 20px;\n background: var(--mj-bg-page);\n position: relative;\n overflow: hidden;\n}\n\n/* ---- Page Header ---- */\n\n.page-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.page-title {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.page-title i {\n color: var(--mj-brand-primary);\n}\n\n/* ---- Header Actions ---- */\n\n.header-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.run-detection-controls {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.entity-doc-select {\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 13px;\n min-width: 200px;\n}\n\n.entity-doc-select:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.run-detection-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n font-size: 13px;\n font-weight: 600;\n transition: background 0.15s;\n white-space: nowrap;\n}\n\n.run-detection-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.run-detection-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* ---- Detection Progress ---- */\n\n.detection-progress-section {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-brand-primary);\n border-radius: 8px;\n padding: 14px 18px;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.progress-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n\n.progress-stage {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.progress-percent {\n font-size: 13px;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.progress-bar-track {\n height: 6px;\n border-radius: 3px;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.progress-bar-fill {\n height: 100%;\n border-radius: 3px;\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n}\n\n.progress-current-item {\n display: block;\n margin-top: 6px;\n font-size: 11px;\n color: var(--mj-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ---- Threshold Controls ---- */\n\n.threshold-controls {\n display: flex;\n gap: 24px;\n padding: 12px 16px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--mj-status-info) 6%, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-subtle);\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.threshold-slider-group {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.threshold-label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.threshold-value {\n margin-left: auto;\n font-weight: 700;\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n}\n\n.threshold-slider {\n width: 100%;\n height: 4px;\n appearance: none;\n -webkit-appearance: none;\n background: var(--mj-border-default);\n border-radius: 2px;\n outline: none;\n cursor: pointer;\n}\n\n.threshold-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n border: 2px solid var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n cursor: pointer;\n}\n\n.threshold-slider::-moz-range-thumb {\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n border: 2px solid var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n cursor: pointer;\n}\n\n.threshold-slider:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.threshold-hint {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n/* ---- Threshold Info (legacy) ---- */\n\n.threshold-info {\n display: flex;\n gap: 16px;\n padding: 8px 14px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-status-info) 8%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-info-border);\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.threshold-item {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-status-info-text);\n font-weight: 500;\n}\n\n.threshold-item i {\n font-size: 11px;\n}\n\n/* ---- KPI Strip ---- */\n\n.kpi-strip {\n display: flex;\n gap: 12px;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.kpi-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n}\n\n.kpi-value {\n font-size: 28px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.kpi-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 2px;\n}\n\n.kpi-pending .kpi-value {\n color: var(--mj-status-warning);\n}\n\n.kpi-approved .kpi-value {\n color: var(--mj-status-success);\n}\n\n.kpi-rejected .kpi-value {\n color: var(--mj-status-error);\n}\n\n/* ---- Filter Bar ---- */\n\n.filter-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.filter-group {\n display: flex;\n align-items: flex-end;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.filter-select {\n height: 34px;\n padding: 4px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 13px;\n outline: none;\n min-width: 160px;\n}\n\n.filter-select:focus {\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.filter-range {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.filter-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.filter-input {\n height: 34px;\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 13px;\n outline: none;\n width: 100px;\n}\n\n.filter-input-score {\n width: 80px;\n}\n\n.filter-input-date {\n width: 140px;\n}\n\n.filter-input:focus {\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.clear-filters-btn {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 34px;\n padding: 0 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 13px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.clear-filters-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* ---- Loading & Empty States ---- */\n\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n min-height: 200px;\n}\n\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n flex: 1;\n min-height: 200px;\n gap: 8px;\n}\n\n.empty-icon {\n font-size: 48px;\n color: var(--mj-text-disabled);\n}\n\n.empty-text {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.empty-subtext {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin: 0;\n}\n\n/* ---- Kanban Board ---- */\n\n.kanban-board {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n overflow-x: auto;\n}\n\n.kanban-column {\n flex: 1;\n min-width: 280px;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.column-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n border-bottom: 1px solid var(--mj-border-subtle);\n flex-shrink: 0;\n}\n\n.column-header-pending {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n}\n\n.column-header-pending i {\n color: var(--mj-status-warning);\n}\n\n.column-header-approved {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n}\n\n.column-header-approved i {\n color: var(--mj-status-success);\n}\n\n.column-header-rejected {\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n}\n\n.column-header-rejected i {\n color: var(--mj-status-error);\n}\n\n.column-title {\n flex: 1;\n}\n\n.column-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 6px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.column-body {\n flex: 1;\n overflow-y: auto;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.column-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 32px 16px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n}\n\n.column-empty i {\n font-size: 24px;\n}\n\n/* ---- Kanban Cards ---- */\n\n.kanban-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n transition: box-shadow 0.15s ease, border-color 0.15s ease;\n flex-shrink: 0; /* Prevent cards from shrinking \u2014 column scrolls instead */\n}\n\n.kanban-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n}\n\n/* Drag and Drop */\n.kanban-card[draggable=\"true\"] {\n cursor: grab;\n}\n\n.kanban-card[draggable=\"true\"]:active {\n cursor: grabbing;\n}\n\n.drop-target-active {\n outline: 2px dashed var(--mj-brand-primary);\n outline-offset: -2px;\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface-card)) !important;\n}\n\n.drop-target-active .column-body {\n min-height: 100px;\n}\n\n.card-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n gap: 8px;\n}\n\n.card-header-left {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n flex: 1;\n}\n\n.card-icon {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n}\n\n.card-title-block {\n min-width: 0;\n flex: 1;\n}\n\n.card-record-name {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-bottom: 2px;\n}\n\n.entity-badge {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border-radius: 3px;\n font-size: 10px;\n font-weight: 500;\n color: var(--mj-text-muted);\n letter-spacing: 0.2px;\n}\n\n/* Score indicator colors */\n\n.score-indicator {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 40px;\n padding: 2px 8px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 700;\n}\n\n.score-high {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.score-medium {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n.score-low {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.card-body {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.match-summaries {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.match-summary-row {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n}\n\n.match-score {\n flex-shrink: 0;\n font-weight: 600;\n font-size: 11px;\n color: var(--mj-text-muted);\n min-width: 30px;\n}\n\n.match-name {\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.match-summary-more {\n font-size: 11px;\n color: var(--mj-text-muted);\n font-style: italic;\n padding-left: 38px;\n}\n\n.card-meta-row {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 11px;\n color: var(--mj-text-muted);\n border-top: 1px solid var(--mj-border-subtle);\n padding-top: 6px;\n}\n\n.card-meta-item {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.card-meta-item i {\n font-size: 10px;\n}\n font-size: 12px;\n max-width: 180px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Comparison Slide-In Panel\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.slide-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 9999;\n animation: fadeIn 0.2s ease;\n}\n\n.slide-backdrop.comparison-closing {\n animation: fadeOut 0.25s ease forwards;\n}\n\n@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }\n@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }\n\n.comparison-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 82vw;\n max-width: 1300px;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -8px 0 40px rgba(0,0,0,0.4);\n z-index: 10000;\n display: flex;\n flex-direction: column;\n animation: slideIn 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n}\n\n.comparison-panel.comparison-closing {\n animation: slideOut 0.25s cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n@keyframes slideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }\n@keyframes slideOut { from { transform: translateX(0); } to { transform: translateX(100%); } }\n\n/* Header */\n.comparison-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n gap: 12px;\n}\n\n.comparison-header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n}\n\n.comparison-entity-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 1rem;\n flex-shrink: 0;\n}\n\n.comparison-title {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.comparison-entity-badge {\n display: inline-block;\n padding: 1px 7px;\n border-radius: 10px;\n font-size: 0.65rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n margin-right: 6px;\n}\n\n.comparison-match-count {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.comparison-header-right {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-shrink: 0;\n}\n\n/* Toggle */\n.comparison-toggle {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.toggle-btn {\n padding: 5px 12px;\n font-size: 0.7rem;\n font-weight: 500;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n}\n\n.toggle-btn:first-child {\n border-right: 1px solid var(--mj-border-default);\n}\n\n.toggle-btn.toggle-active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.comparison-close-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n transition: all 0.15s ease;\n}\n\n.comparison-close-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Loading state */\n.comparison-loading {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Grid wrapper */\n.comparison-grid-wrapper {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* Grid */\n.comparison-grid {\n display: grid;\n min-width: 100%;\n}\n\n.comparison-grid > div {\n padding: 8px 12px;\n font-size: 0.78rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n border-right: 1px solid var(--mj-border-subtle);\n}\n\n/* Corner cell */\n.grid-corner-cell {\n position: sticky;\n top: 0;\n left: 0;\n z-index: 4;\n background: var(--mj-bg-surface-elevated);\n font-size: 0.65rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: flex-end;\n padding-bottom: 6px;\n}\n\n/* Column headers */\n.grid-col-header {\n position: sticky;\n top: 0;\n z-index: 3;\n background: var(--mj-bg-surface-elevated);\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.grid-col-source {\n border-left: 3px solid var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface-elevated));\n}\n\n.col-header-label {\n font-size: 0.6rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-brand-primary);\n}\n\n.col-header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n}\n\n.col-header-name {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.col-header-diff-count {\n font-size: 0.65rem;\n color: var(--mj-status-warning-text);\n}\n\n/* Surviving record selector */\n.surviving-selector {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n}\n\n.surviving-radio {\n appearance: none;\n width: 14px;\n height: 14px;\n border: 2px solid var(--mj-border-strong);\n border-radius: 50%;\n cursor: pointer;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n\n.surviving-radio:checked {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary);\n box-shadow: inset 0 0 0 2px var(--mj-bg-surface-elevated);\n}\n\n.surviving-label {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n}\n\n.surviving-label.is-survivor {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n/* Use all fields button */\n.use-all-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.62rem;\n cursor: pointer;\n transition: all 0.12s ease;\n margin-top: 2px;\n}\n\n.use-all-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.use-all-btn.all-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n/* Per-match actions */\n.comparison-match-actions {\n display: flex;\n gap: 4px;\n margin-top: 3px;\n}\n\n.action-btn-sm {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px solid;\n border-radius: 5px;\n cursor: pointer;\n font-size: 0.65rem;\n transition: all 0.15s ease;\n}\n\n.approve-btn-sm {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border-color: var(--mj-status-success-border);\n}\n.approve-btn-sm:hover { background: rgba(34,197,94,0.25); }\n\n.reject-btn-sm {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border-color: var(--mj-status-error-border);\n}\n.reject-btn-sm:hover { background: rgba(239,68,68,0.25); }\n\n.match-status-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.68rem;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-approved {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.status-rejected {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.match-approved {\n border-top: 3px solid var(--mj-status-success);\n}\n\n.match-rejected {\n border-top: 3px solid var(--mj-status-error);\n opacity: 0.6;\n}\n\n/* Label cells */\n.grid-label-cell {\n position: sticky;\n left: 0;\n z-index: 2;\n background: var(--mj-bg-surface);\n font-size: 0.72rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n}\n\n.grid-row-odd {\n background: color-mix(in srgb, var(--mj-bg-surface-sunken) 50%, var(--mj-bg-surface));\n}\n\n.grid-label-cell.grid-row-odd {\n background: color-mix(in srgb, var(--mj-bg-surface-sunken) 50%, var(--mj-bg-surface));\n}\n\n/* Value cells */\n.grid-value-cell {\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n line-height: 1.45;\n word-break: break-word;\n position: relative;\n cursor: pointer;\n}\n\n.grid-source-cell {\n background: color-mix(in srgb, var(--mj-brand-primary) 3%, var(--mj-bg-surface));\n border-left: 3px solid transparent;\n}\n\n.grid-source-cell.grid-row-odd {\n background: color-mix(in srgb, var(--mj-brand-primary) 3%, color-mix(in srgb, var(--mj-bg-surface-sunken) 50%, var(--mj-bg-surface)));\n}\n\n.grid-source-cell.has-diff-in-row {\n font-weight: 600;\n}\n\n/* Diff highlighting */\n.value-same {\n color: var(--mj-text-muted);\n}\n\n.value-different {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, transparent) !important;\n border-left: 3px solid var(--mj-status-warning);\n color: var(--mj-text-primary);\n}\n\n.field-not-available {\n font-style: italic;\n font-size: 0.72rem;\n color: var(--mj-text-disabled);\n}\n\n/* Field selection radio */\n.field-select-radio {\n position: absolute;\n top: 50%;\n right: 8px;\n transform: translateY(-50%);\n appearance: none;\n width: 16px;\n height: 16px;\n border: 2px solid var(--mj-border-strong);\n border-radius: 50%;\n cursor: pointer;\n transition: all 0.12s ease;\n opacity: 0.4;\n}\n\n.field-select-radio:hover {\n opacity: 0.8;\n}\n\n.field-select-radio:checked {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary);\n box-shadow: inset 0 0 0 2.5px var(--mj-bg-surface);\n opacity: 1;\n}\n\n.grid-value-cell.field-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)) !important;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.grid-value-cell.field-selected .field-select-radio {\n opacity: 1;\n}\n\n/* Footer */\n.comparison-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 20px;\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.comparison-footer-left {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.comparison-summary {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.merge-summary {\n font-size: 0.72rem;\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.merge-summary i {\n font-size: 0.65rem;\n}\n\n.comparison-footer-right {\n display: flex;\n gap: 8px;\n}\n\n.comparison-footer-right .action-btn {\n flex: none;\n padding: 7px 14px;\n}\n\n.merge-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.merge-btn:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.card-actions {\n display: flex;\n gap: 8px;\n padding: 10px 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.action-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 32px;\n padding: 0 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n\n.action-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.approve-btn {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success-text);\n border-color: var(--mj-status-success-border);\n}\n\n.approve-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-success) 20%, var(--mj-bg-surface));\n}\n\n.reject-btn {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error-text);\n border-color: var(--mj-status-error-border);\n}\n\n.reject-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-bg-surface));\n}\n\n/* ---- Responsive ---- */\n\n@media (max-width: 768px) {\n .kanban-board {\n flex-direction: column;\n }\n\n .kanban-column {\n min-width: auto;\n max-height: 400px;\n }\n}\n\n@media (max-width: 480px) {\n .kpi-strip {\n flex-direction: column;\n }\n\n .filter-bar {\n flex-wrap: wrap;\n }\n\n .filter-group {\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n }\n\n .filter-select {\n min-width: auto;\n width: 100%;\n }\n\n .filter-input {\n width: 100%;\n }\n\n .card-actions {\n flex-direction: column;\n }\n\n .action-btn {\n width: 100%;\n }\n}\n\n/* ---- Saving Overlay ---- */\n\n.saving-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-overlay);\n z-index: 10;\n border-radius: 8px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Dependencies Summary (in column headers)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.deps-summary {\n margin-top: 6px;\n padding: 6px 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n font-size: 0.68rem;\n}\n\n.deps-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-weight: 600;\n}\n\n.deps-header i {\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.deps-header:hover {\n color: var(--mj-text-primary);\n}\n\n.deps-total {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.72rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin-bottom: 2px;\n}\n\n.deps-total i {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n.deps-total-number {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 0 6px;\n border-radius: 8px;\n font-size: 0.7rem;\n font-weight: 700;\n}\n\n.deps-total-recommended {\n font-size: 0.58rem;\n color: var(--mj-status-success-text);\n font-weight: 600;\n margin-left: 4px;\n}\n\n.deps-total-recommended i {\n color: var(--mj-status-success-text);\n}\n\n.deps-detail-list {\n margin-top: 4px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.deps-detail-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 3px 0;\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n border-radius: 3px;\n transition: background 0.1s ease;\n}\n\n.deps-detail-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.deps-detail-entity {\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.deps-expand-icon {\n font-size: 0.5rem;\n width: 10px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n\n/* Individual dependency records list */\n.deps-records-list {\n padding-left: 14px;\n margin-bottom: 2px;\n}\n\n.deps-record-row {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 2px 4px;\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n border-radius: 3px;\n transition: color 0.1s ease, background 0.1s ease;\n}\n\n.deps-record-row:hover {\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n}\n\n.deps-record-icon {\n font-size: 0.5rem;\n flex-shrink: 0;\n}\n\n.deps-record-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 0.6rem;\n}\n\n.deps-record-loading {\n padding: 3px 4px;\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.deps-record-loading i {\n margin-right: 4px;\n}\n\n.deps-detail-count {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Merge Confirmation Panel\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.merge-confirm-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.3);\n z-index: 20000;\n animation: fadeIn 0.2s ease;\n}\n\n.merge-confirm-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 520px;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -8px 0 40px rgba(0, 0, 0, 0.4);\n z-index: 20001;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideIn 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.merge-confirm-header {\n padding: 18px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.merge-confirm-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text);\n font-size: 1.1rem;\n flex-shrink: 0;\n}\n\n.merge-confirm-title {\n font-size: 1.05rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.merge-confirm-subtitle {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.merge-confirm-body {\n padding: 20px 24px;\n overflow-y: auto;\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n/* Surviving record card */\n\n.merge-survivor-card {\n padding: 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n border: 1px solid var(--mj-brand-primary);\n border-radius: 8px;\n}\n\n.merge-survivor-label {\n font-size: 0.65rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-brand-primary);\n margin-bottom: 4px;\n}\n\n.merge-survivor-label i {\n margin-right: 4px;\n}\n\n.merge-survivor-name {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.merge-survivor-pk {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n padding: 4px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-radius: 4px;\n font-size: 0.68rem;\n font-family: monospace;\n color: var(--mj-text-secondary);\n}\n\n.merge-survivor-pk i {\n font-size: 0.6rem;\n color: var(--mj-brand-primary);\n}\n\n.merge-survivor-detail {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 4px;\n}\n\n/* Cherry-picked fields section */\n\n.merge-section-label {\n font-size: 0.7rem;\n font-weight: 700;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 6px;\n}\n\n.merge-field-override {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 6px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.merge-field-name {\n font-weight: 600;\n color: var(--mj-text-secondary);\n min-width: 110px;\n flex-shrink: 0;\n}\n\n.merge-field-value {\n color: var(--mj-text-primary);\n}\n\n.merge-field-source {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n margin-left: auto;\n white-space: nowrap;\n}\n\n/* Dependency transfer summary */\n\n.merge-deps-transfer {\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n}\n\n.merge-deps-transfer-label {\n font-size: 0.65rem;\n font-weight: 700;\n text-transform: uppercase;\n color: var(--mj-brand-primary);\n margin-bottom: 6px;\n}\n\n.merge-deps-transfer-label i {\n margin-right: 4px;\n}\n\n.merge-deps-transfer-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 3px 0;\n font-size: 0.75rem;\n color: var(--mj-text-secondary);\n}\n\n.merge-deps-transfer-row i {\n color: var(--mj-brand-primary);\n font-size: 0.65rem;\n width: 14px;\n text-align: center;\n}\n\n/* Records to delete */\n\n.merge-delete-card {\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n}\n\n.merge-delete-label {\n font-size: 0.65rem;\n font-weight: 700;\n text-transform: uppercase;\n color: var(--mj-status-error-text);\n margin-bottom: 6px;\n}\n\n.merge-delete-label i {\n margin-right: 4px;\n}\n\n.merge-delete-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 5px 0;\n font-size: 0.78rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.merge-delete-item:last-child {\n border-bottom: none;\n}\n\n.merge-delete-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.merge-delete-deps {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n/* Confirm footer */\n\n.merge-confirm-footer {\n padding: 14px 24px;\n border-top: 1px solid var(--mj-border-default);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 10px;\n}\n\n.cancel-btn {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n}\n\n.cancel-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.confirm-merge-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.confirm-merge-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.confirm-merge-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* ---- Responsive: Merge Confirm ---- */\n\n@media (max-width: 600px) {\n .merge-confirm-panel {\n width: 100%;\n }\n}\n"] }]
|
|
2676
|
+
}], null, { EmbeddedMode: [{
|
|
2677
|
+
type: Input
|
|
2678
|
+
}] }); })();
|
|
2679
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DuplicateDetectionResourceComponent, { className: "DuplicateDetectionResourceComponent", filePath: "src/AI/components/duplicates/duplicate-detection-resource.component.ts", lineNumber: 105 }); })();
|
|
2680
|
+
export function LoadDuplicateDetectionResource() {
|
|
2681
|
+
// Prevents tree-shaking
|
|
2682
|
+
}
|
|
2683
|
+
//# sourceMappingURL=duplicate-detection-resource.component.js.map
|