@memberjunction/ng-dashboards 5.22.0 → 5.24.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 +947 -64
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +7645 -430
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts +285 -6
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +2454 -277
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
- 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 +20 -2
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +419 -232
- package/dist/AI/components/vectors/vector-management-resource.component.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-share-dialog.component.js +9 -9
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +17 -17
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/Home/home-dashboard.component.js +4 -4
- package/dist/Home/home-dashboard.component.js.map +1 -1
- package/dist/Integration/components/activity/activity.component.d.ts.map +1 -1
- package/dist/Integration/components/activity/activity.component.js +1 -0
- package/dist/Integration/components/activity/activity.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +5 -4
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.d.ts.map +1 -1
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +247 -259
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -1
- package/dist/Integration/components/overview/overview.component.d.ts.map +1 -1
- package/dist/Integration/components/overview/overview.component.js +1 -0
- package/dist/Integration/components/overview/overview.component.js.map +1 -1
- package/dist/Integration/components/pipelines/pipelines.component.d.ts.map +1 -1
- package/dist/Integration/components/pipelines/pipelines.component.js +1 -0
- package/dist/Integration/components/pipelines/pipelines.component.js.map +1 -1
- package/dist/Integration/components/schedules/schedules.component.d.ts.map +1 -1
- package/dist/Integration/components/schedules/schedules.component.js +1 -0
- package/dist/Integration/components/schedules/schedules.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/analytics/analytics-resource.component.d.ts +411 -0
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +4266 -0
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts +140 -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 +780 -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 +8 -2
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +246 -195
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.d.ts +75 -0
- package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.js +601 -0
- package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.d.ts +93 -12
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.js +637 -107
- package/dist/KnowledgeHub/components/search/knowledge-search-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/index.d.ts +3 -0
- package/dist/KnowledgeHub/index.d.ts.map +1 -1
- package/dist/KnowledgeHub/index.js +3 -0
- package/dist/KnowledgeHub/index.js.map +1 -1
- 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/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/Testing/testing-dashboard.component.js +9 -10
- package/dist/Testing/testing-dashboard.component.js.map +1 -1
- package/dist/__tests__/analytics-resource.test.d.ts +2 -0
- package/dist/__tests__/analytics-resource.test.d.ts.map +1 -0
- package/dist/__tests__/analytics-resource.test.js +181 -0
- package/dist/__tests__/analytics-resource.test.js.map +1 -0
- package/dist/__tests__/scheduling.test.d.ts +2 -0
- package/dist/__tests__/scheduling.test.d.ts.map +1 -0
- package/dist/__tests__/scheduling.test.js +205 -0
- package/dist/__tests__/scheduling.test.js.map +1 -0
- 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 +20 -20
- package/dist/ai-dashboards.module.d.ts.map +1 -1
- package/dist/ai-dashboards.module.js +43 -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 +12 -18
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +15 -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 +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +2 -0
- 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/shared/entity-field-display.d.ts +44 -0
- package/dist/shared/entity-field-display.d.ts.map +1 -0
- package/dist/shared/entity-field-display.js +118 -0
- package/dist/shared/entity-field-display.js.map +1 -0
- package/dist/testing-dashboards.module.d.ts +7 -13
- package/dist/testing-dashboards.module.d.ts.map +1 -1
- package/dist/testing-dashboards.module.js +0 -27
- package/dist/testing-dashboards.module.js.map +1 -1
- package/package.json +48 -55
|
@@ -9,278 +9,1305 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
9
9
|
*
|
|
10
10
|
* Dashboard resource for reviewing duplicate detection results in a Kanban-style
|
|
11
11
|
* board with three columns: Pending Review, Approved, and Rejected.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Supports triggering new detection runs with real-time progress via
|
|
13
|
+
* GraphQL subscriptions.
|
|
14
14
|
*/
|
|
15
|
-
import { Component, ChangeDetectorRef, Input, inject } from '@angular/core';
|
|
15
|
+
import { Component, ChangeDetectorRef, Input, inject, ViewEncapsulation, HostListener } from '@angular/core';
|
|
16
16
|
import { Subject } from 'rxjs';
|
|
17
17
|
import { debounceTime, takeUntil } from 'rxjs/operators';
|
|
18
|
-
import { RunView } from '@memberjunction/core';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
18
|
+
import { CompositeKey, LogStatus, 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';
|
|
21
23
|
import * as i0 from "@angular/core";
|
|
22
24
|
import * as i1 from "@angular/forms";
|
|
23
25
|
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
24
|
-
const _forTrack0 = ($index, $item) => $item.
|
|
25
|
-
|
|
26
|
-
|
|
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);
|
|
27
35
|
i0.ɵɵtext(1);
|
|
28
36
|
i0.ɵɵelementEnd();
|
|
29
37
|
} if (rf & 2) {
|
|
30
|
-
const
|
|
31
|
-
i0.ɵɵproperty("value",
|
|
38
|
+
const doc_r1 = ctx.$implicit;
|
|
39
|
+
i0.ɵɵproperty("value", doc_r1.ID);
|
|
32
40
|
i0.ɵɵadvance();
|
|
33
|
-
i0.ɵɵ
|
|
41
|
+
i0.ɵɵtextInterpolate2("", doc_r1.Name, " (", doc_r1.EntityName, ")");
|
|
34
42
|
} }
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
i0.ɵɵ
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
function DuplicateDetectionResourceComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
44
|
+
i0.ɵɵelement(0, "i", 34);
|
|
45
|
+
i0.ɵɵtext(1, " Detecting... ");
|
|
46
|
+
} }
|
|
47
|
+
function DuplicateDetectionResourceComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
48
|
+
i0.ɵɵelement(0, "i", 35);
|
|
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", 41);
|
|
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", 36)(2, "span", 37);
|
|
62
|
+
i0.ɵɵelement(3, "i", 34);
|
|
63
|
+
i0.ɵɵtext(4);
|
|
64
|
+
i0.ɵɵelementEnd();
|
|
65
|
+
i0.ɵɵelementStart(5, "span", 38);
|
|
66
|
+
i0.ɵɵtext(6);
|
|
67
|
+
i0.ɵɵelementEnd()();
|
|
68
|
+
i0.ɵɵelementStart(7, "div", 39);
|
|
69
|
+
i0.ɵɵelement(8, "div", 40);
|
|
70
|
+
i0.ɵɵelementEnd();
|
|
71
|
+
i0.ɵɵconditionalCreate(9, DuplicateDetectionResourceComponent_Conditional_16_Conditional_9_Template, 2, 1, "span", 41);
|
|
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", 42)(2, "label", 43);
|
|
87
|
+
i0.ɵɵelement(3, "i", 44);
|
|
88
|
+
i0.ɵɵtext(4, " Potential Match ");
|
|
89
|
+
i0.ɵɵelementStart(5, "span", 45);
|
|
90
|
+
i0.ɵɵtext(6);
|
|
91
|
+
i0.ɵɵelementEnd()();
|
|
92
|
+
i0.ɵɵelementStart(7, "input", 46);
|
|
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", 47);
|
|
96
|
+
i0.ɵɵtext(9, "Score above which duplicates are flagged for review");
|
|
97
|
+
i0.ɵɵelementEnd()();
|
|
98
|
+
i0.ɵɵelementStart(10, "div", 42)(11, "label", 43);
|
|
99
|
+
i0.ɵɵelement(12, "i", 48);
|
|
100
|
+
i0.ɵɵtext(13, " Absolute Match ");
|
|
101
|
+
i0.ɵɵelementStart(14, "span", 45);
|
|
102
|
+
i0.ɵɵtext(15);
|
|
103
|
+
i0.ɵɵelementEnd()();
|
|
104
|
+
i0.ɵɵelementStart(16, "input", 46);
|
|
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", 47);
|
|
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", 49);
|
|
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", 50);
|
|
40
141
|
i0.ɵɵtext(2, " Clear Filters ");
|
|
41
142
|
i0.ɵɵelementEnd();
|
|
42
143
|
} }
|
|
43
|
-
function
|
|
44
|
-
i0.ɵɵelementStart(0, "div",
|
|
45
|
-
i0.ɵɵelement(1, "
|
|
144
|
+
function DuplicateDetectionResourceComponent_Conditional_62_Template(rf, ctx) { if (rf & 1) {
|
|
145
|
+
i0.ɵɵelementStart(0, "div", 28);
|
|
146
|
+
i0.ɵɵelement(1, "i", 51);
|
|
147
|
+
i0.ɵɵtext(2, " Merging is not available for this entity. Detection results are read-only. ");
|
|
148
|
+
i0.ɵɵelementEnd();
|
|
149
|
+
} }
|
|
150
|
+
function DuplicateDetectionResourceComponent_Conditional_63_Template(rf, ctx) { if (rf & 1) {
|
|
151
|
+
i0.ɵɵelementStart(0, "div", 29);
|
|
152
|
+
i0.ɵɵelement(1, "mj-loading", 52);
|
|
153
|
+
i0.ɵɵelementEnd();
|
|
154
|
+
} }
|
|
155
|
+
function DuplicateDetectionResourceComponent_Conditional_64_Template(rf, ctx) { if (rf & 1) {
|
|
156
|
+
i0.ɵɵelementStart(0, "div", 29);
|
|
157
|
+
i0.ɵɵelement(1, "mj-loading", 52);
|
|
46
158
|
i0.ɵɵelementEnd();
|
|
47
159
|
} }
|
|
48
|
-
function
|
|
49
|
-
i0.ɵɵelementStart(0, "div",
|
|
50
|
-
i0.ɵɵelement(1, "i",
|
|
51
|
-
i0.ɵɵelementStart(2, "p",
|
|
160
|
+
function DuplicateDetectionResourceComponent_Conditional_65_Template(rf, ctx) { if (rf & 1) {
|
|
161
|
+
i0.ɵɵelementStart(0, "div", 30);
|
|
162
|
+
i0.ɵɵelement(1, "i", 53);
|
|
163
|
+
i0.ɵɵelementStart(2, "p", 54);
|
|
52
164
|
i0.ɵɵtext(3, "No duplicate detection results found.");
|
|
53
165
|
i0.ɵɵelementEnd();
|
|
54
|
-
i0.ɵɵelementStart(4, "p",
|
|
55
|
-
i0.ɵɵtext(5, "
|
|
166
|
+
i0.ɵɵelementStart(4, "p", 55);
|
|
167
|
+
i0.ɵɵtext(5, "Select an entity document and click \"Run Detection\" to start.");
|
|
56
168
|
i0.ɵɵelementEnd()();
|
|
57
169
|
} }
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
i0.ɵɵ
|
|
61
|
-
i0.ɵɵtext(3);
|
|
170
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_10_Conditional_13_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
171
|
+
i0.ɵɵelementStart(0, "div", 86)(1, "span", 88);
|
|
172
|
+
i0.ɵɵtext(2);
|
|
62
173
|
i0.ɵɵelementEnd();
|
|
63
|
-
i0.ɵɵelementStart(
|
|
64
|
-
i0.ɵɵtext(
|
|
65
|
-
i0.ɵɵelementEnd()();
|
|
66
|
-
i0.ɵɵelementStart(6, "div", 47)(7, "div", 48);
|
|
67
|
-
i0.ɵɵelement(8, "i", 49);
|
|
68
|
-
i0.ɵɵelementStart(9, "span", 50);
|
|
69
|
-
i0.ɵɵtext(10);
|
|
174
|
+
i0.ɵɵelementStart(3, "span", 89);
|
|
175
|
+
i0.ɵɵtext(4);
|
|
70
176
|
i0.ɵɵelementEnd()();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
i0.ɵɵ
|
|
74
|
-
i0.ɵɵ
|
|
177
|
+
} if (rf & 2) {
|
|
178
|
+
const ms_r9 = ctx.$implicit;
|
|
179
|
+
i0.ɵɵadvance(2);
|
|
180
|
+
i0.ɵɵtextInterpolate1("", (ms_r9.Score * 100).toFixed(0), "%");
|
|
181
|
+
i0.ɵɵadvance(2);
|
|
182
|
+
i0.ɵɵtextInterpolate(ms_r9.Name);
|
|
183
|
+
} }
|
|
184
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_10_Conditional_13_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
185
|
+
i0.ɵɵelementStart(0, "div", 87);
|
|
186
|
+
i0.ɵɵtext(1);
|
|
187
|
+
i0.ɵɵelementEnd();
|
|
188
|
+
} if (rf & 2) {
|
|
189
|
+
const group_r8 = i0.ɵɵnextContext(2).$implicit;
|
|
190
|
+
i0.ɵɵadvance();
|
|
191
|
+
i0.ɵɵtextInterpolate1("+", group_r8.MatchCount - group_r8.TopMatchSummaries.length, " more");
|
|
192
|
+
} }
|
|
193
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_10_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
194
|
+
i0.ɵɵelementStart(0, "div", 77);
|
|
195
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_66_For_10_Conditional_13_For_2_Template, 5, 2, "div", 86, _forTrack2);
|
|
196
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_66_For_10_Conditional_13_Conditional_3_Template, 2, 1, "div", 87);
|
|
197
|
+
i0.ɵɵelementEnd();
|
|
198
|
+
} if (rf & 2) {
|
|
199
|
+
const group_r8 = i0.ɵɵnextContext().$implicit;
|
|
200
|
+
i0.ɵɵadvance();
|
|
201
|
+
i0.ɵɵrepeater(group_r8.TopMatchSummaries);
|
|
202
|
+
i0.ɵɵadvance(2);
|
|
203
|
+
i0.ɵɵconditional(group_r8.MatchCount > group_r8.TopMatchSummaries.length ? 3 : -1);
|
|
204
|
+
} }
|
|
205
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_10_Template(rf, ctx) { if (rf & 1) {
|
|
206
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
207
|
+
i0.ɵɵelementStart(0, "div", 68);
|
|
208
|
+
i0.ɵɵlistener("dragstart", function DuplicateDetectionResourceComponent_Conditional_66_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_66_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_66_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)); });
|
|
209
|
+
i0.ɵɵelementStart(1, "div", 69)(2, "div", 70)(3, "div", 71);
|
|
210
|
+
i0.ɵɵelement(4, "i");
|
|
211
|
+
i0.ɵɵelementEnd();
|
|
212
|
+
i0.ɵɵelementStart(5, "div", 72)(6, "div", 73);
|
|
213
|
+
i0.ɵɵtext(7);
|
|
214
|
+
i0.ɵɵelementEnd();
|
|
215
|
+
i0.ɵɵelementStart(8, "span", 74);
|
|
216
|
+
i0.ɵɵtext(9);
|
|
217
|
+
i0.ɵɵelementEnd()()();
|
|
218
|
+
i0.ɵɵelementStart(10, "span", 75);
|
|
219
|
+
i0.ɵɵtext(11);
|
|
75
220
|
i0.ɵɵelementEnd()();
|
|
76
|
-
i0.ɵɵelementStart(
|
|
77
|
-
i0.ɵɵ
|
|
78
|
-
i0.ɵɵelementStart(
|
|
79
|
-
i0.ɵɵ
|
|
221
|
+
i0.ɵɵelementStart(12, "div", 76);
|
|
222
|
+
i0.ɵɵconditionalCreate(13, DuplicateDetectionResourceComponent_Conditional_66_For_10_Conditional_13_Template, 4, 1, "div", 77);
|
|
223
|
+
i0.ɵɵelementStart(14, "div", 78)(15, "span", 79);
|
|
224
|
+
i0.ɵɵelement(16, "i", 80);
|
|
225
|
+
i0.ɵɵtext(17);
|
|
226
|
+
i0.ɵɵelementEnd();
|
|
227
|
+
i0.ɵɵelementStart(18, "span", 79);
|
|
228
|
+
i0.ɵɵelement(19, "i", 81);
|
|
229
|
+
i0.ɵɵtext(20);
|
|
80
230
|
i0.ɵɵelementEnd()()();
|
|
81
|
-
i0.ɵɵelementStart(
|
|
82
|
-
i0.ɵɵlistener("click", function
|
|
83
|
-
i0.ɵɵelement(
|
|
84
|
-
i0.ɵɵtext(
|
|
85
|
-
i0.ɵɵelementEnd();
|
|
86
|
-
i0.ɵɵelementStart(
|
|
87
|
-
i0.ɵɵlistener("click", function
|
|
88
|
-
i0.ɵɵelement(
|
|
89
|
-
i0.ɵɵtext(
|
|
231
|
+
i0.ɵɵelementStart(21, "div", 82)(22, "button", 83);
|
|
232
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_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()); });
|
|
233
|
+
i0.ɵɵelement(23, "i", 84);
|
|
234
|
+
i0.ɵɵtext(24, " Approve ");
|
|
235
|
+
i0.ɵɵelementEnd();
|
|
236
|
+
i0.ɵɵelementStart(25, "button", 85);
|
|
237
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_66_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()); });
|
|
238
|
+
i0.ɵɵelement(26, "i", 50);
|
|
239
|
+
i0.ɵɵtext(27, " Reject ");
|
|
90
240
|
i0.ɵɵelementEnd()()();
|
|
91
241
|
} if (rf & 2) {
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
i0.ɵɵadvance(
|
|
95
|
-
i0.ɵɵ
|
|
242
|
+
const group_r8 = ctx.$implicit;
|
|
243
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
244
|
+
i0.ɵɵadvance(4);
|
|
245
|
+
i0.ɵɵclassMap(group_r8.EntityIcon);
|
|
246
|
+
i0.ɵɵadvance(2);
|
|
247
|
+
i0.ɵɵproperty("title", group_r8.RecordName);
|
|
96
248
|
i0.ɵɵadvance();
|
|
97
|
-
i0.ɵɵ
|
|
249
|
+
i0.ɵɵtextInterpolate(group_r8.RecordName);
|
|
250
|
+
i0.ɵɵadvance(2);
|
|
251
|
+
i0.ɵɵtextInterpolate(group_r8.EntityName);
|
|
98
252
|
i0.ɵɵadvance();
|
|
99
|
-
i0.ɵɵ
|
|
100
|
-
i0.ɵɵadvance(4);
|
|
101
|
-
i0.ɵɵproperty("title", group_r5.RecordId);
|
|
253
|
+
i0.ɵɵclassMap(ctx_r1.GetScoreClass(group_r8.HighestScore));
|
|
102
254
|
i0.ɵɵadvance();
|
|
103
|
-
i0.ɵɵ
|
|
104
|
-
i0.ɵɵadvance(
|
|
105
|
-
i0.ɵɵ
|
|
255
|
+
i0.ɵɵtextInterpolate1(" ", (group_r8.HighestScore * 100).toFixed(0), "% ");
|
|
256
|
+
i0.ɵɵadvance(2);
|
|
257
|
+
i0.ɵɵconditional(group_r8.TopMatchSummaries.length > 0 ? 13 : -1);
|
|
106
258
|
i0.ɵɵadvance(4);
|
|
107
|
-
i0.ɵɵ
|
|
259
|
+
i0.ɵɵtextInterpolate2(" ", group_r8.MatchCount, " match", group_r8.MatchCount !== 1 ? "es" : "", " ");
|
|
260
|
+
i0.ɵɵadvance(3);
|
|
261
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatDate(group_r8.MatchedAt), " ");
|
|
108
262
|
i0.ɵɵadvance(2);
|
|
109
|
-
i0.ɵɵproperty("disabled",
|
|
263
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving);
|
|
110
264
|
i0.ɵɵadvance(3);
|
|
111
|
-
i0.ɵɵproperty("disabled",
|
|
265
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving);
|
|
112
266
|
} }
|
|
113
|
-
function
|
|
114
|
-
i0.ɵɵelementStart(0, "div",
|
|
115
|
-
i0.ɵɵelement(1, "i",
|
|
267
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
268
|
+
i0.ɵɵelementStart(0, "div", 63);
|
|
269
|
+
i0.ɵɵelement(1, "i", 65);
|
|
116
270
|
i0.ɵɵelementStart(2, "span");
|
|
117
271
|
i0.ɵɵtext(3, "No pending items");
|
|
118
272
|
i0.ɵɵelementEnd()();
|
|
119
273
|
} }
|
|
120
|
-
function
|
|
121
|
-
i0.ɵɵelementStart(0, "div",
|
|
122
|
-
i0.ɵɵtext(
|
|
274
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_21_Conditional_13_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
275
|
+
i0.ɵɵelementStart(0, "div", 86)(1, "span", 88);
|
|
276
|
+
i0.ɵɵtext(2);
|
|
123
277
|
i0.ɵɵelementEnd();
|
|
124
|
-
i0.ɵɵelementStart(
|
|
125
|
-
i0.ɵɵtext(
|
|
278
|
+
i0.ɵɵelementStart(3, "span", 89);
|
|
279
|
+
i0.ɵɵtext(4);
|
|
126
280
|
i0.ɵɵelementEnd()();
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
i0.ɵɵ
|
|
130
|
-
i0.ɵɵ
|
|
131
|
-
i0.ɵɵ
|
|
132
|
-
i0.ɵɵ
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
i0.ɵɵ
|
|
281
|
+
} if (rf & 2) {
|
|
282
|
+
const ms_r12 = ctx.$implicit;
|
|
283
|
+
i0.ɵɵadvance(2);
|
|
284
|
+
i0.ɵɵtextInterpolate1("", (ms_r12.Score * 100).toFixed(0), "%");
|
|
285
|
+
i0.ɵɵadvance(2);
|
|
286
|
+
i0.ɵɵtextInterpolate(ms_r12.Name);
|
|
287
|
+
} }
|
|
288
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_21_Conditional_13_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
289
|
+
i0.ɵɵelementStart(0, "div", 87);
|
|
290
|
+
i0.ɵɵtext(1);
|
|
291
|
+
i0.ɵɵelementEnd();
|
|
292
|
+
} if (rf & 2) {
|
|
293
|
+
const group_r11 = i0.ɵɵnextContext(2).$implicit;
|
|
294
|
+
i0.ɵɵadvance();
|
|
295
|
+
i0.ɵɵtextInterpolate1("+", group_r11.MatchCount - group_r11.TopMatchSummaries.length, " more");
|
|
296
|
+
} }
|
|
297
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_21_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
298
|
+
i0.ɵɵelementStart(0, "div", 77);
|
|
299
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_66_For_21_Conditional_13_For_2_Template, 5, 2, "div", 86, _forTrack2);
|
|
300
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_66_For_21_Conditional_13_Conditional_3_Template, 2, 1, "div", 87);
|
|
301
|
+
i0.ɵɵelementEnd();
|
|
302
|
+
} if (rf & 2) {
|
|
303
|
+
const group_r11 = i0.ɵɵnextContext().$implicit;
|
|
304
|
+
i0.ɵɵadvance();
|
|
305
|
+
i0.ɵɵrepeater(group_r11.TopMatchSummaries);
|
|
306
|
+
i0.ɵɵadvance(2);
|
|
307
|
+
i0.ɵɵconditional(group_r11.MatchCount > group_r11.TopMatchSummaries.length ? 3 : -1);
|
|
308
|
+
} }
|
|
309
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_21_Template(rf, ctx) { if (rf & 1) {
|
|
310
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
311
|
+
i0.ɵɵelementStart(0, "div", 68);
|
|
312
|
+
i0.ɵɵlistener("dragstart", function DuplicateDetectionResourceComponent_Conditional_66_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_66_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_66_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)); });
|
|
313
|
+
i0.ɵɵelementStart(1, "div", 69)(2, "div", 70)(3, "div", 71);
|
|
314
|
+
i0.ɵɵelement(4, "i");
|
|
315
|
+
i0.ɵɵelementEnd();
|
|
316
|
+
i0.ɵɵelementStart(5, "div", 72)(6, "div", 73);
|
|
317
|
+
i0.ɵɵtext(7);
|
|
318
|
+
i0.ɵɵelementEnd();
|
|
319
|
+
i0.ɵɵelementStart(8, "span", 74);
|
|
320
|
+
i0.ɵɵtext(9);
|
|
321
|
+
i0.ɵɵelementEnd()()();
|
|
322
|
+
i0.ɵɵelementStart(10, "span", 75);
|
|
323
|
+
i0.ɵɵtext(11);
|
|
136
324
|
i0.ɵɵelementEnd()();
|
|
137
|
-
i0.ɵɵelementStart(
|
|
138
|
-
i0.ɵɵ
|
|
139
|
-
i0.ɵɵelementStart(
|
|
140
|
-
i0.ɵɵ
|
|
325
|
+
i0.ɵɵelementStart(12, "div", 76);
|
|
326
|
+
i0.ɵɵconditionalCreate(13, DuplicateDetectionResourceComponent_Conditional_66_For_21_Conditional_13_Template, 4, 1, "div", 77);
|
|
327
|
+
i0.ɵɵelementStart(14, "div", 78)(15, "span", 79);
|
|
328
|
+
i0.ɵɵelement(16, "i", 80);
|
|
329
|
+
i0.ɵɵtext(17);
|
|
330
|
+
i0.ɵɵelementEnd();
|
|
331
|
+
i0.ɵɵelementStart(18, "span", 79);
|
|
332
|
+
i0.ɵɵelement(19, "i", 81);
|
|
333
|
+
i0.ɵɵtext(20);
|
|
141
334
|
i0.ɵɵelementEnd()()()();
|
|
142
335
|
} if (rf & 2) {
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
i0.ɵɵadvance(
|
|
146
|
-
i0.ɵɵ
|
|
336
|
+
const group_r11 = ctx.$implicit;
|
|
337
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
338
|
+
i0.ɵɵadvance(4);
|
|
339
|
+
i0.ɵɵclassMap(group_r11.EntityIcon);
|
|
340
|
+
i0.ɵɵadvance(2);
|
|
341
|
+
i0.ɵɵproperty("title", group_r11.RecordName);
|
|
147
342
|
i0.ɵɵadvance();
|
|
148
|
-
i0.ɵɵ
|
|
343
|
+
i0.ɵɵtextInterpolate(group_r11.RecordName);
|
|
344
|
+
i0.ɵɵadvance(2);
|
|
345
|
+
i0.ɵɵtextInterpolate(group_r11.EntityName);
|
|
149
346
|
i0.ɵɵadvance();
|
|
150
|
-
i0.ɵɵ
|
|
151
|
-
i0.ɵɵadvance(4);
|
|
152
|
-
i0.ɵɵproperty("title", group_r6.RecordId);
|
|
347
|
+
i0.ɵɵclassMap(ctx_r1.GetScoreClass(group_r11.HighestScore));
|
|
153
348
|
i0.ɵɵadvance();
|
|
154
|
-
i0.ɵɵ
|
|
155
|
-
i0.ɵɵadvance(
|
|
156
|
-
i0.ɵɵ
|
|
349
|
+
i0.ɵɵtextInterpolate1(" ", (group_r11.HighestScore * 100).toFixed(0), "% ");
|
|
350
|
+
i0.ɵɵadvance(2);
|
|
351
|
+
i0.ɵɵconditional(group_r11.TopMatchSummaries.length > 0 ? 13 : -1);
|
|
157
352
|
i0.ɵɵadvance(4);
|
|
158
|
-
i0.ɵɵ
|
|
353
|
+
i0.ɵɵtextInterpolate2(" ", group_r11.MatchCount, " match", group_r11.MatchCount !== 1 ? "es" : "", " ");
|
|
354
|
+
i0.ɵɵadvance(3);
|
|
355
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatDate(group_r11.MatchedAt), " ");
|
|
159
356
|
} }
|
|
160
|
-
function
|
|
161
|
-
i0.ɵɵelementStart(0, "div",
|
|
162
|
-
i0.ɵɵelement(1, "i",
|
|
357
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
358
|
+
i0.ɵɵelementStart(0, "div", 63);
|
|
359
|
+
i0.ɵɵelement(1, "i", 90);
|
|
163
360
|
i0.ɵɵelementStart(2, "span");
|
|
164
361
|
i0.ɵɵtext(3, "No approved items");
|
|
165
362
|
i0.ɵɵelementEnd()();
|
|
166
363
|
} }
|
|
167
|
-
function
|
|
168
|
-
i0.ɵɵelementStart(0, "div",
|
|
169
|
-
i0.ɵɵtext(
|
|
364
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_32_Conditional_13_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
365
|
+
i0.ɵɵelementStart(0, "div", 86)(1, "span", 88);
|
|
366
|
+
i0.ɵɵtext(2);
|
|
170
367
|
i0.ɵɵelementEnd();
|
|
171
|
-
i0.ɵɵelementStart(
|
|
172
|
-
i0.ɵɵtext(
|
|
368
|
+
i0.ɵɵelementStart(3, "span", 89);
|
|
369
|
+
i0.ɵɵtext(4);
|
|
173
370
|
i0.ɵɵelementEnd()();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
i0.ɵɵ
|
|
177
|
-
i0.ɵɵ
|
|
178
|
-
i0.ɵɵ
|
|
179
|
-
i0.ɵɵ
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
i0.ɵɵ
|
|
371
|
+
} if (rf & 2) {
|
|
372
|
+
const ms_r15 = ctx.$implicit;
|
|
373
|
+
i0.ɵɵadvance(2);
|
|
374
|
+
i0.ɵɵtextInterpolate1("", (ms_r15.Score * 100).toFixed(0), "%");
|
|
375
|
+
i0.ɵɵadvance(2);
|
|
376
|
+
i0.ɵɵtextInterpolate(ms_r15.Name);
|
|
377
|
+
} }
|
|
378
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_32_Conditional_13_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
379
|
+
i0.ɵɵelementStart(0, "div", 87);
|
|
380
|
+
i0.ɵɵtext(1);
|
|
381
|
+
i0.ɵɵelementEnd();
|
|
382
|
+
} if (rf & 2) {
|
|
383
|
+
const group_r14 = i0.ɵɵnextContext(2).$implicit;
|
|
384
|
+
i0.ɵɵadvance();
|
|
385
|
+
i0.ɵɵtextInterpolate1("+", group_r14.MatchCount - group_r14.TopMatchSummaries.length, " more");
|
|
386
|
+
} }
|
|
387
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_32_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
388
|
+
i0.ɵɵelementStart(0, "div", 77);
|
|
389
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_66_For_32_Conditional_13_For_2_Template, 5, 2, "div", 86, _forTrack2);
|
|
390
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_66_For_32_Conditional_13_Conditional_3_Template, 2, 1, "div", 87);
|
|
391
|
+
i0.ɵɵelementEnd();
|
|
392
|
+
} if (rf & 2) {
|
|
393
|
+
const group_r14 = i0.ɵɵnextContext().$implicit;
|
|
394
|
+
i0.ɵɵadvance();
|
|
395
|
+
i0.ɵɵrepeater(group_r14.TopMatchSummaries);
|
|
396
|
+
i0.ɵɵadvance(2);
|
|
397
|
+
i0.ɵɵconditional(group_r14.MatchCount > group_r14.TopMatchSummaries.length ? 3 : -1);
|
|
398
|
+
} }
|
|
399
|
+
function DuplicateDetectionResourceComponent_Conditional_66_For_32_Template(rf, ctx) { if (rf & 1) {
|
|
400
|
+
const _r13 = i0.ɵɵgetCurrentView();
|
|
401
|
+
i0.ɵɵelementStart(0, "div", 68);
|
|
402
|
+
i0.ɵɵlistener("dragstart", function DuplicateDetectionResourceComponent_Conditional_66_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_66_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_66_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)); });
|
|
403
|
+
i0.ɵɵelementStart(1, "div", 69)(2, "div", 70)(3, "div", 71);
|
|
404
|
+
i0.ɵɵelement(4, "i");
|
|
405
|
+
i0.ɵɵelementEnd();
|
|
406
|
+
i0.ɵɵelementStart(5, "div", 72)(6, "div", 73);
|
|
407
|
+
i0.ɵɵtext(7);
|
|
408
|
+
i0.ɵɵelementEnd();
|
|
409
|
+
i0.ɵɵelementStart(8, "span", 74);
|
|
410
|
+
i0.ɵɵtext(9);
|
|
411
|
+
i0.ɵɵelementEnd()()();
|
|
412
|
+
i0.ɵɵelementStart(10, "span", 75);
|
|
413
|
+
i0.ɵɵtext(11);
|
|
183
414
|
i0.ɵɵelementEnd()();
|
|
184
|
-
i0.ɵɵelementStart(
|
|
185
|
-
i0.ɵɵ
|
|
186
|
-
i0.ɵɵelementStart(
|
|
187
|
-
i0.ɵɵ
|
|
415
|
+
i0.ɵɵelementStart(12, "div", 76);
|
|
416
|
+
i0.ɵɵconditionalCreate(13, DuplicateDetectionResourceComponent_Conditional_66_For_32_Conditional_13_Template, 4, 1, "div", 77);
|
|
417
|
+
i0.ɵɵelementStart(14, "div", 78)(15, "span", 79);
|
|
418
|
+
i0.ɵɵelement(16, "i", 80);
|
|
419
|
+
i0.ɵɵtext(17);
|
|
420
|
+
i0.ɵɵelementEnd();
|
|
421
|
+
i0.ɵɵelementStart(18, "span", 79);
|
|
422
|
+
i0.ɵɵelement(19, "i", 81);
|
|
423
|
+
i0.ɵɵtext(20);
|
|
188
424
|
i0.ɵɵelementEnd()()()();
|
|
189
425
|
} if (rf & 2) {
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
i0.ɵɵadvance(
|
|
193
|
-
i0.ɵɵ
|
|
426
|
+
const group_r14 = ctx.$implicit;
|
|
427
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
428
|
+
i0.ɵɵadvance(4);
|
|
429
|
+
i0.ɵɵclassMap(group_r14.EntityIcon);
|
|
430
|
+
i0.ɵɵadvance(2);
|
|
431
|
+
i0.ɵɵproperty("title", group_r14.RecordName);
|
|
194
432
|
i0.ɵɵadvance();
|
|
195
|
-
i0.ɵɵ
|
|
433
|
+
i0.ɵɵtextInterpolate(group_r14.RecordName);
|
|
434
|
+
i0.ɵɵadvance(2);
|
|
435
|
+
i0.ɵɵtextInterpolate(group_r14.EntityName);
|
|
196
436
|
i0.ɵɵadvance();
|
|
197
|
-
i0.ɵɵ
|
|
198
|
-
i0.ɵɵadvance(4);
|
|
199
|
-
i0.ɵɵproperty("title", group_r7.RecordId);
|
|
437
|
+
i0.ɵɵclassMap(ctx_r1.GetScoreClass(group_r14.HighestScore));
|
|
200
438
|
i0.ɵɵadvance();
|
|
201
|
-
i0.ɵɵ
|
|
202
|
-
i0.ɵɵadvance(
|
|
203
|
-
i0.ɵɵ
|
|
439
|
+
i0.ɵɵtextInterpolate1(" ", (group_r14.HighestScore * 100).toFixed(0), "% ");
|
|
440
|
+
i0.ɵɵadvance(2);
|
|
441
|
+
i0.ɵɵconditional(group_r14.TopMatchSummaries.length > 0 ? 13 : -1);
|
|
204
442
|
i0.ɵɵadvance(4);
|
|
205
|
-
i0.ɵɵ
|
|
443
|
+
i0.ɵɵtextInterpolate2(" ", group_r14.MatchCount, " match", group_r14.MatchCount !== 1 ? "es" : "", " ");
|
|
444
|
+
i0.ɵɵadvance(3);
|
|
445
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatDate(group_r14.MatchedAt), " ");
|
|
206
446
|
} }
|
|
207
|
-
function
|
|
208
|
-
i0.ɵɵelementStart(0, "div",
|
|
209
|
-
i0.ɵɵelement(1, "i",
|
|
447
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Conditional_33_Template(rf, ctx) { if (rf & 1) {
|
|
448
|
+
i0.ɵɵelementStart(0, "div", 63);
|
|
449
|
+
i0.ɵɵelement(1, "i", 90);
|
|
210
450
|
i0.ɵɵelementStart(2, "span");
|
|
211
451
|
i0.ɵɵtext(3, "No rejected items");
|
|
212
452
|
i0.ɵɵelementEnd()();
|
|
213
453
|
} }
|
|
214
|
-
function
|
|
215
|
-
|
|
216
|
-
i0.ɵɵ
|
|
217
|
-
i0.ɵɵ
|
|
454
|
+
function DuplicateDetectionResourceComponent_Conditional_66_Template(rf, ctx) { if (rf & 1) {
|
|
455
|
+
const _r6 = i0.ɵɵgetCurrentView();
|
|
456
|
+
i0.ɵɵelementStart(0, "div", 31)(1, "div", 56)(2, "div", 57);
|
|
457
|
+
i0.ɵɵelement(3, "i", 58);
|
|
458
|
+
i0.ɵɵelementStart(4, "span", 59);
|
|
218
459
|
i0.ɵɵtext(5, "Pending Review");
|
|
219
460
|
i0.ɵɵelementEnd();
|
|
220
|
-
i0.ɵɵelementStart(6, "span",
|
|
461
|
+
i0.ɵɵelementStart(6, "span", 60);
|
|
221
462
|
i0.ɵɵtext(7);
|
|
222
463
|
i0.ɵɵelementEnd()();
|
|
223
|
-
i0.ɵɵelementStart(8, "div",
|
|
224
|
-
i0.ɵɵ
|
|
225
|
-
i0.ɵɵ
|
|
464
|
+
i0.ɵɵelementStart(8, "div", 61);
|
|
465
|
+
i0.ɵɵlistener("dragover", function DuplicateDetectionResourceComponent_Conditional_66_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_66_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_66_Template_div_drop_8_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDrop($event, "Pending")); });
|
|
466
|
+
i0.ɵɵrepeaterCreate(9, DuplicateDetectionResourceComponent_Conditional_66_For_10_Template, 28, 14, "div", 62, _forTrack1);
|
|
467
|
+
i0.ɵɵconditionalCreate(11, DuplicateDetectionResourceComponent_Conditional_66_Conditional_11_Template, 4, 0, "div", 63);
|
|
226
468
|
i0.ɵɵelementEnd()();
|
|
227
|
-
i0.ɵɵelementStart(12, "div",
|
|
228
|
-
i0.ɵɵelement(14, "i",
|
|
229
|
-
i0.ɵɵelementStart(15, "span",
|
|
469
|
+
i0.ɵɵelementStart(12, "div", 56)(13, "div", 64);
|
|
470
|
+
i0.ɵɵelement(14, "i", 65);
|
|
471
|
+
i0.ɵɵelementStart(15, "span", 59);
|
|
230
472
|
i0.ɵɵtext(16, "Approved");
|
|
231
473
|
i0.ɵɵelementEnd();
|
|
232
|
-
i0.ɵɵelementStart(17, "span",
|
|
474
|
+
i0.ɵɵelementStart(17, "span", 60);
|
|
233
475
|
i0.ɵɵtext(18);
|
|
234
476
|
i0.ɵɵelementEnd()();
|
|
235
|
-
i0.ɵɵelementStart(19, "div",
|
|
236
|
-
i0.ɵɵ
|
|
237
|
-
i0.ɵɵ
|
|
477
|
+
i0.ɵɵelementStart(19, "div", 61);
|
|
478
|
+
i0.ɵɵlistener("dragover", function DuplicateDetectionResourceComponent_Conditional_66_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_66_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_66_Template_div_drop_19_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDrop($event, "Approved")); });
|
|
479
|
+
i0.ɵɵrepeaterCreate(20, DuplicateDetectionResourceComponent_Conditional_66_For_21_Template, 21, 12, "div", 62, _forTrack1);
|
|
480
|
+
i0.ɵɵconditionalCreate(22, DuplicateDetectionResourceComponent_Conditional_66_Conditional_22_Template, 4, 0, "div", 63);
|
|
238
481
|
i0.ɵɵelementEnd()();
|
|
239
|
-
i0.ɵɵelementStart(23, "div",
|
|
240
|
-
i0.ɵɵelement(25, "i",
|
|
241
|
-
i0.ɵɵelementStart(26, "span",
|
|
482
|
+
i0.ɵɵelementStart(23, "div", 56)(24, "div", 66);
|
|
483
|
+
i0.ɵɵelement(25, "i", 67);
|
|
484
|
+
i0.ɵɵelementStart(26, "span", 59);
|
|
242
485
|
i0.ɵɵtext(27, "Rejected");
|
|
243
486
|
i0.ɵɵelementEnd();
|
|
244
|
-
i0.ɵɵelementStart(28, "span",
|
|
487
|
+
i0.ɵɵelementStart(28, "span", 60);
|
|
245
488
|
i0.ɵɵtext(29);
|
|
246
489
|
i0.ɵɵelementEnd()();
|
|
247
|
-
i0.ɵɵelementStart(30, "div",
|
|
248
|
-
i0.ɵɵ
|
|
249
|
-
i0.ɵɵ
|
|
490
|
+
i0.ɵɵelementStart(30, "div", 61);
|
|
491
|
+
i0.ɵɵlistener("dragover", function DuplicateDetectionResourceComponent_Conditional_66_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_66_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_66_Template_div_drop_30_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDrop($event, "Rejected")); });
|
|
492
|
+
i0.ɵɵrepeaterCreate(31, DuplicateDetectionResourceComponent_Conditional_66_For_32_Template, 21, 12, "div", 62, _forTrack1);
|
|
493
|
+
i0.ɵɵconditionalCreate(33, DuplicateDetectionResourceComponent_Conditional_66_Conditional_33_Template, 4, 0, "div", 63);
|
|
250
494
|
i0.ɵɵelementEnd()()();
|
|
251
495
|
} if (rf & 2) {
|
|
252
|
-
const
|
|
253
|
-
i0.ɵɵadvance(
|
|
254
|
-
i0.ɵɵ
|
|
496
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
497
|
+
i0.ɵɵadvance();
|
|
498
|
+
i0.ɵɵclassProp("drop-target-active", ctx_r1.DragOverColumn === "Pending" && (ctx_r1.DraggedGroup == null ? null : ctx_r1.DraggedGroup.ApprovalStatus) !== "Pending");
|
|
499
|
+
i0.ɵɵadvance(6);
|
|
500
|
+
i0.ɵɵtextInterpolate(ctx_r1.PendingCount);
|
|
255
501
|
i0.ɵɵadvance(2);
|
|
256
|
-
i0.ɵɵrepeater(
|
|
502
|
+
i0.ɵɵrepeater(ctx_r1.PendingGroups);
|
|
257
503
|
i0.ɵɵadvance(2);
|
|
258
|
-
i0.ɵɵconditional(
|
|
259
|
-
i0.ɵɵadvance(
|
|
260
|
-
i0.ɵɵ
|
|
504
|
+
i0.ɵɵconditional(ctx_r1.PendingGroups.length === 0 ? 11 : -1);
|
|
505
|
+
i0.ɵɵadvance();
|
|
506
|
+
i0.ɵɵclassProp("drop-target-active", ctx_r1.DragOverColumn === "Approved" && (ctx_r1.DraggedGroup == null ? null : ctx_r1.DraggedGroup.ApprovalStatus) !== "Approved");
|
|
507
|
+
i0.ɵɵadvance(6);
|
|
508
|
+
i0.ɵɵtextInterpolate(ctx_r1.ApprovedCount);
|
|
509
|
+
i0.ɵɵadvance(2);
|
|
510
|
+
i0.ɵɵrepeater(ctx_r1.ApprovedGroups);
|
|
511
|
+
i0.ɵɵadvance(2);
|
|
512
|
+
i0.ɵɵconditional(ctx_r1.ApprovedGroups.length === 0 ? 22 : -1);
|
|
513
|
+
i0.ɵɵadvance();
|
|
514
|
+
i0.ɵɵclassProp("drop-target-active", ctx_r1.DragOverColumn === "Rejected" && (ctx_r1.DraggedGroup == null ? null : ctx_r1.DraggedGroup.ApprovalStatus) !== "Rejected");
|
|
515
|
+
i0.ɵɵadvance(6);
|
|
516
|
+
i0.ɵɵtextInterpolate(ctx_r1.RejectedCount);
|
|
517
|
+
i0.ɵɵadvance(2);
|
|
518
|
+
i0.ɵɵrepeater(ctx_r1.RejectedGroups);
|
|
519
|
+
i0.ɵɵadvance(2);
|
|
520
|
+
i0.ɵɵconditional(ctx_r1.RejectedGroups.length === 0 ? 33 : -1);
|
|
521
|
+
} }
|
|
522
|
+
function DuplicateDetectionResourceComponent_Conditional_67_Template(rf, ctx) { if (rf & 1) {
|
|
523
|
+
i0.ɵɵelementStart(0, "div", 32);
|
|
524
|
+
i0.ɵɵelement(1, "mj-loading", 91);
|
|
525
|
+
i0.ɵɵelementEnd();
|
|
526
|
+
} }
|
|
527
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
528
|
+
i0.ɵɵelementStart(0, "div", 104);
|
|
529
|
+
i0.ɵɵelement(1, "mj-loading", 130);
|
|
530
|
+
i0.ɵɵelementEnd();
|
|
531
|
+
} }
|
|
532
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_44_Template(rf, ctx) { if (rf & 1) {
|
|
533
|
+
i0.ɵɵelementStart(0, "span", 120);
|
|
534
|
+
i0.ɵɵelement(1, "i", 131);
|
|
535
|
+
i0.ɵɵtext(2, " Most deps");
|
|
536
|
+
i0.ɵɵelementEnd();
|
|
537
|
+
} }
|
|
538
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_For_2_Conditional_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
539
|
+
i0.ɵɵelementStart(0, "div", 140);
|
|
540
|
+
i0.ɵɵelement(1, "i", 34);
|
|
541
|
+
i0.ɵɵtext(2, " Loading...");
|
|
542
|
+
i0.ɵɵelementEnd();
|
|
543
|
+
} }
|
|
544
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_For_2_Conditional_7_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
545
|
+
const _r20 = i0.ɵɵgetCurrentView();
|
|
546
|
+
i0.ɵɵelementStart(0, "div", 142);
|
|
547
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_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()); });
|
|
548
|
+
i0.ɵɵelement(1, "i", 143);
|
|
549
|
+
i0.ɵɵelementStart(2, "span", 144);
|
|
550
|
+
i0.ɵɵtext(3);
|
|
551
|
+
i0.ɵɵelementEnd()();
|
|
552
|
+
} if (rf & 2) {
|
|
553
|
+
const record_r21 = ctx.$implicit;
|
|
554
|
+
i0.ɵɵadvance(3);
|
|
555
|
+
i0.ɵɵtextInterpolate(record_r21.Name);
|
|
556
|
+
} }
|
|
557
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_For_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
558
|
+
i0.ɵɵelementStart(0, "div", 139);
|
|
559
|
+
i0.ɵɵconditionalCreate(1, DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_For_2_Conditional_7_Conditional_1_Template, 3, 0, "div", 140);
|
|
560
|
+
i0.ɵɵrepeaterCreate(2, DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_For_2_Conditional_7_For_3_Template, 4, 1, "div", 141, i0.ɵɵrepeaterTrackByIndex);
|
|
561
|
+
i0.ɵɵelementEnd();
|
|
562
|
+
} if (rf & 2) {
|
|
563
|
+
const dep_r19 = i0.ɵɵnextContext().$implicit;
|
|
564
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
565
|
+
i0.ɵɵadvance();
|
|
566
|
+
i0.ɵɵconditional(ctx_r1.IsDepRecordsLoading(0, dep_r19.Entity) ? 1 : -1);
|
|
567
|
+
i0.ɵɵadvance();
|
|
568
|
+
i0.ɵɵrepeater(ctx_r1.GetDepRecords(0, dep_r19.Entity));
|
|
569
|
+
} }
|
|
570
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
571
|
+
const _r18 = i0.ɵɵgetCurrentView();
|
|
572
|
+
i0.ɵɵelementStart(0, "div", 134)(1, "div", 135);
|
|
573
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_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()); });
|
|
574
|
+
i0.ɵɵelementStart(2, "span", 136);
|
|
575
|
+
i0.ɵɵelement(3, "i", 137);
|
|
576
|
+
i0.ɵɵtext(4);
|
|
577
|
+
i0.ɵɵelementEnd();
|
|
578
|
+
i0.ɵɵelementStart(5, "span", 138);
|
|
579
|
+
i0.ɵɵtext(6);
|
|
580
|
+
i0.ɵɵelementEnd()();
|
|
581
|
+
i0.ɵɵconditionalCreate(7, DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_For_2_Conditional_7_Template, 4, 1, "div", 139);
|
|
582
|
+
i0.ɵɵelementEnd();
|
|
583
|
+
} if (rf & 2) {
|
|
584
|
+
const dep_r19 = ctx.$implicit;
|
|
585
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
586
|
+
i0.ɵɵadvance(3);
|
|
587
|
+
i0.ɵɵclassProp("fa-chevron-right", !ctx_r1.IsDepEntityGroupExpanded(0, dep_r19.Entity))("fa-chevron-down", ctx_r1.IsDepEntityGroupExpanded(0, dep_r19.Entity));
|
|
588
|
+
i0.ɵɵadvance();
|
|
589
|
+
i0.ɵɵtextInterpolate1(" ", dep_r19.Entity, " ");
|
|
590
|
+
i0.ɵɵadvance(2);
|
|
591
|
+
i0.ɵɵtextInterpolate(dep_r19.Count);
|
|
592
|
+
i0.ɵɵadvance();
|
|
593
|
+
i0.ɵɵconditional(ctx_r1.IsDepEntityGroupExpanded(0, dep_r19.Entity) ? 7 : -1);
|
|
594
|
+
} }
|
|
595
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
596
|
+
i0.ɵɵelementStart(0, "div", 133);
|
|
597
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_For_2_Template, 8, 7, "div", 134, _forTrack5);
|
|
598
|
+
i0.ɵɵelementEnd();
|
|
599
|
+
} if (rf & 2) {
|
|
600
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
601
|
+
i0.ɵɵadvance();
|
|
602
|
+
i0.ɵɵrepeater(ctx_r1.GetGroupedDeps(0));
|
|
603
|
+
} }
|
|
604
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Template(rf, ctx) { if (rf & 1) {
|
|
605
|
+
const _r17 = i0.ɵɵgetCurrentView();
|
|
606
|
+
i0.ɵɵelementStart(0, "div", 132);
|
|
607
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleDepsExpanded(0)); });
|
|
608
|
+
i0.ɵɵelementStart(1, "span");
|
|
609
|
+
i0.ɵɵtext(2);
|
|
610
|
+
i0.ɵɵelementEnd();
|
|
611
|
+
i0.ɵɵelement(3, "i", 115);
|
|
612
|
+
i0.ɵɵelementEnd();
|
|
613
|
+
i0.ɵɵconditionalCreate(4, DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Conditional_4_Template, 3, 0, "div", 133);
|
|
614
|
+
} if (rf & 2) {
|
|
615
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
616
|
+
i0.ɵɵadvance(2);
|
|
617
|
+
i0.ɵɵtextInterpolate(ctx_r1.IsDepsExpanded(0) ? "Hide details" : "Show details");
|
|
618
|
+
i0.ɵɵadvance();
|
|
619
|
+
i0.ɵɵclassProp("fa-chevron-down", !ctx_r1.IsDepsExpanded(0))("fa-chevron-up", ctx_r1.IsDepsExpanded(0));
|
|
620
|
+
i0.ɵɵadvance();
|
|
621
|
+
i0.ɵɵconditional(ctx_r1.IsDepsExpanded(0) ? 4 : -1);
|
|
622
|
+
} }
|
|
623
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
624
|
+
i0.ɵɵelementStart(0, "span", 120);
|
|
625
|
+
i0.ɵɵelement(1, "i", 131);
|
|
626
|
+
i0.ɵɵtext(2, " Most deps");
|
|
627
|
+
i0.ɵɵelementEnd();
|
|
628
|
+
} }
|
|
629
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
630
|
+
i0.ɵɵelementStart(0, "div", 140);
|
|
631
|
+
i0.ɵɵelement(1, "i", 34);
|
|
632
|
+
i0.ɵɵtext(2, " Loading...");
|
|
633
|
+
i0.ɵɵelementEnd();
|
|
634
|
+
} }
|
|
635
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
636
|
+
const _r27 = i0.ɵɵgetCurrentView();
|
|
637
|
+
i0.ɵɵelementStart(0, "div", 142);
|
|
638
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_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()); });
|
|
639
|
+
i0.ɵɵelement(1, "i", 143);
|
|
640
|
+
i0.ɵɵelementStart(2, "span", 144);
|
|
641
|
+
i0.ɵɵtext(3);
|
|
642
|
+
i0.ɵɵelementEnd()();
|
|
643
|
+
} if (rf & 2) {
|
|
644
|
+
const record_r28 = ctx.$implicit;
|
|
645
|
+
i0.ɵɵadvance(3);
|
|
646
|
+
i0.ɵɵtextInterpolate(record_r28.Name);
|
|
647
|
+
} }
|
|
648
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
649
|
+
i0.ɵɵelementStart(0, "div", 139);
|
|
650
|
+
i0.ɵɵconditionalCreate(1, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_Conditional_1_Template, 3, 0, "div", 140);
|
|
651
|
+
i0.ɵɵrepeaterCreate(2, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_For_3_Template, 4, 1, "div", 141, i0.ɵɵrepeaterTrackByIndex);
|
|
652
|
+
i0.ɵɵelementEnd();
|
|
653
|
+
} if (rf & 2) {
|
|
654
|
+
const dep_r26 = i0.ɵɵnextContext().$implicit;
|
|
655
|
+
const ɵ$index_567_r23 = i0.ɵɵnextContext(3).$index;
|
|
656
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
657
|
+
i0.ɵɵadvance();
|
|
658
|
+
i0.ɵɵconditional(ctx_r1.IsDepRecordsLoading(ɵ$index_567_r23 + 1, dep_r26.Entity) ? 1 : -1);
|
|
659
|
+
i0.ɵɵadvance();
|
|
660
|
+
i0.ɵɵrepeater(ctx_r1.GetDepRecords(ɵ$index_567_r23 + 1, dep_r26.Entity));
|
|
661
|
+
} }
|
|
662
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
663
|
+
const _r25 = i0.ɵɵgetCurrentView();
|
|
664
|
+
i0.ɵɵelementStart(0, "div", 134)(1, "div", 135);
|
|
665
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Template_div_click_1_listener($event) { const dep_r26 = i0.ɵɵrestoreView(_r25).$implicit; const ɵ$index_567_r23 = i0.ɵɵnextContext(3).$index; const ctx_r1 = i0.ɵɵnextContext(2); ctx_r1.ToggleDepEntityGroup(ɵ$index_567_r23 + 1, dep_r26.Entity); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
666
|
+
i0.ɵɵelementStart(2, "span", 136);
|
|
667
|
+
i0.ɵɵelement(3, "i", 137);
|
|
668
|
+
i0.ɵɵtext(4);
|
|
669
|
+
i0.ɵɵelementEnd();
|
|
670
|
+
i0.ɵɵelementStart(5, "span", 138);
|
|
671
|
+
i0.ɵɵtext(6);
|
|
672
|
+
i0.ɵɵelementEnd()();
|
|
673
|
+
i0.ɵɵconditionalCreate(7, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Conditional_7_Template, 4, 1, "div", 139);
|
|
674
|
+
i0.ɵɵelementEnd();
|
|
675
|
+
} if (rf & 2) {
|
|
676
|
+
const dep_r26 = ctx.$implicit;
|
|
677
|
+
const ɵ$index_567_r23 = i0.ɵɵnextContext(3).$index;
|
|
678
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
679
|
+
i0.ɵɵadvance(3);
|
|
680
|
+
i0.ɵɵclassProp("fa-chevron-right", !ctx_r1.IsDepEntityGroupExpanded(ɵ$index_567_r23 + 1, dep_r26.Entity))("fa-chevron-down", ctx_r1.IsDepEntityGroupExpanded(ɵ$index_567_r23 + 1, dep_r26.Entity));
|
|
681
|
+
i0.ɵɵadvance();
|
|
682
|
+
i0.ɵɵtextInterpolate1(" ", dep_r26.Entity, " ");
|
|
683
|
+
i0.ɵɵadvance(2);
|
|
684
|
+
i0.ɵɵtextInterpolate(dep_r26.Count);
|
|
685
|
+
i0.ɵɵadvance();
|
|
686
|
+
i0.ɵɵconditional(ctx_r1.IsDepEntityGroupExpanded(ɵ$index_567_r23 + 1, dep_r26.Entity) ? 7 : -1);
|
|
687
|
+
} }
|
|
688
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
689
|
+
i0.ɵɵelementStart(0, "div", 133);
|
|
690
|
+
i0.ɵɵrepeaterCreate(1, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_For_2_Template, 8, 7, "div", 134, _forTrack5);
|
|
691
|
+
i0.ɵɵelementEnd();
|
|
692
|
+
} if (rf & 2) {
|
|
693
|
+
const ɵ$index_567_r23 = i0.ɵɵnextContext(2).$index;
|
|
694
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
695
|
+
i0.ɵɵadvance();
|
|
696
|
+
i0.ɵɵrepeater(ctx_r1.GetGroupedDeps(ɵ$index_567_r23 + 1));
|
|
697
|
+
} }
|
|
698
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
699
|
+
const _r24 = i0.ɵɵgetCurrentView();
|
|
700
|
+
i0.ɵɵelementStart(0, "div", 132);
|
|
701
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r24); const ɵ$index_567_r23 = i0.ɵɵnextContext().$index; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleDepsExpanded(ɵ$index_567_r23 + 1)); });
|
|
702
|
+
i0.ɵɵelementStart(1, "span");
|
|
703
|
+
i0.ɵɵtext(2);
|
|
704
|
+
i0.ɵɵelementEnd();
|
|
705
|
+
i0.ɵɵelement(3, "i", 115);
|
|
706
|
+
i0.ɵɵelementEnd();
|
|
707
|
+
i0.ɵɵconditionalCreate(4, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Conditional_4_Template, 3, 0, "div", 133);
|
|
708
|
+
} if (rf & 2) {
|
|
709
|
+
const ɵ$index_567_r23 = i0.ɵɵnextContext().$index;
|
|
710
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
711
|
+
i0.ɵɵadvance(2);
|
|
712
|
+
i0.ɵɵtextInterpolate(ctx_r1.IsDepsExpanded(ɵ$index_567_r23 + 1) ? "Hide details" : "Show details");
|
|
713
|
+
i0.ɵɵadvance();
|
|
714
|
+
i0.ɵɵclassProp("fa-chevron-down", !ctx_r1.IsDepsExpanded(ɵ$index_567_r23 + 1))("fa-chevron-up", ctx_r1.IsDepsExpanded(ɵ$index_567_r23 + 1));
|
|
715
|
+
i0.ɵɵadvance();
|
|
716
|
+
i0.ɵɵconditional(ctx_r1.IsDepsExpanded(ɵ$index_567_r23 + 1) ? 4 : -1);
|
|
717
|
+
} }
|
|
718
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_24_Template(rf, ctx) { if (rf & 1) {
|
|
719
|
+
const _r29 = i0.ɵɵgetCurrentView();
|
|
720
|
+
i0.ɵɵelementStart(0, "button", 150);
|
|
721
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_24_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r29); const match_r30 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.RejectIndividualMatch(match_r30)); });
|
|
722
|
+
i0.ɵɵelement(1, "i", 67);
|
|
723
|
+
i0.ɵɵtext(2, " Skip ");
|
|
724
|
+
i0.ɵɵelementEnd();
|
|
725
|
+
} }
|
|
726
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_25_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
727
|
+
const _r31 = i0.ɵɵgetCurrentView();
|
|
728
|
+
i0.ɵɵelementStart(0, "button", 153);
|
|
729
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_25_Conditional_3_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r31); const match_r30 = i0.ɵɵnextContext(2).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.UndoRejectIndividualMatch(match_r30)); });
|
|
730
|
+
i0.ɵɵelement(1, "i", 154);
|
|
731
|
+
i0.ɵɵtext(2, " Undo ");
|
|
732
|
+
i0.ɵɵelementEnd();
|
|
733
|
+
} }
|
|
734
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_25_Template(rf, ctx) { if (rf & 1) {
|
|
735
|
+
i0.ɵɵelementStart(0, "span", 151);
|
|
736
|
+
i0.ɵɵelement(1, "i", 115);
|
|
737
|
+
i0.ɵɵtext(2);
|
|
738
|
+
i0.ɵɵelementEnd();
|
|
739
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_25_Conditional_3_Template, 3, 0, "button", 152);
|
|
740
|
+
} if (rf & 2) {
|
|
741
|
+
const match_r30 = i0.ɵɵnextContext().$implicit;
|
|
742
|
+
i0.ɵɵclassProp("status-approved", match_r30.Match.ApprovalStatus === "Approved")("status-rejected", match_r30.Match.ApprovalStatus === "Rejected");
|
|
743
|
+
i0.ɵɵadvance();
|
|
744
|
+
i0.ɵɵclassProp("fa-check", match_r30.Match.ApprovalStatus === "Approved")("fa-times", match_r30.Match.ApprovalStatus === "Rejected");
|
|
745
|
+
i0.ɵɵadvance();
|
|
746
|
+
i0.ɵɵtextInterpolate1(" ", match_r30.Match.ApprovalStatus === "Rejected" ? "Skipped" : match_r30.Match.ApprovalStatus, " ");
|
|
747
|
+
i0.ɵɵadvance();
|
|
748
|
+
i0.ɵɵconditional(match_r30.Match.ApprovalStatus === "Rejected" ? 3 : -1);
|
|
749
|
+
} }
|
|
750
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_47_Template(rf, ctx) { if (rf & 1) {
|
|
751
|
+
const _r22 = i0.ɵɵgetCurrentView();
|
|
752
|
+
i0.ɵɵelementStart(0, "div", 145)(1, "div", 146)(2, "span", 110);
|
|
753
|
+
i0.ɵɵtext(3);
|
|
754
|
+
i0.ɵɵelementEnd();
|
|
755
|
+
i0.ɵɵelementStart(4, "span", 75);
|
|
756
|
+
i0.ɵɵtext(5);
|
|
757
|
+
i0.ɵɵelementEnd()();
|
|
758
|
+
i0.ɵɵelementStart(6, "span", 147);
|
|
759
|
+
i0.ɵɵtext(7);
|
|
760
|
+
i0.ɵɵelementEnd();
|
|
761
|
+
i0.ɵɵelementStart(8, "div", 111)(9, "input", 112);
|
|
762
|
+
i0.ɵɵlistener("change", function DuplicateDetectionResourceComponent_Conditional_68_For_47_Template_input_change_9_listener() { const ɵ$index_567_r23 = i0.ɵɵrestoreView(_r22).$index; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SetSurvivor(ɵ$index_567_r23 + 1)); });
|
|
763
|
+
i0.ɵɵelementEnd();
|
|
764
|
+
i0.ɵɵelementStart(10, "span", 113);
|
|
765
|
+
i0.ɵɵtext(11);
|
|
766
|
+
i0.ɵɵelementEnd()();
|
|
767
|
+
i0.ɵɵelementStart(12, "button", 114);
|
|
768
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_47_Template_button_click_12_listener() { const ɵ$index_567_r23 = i0.ɵɵrestoreView(_r22).$index; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.UseAllFieldsFrom(ɵ$index_567_r23 + 1)); });
|
|
769
|
+
i0.ɵɵelement(13, "i", 115);
|
|
770
|
+
i0.ɵɵtext(14);
|
|
771
|
+
i0.ɵɵelementEnd();
|
|
772
|
+
i0.ɵɵelementStart(15, "div", 116)(16, "div", 117);
|
|
773
|
+
i0.ɵɵelement(17, "i", 118);
|
|
774
|
+
i0.ɵɵelementStart(18, "span", 119);
|
|
775
|
+
i0.ɵɵtext(19);
|
|
776
|
+
i0.ɵɵelementEnd();
|
|
777
|
+
i0.ɵɵtext(20);
|
|
778
|
+
i0.ɵɵconditionalCreate(21, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_21_Template, 3, 0, "span", 120);
|
|
779
|
+
i0.ɵɵelementEnd();
|
|
780
|
+
i0.ɵɵconditionalCreate(22, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_22_Template, 5, 6);
|
|
781
|
+
i0.ɵɵelementEnd();
|
|
782
|
+
i0.ɵɵelementStart(23, "div", 148);
|
|
783
|
+
i0.ɵɵconditionalCreate(24, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_24_Template, 3, 0, "button", 149)(25, DuplicateDetectionResourceComponent_Conditional_68_For_47_Conditional_25_Template, 4, 10);
|
|
784
|
+
i0.ɵɵelementEnd()();
|
|
785
|
+
} if (rf & 2) {
|
|
786
|
+
const match_r30 = ctx.$implicit;
|
|
787
|
+
const ɵ$index_567_r23 = ctx.$index;
|
|
788
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
789
|
+
i0.ɵɵclassProp("match-approved", match_r30.Match.ApprovalStatus === "Approved")("match-rejected", match_r30.Match.ApprovalStatus === "Rejected");
|
|
790
|
+
i0.ɵɵadvance(3);
|
|
791
|
+
i0.ɵɵtextInterpolate(match_r30.Name);
|
|
792
|
+
i0.ɵɵadvance();
|
|
793
|
+
i0.ɵɵclassMap(ctx_r1.GetScoreClass(match_r30.Score));
|
|
794
|
+
i0.ɵɵadvance();
|
|
795
|
+
i0.ɵɵtextInterpolate1(" ", (match_r30.Score * 100).toFixed(0), "% ");
|
|
796
|
+
i0.ɵɵadvance(2);
|
|
797
|
+
i0.ɵɵtextInterpolate2("", match_r30.DiffCount, " difference", match_r30.DiffCount !== 1 ? "s" : "");
|
|
798
|
+
i0.ɵɵadvance(2);
|
|
799
|
+
i0.ɵɵproperty("checked", ctx_r1.SurvivorColumnIndex === ɵ$index_567_r23 + 1);
|
|
800
|
+
i0.ɵɵadvance();
|
|
801
|
+
i0.ɵɵclassProp("is-survivor", ctx_r1.SurvivorColumnIndex === ɵ$index_567_r23 + 1);
|
|
802
|
+
i0.ɵɵadvance();
|
|
803
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.SurvivorColumnIndex === ɵ$index_567_r23 + 1 ? "Surviving Record" : "Set as survivor", " ");
|
|
804
|
+
i0.ɵɵadvance();
|
|
805
|
+
i0.ɵɵclassProp("all-selected", ctx_r1.AllFieldsSelectedFrom(ɵ$index_567_r23 + 1));
|
|
806
|
+
i0.ɵɵadvance();
|
|
807
|
+
i0.ɵɵclassProp("fa-check-double", ctx_r1.AllFieldsSelectedFrom(ɵ$index_567_r23 + 1))("fa-clone", !ctx_r1.AllFieldsSelectedFrom(ɵ$index_567_r23 + 1));
|
|
808
|
+
i0.ɵɵadvance();
|
|
809
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.AllFieldsSelectedFrom(ɵ$index_567_r23 + 1) ? "Using all fields" : "Use all fields", " ");
|
|
810
|
+
i0.ɵɵadvance(5);
|
|
811
|
+
i0.ɵɵtextInterpolate(ctx_r1.GetTotalDeps(ɵ$index_567_r23 + 1));
|
|
812
|
+
i0.ɵɵadvance();
|
|
813
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.GetTotalDeps(ɵ$index_567_r23 + 1) === 1 ? "dependency" : "dependencies", " ");
|
|
814
|
+
i0.ɵɵadvance();
|
|
815
|
+
i0.ɵɵconditional(ctx_r1.GetMaxDepsColumnIndex() === ɵ$index_567_r23 + 1 && ctx_r1.GetTotalDeps(ɵ$index_567_r23 + 1) > 0 ? 21 : -1);
|
|
816
|
+
i0.ɵɵadvance();
|
|
817
|
+
i0.ɵɵconditional(ctx_r1.GetGroupedDeps(ɵ$index_567_r23 + 1).length > 0 ? 22 : -1);
|
|
261
818
|
i0.ɵɵadvance(2);
|
|
262
|
-
i0.ɵɵ
|
|
819
|
+
i0.ɵɵconditional(match_r30.Match.ApprovalStatus === "Pending" ? 24 : 25);
|
|
820
|
+
} }
|
|
821
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_49_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
822
|
+
i0.ɵɵtext(0);
|
|
823
|
+
} if (rf & 2) {
|
|
824
|
+
const field_r33 = i0.ɵɵnextContext().$implicit;
|
|
825
|
+
i0.ɵɵtextInterpolate1(" ", field_r33.SourceValue, " ");
|
|
826
|
+
} }
|
|
827
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_49_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
828
|
+
i0.ɵɵelementStart(0, "span", 157);
|
|
829
|
+
i0.ɵɵtext(1, "(not available)");
|
|
830
|
+
i0.ɵɵelementEnd();
|
|
831
|
+
} }
|
|
832
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_49_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
833
|
+
const _r34 = i0.ɵɵgetCurrentView();
|
|
834
|
+
i0.ɵɵelementStart(0, "input", 160);
|
|
835
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_49_Conditional_5_Template_input_click_0_listener($event) { i0.ɵɵrestoreView(_r34); return i0.ɵɵresetView($event.stopPropagation()); })("change", function DuplicateDetectionResourceComponent_Conditional_68_For_49_Conditional_5_Template_input_change_0_listener() { i0.ɵɵrestoreView(_r34); const field_r33 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectFieldValue(field_r33, 0)); });
|
|
836
|
+
i0.ɵɵelementEnd();
|
|
837
|
+
} if (rf & 2) {
|
|
838
|
+
const field_r33 = i0.ɵɵnextContext().$implicit;
|
|
839
|
+
i0.ɵɵproperty("name", "field-" + field_r33.FieldName)("checked", field_r33.SelectedColumnIndex === 0);
|
|
840
|
+
} }
|
|
841
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
842
|
+
const _r39 = i0.ɵɵgetCurrentView();
|
|
843
|
+
i0.ɵɵelementStart(0, "input", 160);
|
|
844
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Conditional_1_Conditional_1_Template_input_click_0_listener($event) { i0.ɵɵrestoreView(_r39); return i0.ɵɵresetView($event.stopPropagation()); })("change", function DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Conditional_1_Conditional_1_Template_input_change_0_listener() { i0.ɵɵrestoreView(_r39); const ɵ$index_685_r38 = i0.ɵɵnextContext(2).$index; const field_r33 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectFieldValue(field_r33, ɵ$index_685_r38 + 1)); });
|
|
845
|
+
i0.ɵɵelementEnd();
|
|
846
|
+
} if (rf & 2) {
|
|
847
|
+
const ɵ$index_685_r38 = i0.ɵɵnextContext(2).$index;
|
|
848
|
+
const field_r33 = i0.ɵɵnextContext().$implicit;
|
|
849
|
+
i0.ɵɵproperty("name", "field-" + field_r33.FieldName)("checked", field_r33.SelectedColumnIndex === ɵ$index_685_r38 + 1);
|
|
850
|
+
} }
|
|
851
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
852
|
+
i0.ɵɵtext(0);
|
|
853
|
+
i0.ɵɵconditionalCreate(1, DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Conditional_1_Conditional_1_Template, 1, 2, "input", 158);
|
|
854
|
+
} if (rf & 2) {
|
|
855
|
+
const ctx_r39 = i0.ɵɵnextContext();
|
|
856
|
+
const matchVal_r37 = ctx_r39.$implicit;
|
|
857
|
+
const ɵ$index_685_r38 = ctx_r39.$index;
|
|
858
|
+
const field_r33 = i0.ɵɵnextContext().$implicit;
|
|
859
|
+
i0.ɵɵtextInterpolate1(" ", matchVal_r37, " ");
|
|
860
|
+
i0.ɵɵadvance();
|
|
861
|
+
i0.ɵɵconditional(field_r33.HasDifference || field_r33.SelectedColumnIndex === ɵ$index_685_r38 + 1 ? 1 : -1);
|
|
862
|
+
} }
|
|
863
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
864
|
+
i0.ɵɵelementStart(0, "span", 157);
|
|
865
|
+
i0.ɵɵtext(1, "(not available)");
|
|
866
|
+
i0.ɵɵelementEnd();
|
|
867
|
+
} }
|
|
868
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Template(rf, ctx) { if (rf & 1) {
|
|
869
|
+
const _r35 = i0.ɵɵgetCurrentView();
|
|
870
|
+
i0.ɵɵelementStart(0, "div", 161);
|
|
871
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Template_div_click_0_listener() { const ctx_r35 = i0.ɵɵrestoreView(_r35); const matchVal_r37 = ctx_r35.$implicit; const ɵ$index_685_r38 = ctx_r35.$index; const field_r33 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(matchVal_r37 != null ? ctx_r1.SelectFieldValue(field_r33, ɵ$index_685_r38 + 1) : null); });
|
|
872
|
+
i0.ɵɵconditionalCreate(1, DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Conditional_1_Template, 2, 2)(2, DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Conditional_2_Template, 2, 0, "span", 157);
|
|
873
|
+
i0.ɵɵelementEnd();
|
|
874
|
+
} if (rf & 2) {
|
|
875
|
+
const matchVal_r37 = ctx.$implicit;
|
|
876
|
+
const ɵ$index_685_r38 = ctx.$index;
|
|
877
|
+
const ctx_r40 = i0.ɵɵnextContext();
|
|
878
|
+
const field_r33 = ctx_r40.$implicit;
|
|
879
|
+
const ɵ$index_670_r42 = ctx_r40.$index;
|
|
880
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
881
|
+
i0.ɵɵclassProp("grid-row-odd", ɵ$index_670_r42 % 2 !== 0)("value-same", ctx_r1.AreValuesEqual(field_r33.SourceValue, matchVal_r37))("value-different", matchVal_r37 != null && field_r33.SourceValue != null && !ctx_r1.AreValuesEqual(field_r33.SourceValue, matchVal_r37))("field-selected", field_r33.SelectedColumnIndex === ɵ$index_685_r38 + 1);
|
|
882
|
+
i0.ɵɵadvance();
|
|
883
|
+
i0.ɵɵconditional(matchVal_r37 != null ? 1 : 2);
|
|
884
|
+
} }
|
|
885
|
+
function DuplicateDetectionResourceComponent_Conditional_68_For_49_Template(rf, ctx) { if (rf & 1) {
|
|
886
|
+
const _r32 = i0.ɵɵgetCurrentView();
|
|
887
|
+
i0.ɵɵelementStart(0, "div", 155);
|
|
888
|
+
i0.ɵɵtext(1);
|
|
889
|
+
i0.ɵɵelementEnd();
|
|
890
|
+
i0.ɵɵelementStart(2, "div", 156);
|
|
891
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_For_49_Template_div_click_2_listener() { const field_r33 = i0.ɵɵrestoreView(_r32).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectFieldValue(field_r33, 0)); });
|
|
892
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_68_For_49_Conditional_3_Template, 1, 1)(4, DuplicateDetectionResourceComponent_Conditional_68_For_49_Conditional_4_Template, 2, 0, "span", 157);
|
|
893
|
+
i0.ɵɵconditionalCreate(5, DuplicateDetectionResourceComponent_Conditional_68_For_49_Conditional_5_Template, 1, 2, "input", 158);
|
|
894
|
+
i0.ɵɵelementEnd();
|
|
895
|
+
i0.ɵɵrepeaterCreate(6, DuplicateDetectionResourceComponent_Conditional_68_For_49_For_7_Template, 3, 9, "div", 159, i0.ɵɵrepeaterTrackByIndex);
|
|
896
|
+
} if (rf & 2) {
|
|
897
|
+
const field_r33 = ctx.$implicit;
|
|
898
|
+
const ɵ$index_670_r42 = ctx.$index;
|
|
899
|
+
i0.ɵɵclassProp("grid-row-odd", ɵ$index_670_r42 % 2 !== 0);
|
|
900
|
+
i0.ɵɵadvance();
|
|
901
|
+
i0.ɵɵtextInterpolate1(" ", field_r33.DisplayName, " ");
|
|
902
|
+
i0.ɵɵadvance();
|
|
903
|
+
i0.ɵɵclassProp("grid-row-odd", ɵ$index_670_r42 % 2 !== 0)("has-diff-in-row", field_r33.HasDifference)("field-selected", field_r33.SelectedColumnIndex === 0);
|
|
904
|
+
i0.ɵɵadvance();
|
|
905
|
+
i0.ɵɵconditional(field_r33.SourceValue != null ? 3 : 4);
|
|
263
906
|
i0.ɵɵadvance(2);
|
|
264
|
-
i0.ɵɵconditional(
|
|
907
|
+
i0.ɵɵconditional(field_r33.HasDifference || field_r33.SelectedColumnIndex === 0 ? 5 : -1);
|
|
908
|
+
i0.ɵɵadvance();
|
|
909
|
+
i0.ɵɵrepeater(field_r33.MatchValues);
|
|
910
|
+
} }
|
|
911
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_59_Template(rf, ctx) { if (rf & 1) {
|
|
912
|
+
i0.ɵɵtext(0);
|
|
913
|
+
} if (rf & 2) {
|
|
914
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
915
|
+
i0.ɵɵtextInterpolate2(" \u00B7 ", ctx_r1.CherryPickedCount(), " field", ctx_r1.CherryPickedCount() !== 1 ? "s" : "", " cherry-picked ");
|
|
916
|
+
} }
|
|
917
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Conditional_67_Template(rf, ctx) { if (rf & 1) {
|
|
918
|
+
i0.ɵɵelementStart(0, "span", 129);
|
|
919
|
+
i0.ɵɵelement(1, "i", 162);
|
|
920
|
+
i0.ɵɵtext(2, " Merging disabled for this entity");
|
|
921
|
+
i0.ɵɵelementEnd();
|
|
922
|
+
} }
|
|
923
|
+
function DuplicateDetectionResourceComponent_Conditional_68_Template(rf, ctx) { if (rf & 1) {
|
|
924
|
+
const _r16 = i0.ɵɵgetCurrentView();
|
|
925
|
+
i0.ɵɵelementStart(0, "div", 92);
|
|
926
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseComparison()); });
|
|
927
|
+
i0.ɵɵelementEnd();
|
|
928
|
+
i0.ɵɵelementStart(1, "div", 93);
|
|
929
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r16); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
930
|
+
i0.ɵɵelementStart(2, "div", 94)(3, "div", 95)(4, "div", 96);
|
|
931
|
+
i0.ɵɵelement(5, "i");
|
|
932
|
+
i0.ɵɵelementEnd();
|
|
933
|
+
i0.ɵɵelementStart(6, "div")(7, "div", 97);
|
|
934
|
+
i0.ɵɵtext(8);
|
|
935
|
+
i0.ɵɵelementEnd();
|
|
936
|
+
i0.ɵɵelementStart(9, "span", 98);
|
|
937
|
+
i0.ɵɵtext(10);
|
|
938
|
+
i0.ɵɵelementEnd();
|
|
939
|
+
i0.ɵɵelementStart(11, "span", 99);
|
|
940
|
+
i0.ɵɵtext(12);
|
|
941
|
+
i0.ɵɵelementEnd()()();
|
|
942
|
+
i0.ɵɵelementStart(13, "div", 100)(14, "div", 101)(15, "button", 102);
|
|
943
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ComparisonShowAllFields = true); });
|
|
944
|
+
i0.ɵɵtext(16, "All Fields");
|
|
945
|
+
i0.ɵɵelementEnd();
|
|
946
|
+
i0.ɵɵelementStart(17, "button", 102);
|
|
947
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_Template_button_click_17_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ComparisonShowAllFields = false); });
|
|
948
|
+
i0.ɵɵtext(18, "Differences Only");
|
|
949
|
+
i0.ɵɵelementEnd()();
|
|
950
|
+
i0.ɵɵelementStart(19, "button", 103);
|
|
951
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_Template_button_click_19_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseComparison()); });
|
|
952
|
+
i0.ɵɵelement(20, "i", 50);
|
|
953
|
+
i0.ɵɵelementEnd()()();
|
|
954
|
+
i0.ɵɵconditionalCreate(21, DuplicateDetectionResourceComponent_Conditional_68_Conditional_21_Template, 2, 0, "div", 104);
|
|
955
|
+
i0.ɵɵelementStart(22, "div", 105)(23, "div", 106)(24, "div", 107);
|
|
956
|
+
i0.ɵɵtext(25, "Field");
|
|
957
|
+
i0.ɵɵelementEnd();
|
|
958
|
+
i0.ɵɵelementStart(26, "div", 108)(27, "span", 109);
|
|
959
|
+
i0.ɵɵtext(28, "Source");
|
|
960
|
+
i0.ɵɵelementEnd();
|
|
961
|
+
i0.ɵɵelementStart(29, "span", 110);
|
|
962
|
+
i0.ɵɵtext(30);
|
|
963
|
+
i0.ɵɵelementEnd();
|
|
964
|
+
i0.ɵɵelementStart(31, "div", 111)(32, "input", 112);
|
|
965
|
+
i0.ɵɵlistener("change", function DuplicateDetectionResourceComponent_Conditional_68_Template_input_change_32_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetSurvivor(0)); });
|
|
966
|
+
i0.ɵɵelementEnd();
|
|
967
|
+
i0.ɵɵelementStart(33, "span", 113);
|
|
968
|
+
i0.ɵɵtext(34);
|
|
969
|
+
i0.ɵɵelementEnd()();
|
|
970
|
+
i0.ɵɵelementStart(35, "button", 114);
|
|
971
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_Template_button_click_35_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.UseAllFieldsFrom(0)); });
|
|
972
|
+
i0.ɵɵelement(36, "i", 115);
|
|
973
|
+
i0.ɵɵtext(37);
|
|
974
|
+
i0.ɵɵelementEnd();
|
|
975
|
+
i0.ɵɵelementStart(38, "div", 116)(39, "div", 117);
|
|
976
|
+
i0.ɵɵelement(40, "i", 118);
|
|
977
|
+
i0.ɵɵelementStart(41, "span", 119);
|
|
978
|
+
i0.ɵɵtext(42);
|
|
979
|
+
i0.ɵɵelementEnd();
|
|
980
|
+
i0.ɵɵtext(43);
|
|
981
|
+
i0.ɵɵconditionalCreate(44, DuplicateDetectionResourceComponent_Conditional_68_Conditional_44_Template, 3, 0, "span", 120);
|
|
982
|
+
i0.ɵɵelementEnd();
|
|
983
|
+
i0.ɵɵconditionalCreate(45, DuplicateDetectionResourceComponent_Conditional_68_Conditional_45_Template, 5, 6);
|
|
984
|
+
i0.ɵɵelementEnd()();
|
|
985
|
+
i0.ɵɵrepeaterCreate(46, DuplicateDetectionResourceComponent_Conditional_68_For_47_Template, 26, 26, "div", 121, _forTrack3);
|
|
986
|
+
i0.ɵɵrepeaterCreate(48, DuplicateDetectionResourceComponent_Conditional_68_For_49_Template, 8, 11, null, null, _forTrack4);
|
|
987
|
+
i0.ɵɵelementEnd()();
|
|
988
|
+
i0.ɵɵelementStart(50, "div", 122)(51, "div", 123)(52, "span", 124);
|
|
989
|
+
i0.ɵɵtext(53);
|
|
990
|
+
i0.ɵɵelementEnd();
|
|
991
|
+
i0.ɵɵelementStart(54, "span", 125);
|
|
992
|
+
i0.ɵɵelement(55, "i", 126);
|
|
993
|
+
i0.ɵɵtext(56, " Surviving: ");
|
|
994
|
+
i0.ɵɵelementStart(57, "strong");
|
|
995
|
+
i0.ɵɵtext(58);
|
|
996
|
+
i0.ɵɵelementEnd();
|
|
997
|
+
i0.ɵɵconditionalCreate(59, DuplicateDetectionResourceComponent_Conditional_68_Conditional_59_Template, 1, 2);
|
|
998
|
+
i0.ɵɵelementEnd()();
|
|
999
|
+
i0.ɵɵelementStart(60, "div", 127)(61, "button", 85);
|
|
1000
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_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()); });
|
|
1001
|
+
i0.ɵɵelement(62, "i", 50);
|
|
1002
|
+
i0.ɵɵtext(63, " Reject All ");
|
|
1003
|
+
i0.ɵɵelementEnd();
|
|
1004
|
+
i0.ɵɵelementStart(64, "button", 128);
|
|
1005
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_68_Template_button_click_64_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OpenMergeConfirm()); });
|
|
1006
|
+
i0.ɵɵelement(65, "i", 126);
|
|
1007
|
+
i0.ɵɵtext(66, " Merge Records ");
|
|
1008
|
+
i0.ɵɵelementEnd();
|
|
1009
|
+
i0.ɵɵconditionalCreate(67, DuplicateDetectionResourceComponent_Conditional_68_Conditional_67_Template, 3, 0, "span", 129);
|
|
1010
|
+
i0.ɵɵelementEnd()()();
|
|
1011
|
+
} if (rf & 2) {
|
|
1012
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
1013
|
+
i0.ɵɵclassProp("comparison-closing", ctx_r1.ComparisonClosing);
|
|
1014
|
+
i0.ɵɵadvance();
|
|
1015
|
+
i0.ɵɵclassProp("comparison-closing", ctx_r1.ComparisonClosing);
|
|
1016
|
+
i0.ɵɵadvance(4);
|
|
1017
|
+
i0.ɵɵclassMap(ctx_r1.ComparisonGroup.EntityIcon);
|
|
1018
|
+
i0.ɵɵadvance(3);
|
|
1019
|
+
i0.ɵɵtextInterpolate(ctx_r1.ComparisonGroup.RecordName);
|
|
1020
|
+
i0.ɵɵadvance(2);
|
|
1021
|
+
i0.ɵɵtextInterpolate(ctx_r1.ComparisonGroup.EntityName);
|
|
1022
|
+
i0.ɵɵadvance(2);
|
|
1023
|
+
i0.ɵɵtextInterpolate2(" ", ctx_r1.ComparisonGroup.MatchCount, " potential duplicate", ctx_r1.ComparisonGroup.MatchCount !== 1 ? "s" : "", " ");
|
|
1024
|
+
i0.ɵɵadvance(3);
|
|
1025
|
+
i0.ɵɵclassProp("toggle-active", ctx_r1.ComparisonShowAllFields);
|
|
1026
|
+
i0.ɵɵadvance(2);
|
|
1027
|
+
i0.ɵɵclassProp("toggle-active", !ctx_r1.ComparisonShowAllFields);
|
|
1028
|
+
i0.ɵɵadvance(4);
|
|
1029
|
+
i0.ɵɵconditional(ctx_r1.ComparisonLoading ? 21 : -1);
|
|
1030
|
+
i0.ɵɵadvance();
|
|
1031
|
+
i0.ɵɵproperty("hidden", ctx_r1.ComparisonLoading);
|
|
1032
|
+
i0.ɵɵadvance();
|
|
1033
|
+
i0.ɵɵstyleProp("grid-template-columns", "160px repeat(" + (1 + ctx_r1.ComparisonMatches.length) + ", minmax(180px, 1fr))");
|
|
265
1034
|
i0.ɵɵadvance(7);
|
|
266
|
-
i0.ɵɵtextInterpolate(
|
|
1035
|
+
i0.ɵɵtextInterpolate(ctx_r1.ComparisonGroup.RecordName);
|
|
1036
|
+
i0.ɵɵadvance(2);
|
|
1037
|
+
i0.ɵɵproperty("checked", ctx_r1.SurvivorColumnIndex === 0);
|
|
1038
|
+
i0.ɵɵadvance();
|
|
1039
|
+
i0.ɵɵclassProp("is-survivor", ctx_r1.SurvivorColumnIndex === 0);
|
|
1040
|
+
i0.ɵɵadvance();
|
|
1041
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.SurvivorColumnIndex === 0 ? "Surviving Record" : "Set as survivor", " ");
|
|
1042
|
+
i0.ɵɵadvance();
|
|
1043
|
+
i0.ɵɵclassProp("all-selected", ctx_r1.AllFieldsSelectedFrom(0));
|
|
1044
|
+
i0.ɵɵadvance();
|
|
1045
|
+
i0.ɵɵclassProp("fa-check-double", ctx_r1.AllFieldsSelectedFrom(0))("fa-clone", !ctx_r1.AllFieldsSelectedFrom(0));
|
|
1046
|
+
i0.ɵɵadvance();
|
|
1047
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.AllFieldsSelectedFrom(0) ? "Using all fields" : "Use all fields", " ");
|
|
1048
|
+
i0.ɵɵadvance(5);
|
|
1049
|
+
i0.ɵɵtextInterpolate(ctx_r1.GetTotalDeps(0));
|
|
1050
|
+
i0.ɵɵadvance();
|
|
1051
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.GetTotalDeps(0) === 1 ? "dependency" : "dependencies", " ");
|
|
1052
|
+
i0.ɵɵadvance();
|
|
1053
|
+
i0.ɵɵconditional(ctx_r1.GetMaxDepsColumnIndex() === 0 && ctx_r1.GetTotalDeps(0) > 0 ? 44 : -1);
|
|
1054
|
+
i0.ɵɵadvance();
|
|
1055
|
+
i0.ɵɵconditional(ctx_r1.GetGroupedDeps(0).length > 0 ? 45 : -1);
|
|
1056
|
+
i0.ɵɵadvance();
|
|
1057
|
+
i0.ɵɵrepeater(ctx_r1.ComparisonMatches);
|
|
1058
|
+
i0.ɵɵadvance(2);
|
|
1059
|
+
i0.ɵɵrepeater(ctx_r1.GetVisibleFields());
|
|
1060
|
+
i0.ɵɵadvance(5);
|
|
1061
|
+
i0.ɵɵtextInterpolate2(" Showing ", ctx_r1.GetVisibleFields().length, " of ", ctx_r1.ComparisonFields.length, " fields ");
|
|
1062
|
+
i0.ɵɵadvance(5);
|
|
1063
|
+
i0.ɵɵtextInterpolate(ctx_r1.SurvivorName());
|
|
1064
|
+
i0.ɵɵadvance();
|
|
1065
|
+
i0.ɵɵconditional(ctx_r1.CherryPickedCount() > 0 ? 59 : -1);
|
|
1066
|
+
i0.ɵɵadvance(2);
|
|
1067
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving);
|
|
1068
|
+
i0.ɵɵadvance(3);
|
|
1069
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving || !ctx_r1.HasMergeableMatches || !ctx_r1.MergeEnabled)("title", !ctx_r1.MergeEnabled ? "Merging is not enabled for this entity" : ctx_r1.HasMergeableMatches ? "Merge non-skipped records" : "All matches have been skipped");
|
|
1070
|
+
i0.ɵɵadvance(3);
|
|
1071
|
+
i0.ɵɵconditional(!ctx_r1.MergeEnabled ? 67 : -1);
|
|
1072
|
+
} }
|
|
1073
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Conditional_22_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
1074
|
+
i0.ɵɵelementStart(0, "div", 187)(1, "span", 188);
|
|
1075
|
+
i0.ɵɵtext(2);
|
|
1076
|
+
i0.ɵɵelementEnd();
|
|
1077
|
+
i0.ɵɵelementStart(3, "span", 189);
|
|
1078
|
+
i0.ɵɵtext(4);
|
|
1079
|
+
i0.ɵɵelementEnd();
|
|
1080
|
+
i0.ɵɵelementStart(5, "span", 190);
|
|
1081
|
+
i0.ɵɵtext(6);
|
|
1082
|
+
i0.ɵɵelementEnd()();
|
|
1083
|
+
} if (rf & 2) {
|
|
1084
|
+
const field_r44 = ctx.$implicit;
|
|
1085
|
+
i0.ɵɵadvance(2);
|
|
1086
|
+
i0.ɵɵtextInterpolate(field_r44.DisplayName);
|
|
267
1087
|
i0.ɵɵadvance(2);
|
|
268
|
-
i0.ɵɵ
|
|
1088
|
+
i0.ɵɵtextInterpolate1("\"", field_r44.Value, "\"");
|
|
269
1089
|
i0.ɵɵadvance(2);
|
|
270
|
-
i0.ɵɵ
|
|
1090
|
+
i0.ɵɵtextInterpolate1("from ", field_r44.SourceName);
|
|
271
1091
|
} }
|
|
272
|
-
function
|
|
273
|
-
i0.ɵɵelementStart(0, "div",
|
|
274
|
-
i0.ɵɵ
|
|
1092
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
1093
|
+
i0.ɵɵelementStart(0, "div")(1, "div", 186);
|
|
1094
|
+
i0.ɵɵtext(2);
|
|
275
1095
|
i0.ɵɵelementEnd();
|
|
1096
|
+
i0.ɵɵrepeaterCreate(3, DuplicateDetectionResourceComponent_Conditional_69_Conditional_22_For_4_Template, 7, 3, "div", 187, _forTrack4);
|
|
1097
|
+
i0.ɵɵelementEnd();
|
|
1098
|
+
} if (rf & 2) {
|
|
1099
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
1100
|
+
i0.ɵɵadvance(2);
|
|
1101
|
+
i0.ɵɵtextInterpolate2("Field Value Overrides (", ctx_r1.GetCherryPickedFields().length, " field", ctx_r1.GetCherryPickedFields().length !== 1 ? "s" : "", ")");
|
|
1102
|
+
i0.ɵɵadvance();
|
|
1103
|
+
i0.ɵɵrepeater(ctx_r1.GetCherryPickedFields());
|
|
1104
|
+
} }
|
|
1105
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Conditional_23_For_5_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
1106
|
+
i0.ɵɵelementStart(0, "strong");
|
|
1107
|
+
i0.ɵɵtext(1);
|
|
1108
|
+
i0.ɵɵelementEnd();
|
|
1109
|
+
i0.ɵɵtext(2);
|
|
1110
|
+
i0.ɵɵelementStart(3, "strong");
|
|
1111
|
+
i0.ɵɵtext(4);
|
|
1112
|
+
i0.ɵɵelementEnd();
|
|
1113
|
+
i0.ɵɵtext(5);
|
|
1114
|
+
} if (rf & 2) {
|
|
1115
|
+
const col_r45 = i0.ɵɵnextContext().$implicit;
|
|
1116
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
1117
|
+
i0.ɵɵadvance();
|
|
1118
|
+
i0.ɵɵtextInterpolate(col_r45.DepCount);
|
|
1119
|
+
i0.ɵɵadvance();
|
|
1120
|
+
i0.ɵɵtextInterpolate1(" ", col_r45.DepCount === 1 ? "dependency" : "dependencies", " from ");
|
|
1121
|
+
i0.ɵɵadvance(2);
|
|
1122
|
+
i0.ɵɵtextInterpolate(col_r45.Name);
|
|
1123
|
+
i0.ɵɵadvance();
|
|
1124
|
+
i0.ɵɵtextInterpolate1(" \u2192 ", ctx_r1.SurvivorName(), " ");
|
|
1125
|
+
} }
|
|
1126
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Conditional_23_For_5_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
1127
|
+
i0.ɵɵelementStart(0, "strong");
|
|
1128
|
+
i0.ɵɵtext(1, "0");
|
|
1129
|
+
i0.ɵɵelementEnd();
|
|
1130
|
+
i0.ɵɵtext(2, " dependencies from ");
|
|
1131
|
+
i0.ɵɵelementStart(3, "strong");
|
|
1132
|
+
i0.ɵɵtext(4);
|
|
1133
|
+
i0.ɵɵelementEnd();
|
|
1134
|
+
} if (rf & 2) {
|
|
1135
|
+
const col_r45 = i0.ɵɵnextContext().$implicit;
|
|
1136
|
+
i0.ɵɵadvance(4);
|
|
1137
|
+
i0.ɵɵtextInterpolate(col_r45.Name);
|
|
1138
|
+
} }
|
|
1139
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Conditional_23_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
1140
|
+
i0.ɵɵelementStart(0, "div", 193);
|
|
1141
|
+
i0.ɵɵelement(1, "i", 194);
|
|
1142
|
+
i0.ɵɵelementStart(2, "span");
|
|
1143
|
+
i0.ɵɵconditionalCreate(3, DuplicateDetectionResourceComponent_Conditional_69_Conditional_23_For_5_Conditional_3_Template, 6, 4)(4, DuplicateDetectionResourceComponent_Conditional_69_Conditional_23_For_5_Conditional_4_Template, 5, 1);
|
|
1144
|
+
i0.ɵɵelementEnd()();
|
|
1145
|
+
} if (rf & 2) {
|
|
1146
|
+
const col_r45 = ctx.$implicit;
|
|
1147
|
+
i0.ɵɵadvance(3);
|
|
1148
|
+
i0.ɵɵconditional(col_r45.DepCount > 0 ? 3 : 4);
|
|
1149
|
+
} }
|
|
1150
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
1151
|
+
i0.ɵɵelementStart(0, "div", 177)(1, "div", 191);
|
|
1152
|
+
i0.ɵɵelement(2, "i", 192);
|
|
1153
|
+
i0.ɵɵtext(3, " Dependencies to Transfer");
|
|
1154
|
+
i0.ɵɵelementEnd();
|
|
1155
|
+
i0.ɵɵrepeaterCreate(4, DuplicateDetectionResourceComponent_Conditional_69_Conditional_23_For_5_Template, 5, 1, "div", 193, _forTrack6);
|
|
1156
|
+
i0.ɵɵelementEnd();
|
|
1157
|
+
} if (rf & 2) {
|
|
1158
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
1159
|
+
i0.ɵɵadvance(4);
|
|
1160
|
+
i0.ɵɵrepeater(ctx_r1.GetNonSurvivorColumns());
|
|
1161
|
+
} }
|
|
1162
|
+
function DuplicateDetectionResourceComponent_Conditional_69_For_29_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
1163
|
+
i0.ɵɵtext(0);
|
|
1164
|
+
} if (rf & 2) {
|
|
1165
|
+
const col_r46 = i0.ɵɵnextContext().$implicit;
|
|
1166
|
+
i0.ɵɵtextInterpolate2(" ", col_r46.DepCount, " dep", col_r46.DepCount !== 1 ? "s" : "", " transferring ");
|
|
1167
|
+
} }
|
|
1168
|
+
function DuplicateDetectionResourceComponent_Conditional_69_For_29_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
1169
|
+
i0.ɵɵtext(0, " no deps ");
|
|
1170
|
+
} }
|
|
1171
|
+
function DuplicateDetectionResourceComponent_Conditional_69_For_29_Template(rf, ctx) { if (rf & 1) {
|
|
1172
|
+
i0.ɵɵelementStart(0, "div", 181)(1, "span", 195);
|
|
1173
|
+
i0.ɵɵtext(2);
|
|
1174
|
+
i0.ɵɵelementEnd();
|
|
1175
|
+
i0.ɵɵelementStart(3, "span", 196);
|
|
1176
|
+
i0.ɵɵconditionalCreate(4, DuplicateDetectionResourceComponent_Conditional_69_For_29_Conditional_4_Template, 1, 2)(5, DuplicateDetectionResourceComponent_Conditional_69_For_29_Conditional_5_Template, 1, 0);
|
|
1177
|
+
i0.ɵɵelementEnd()();
|
|
1178
|
+
} if (rf & 2) {
|
|
1179
|
+
const col_r46 = ctx.$implicit;
|
|
1180
|
+
i0.ɵɵadvance(2);
|
|
1181
|
+
i0.ɵɵtextInterpolate(col_r46.Name);
|
|
1182
|
+
i0.ɵɵadvance(2);
|
|
1183
|
+
i0.ɵɵconditional(col_r46.DepCount > 0 ? 4 : 5);
|
|
1184
|
+
} }
|
|
1185
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Conditional_35_Template(rf, ctx) { if (rf & 1) {
|
|
1186
|
+
i0.ɵɵelement(0, "i", 34);
|
|
1187
|
+
i0.ɵɵtext(1, " Merging... ");
|
|
1188
|
+
} }
|
|
1189
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Conditional_36_Template(rf, ctx) { if (rf & 1) {
|
|
1190
|
+
i0.ɵɵelement(0, "i", 126);
|
|
1191
|
+
i0.ɵɵtext(1);
|
|
1192
|
+
} if (rf & 2) {
|
|
1193
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
1194
|
+
i0.ɵɵadvance();
|
|
1195
|
+
i0.ɵɵtextInterpolate1(" Confirm Merge (", ctx_r1.GetNonSurvivorColumns().length + 1, " records \u2192 1) ");
|
|
1196
|
+
} }
|
|
1197
|
+
function DuplicateDetectionResourceComponent_Conditional_69_Template(rf, ctx) { if (rf & 1) {
|
|
1198
|
+
const _r43 = i0.ɵɵgetCurrentView();
|
|
1199
|
+
i0.ɵɵelementStart(0, "div", 163);
|
|
1200
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_69_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r43); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseMergeConfirm()); });
|
|
1201
|
+
i0.ɵɵelementStart(1, "div", 164);
|
|
1202
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_69_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r43); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
1203
|
+
i0.ɵɵelementStart(2, "div", 165)(3, "div", 166);
|
|
1204
|
+
i0.ɵɵelement(4, "i", 126);
|
|
1205
|
+
i0.ɵɵelementEnd();
|
|
1206
|
+
i0.ɵɵelementStart(5, "div")(6, "div", 167);
|
|
1207
|
+
i0.ɵɵtext(7, "Confirm Record Merge");
|
|
1208
|
+
i0.ɵɵelementEnd();
|
|
1209
|
+
i0.ɵɵelementStart(8, "div", 168);
|
|
1210
|
+
i0.ɵɵtext(9, "This action cannot be undone. Please review carefully.");
|
|
1211
|
+
i0.ɵɵelementEnd()()();
|
|
1212
|
+
i0.ɵɵelementStart(10, "div", 169)(11, "div", 170)(12, "div", 171);
|
|
1213
|
+
i0.ɵɵelement(13, "i", 172);
|
|
1214
|
+
i0.ɵɵtext(14, " Surviving Record");
|
|
1215
|
+
i0.ɵɵelementEnd();
|
|
1216
|
+
i0.ɵɵelementStart(15, "div", 173);
|
|
1217
|
+
i0.ɵɵtext(16);
|
|
1218
|
+
i0.ɵɵelementEnd();
|
|
1219
|
+
i0.ɵɵelementStart(17, "div", 174);
|
|
1220
|
+
i0.ɵɵelement(18, "i", 175);
|
|
1221
|
+
i0.ɵɵtext(19);
|
|
1222
|
+
i0.ɵɵelementEnd();
|
|
1223
|
+
i0.ɵɵelementStart(20, "div", 176);
|
|
1224
|
+
i0.ɵɵtext(21, "This record's ID will be retained. All dependencies from merged records will be transferred here.");
|
|
1225
|
+
i0.ɵɵelementEnd()();
|
|
1226
|
+
i0.ɵɵconditionalCreate(22, DuplicateDetectionResourceComponent_Conditional_69_Conditional_22_Template, 5, 2, "div");
|
|
1227
|
+
i0.ɵɵconditionalCreate(23, DuplicateDetectionResourceComponent_Conditional_69_Conditional_23_Template, 6, 0, "div", 177);
|
|
1228
|
+
i0.ɵɵelementStart(24, "div", 178)(25, "div", 179);
|
|
1229
|
+
i0.ɵɵelement(26, "i", 180);
|
|
1230
|
+
i0.ɵɵtext(27, " Records to Delete After Merge");
|
|
1231
|
+
i0.ɵɵelementEnd();
|
|
1232
|
+
i0.ɵɵrepeaterCreate(28, DuplicateDetectionResourceComponent_Conditional_69_For_29_Template, 6, 2, "div", 181, _forTrack6);
|
|
1233
|
+
i0.ɵɵelementEnd()();
|
|
1234
|
+
i0.ɵɵelementStart(30, "div", 182)(31, "button", 183);
|
|
1235
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_69_Template_button_click_31_listener() { i0.ɵɵrestoreView(_r43); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CloseMergeConfirm()); });
|
|
1236
|
+
i0.ɵɵelement(32, "i", 184);
|
|
1237
|
+
i0.ɵɵtext(33, " Back ");
|
|
1238
|
+
i0.ɵɵelementEnd();
|
|
1239
|
+
i0.ɵɵelementStart(34, "button", 185);
|
|
1240
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Conditional_69_Template_button_click_34_listener() { i0.ɵɵrestoreView(_r43); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ExecuteMerge()); });
|
|
1241
|
+
i0.ɵɵconditionalCreate(35, DuplicateDetectionResourceComponent_Conditional_69_Conditional_35_Template, 2, 0)(36, DuplicateDetectionResourceComponent_Conditional_69_Conditional_36_Template, 2, 1);
|
|
1242
|
+
i0.ɵɵelementEnd()()()();
|
|
1243
|
+
} if (rf & 2) {
|
|
1244
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
1245
|
+
i0.ɵɵadvance(16);
|
|
1246
|
+
i0.ɵɵtextInterpolate(ctx_r1.SurvivorName());
|
|
1247
|
+
i0.ɵɵadvance(3);
|
|
1248
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.SurvivorKeyDisplay(), " ");
|
|
1249
|
+
i0.ɵɵadvance(3);
|
|
1250
|
+
i0.ɵɵconditional(ctx_r1.GetCherryPickedFields().length > 0 ? 22 : -1);
|
|
1251
|
+
i0.ɵɵadvance();
|
|
1252
|
+
i0.ɵɵconditional(ctx_r1.GetNonSurvivorColumns().length > 0 ? 23 : -1);
|
|
1253
|
+
i0.ɵɵadvance(5);
|
|
1254
|
+
i0.ɵɵrepeater(ctx_r1.GetNonSurvivorColumns());
|
|
1255
|
+
i0.ɵɵadvance(3);
|
|
1256
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsMerging);
|
|
1257
|
+
i0.ɵɵadvance(3);
|
|
1258
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsMerging);
|
|
1259
|
+
i0.ɵɵadvance();
|
|
1260
|
+
i0.ɵɵconditional(ctx_r1.IsMerging ? 35 : 36);
|
|
276
1261
|
} }
|
|
277
1262
|
let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceComponent extends BaseResourceComponent {
|
|
1263
|
+
/** Close comparison panel on Escape key */
|
|
1264
|
+
OnEscapeKey() {
|
|
1265
|
+
if (this.ComparisonGroup) {
|
|
1266
|
+
this.CloseComparison();
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
278
1269
|
cdr = inject(ChangeDetectorRef);
|
|
1270
|
+
navigationService = inject(NavigationService);
|
|
279
1271
|
destroy$ = new Subject();
|
|
280
1272
|
filterSubject = new Subject();
|
|
281
1273
|
// Loading state
|
|
282
1274
|
IsLoading = false;
|
|
1275
|
+
/** Whether the results area (runs/details/matches) is still loading */
|
|
1276
|
+
IsLoadingResults = false;
|
|
283
1277
|
IsSaving = false;
|
|
1278
|
+
// ── Comparison Panel State ──
|
|
1279
|
+
/** The group being compared (null = panel closed) */
|
|
1280
|
+
ComparisonGroup = null;
|
|
1281
|
+
/** Whether the comparison panel is loading entity records */
|
|
1282
|
+
ComparisonLoading = false;
|
|
1283
|
+
/** Whether to show all fields or only differences */
|
|
1284
|
+
ComparisonShowAllFields = true;
|
|
1285
|
+
/** Precomputed field rows for the comparison grid */
|
|
1286
|
+
ComparisonFields = [];
|
|
1287
|
+
/** Parsed match info for comparison columns */
|
|
1288
|
+
ComparisonMatches = [];
|
|
1289
|
+
/** Index of the surviving record column (0 = source, 1+ = match index) */
|
|
1290
|
+
SurvivorColumnIndex = 0;
|
|
1291
|
+
/** Whether the panel is animating closed */
|
|
1292
|
+
ComparisonClosing = false;
|
|
1293
|
+
/** Loaded entity records keyed by record ID (populated on panel open via RunView) */
|
|
1294
|
+
comparisonRecords = new Map();
|
|
1295
|
+
// ── Dependencies State ──
|
|
1296
|
+
/** Dependencies per record, keyed by composite key string */
|
|
1297
|
+
ComparisonDependencies = new Map();
|
|
1298
|
+
/** Which column indices have expanded dependency details */
|
|
1299
|
+
DepsExpandedColumns = new Set();
|
|
1300
|
+
/** Tracks which entity groups within deps are expanded (key: "columnIndex::entityName") */
|
|
1301
|
+
depsEntityGroupExpanded = new Set();
|
|
1302
|
+
// ── Merge Confirmation State ──
|
|
1303
|
+
/** Whether the merge confirmation panel is visible */
|
|
1304
|
+
ShowMergeConfirm = false;
|
|
1305
|
+
/** Whether the merge is currently executing */
|
|
1306
|
+
IsMerging = false;
|
|
1307
|
+
/** Whether the current entity allows record merging (controls merge button availability) */
|
|
1308
|
+
MergeEnabled = true;
|
|
1309
|
+
/** Whether merge-not-available inline banner should be shown in the results area */
|
|
1310
|
+
ShowMergeWarningBanner = false;
|
|
284
1311
|
// Raw data
|
|
285
1312
|
Runs = [];
|
|
286
1313
|
Details = [];
|
|
@@ -300,6 +1327,29 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
300
1327
|
};
|
|
301
1328
|
// Available entity names from loaded runs
|
|
302
1329
|
EntityNames = [];
|
|
1330
|
+
// Detection run state
|
|
1331
|
+
IsDetecting = false;
|
|
1332
|
+
DetectionProgress = 0;
|
|
1333
|
+
DetectionStage = '';
|
|
1334
|
+
/** Raw stage key from the last progress event — used to detect phase transitions */
|
|
1335
|
+
detectionRawStage = '';
|
|
1336
|
+
/** Runtime threshold overrides — initialized from entity doc, adjustable via sliders */
|
|
1337
|
+
RunPotentialThreshold = 0.70;
|
|
1338
|
+
RunAbsoluteThreshold = 0.95;
|
|
1339
|
+
DetectionCurrentItem = '';
|
|
1340
|
+
// Entity document picker
|
|
1341
|
+
EntityDocuments = [];
|
|
1342
|
+
_selectedEntityDocumentID = '';
|
|
1343
|
+
get SelectedEntityDocumentID() { return this._selectedEntityDocumentID; }
|
|
1344
|
+
set SelectedEntityDocumentID(value) {
|
|
1345
|
+
this._selectedEntityDocumentID = value;
|
|
1346
|
+
// Sync threshold sliders from selected entity document
|
|
1347
|
+
const doc = this.EntityDocuments.find(d => UUIDsEqual(d.ID, value));
|
|
1348
|
+
if (doc) {
|
|
1349
|
+
this.RunPotentialThreshold = doc.PotentialMatchThreshold;
|
|
1350
|
+
this.RunAbsoluteThreshold = doc.AbsoluteMatchThreshold;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
303
1353
|
/** Whether this component is embedded inside the Knowledge Hub shell */
|
|
304
1354
|
EmbeddedMode = false;
|
|
305
1355
|
/** View mode: 'kanban' (card board) or 'table' (paged grid) */
|
|
@@ -381,9 +1431,22 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
381
1431
|
get RejectedCount() {
|
|
382
1432
|
return this.RejectedGroups.length;
|
|
383
1433
|
}
|
|
1434
|
+
/** Get selected entity document threshold info */
|
|
1435
|
+
get SelectedDocumentThresholds() {
|
|
1436
|
+
if (!this.SelectedEntityDocumentID)
|
|
1437
|
+
return null;
|
|
1438
|
+
return this.EntityDocuments.find(d => UUIDsEqual(d.ID, this.SelectedEntityDocumentID)) ?? null;
|
|
1439
|
+
}
|
|
384
1440
|
async ngAfterViewInit() {
|
|
385
1441
|
this.setupFilterDebounce();
|
|
386
1442
|
await this.LoadData();
|
|
1443
|
+
this.navigationService.SetAgentContext(this, {
|
|
1444
|
+
DetectionStatus: this.IsDetecting ? 'running' : 'idle',
|
|
1445
|
+
PendingCount: this.PendingGroups.length,
|
|
1446
|
+
ApprovedCount: this.ApprovedGroups.length,
|
|
1447
|
+
RejectedCount: this.RejectedGroups.length,
|
|
1448
|
+
SelectedEntityDoc: this.SelectedEntityDocumentID || null,
|
|
1449
|
+
});
|
|
387
1450
|
this.NotifyLoadComplete();
|
|
388
1451
|
}
|
|
389
1452
|
ngOnDestroy() {
|
|
@@ -398,49 +1461,147 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
398
1461
|
}
|
|
399
1462
|
/**
|
|
400
1463
|
* Loads all duplicate run data and builds the Kanban groups.
|
|
1464
|
+
* Split into two phases so that controls become interactive immediately:
|
|
1465
|
+
* Phase 1 - entity docs from KH engine cache (instant)
|
|
1466
|
+
* Phase 2 - runs/details/matches via RunViews (heavy)
|
|
401
1467
|
*/
|
|
402
1468
|
async LoadData() {
|
|
403
1469
|
this.IsLoading = true;
|
|
404
1470
|
this.cdr.detectChanges();
|
|
405
1471
|
try {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
{
|
|
415
|
-
EntityName: 'MJ: Duplicate Run Details',
|
|
416
|
-
ExtraFilter: "MatchStatus = 'Complete'",
|
|
417
|
-
OrderBy: '__mj_CreatedAt DESC',
|
|
418
|
-
ResultType: 'entity_object'
|
|
419
|
-
},
|
|
420
|
-
{
|
|
421
|
-
EntityName: 'MJ: Duplicate Run Detail Matches',
|
|
422
|
-
OrderBy: 'MatchProbability DESC',
|
|
423
|
-
ResultType: 'entity_object'
|
|
424
|
-
}
|
|
425
|
-
]);
|
|
426
|
-
if (runsResult.Success) {
|
|
427
|
-
this.Runs = runsResult.Results;
|
|
428
|
-
}
|
|
429
|
-
if (detailsResult.Success) {
|
|
430
|
-
this.Details = detailsResult.Results;
|
|
431
|
-
}
|
|
432
|
-
if (matchesResult.Success) {
|
|
433
|
-
this.Matches = matchesResult.Results;
|
|
434
|
-
}
|
|
435
|
-
this.extractEntityNames();
|
|
436
|
-
this.buildGroups();
|
|
437
|
-
this.applyFilters();
|
|
1472
|
+
// Phase 1: Populate entity document picker from cache (instant)
|
|
1473
|
+
await this.loadEntityDocuments();
|
|
1474
|
+
// Controls are now interactive - only the results area is loading
|
|
1475
|
+
this.IsLoading = false;
|
|
1476
|
+
this.IsLoadingResults = true;
|
|
1477
|
+
this.cdr.detectChanges();
|
|
1478
|
+
// Phase 2: Load heavy run/detail/match data
|
|
1479
|
+
await this.loadRunData();
|
|
438
1480
|
}
|
|
439
1481
|
catch (error) {
|
|
440
1482
|
console.error('Error loading duplicate detection data:', error);
|
|
441
1483
|
}
|
|
442
1484
|
finally {
|
|
443
1485
|
this.IsLoading = false;
|
|
1486
|
+
this.IsLoadingResults = false;
|
|
1487
|
+
this.cdr.detectChanges();
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
/** Phase 1: Load entity documents from KH engine cache (instant). */
|
|
1491
|
+
async loadEntityDocuments() {
|
|
1492
|
+
const engine = KnowledgeHubMetadataEngine.Instance;
|
|
1493
|
+
await engine.Config(false);
|
|
1494
|
+
this.buildEntityDocumentOptionsFromEngine(engine.GetActiveEntityDocuments());
|
|
1495
|
+
}
|
|
1496
|
+
/** Phase 2: Load runs, details, and matches via RunViews batch. */
|
|
1497
|
+
async loadRunData() {
|
|
1498
|
+
const rv = new RunView();
|
|
1499
|
+
const [runsResult, detailsResult, matchesResult] = await rv.RunViews([
|
|
1500
|
+
{
|
|
1501
|
+
EntityName: 'MJ: Duplicate Runs',
|
|
1502
|
+
ExtraFilter: "ProcessingStatus IN ('Complete', 'Failed', 'In Progress')",
|
|
1503
|
+
OrderBy: 'StartedAt DESC',
|
|
1504
|
+
ResultType: 'entity_object'
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
EntityName: 'MJ: Duplicate Run Details',
|
|
1508
|
+
ExtraFilter: "MatchStatus = 'Complete'",
|
|
1509
|
+
OrderBy: '__mj_CreatedAt DESC',
|
|
1510
|
+
ResultType: 'entity_object'
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
EntityName: 'MJ: Duplicate Run Detail Matches',
|
|
1514
|
+
OrderBy: 'MatchProbability DESC',
|
|
1515
|
+
ResultType: 'entity_object'
|
|
1516
|
+
}
|
|
1517
|
+
]);
|
|
1518
|
+
if (runsResult.Success) {
|
|
1519
|
+
this.Runs = runsResult.Results;
|
|
1520
|
+
}
|
|
1521
|
+
if (detailsResult.Success) {
|
|
1522
|
+
this.Details = detailsResult.Results;
|
|
1523
|
+
}
|
|
1524
|
+
if (matchesResult.Success) {
|
|
1525
|
+
this.Matches = matchesResult.Results;
|
|
1526
|
+
}
|
|
1527
|
+
this.buildGroups();
|
|
1528
|
+
this.extractEntityNames();
|
|
1529
|
+
this.computeDataRanges();
|
|
1530
|
+
this.applyFilters();
|
|
1531
|
+
// Reconnect to any in-progress detection run
|
|
1532
|
+
this.reconnectToActiveRun();
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Check if there's an in-progress detection run and reconnect to its
|
|
1536
|
+
* progress subscription. This handles the case where the user navigated
|
|
1537
|
+
* away and came back while a run was active.
|
|
1538
|
+
*/
|
|
1539
|
+
reconnectToActiveRun() {
|
|
1540
|
+
if (this.IsDetecting)
|
|
1541
|
+
return; // Already tracking a run
|
|
1542
|
+
const activeRun = this.Runs.find(r => r.ProcessingStatus === 'In Progress');
|
|
1543
|
+
if (!activeRun)
|
|
1544
|
+
return;
|
|
1545
|
+
LogStatus(`[DuplicateDetection] Reconnecting to in-progress run ${activeRun.ID}`);
|
|
1546
|
+
this.IsDetecting = true;
|
|
1547
|
+
this.DetectionProgress = 0;
|
|
1548
|
+
this.DetectionStage = 'Reconnecting...';
|
|
1549
|
+
this.cdr.detectChanges();
|
|
1550
|
+
this.subscribeToPipelineProgress(activeRun.ID);
|
|
1551
|
+
}
|
|
1552
|
+
/**
|
|
1553
|
+
* Trigger a new duplicate detection run by creating a DuplicateRun entity.
|
|
1554
|
+
* The server hook auto-triggers detection when a run is saved with EndedAt === null.
|
|
1555
|
+
*/
|
|
1556
|
+
async RunDetection() {
|
|
1557
|
+
if (this.IsDetecting || !this.SelectedEntityDocumentID)
|
|
1558
|
+
return;
|
|
1559
|
+
const selectedDoc = this.EntityDocuments.find(d => UUIDsEqual(d.ID, this.SelectedEntityDocumentID));
|
|
1560
|
+
if (!selectedDoc)
|
|
1561
|
+
return;
|
|
1562
|
+
this.IsDetecting = true;
|
|
1563
|
+
this.DetectionProgress = 0;
|
|
1564
|
+
this.DetectionStage = 'Initializing...';
|
|
1565
|
+
this.DetectionCurrentItem = '';
|
|
1566
|
+
this.cdr.detectChanges();
|
|
1567
|
+
try {
|
|
1568
|
+
const md = new Metadata();
|
|
1569
|
+
const dupeRun = await md.GetEntityObject('MJ: Duplicate Runs');
|
|
1570
|
+
dupeRun.NewRecord();
|
|
1571
|
+
// Look up the EntityID from the entity document's entity name
|
|
1572
|
+
const entityInfo = md.Entities.find(e => e.Name === selectedDoc.EntityName);
|
|
1573
|
+
if (!entityInfo) {
|
|
1574
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Entity "${selectedDoc.EntityName}" not found in metadata`, 'error', 5000);
|
|
1575
|
+
this.IsDetecting = false;
|
|
1576
|
+
this.DetectionStage = '';
|
|
1577
|
+
this.cdr.detectChanges();
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
// DD-1: Track whether merging is available for the selected entity
|
|
1581
|
+
this.MergeEnabled = entityInfo.AllowRecordMerge;
|
|
1582
|
+
this.ShowMergeWarningBanner = !entityInfo.AllowRecordMerge;
|
|
1583
|
+
dupeRun.EntityID = entityInfo.ID;
|
|
1584
|
+
dupeRun.StartedByUserID = new Metadata().CurrentUser.ID;
|
|
1585
|
+
dupeRun.StartedAt = new Date();
|
|
1586
|
+
dupeRun.ProcessingStatus = 'In Progress';
|
|
1587
|
+
dupeRun.ApprovalStatus = 'Pending';
|
|
1588
|
+
const saved = await dupeRun.Save();
|
|
1589
|
+
if (!saved) {
|
|
1590
|
+
console.error('Failed to create duplicate run:', dupeRun.LatestResult?.Message || 'unknown error');
|
|
1591
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Failed to start detection: ${dupeRun.LatestResult?.Message || 'unknown error'}`, 'error', 5000);
|
|
1592
|
+
this.IsDetecting = false;
|
|
1593
|
+
this.DetectionStage = '';
|
|
1594
|
+
this.cdr.detectChanges();
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
// Subscribe to progress using the run ID as PipelineRunID
|
|
1598
|
+
this.subscribeToPipelineProgress(dupeRun.ID);
|
|
1599
|
+
}
|
|
1600
|
+
catch (error) {
|
|
1601
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1602
|
+
console.error('[DuplicateDetection] Error starting detection:', msg);
|
|
1603
|
+
this.IsDetecting = false;
|
|
1604
|
+
this.DetectionStage = '';
|
|
444
1605
|
this.cdr.detectChanges();
|
|
445
1606
|
}
|
|
446
1607
|
}
|
|
@@ -456,18 +1617,124 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
456
1617
|
async RejectMatch(group) {
|
|
457
1618
|
await this.updateGroupApprovalStatus(group, 'Rejected');
|
|
458
1619
|
}
|
|
1620
|
+
// ════════════════════════════════════════════
|
|
1621
|
+
// Drag and Drop
|
|
1622
|
+
// ════════════════════════════════════════════
|
|
1623
|
+
/** The group currently being dragged */
|
|
1624
|
+
DraggedGroup = null;
|
|
1625
|
+
/** Which column is being dragged over (for highlight) */
|
|
1626
|
+
DragOverColumn = null;
|
|
1627
|
+
OnDragStart(event, group) {
|
|
1628
|
+
this.DraggedGroup = group;
|
|
1629
|
+
if (event.dataTransfer) {
|
|
1630
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
1631
|
+
event.dataTransfer.setData('text/plain', group.DetailId);
|
|
1632
|
+
}
|
|
1633
|
+
// Add a slight delay so the drag ghost renders before we add the dragging class
|
|
1634
|
+
setTimeout(() => this.cdr.detectChanges(), 0);
|
|
1635
|
+
}
|
|
1636
|
+
OnDragEnd() {
|
|
1637
|
+
this.DraggedGroup = null;
|
|
1638
|
+
this.DragOverColumn = null;
|
|
1639
|
+
this.cdr.detectChanges();
|
|
1640
|
+
}
|
|
1641
|
+
OnDragOver(event, column) {
|
|
1642
|
+
event.preventDefault(); // Required to allow drop
|
|
1643
|
+
if (event.dataTransfer) {
|
|
1644
|
+
event.dataTransfer.dropEffect = 'move';
|
|
1645
|
+
}
|
|
1646
|
+
if (this.DragOverColumn !== column) {
|
|
1647
|
+
this.DragOverColumn = column;
|
|
1648
|
+
this.cdr.detectChanges();
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
OnDragLeave(event, column) {
|
|
1652
|
+
// Only clear if leaving the column (not entering a child element)
|
|
1653
|
+
const related = event.relatedTarget;
|
|
1654
|
+
const columnEl = event.currentTarget;
|
|
1655
|
+
if (!columnEl.contains(related)) {
|
|
1656
|
+
if (this.DragOverColumn === column) {
|
|
1657
|
+
this.DragOverColumn = null;
|
|
1658
|
+
this.cdr.detectChanges();
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
async OnDrop(event, targetStatus) {
|
|
1663
|
+
event.preventDefault();
|
|
1664
|
+
this.DragOverColumn = null;
|
|
1665
|
+
if (!this.DraggedGroup)
|
|
1666
|
+
return;
|
|
1667
|
+
if (this.DraggedGroup.ApprovalStatus === targetStatus) {
|
|
1668
|
+
this.DraggedGroup = null;
|
|
1669
|
+
this.cdr.detectChanges();
|
|
1670
|
+
return; // Already in this column
|
|
1671
|
+
}
|
|
1672
|
+
const group = this.DraggedGroup;
|
|
1673
|
+
this.DraggedGroup = null;
|
|
1674
|
+
await this.updateGroupApprovalStatus(group, targetStatus);
|
|
1675
|
+
}
|
|
1676
|
+
/** Whether a card is currently being dragged */
|
|
1677
|
+
get IsDragging() {
|
|
1678
|
+
return this.DraggedGroup !== null;
|
|
1679
|
+
}
|
|
459
1680
|
/** Handle filter changes with debounce */
|
|
460
1681
|
OnFilterChange() {
|
|
461
1682
|
this.filterSubject.next();
|
|
462
1683
|
}
|
|
1684
|
+
/** Computed range bounds from actual data — used as min/max/placeholder for filter inputs */
|
|
1685
|
+
DataMinScore = 0;
|
|
1686
|
+
DataMaxScore = 1;
|
|
1687
|
+
DataMinDate = '';
|
|
1688
|
+
DataMaxDate = '';
|
|
1689
|
+
/** Compute the actual data ranges from AllGroups */
|
|
1690
|
+
computeDataRanges() {
|
|
1691
|
+
if (this.AllGroups.length === 0) {
|
|
1692
|
+
this.DataMinScore = 0;
|
|
1693
|
+
this.DataMaxScore = 1;
|
|
1694
|
+
this.DataMinDate = '';
|
|
1695
|
+
this.DataMaxDate = '';
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
let minScore = 1, maxScore = 0;
|
|
1699
|
+
let minDate = null, maxDate = null;
|
|
1700
|
+
for (const group of this.AllGroups) {
|
|
1701
|
+
if (group.HighestScore < minScore)
|
|
1702
|
+
minScore = group.HighestScore;
|
|
1703
|
+
if (group.HighestScore > maxScore)
|
|
1704
|
+
maxScore = group.HighestScore;
|
|
1705
|
+
const d = new Date(group.MatchedAt);
|
|
1706
|
+
if (!isNaN(d.getTime())) {
|
|
1707
|
+
if (!minDate || d < minDate)
|
|
1708
|
+
minDate = d;
|
|
1709
|
+
if (!maxDate || d > maxDate)
|
|
1710
|
+
maxDate = d;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
this.DataMinScore = Math.floor(minScore * 100) / 100;
|
|
1714
|
+
this.DataMaxScore = Math.ceil(maxScore * 100) / 100;
|
|
1715
|
+
this.DataMinDate = minDate ? this.toInputDate(minDate) : '';
|
|
1716
|
+
this.DataMaxDate = maxDate ? this.toInputDate(maxDate) : '';
|
|
1717
|
+
// Leave filters empty by default — no filtering until user explicitly sets values
|
|
1718
|
+
this.Filters.MinScore = 0;
|
|
1719
|
+
this.Filters.MaxScore = 1;
|
|
1720
|
+
this.Filters.DateFrom = '';
|
|
1721
|
+
this.Filters.DateTo = '';
|
|
1722
|
+
}
|
|
1723
|
+
/** Format a Date to YYYY-MM-DD for input[type=date] using local time */
|
|
1724
|
+
toInputDate(d) {
|
|
1725
|
+
const year = d.getFullYear();
|
|
1726
|
+
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
1727
|
+
const day = String(d.getDate()).padStart(2, '0');
|
|
1728
|
+
return `${year}-${month}-${day}`;
|
|
1729
|
+
}
|
|
463
1730
|
/** Clear all filters */
|
|
464
1731
|
ClearFilters() {
|
|
465
1732
|
this.Filters = {
|
|
466
|
-
EntityName: '',
|
|
1733
|
+
EntityName: this.EntityNames.length === 1 ? this.EntityNames[0] : '',
|
|
467
1734
|
MinScore: 0,
|
|
468
1735
|
MaxScore: 1,
|
|
469
1736
|
DateFrom: '',
|
|
470
|
-
DateTo: ''
|
|
1737
|
+
DateTo: '',
|
|
471
1738
|
};
|
|
472
1739
|
this.applyFilters();
|
|
473
1740
|
}
|
|
@@ -488,6 +1755,39 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
488
1755
|
return 'Low';
|
|
489
1756
|
}
|
|
490
1757
|
/** Format a date for display */
|
|
1758
|
+
/**
|
|
1759
|
+
* Format a composite key string (e.g., "ID|5A07433E-F36B-1410-8AA5-00F1597429B5")
|
|
1760
|
+
* into a readable format. For single-key entities, shows just the value truncated.
|
|
1761
|
+
* For composite keys, shows key: value pairs.
|
|
1762
|
+
*/
|
|
1763
|
+
/** Whether there are any non-skipped matches available for merging */
|
|
1764
|
+
get HasMergeableMatches() {
|
|
1765
|
+
return this.ComparisonMatches.some(m => m.Match.ApprovalStatus !== 'Rejected');
|
|
1766
|
+
}
|
|
1767
|
+
FormatRecordID(recordID) {
|
|
1768
|
+
if (!recordID)
|
|
1769
|
+
return '';
|
|
1770
|
+
const pairs = recordID.split('||');
|
|
1771
|
+
if (pairs.length === 1) {
|
|
1772
|
+
// Single key — extract just the value
|
|
1773
|
+
const parts = pairs[0].split('|');
|
|
1774
|
+
if (parts.length === 2) {
|
|
1775
|
+
const val = parts[1];
|
|
1776
|
+
// Truncate long UUIDs
|
|
1777
|
+
return val.length > 12 ? val.substring(0, 8) + '...' : val;
|
|
1778
|
+
}
|
|
1779
|
+
return recordID.length > 12 ? recordID.substring(0, 8) + '...' : recordID;
|
|
1780
|
+
}
|
|
1781
|
+
// Composite key — show key: truncated value pairs
|
|
1782
|
+
return pairs.map(p => {
|
|
1783
|
+
const parts = p.split('|');
|
|
1784
|
+
if (parts.length === 2) {
|
|
1785
|
+
const val = parts[1].length > 8 ? parts[1].substring(0, 8) + '...' : parts[1];
|
|
1786
|
+
return `${parts[0]}: ${val}`;
|
|
1787
|
+
}
|
|
1788
|
+
return p;
|
|
1789
|
+
}).join(', ');
|
|
1790
|
+
}
|
|
491
1791
|
FormatDate(date) {
|
|
492
1792
|
if (!date)
|
|
493
1793
|
return '';
|
|
@@ -496,9 +1796,9 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
496
1796
|
}
|
|
497
1797
|
/** Whether any filters are active */
|
|
498
1798
|
get HasActiveFilters() {
|
|
499
|
-
return this.Filters.EntityName !== '' ||
|
|
1799
|
+
return (this.EntityNames.length > 1 && this.Filters.EntityName !== '') ||
|
|
500
1800
|
this.Filters.MinScore > 0 ||
|
|
501
|
-
this.Filters.MaxScore < 1 ||
|
|
1801
|
+
(this.Filters.MaxScore > 0 && this.Filters.MaxScore < 1) ||
|
|
502
1802
|
this.Filters.DateFrom !== '' ||
|
|
503
1803
|
this.Filters.DateTo !== '';
|
|
504
1804
|
}
|
|
@@ -508,15 +1808,155 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
508
1808
|
this.applyFilters();
|
|
509
1809
|
});
|
|
510
1810
|
}
|
|
1811
|
+
/** Build entity document options from KnowledgeHubMetadataEngine cached entities */
|
|
1812
|
+
buildEntityDocumentOptionsFromEngine(docs) {
|
|
1813
|
+
this.EntityDocuments = docs.map(d => ({
|
|
1814
|
+
ID: d.ID,
|
|
1815
|
+
Name: d.Name ?? 'Unnamed',
|
|
1816
|
+
EntityName: d.Entity ?? '',
|
|
1817
|
+
PotentialMatchThreshold: this.normalizeDupeThreshold(d.PotentialMatchThreshold, 0.70),
|
|
1818
|
+
AbsoluteMatchThreshold: this.normalizeDupeThreshold(d.AbsoluteMatchThreshold, 0.95)
|
|
1819
|
+
}));
|
|
1820
|
+
// Auto-select the first entity document if available
|
|
1821
|
+
if (this.EntityDocuments.length > 0 && !this.SelectedEntityDocumentID) {
|
|
1822
|
+
this.SelectedEntityDocumentID = this.EntityDocuments[0].ID;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
/** Subscribe to PipelineProgress for a specific detection run */
|
|
1826
|
+
/**
|
|
1827
|
+
* Normalizes a duplicate threshold value — treats null, undefined, 0, and 1.0
|
|
1828
|
+
* as "not configured" and falls back to a sensible default.
|
|
1829
|
+
* Thresholds of exactly 1.0 mean "100% match only" which is effectively useless
|
|
1830
|
+
* for real-world duplicate detection.
|
|
1831
|
+
*/
|
|
1832
|
+
normalizeDupeThreshold(value, fallback) {
|
|
1833
|
+
if (value == null || value <= 0 || value >= 1.0) {
|
|
1834
|
+
return fallback;
|
|
1835
|
+
}
|
|
1836
|
+
return value;
|
|
1837
|
+
}
|
|
1838
|
+
/** Handle potential threshold slider change */
|
|
1839
|
+
OnPotentialThresholdChanged(value) {
|
|
1840
|
+
this.RunPotentialThreshold = value;
|
|
1841
|
+
}
|
|
1842
|
+
/** Handle absolute threshold slider change */
|
|
1843
|
+
OnAbsoluteThresholdChanged(value) {
|
|
1844
|
+
this.RunAbsoluteThreshold = value;
|
|
1845
|
+
}
|
|
1846
|
+
subscribeToPipelineProgress(pipelineRunID) {
|
|
1847
|
+
const provider = Metadata.Provider;
|
|
1848
|
+
const subscriptionQuery = `
|
|
1849
|
+
subscription PipelineProgress($pipelineRunID: String!) {
|
|
1850
|
+
PipelineProgress(pipelineRunID: $pipelineRunID) {
|
|
1851
|
+
PipelineRunID
|
|
1852
|
+
Stage
|
|
1853
|
+
TotalItems
|
|
1854
|
+
ProcessedItems
|
|
1855
|
+
CurrentItem
|
|
1856
|
+
PercentComplete
|
|
1857
|
+
ElapsedMs
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
`;
|
|
1861
|
+
let idleTimer = null;
|
|
1862
|
+
const finishDetection = (success) => {
|
|
1863
|
+
if (idleTimer)
|
|
1864
|
+
clearTimeout(idleTimer);
|
|
1865
|
+
rxSub?.unsubscribe();
|
|
1866
|
+
Promise.resolve().then(async () => {
|
|
1867
|
+
this.IsDetecting = false;
|
|
1868
|
+
this.DetectionStage = success ? 'Complete' : 'Error';
|
|
1869
|
+
this.DetectionProgress = success ? 100 : 0;
|
|
1870
|
+
if (success) {
|
|
1871
|
+
await this.LoadData();
|
|
1872
|
+
}
|
|
1873
|
+
this.cdr.detectChanges();
|
|
1874
|
+
});
|
|
1875
|
+
};
|
|
1876
|
+
const resetIdleTimer = () => {
|
|
1877
|
+
if (idleTimer)
|
|
1878
|
+
clearTimeout(idleTimer);
|
|
1879
|
+
idleTimer = setTimeout(() => {
|
|
1880
|
+
if (this.IsDetecting) {
|
|
1881
|
+
finishDetection(true);
|
|
1882
|
+
}
|
|
1883
|
+
}, 60000); // 60s timeout for duplicate detection (can be very long)
|
|
1884
|
+
};
|
|
1885
|
+
resetIdleTimer();
|
|
1886
|
+
// Reset phase tracking for this new subscription
|
|
1887
|
+
this.detectionRawStage = '';
|
|
1888
|
+
const sub = provider.subscribe(subscriptionQuery, { pipelineRunID });
|
|
1889
|
+
const rxSub = sub.pipe(takeUntil(this.destroy$)).subscribe({
|
|
1890
|
+
next: (data) => {
|
|
1891
|
+
const progress = data['PipelineProgress'];
|
|
1892
|
+
if (!progress)
|
|
1893
|
+
return;
|
|
1894
|
+
const stage = progress['Stage'];
|
|
1895
|
+
const pct = Math.max(0, Math.min(100, progress['PercentComplete']));
|
|
1896
|
+
const currentItem = progress['CurrentItem'];
|
|
1897
|
+
// Detect phase transitions vs. within-phase updates
|
|
1898
|
+
const isNewPhase = stage !== this.detectionRawStage;
|
|
1899
|
+
if (isNewPhase) {
|
|
1900
|
+
// New phase: reset progress and update display
|
|
1901
|
+
this.detectionRawStage = stage;
|
|
1902
|
+
this.DetectionProgress = pct;
|
|
1903
|
+
this.DetectionStage = this.formatDetectionStage(stage);
|
|
1904
|
+
}
|
|
1905
|
+
else {
|
|
1906
|
+
// Same phase: only move forward (never backward)
|
|
1907
|
+
if (pct >= this.DetectionProgress) {
|
|
1908
|
+
this.DetectionProgress = pct;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
this.DetectionCurrentItem = currentItem ?? '';
|
|
1912
|
+
this.cdr.detectChanges();
|
|
1913
|
+
if (stage === 'complete') {
|
|
1914
|
+
finishDetection(true);
|
|
1915
|
+
}
|
|
1916
|
+
else if (stage === 'error') {
|
|
1917
|
+
finishDetection(false);
|
|
1918
|
+
}
|
|
1919
|
+
else {
|
|
1920
|
+
resetIdleTimer();
|
|
1921
|
+
}
|
|
1922
|
+
},
|
|
1923
|
+
error: (err) => {
|
|
1924
|
+
console.error('[DuplicateDetection] Pipeline subscription error:', err);
|
|
1925
|
+
finishDetection(false);
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
/** Format detection stage names for display */
|
|
1930
|
+
formatDetectionStage(stage) {
|
|
1931
|
+
const stageMap = {
|
|
1932
|
+
'vectorize': 'Vectorizing records',
|
|
1933
|
+
'autotag': 'Analyzing matches',
|
|
1934
|
+
'extract': 'Querying vector database',
|
|
1935
|
+
'complete': 'Complete',
|
|
1936
|
+
'error': 'Error'
|
|
1937
|
+
};
|
|
1938
|
+
return stageMap[stage] ?? stage;
|
|
1939
|
+
}
|
|
511
1940
|
/** Extract unique entity names from loaded runs */
|
|
512
1941
|
extractEntityNames() {
|
|
513
1942
|
const nameSet = new Set();
|
|
1943
|
+
// Extract from runs first
|
|
514
1944
|
for (const run of this.Runs) {
|
|
515
1945
|
if (run.Entity) {
|
|
516
1946
|
nameSet.add(run.Entity);
|
|
517
1947
|
}
|
|
518
1948
|
}
|
|
1949
|
+
// Also extract from groups (covers cases where runs failed but details/matches exist)
|
|
1950
|
+
for (const group of this.AllGroups) {
|
|
1951
|
+
if (group.EntityName && group.EntityName !== 'Unknown') {
|
|
1952
|
+
nameSet.add(group.EntityName);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
519
1955
|
this.EntityNames = Array.from(nameSet).sort();
|
|
1956
|
+
// Auto-select if only one entity — no point showing "All Entities" for a single option
|
|
1957
|
+
if (this.EntityNames.length === 1) {
|
|
1958
|
+
this.Filters.EntityName = this.EntityNames[0];
|
|
1959
|
+
}
|
|
520
1960
|
}
|
|
521
1961
|
/**
|
|
522
1962
|
* Build DuplicateGroup objects by joining details to their matches,
|
|
@@ -545,20 +1985,30 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
545
1985
|
continue; // Skip details with no matches
|
|
546
1986
|
}
|
|
547
1987
|
const run = runMap.get(detail.DuplicateRunID);
|
|
548
|
-
const entityName = run?.Entity ?? 'Unknown';
|
|
549
1988
|
const highestScore = this.computeHighestScore(detailMatches);
|
|
550
1989
|
const dominantStatus = this.computeDominantApprovalStatus(detailMatches);
|
|
551
1990
|
const latestMatchDate = this.computeLatestMatchDate(detailMatches);
|
|
1991
|
+
// Parse source record metadata (stored as JSON by the detector)
|
|
1992
|
+
const metadata = this.parseRecordMetadata(detail.RecordMetadata);
|
|
1993
|
+
const entityName = metadata.Entity || run?.Entity || 'Unknown';
|
|
1994
|
+
const entityIcon = metadata.EntityIcon || 'fa-solid fa-database';
|
|
1995
|
+
const recordName = this.resolveRecordName(metadata, entityName, detail.RecordID);
|
|
1996
|
+
// Build top match summaries from match metadata
|
|
1997
|
+
const topMatchSummaries = this.buildTopMatchSummaries(detailMatches, 3);
|
|
552
1998
|
this.AllGroups.push({
|
|
553
1999
|
DetailId: detail.ID,
|
|
554
2000
|
RunId: detail.DuplicateRunID,
|
|
555
2001
|
RecordId: detail.RecordID,
|
|
556
2002
|
EntityName: entityName,
|
|
2003
|
+
EntityIcon: entityIcon,
|
|
2004
|
+
RecordName: recordName,
|
|
557
2005
|
ApprovalStatus: dominantStatus,
|
|
558
2006
|
MatchCount: detailMatches.length,
|
|
559
2007
|
HighestScore: highestScore,
|
|
560
2008
|
Matches: detailMatches,
|
|
561
|
-
MatchedAt: latestMatchDate
|
|
2009
|
+
MatchedAt: latestMatchDate,
|
|
2010
|
+
Metadata: metadata,
|
|
2011
|
+
TopMatchSummaries: topMatchSummaries,
|
|
562
2012
|
});
|
|
563
2013
|
}
|
|
564
2014
|
}
|
|
@@ -572,6 +2022,689 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
572
2022
|
}
|
|
573
2023
|
return max;
|
|
574
2024
|
}
|
|
2025
|
+
// ════════════════════════════════════════════
|
|
2026
|
+
// Comparison Panel
|
|
2027
|
+
// ════════════════════════════════════════════
|
|
2028
|
+
/** Open the comparison slide-in panel for a group — loads real entity records */
|
|
2029
|
+
async OpenComparison(group) {
|
|
2030
|
+
this.ComparisonGroup = group;
|
|
2031
|
+
this.ComparisonShowAllFields = true;
|
|
2032
|
+
this.SurvivorColumnIndex = 0;
|
|
2033
|
+
this.ComparisonClosing = false;
|
|
2034
|
+
this.ComparisonLoading = true;
|
|
2035
|
+
this.ComparisonFields = [];
|
|
2036
|
+
this.ComparisonMatches = [];
|
|
2037
|
+
this.comparisonRecords.clear();
|
|
2038
|
+
this.ComparisonDependencies.clear();
|
|
2039
|
+
this.DepsExpandedColumns.clear();
|
|
2040
|
+
this.depsEntityGroupExpanded.clear();
|
|
2041
|
+
this.ShowMergeConfirm = false;
|
|
2042
|
+
this.cdr.detectChanges();
|
|
2043
|
+
// Load actual entity records and dependencies in parallel
|
|
2044
|
+
await Promise.all([
|
|
2045
|
+
this.loadComparisonRecords(group),
|
|
2046
|
+
this.loadComparisonDependencies(group)
|
|
2047
|
+
]);
|
|
2048
|
+
this.buildComparisonData();
|
|
2049
|
+
this.ComparisonLoading = false;
|
|
2050
|
+
this.cdr.detectChanges();
|
|
2051
|
+
}
|
|
2052
|
+
/** Close the comparison panel with slide-out animation */
|
|
2053
|
+
CloseComparison() {
|
|
2054
|
+
this.ShowMergeConfirm = false;
|
|
2055
|
+
this.ComparisonClosing = true;
|
|
2056
|
+
this.cdr.detectChanges();
|
|
2057
|
+
setTimeout(() => {
|
|
2058
|
+
this.ComparisonGroup = null;
|
|
2059
|
+
this.ComparisonClosing = false;
|
|
2060
|
+
this.ComparisonFields = [];
|
|
2061
|
+
this.ComparisonMatches = [];
|
|
2062
|
+
this.ComparisonDependencies.clear();
|
|
2063
|
+
this.DepsExpandedColumns.clear();
|
|
2064
|
+
this.depsEntityGroupExpanded.clear();
|
|
2065
|
+
this.cdr.detectChanges();
|
|
2066
|
+
}, 250);
|
|
2067
|
+
}
|
|
2068
|
+
/** Get visible fields based on the toggle state */
|
|
2069
|
+
GetVisibleFields() {
|
|
2070
|
+
return this.ComparisonShowAllFields
|
|
2071
|
+
? this.ComparisonFields
|
|
2072
|
+
: this.ComparisonFields.filter(f => f.HasDifference);
|
|
2073
|
+
}
|
|
2074
|
+
/** Case-insensitive, trimmed comparison of two field values */
|
|
2075
|
+
AreValuesEqual(a, b) {
|
|
2076
|
+
if (a == null && b == null)
|
|
2077
|
+
return true;
|
|
2078
|
+
if (a == null || b == null)
|
|
2079
|
+
return false;
|
|
2080
|
+
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
2081
|
+
}
|
|
2082
|
+
/** Set the surviving record column index */
|
|
2083
|
+
SetSurvivor(columnIndex) {
|
|
2084
|
+
this.SurvivorColumnIndex = columnIndex;
|
|
2085
|
+
// When switching survivor, reset all field selections to the new survivor
|
|
2086
|
+
for (const field of this.ComparisonFields) {
|
|
2087
|
+
field.SelectedColumnIndex = columnIndex;
|
|
2088
|
+
}
|
|
2089
|
+
this.cdr.detectChanges();
|
|
2090
|
+
}
|
|
2091
|
+
/** Select all field values from a specific column */
|
|
2092
|
+
UseAllFieldsFrom(columnIndex) {
|
|
2093
|
+
for (const field of this.ComparisonFields) {
|
|
2094
|
+
// Only select if the column has a value for this field
|
|
2095
|
+
const val = columnIndex === 0 ? field.SourceValue : field.MatchValues[columnIndex - 1];
|
|
2096
|
+
if (val != null) {
|
|
2097
|
+
field.SelectedColumnIndex = columnIndex;
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
this.cdr.detectChanges();
|
|
2101
|
+
}
|
|
2102
|
+
/** Select a specific field value from a column */
|
|
2103
|
+
SelectFieldValue(field, columnIndex) {
|
|
2104
|
+
field.SelectedColumnIndex = columnIndex;
|
|
2105
|
+
this.cdr.detectChanges();
|
|
2106
|
+
}
|
|
2107
|
+
/** Check if all fields are selected from a given column */
|
|
2108
|
+
AllFieldsSelectedFrom(columnIndex) {
|
|
2109
|
+
return this.ComparisonFields.every(f => f.SelectedColumnIndex === columnIndex);
|
|
2110
|
+
}
|
|
2111
|
+
/** Count how many fields are cherry-picked from non-survivor columns */
|
|
2112
|
+
CherryPickedCount() {
|
|
2113
|
+
return this.ComparisonFields.filter(f => f.SelectedColumnIndex !== this.SurvivorColumnIndex).length;
|
|
2114
|
+
}
|
|
2115
|
+
/** Get the name of the surviving record */
|
|
2116
|
+
SurvivorName() {
|
|
2117
|
+
if (!this.ComparisonGroup)
|
|
2118
|
+
return '';
|
|
2119
|
+
if (this.SurvivorColumnIndex === 0)
|
|
2120
|
+
return this.ComparisonGroup.RecordName;
|
|
2121
|
+
const match = this.ComparisonMatches[this.SurvivorColumnIndex - 1];
|
|
2122
|
+
return match?.Name || 'Unknown';
|
|
2123
|
+
}
|
|
2124
|
+
/** Get the primary key display string for the surviving record */
|
|
2125
|
+
SurvivorKeyDisplay() {
|
|
2126
|
+
if (!this.ComparisonGroup)
|
|
2127
|
+
return '';
|
|
2128
|
+
const keyStr = this.getCompositeKeyStringForColumn(this.SurvivorColumnIndex);
|
|
2129
|
+
if (!keyStr)
|
|
2130
|
+
return '';
|
|
2131
|
+
const ck = new CompositeKey();
|
|
2132
|
+
ck.LoadFromConcatenatedString(keyStr);
|
|
2133
|
+
return ck.KeyValuePairs.map(kv => `${kv.FieldName}: ${kv.Value}`).join(', ');
|
|
2134
|
+
}
|
|
2135
|
+
// ════════════════════════════════════════════
|
|
2136
|
+
// Dependencies
|
|
2137
|
+
// ════════════════════════════════════════════
|
|
2138
|
+
/** Get dependencies for a column (0 = source, 1+ = match index) */
|
|
2139
|
+
GetDepsForColumn(columnIndex) {
|
|
2140
|
+
const keyStr = this.getCompositeKeyStringForColumn(columnIndex);
|
|
2141
|
+
return keyStr ? (this.ComparisonDependencies.get(keyStr) ?? []) : [];
|
|
2142
|
+
}
|
|
2143
|
+
/** Get deps grouped by related entity for a column */
|
|
2144
|
+
GetGroupedDeps(columnIndex) {
|
|
2145
|
+
const deps = this.GetDepsForColumn(columnIndex);
|
|
2146
|
+
const grouped = new Map();
|
|
2147
|
+
for (const dep of deps) {
|
|
2148
|
+
const name = dep.RelatedEntityName;
|
|
2149
|
+
grouped.set(name, (grouped.get(name) ?? 0) + 1);
|
|
2150
|
+
}
|
|
2151
|
+
return Array.from(grouped.entries())
|
|
2152
|
+
.map(([Entity, Count]) => ({ Entity, Count }))
|
|
2153
|
+
.sort((a, b) => b.Count - a.Count);
|
|
2154
|
+
}
|
|
2155
|
+
/** Cached dependent records loaded on demand, keyed by "columnIndex::entityName" */
|
|
2156
|
+
depRecordsCache = new Map();
|
|
2157
|
+
/** Tracks which entity groups are currently loading */
|
|
2158
|
+
depRecordsLoading = new Set();
|
|
2159
|
+
/** Get cached dependent records for an entity group (empty until expanded and loaded) */
|
|
2160
|
+
GetDepRecords(columnIndex, entityName) {
|
|
2161
|
+
return this.depRecordsCache.get(`${columnIndex}::${entityName}`) ?? [];
|
|
2162
|
+
}
|
|
2163
|
+
/** Check if dep records are loading for an entity group */
|
|
2164
|
+
IsDepRecordsLoading(columnIndex, entityName) {
|
|
2165
|
+
return this.depRecordsLoading.has(`${columnIndex}::${entityName}`);
|
|
2166
|
+
}
|
|
2167
|
+
/** Navigate to a dependent record */
|
|
2168
|
+
OpenDepRecord(record) {
|
|
2169
|
+
if (record.PrimaryKey) {
|
|
2170
|
+
this.navigationService.OpenEntityRecord(record.EntityName, record.PrimaryKey);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
/** Toggle expanded state for a specific entity group — lazy-loads records on first expand */
|
|
2174
|
+
async ToggleDepEntityGroup(columnIndex, entityName) {
|
|
2175
|
+
const key = `${columnIndex}::${entityName}`;
|
|
2176
|
+
if (this.depsEntityGroupExpanded.has(key)) {
|
|
2177
|
+
this.depsEntityGroupExpanded.delete(key);
|
|
2178
|
+
this.cdr.detectChanges();
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
this.depsEntityGroupExpanded.add(key);
|
|
2182
|
+
this.cdr.detectChanges();
|
|
2183
|
+
// Lazy-load actual dependent records on first expand
|
|
2184
|
+
if (!this.depRecordsCache.has(key)) {
|
|
2185
|
+
await this.loadDepRecordsForGroup(columnIndex, entityName);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
/** Load dependent records for an entity group via RunView */
|
|
2189
|
+
async loadDepRecordsForGroup(columnIndex, relatedEntityName) {
|
|
2190
|
+
const key = `${columnIndex}::${relatedEntityName}`;
|
|
2191
|
+
this.depRecordsLoading.add(key);
|
|
2192
|
+
this.cdr.detectChanges();
|
|
2193
|
+
try {
|
|
2194
|
+
// Get the FK field name from the dependency info
|
|
2195
|
+
const deps = this.GetDepsForColumn(columnIndex)
|
|
2196
|
+
.filter(d => d.RelatedEntityName === relatedEntityName);
|
|
2197
|
+
if (deps.length === 0)
|
|
2198
|
+
return;
|
|
2199
|
+
const dep = deps[0];
|
|
2200
|
+
const fkFieldName = dep.FieldName;
|
|
2201
|
+
// Get the parent record's primary key value from the column's composite key string
|
|
2202
|
+
const keyStr = this.getCompositeKeyStringForColumn(columnIndex);
|
|
2203
|
+
if (!keyStr)
|
|
2204
|
+
return;
|
|
2205
|
+
const parentCK = new CompositeKey();
|
|
2206
|
+
parentCK.LoadFromConcatenatedString(keyStr);
|
|
2207
|
+
const parentKeyValue = parentCK.KeyValuePairs[0]?.Value;
|
|
2208
|
+
if (!parentKeyValue)
|
|
2209
|
+
return;
|
|
2210
|
+
// Query the related entity for records pointing at this parent via the FK field
|
|
2211
|
+
const rv = new RunView();
|
|
2212
|
+
const md = new Metadata();
|
|
2213
|
+
const relatedEntityInfo = md.Entities.find(e => e.Name === relatedEntityName);
|
|
2214
|
+
const nameField = relatedEntityInfo?.NameField;
|
|
2215
|
+
const pkFieldName = relatedEntityInfo?.FirstPrimaryKey?.Name || 'ID';
|
|
2216
|
+
const result = await rv.RunView({
|
|
2217
|
+
EntityName: relatedEntityName,
|
|
2218
|
+
ExtraFilter: `${fkFieldName}='${parentKeyValue}'`,
|
|
2219
|
+
Fields: nameField ? [pkFieldName, nameField.Name] : [pkFieldName],
|
|
2220
|
+
MaxRows: 50,
|
|
2221
|
+
ResultType: 'simple',
|
|
2222
|
+
});
|
|
2223
|
+
const records = [];
|
|
2224
|
+
if (result.Success && result.Results) {
|
|
2225
|
+
for (const row of result.Results) {
|
|
2226
|
+
const pk = new CompositeKey([{ FieldName: pkFieldName, Value: String(row[pkFieldName] || '') }]);
|
|
2227
|
+
const name = nameField ? String(row[nameField.Name] || '') : String(row[pkFieldName] || '');
|
|
2228
|
+
records.push({ Name: name || pk.Values(), PrimaryKey: pk, EntityName: relatedEntityName });
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
this.depRecordsCache.set(key, records);
|
|
2232
|
+
}
|
|
2233
|
+
catch (error) {
|
|
2234
|
+
console.warn('[DuplicateDetection] Error loading dep records:', error);
|
|
2235
|
+
this.depRecordsCache.set(key, []);
|
|
2236
|
+
}
|
|
2237
|
+
finally {
|
|
2238
|
+
this.depRecordsLoading.delete(key);
|
|
2239
|
+
this.cdr.detectChanges();
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
/** Check if a specific entity group is expanded */
|
|
2243
|
+
IsDepEntityGroupExpanded(columnIndex, entityName) {
|
|
2244
|
+
return this.depsEntityGroupExpanded.has(`${columnIndex}::${entityName}`);
|
|
2245
|
+
}
|
|
2246
|
+
/** Get total dependency count for a column */
|
|
2247
|
+
GetTotalDeps(columnIndex) {
|
|
2248
|
+
return this.GetDepsForColumn(columnIndex).length;
|
|
2249
|
+
}
|
|
2250
|
+
/** Toggle expanded state for dependency details in a column */
|
|
2251
|
+
ToggleDepsExpanded(columnIndex) {
|
|
2252
|
+
if (this.DepsExpandedColumns.has(columnIndex)) {
|
|
2253
|
+
this.DepsExpandedColumns.delete(columnIndex);
|
|
2254
|
+
}
|
|
2255
|
+
else {
|
|
2256
|
+
this.DepsExpandedColumns.add(columnIndex);
|
|
2257
|
+
}
|
|
2258
|
+
this.cdr.detectChanges();
|
|
2259
|
+
}
|
|
2260
|
+
/** Check if dependency details are expanded for a column */
|
|
2261
|
+
IsDepsExpanded(columnIndex) {
|
|
2262
|
+
return this.DepsExpandedColumns.has(columnIndex);
|
|
2263
|
+
}
|
|
2264
|
+
/** Get the column index with the most dependencies */
|
|
2265
|
+
GetMaxDepsColumnIndex() {
|
|
2266
|
+
let maxDeps = -1;
|
|
2267
|
+
let maxIndex = 0;
|
|
2268
|
+
const totalColumns = 1 + this.ComparisonMatches.length;
|
|
2269
|
+
for (let i = 0; i < totalColumns; i++) {
|
|
2270
|
+
const count = this.GetTotalDeps(i);
|
|
2271
|
+
if (count > maxDeps) {
|
|
2272
|
+
maxDeps = count;
|
|
2273
|
+
maxIndex = i;
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
return maxIndex;
|
|
2277
|
+
}
|
|
2278
|
+
/** Get the composite key string for a column index */
|
|
2279
|
+
getCompositeKeyStringForColumn(columnIndex) {
|
|
2280
|
+
if (!this.ComparisonGroup)
|
|
2281
|
+
return null;
|
|
2282
|
+
if (columnIndex === 0)
|
|
2283
|
+
return this.ComparisonGroup.RecordId;
|
|
2284
|
+
const match = this.ComparisonMatches[columnIndex - 1];
|
|
2285
|
+
return match?.Match.MatchRecordID ?? null;
|
|
2286
|
+
}
|
|
2287
|
+
/** Get the name for a column index */
|
|
2288
|
+
GetColumnName(columnIndex) {
|
|
2289
|
+
if (!this.ComparisonGroup)
|
|
2290
|
+
return '';
|
|
2291
|
+
if (columnIndex === 0)
|
|
2292
|
+
return this.ComparisonGroup.RecordName;
|
|
2293
|
+
const match = this.ComparisonMatches[columnIndex - 1];
|
|
2294
|
+
return match?.Name ?? 'Unknown';
|
|
2295
|
+
}
|
|
2296
|
+
// ════════════════════════════════════════════
|
|
2297
|
+
// ════════════════════════════════════════════
|
|
2298
|
+
// Merge Confirmation
|
|
2299
|
+
// ════════════════════════════════════════════
|
|
2300
|
+
/** Open the merge confirmation panel */
|
|
2301
|
+
OpenMergeConfirm() {
|
|
2302
|
+
this.ShowMergeConfirm = true;
|
|
2303
|
+
this.cdr.detectChanges();
|
|
2304
|
+
}
|
|
2305
|
+
/** Close the merge confirmation panel */
|
|
2306
|
+
CloseMergeConfirm() {
|
|
2307
|
+
this.ShowMergeConfirm = false;
|
|
2308
|
+
this.cdr.detectChanges();
|
|
2309
|
+
}
|
|
2310
|
+
/** Get the list of cherry-picked field overrides (fields picked from non-survivor columns) */
|
|
2311
|
+
GetCherryPickedFields() {
|
|
2312
|
+
return this.ComparisonFields
|
|
2313
|
+
.filter(f => f.SelectedColumnIndex !== this.SurvivorColumnIndex)
|
|
2314
|
+
.map(f => {
|
|
2315
|
+
const value = f.SelectedColumnIndex === 0
|
|
2316
|
+
? f.SourceValue
|
|
2317
|
+
: f.MatchValues[f.SelectedColumnIndex - 1];
|
|
2318
|
+
return {
|
|
2319
|
+
FieldName: f.FieldName,
|
|
2320
|
+
DisplayName: f.DisplayName,
|
|
2321
|
+
Value: value ?? '(empty)',
|
|
2322
|
+
SourceName: this.GetColumnName(f.SelectedColumnIndex)
|
|
2323
|
+
};
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
/** Get non-surviving columns with their dependency counts */
|
|
2327
|
+
/** Get non-surviving columns excluding skipped (Rejected) matches for merge confirmation display */
|
|
2328
|
+
GetNonSurvivorColumns() {
|
|
2329
|
+
const result = [];
|
|
2330
|
+
const totalColumns = 1 + this.ComparisonMatches.length;
|
|
2331
|
+
for (let i = 0; i < totalColumns; i++) {
|
|
2332
|
+
if (i === this.SurvivorColumnIndex)
|
|
2333
|
+
continue;
|
|
2334
|
+
// Skip records marked as Rejected/Skipped
|
|
2335
|
+
if (i > 0) {
|
|
2336
|
+
const matchInfo = this.ComparisonMatches[i - 1];
|
|
2337
|
+
if (matchInfo?.Match?.ApprovalStatus === 'Rejected')
|
|
2338
|
+
continue;
|
|
2339
|
+
}
|
|
2340
|
+
result.push({
|
|
2341
|
+
ColumnIndex: i,
|
|
2342
|
+
Name: this.GetColumnName(i),
|
|
2343
|
+
DepCount: this.GetTotalDeps(i)
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
return result;
|
|
2347
|
+
}
|
|
2348
|
+
/** Execute the merge operation */
|
|
2349
|
+
async ExecuteMerge() {
|
|
2350
|
+
if (!this.ComparisonGroup || this.IsMerging)
|
|
2351
|
+
return;
|
|
2352
|
+
this.IsMerging = true;
|
|
2353
|
+
this.cdr.detectChanges();
|
|
2354
|
+
try {
|
|
2355
|
+
const request = new RecordMergeRequest();
|
|
2356
|
+
request.EntityName = this.ComparisonGroup.EntityName;
|
|
2357
|
+
// Build surviving record composite key
|
|
2358
|
+
const survivorKeyStr = this.getCompositeKeyStringForColumn(this.SurvivorColumnIndex);
|
|
2359
|
+
if (!survivorKeyStr)
|
|
2360
|
+
return;
|
|
2361
|
+
const survivorKey = new CompositeKey();
|
|
2362
|
+
survivorKey.SimpleLoadFromURLSegment(survivorKeyStr);
|
|
2363
|
+
request.SurvivingRecordCompositeKey = survivorKey;
|
|
2364
|
+
// Build records to merge (non-survivors)
|
|
2365
|
+
request.RecordsToMerge = this.buildNonSurvivorKeys();
|
|
2366
|
+
// Build field map for cherry-picked fields
|
|
2367
|
+
const cherryPicked = this.GetCherryPickedFields();
|
|
2368
|
+
if (cherryPicked.length > 0) {
|
|
2369
|
+
request.FieldMap = cherryPicked.map(f => ({
|
|
2370
|
+
FieldName: f.FieldName,
|
|
2371
|
+
Value: f.Value
|
|
2372
|
+
}));
|
|
2373
|
+
}
|
|
2374
|
+
const result = await Metadata.Provider.MergeRecords(request);
|
|
2375
|
+
if (result.Success) {
|
|
2376
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Successfully merged ${request.RecordsToMerge.length + 1} records into one.`, 'success', 5000);
|
|
2377
|
+
// Close both panels and reload
|
|
2378
|
+
this.ShowMergeConfirm = false;
|
|
2379
|
+
this.ComparisonGroup = null;
|
|
2380
|
+
this.ComparisonFields = [];
|
|
2381
|
+
this.ComparisonMatches = [];
|
|
2382
|
+
this.ComparisonDependencies.clear();
|
|
2383
|
+
this.DepsExpandedColumns.clear();
|
|
2384
|
+
this.depsEntityGroupExpanded.clear();
|
|
2385
|
+
await this.LoadData();
|
|
2386
|
+
}
|
|
2387
|
+
else {
|
|
2388
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Merge failed: ${result.OverallStatus}`, 'error', 5000);
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
catch (error) {
|
|
2392
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2393
|
+
console.error('[DuplicateDetection] Merge error:', msg);
|
|
2394
|
+
MJNotificationService.Instance.CreateSimpleNotification(`Merge error: ${msg}`, 'error', 5000);
|
|
2395
|
+
}
|
|
2396
|
+
finally {
|
|
2397
|
+
this.IsMerging = false;
|
|
2398
|
+
this.cdr.detectChanges();
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
/** Build CompositeKey array for non-surviving records */
|
|
2402
|
+
buildNonSurvivorKeys() {
|
|
2403
|
+
const keys = [];
|
|
2404
|
+
const totalColumns = 1 + this.ComparisonMatches.length;
|
|
2405
|
+
for (let i = 0; i < totalColumns; i++) {
|
|
2406
|
+
if (i === this.SurvivorColumnIndex)
|
|
2407
|
+
continue;
|
|
2408
|
+
// Skip records that the user has marked as "Skipped" (Rejected)
|
|
2409
|
+
if (i > 0) {
|
|
2410
|
+
const matchInfo = this.ComparisonMatches[i - 1];
|
|
2411
|
+
if (matchInfo?.Match?.ApprovalStatus === 'Rejected')
|
|
2412
|
+
continue;
|
|
2413
|
+
}
|
|
2414
|
+
const keyStr = this.getCompositeKeyStringForColumn(i);
|
|
2415
|
+
if (keyStr) {
|
|
2416
|
+
const ck = new CompositeKey();
|
|
2417
|
+
ck.SimpleLoadFromURLSegment(keyStr);
|
|
2418
|
+
keys.push(ck);
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
return keys;
|
|
2422
|
+
}
|
|
2423
|
+
/** Approve an individual match */
|
|
2424
|
+
async ApproveIndividualMatch(matchInfo) {
|
|
2425
|
+
this.IsSaving = true;
|
|
2426
|
+
matchInfo.Match.ApprovalStatus = 'Approved';
|
|
2427
|
+
await matchInfo.Match.Save();
|
|
2428
|
+
this.IsSaving = false;
|
|
2429
|
+
this.cdr.detectChanges();
|
|
2430
|
+
}
|
|
2431
|
+
/** Reject an individual match (skip it from merge) */
|
|
2432
|
+
async RejectIndividualMatch(matchInfo) {
|
|
2433
|
+
this.IsSaving = true;
|
|
2434
|
+
matchInfo.Match.ApprovalStatus = 'Rejected';
|
|
2435
|
+
await matchInfo.Match.Save();
|
|
2436
|
+
this.IsSaving = false;
|
|
2437
|
+
this.cdr.detectChanges();
|
|
2438
|
+
}
|
|
2439
|
+
/** Undo a rejected individual match (restore to Pending) */
|
|
2440
|
+
async UndoRejectIndividualMatch(matchInfo) {
|
|
2441
|
+
this.IsSaving = true;
|
|
2442
|
+
matchInfo.Match.ApprovalStatus = 'Pending';
|
|
2443
|
+
await matchInfo.Match.Save();
|
|
2444
|
+
this.IsSaving = false;
|
|
2445
|
+
this.cdr.detectChanges();
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Load the actual entity records for the source + all matches in one RunView call.
|
|
2449
|
+
* Record IDs are stored as composite key strings (e.g., "ID|uuid") — we parse each
|
|
2450
|
+
* into a CompositeKey, generate WHERE clauses, and OR them together for one query.
|
|
2451
|
+
* Results stored in comparisonRecords map keyed by the composite key string.
|
|
2452
|
+
*/
|
|
2453
|
+
async loadComparisonRecords(group) {
|
|
2454
|
+
this.comparisonRecords.clear();
|
|
2455
|
+
// Collect all composite key strings (source + matches)
|
|
2456
|
+
const keyStrings = [group.RecordId];
|
|
2457
|
+
for (const m of group.Matches) {
|
|
2458
|
+
if (m.MatchRecordID) {
|
|
2459
|
+
keyStrings.push(m.MatchRecordID);
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
// Parse each into a CompositeKey and build WHERE clauses
|
|
2463
|
+
const whereClauses = [];
|
|
2464
|
+
for (const keyStr of keyStrings) {
|
|
2465
|
+
const ck = new CompositeKey();
|
|
2466
|
+
ck.SimpleLoadFromURLSegment(keyStr);
|
|
2467
|
+
if (ck.KeyValuePairs.length > 0) {
|
|
2468
|
+
whereClauses.push(`(${ck.ToWhereClause()})`);
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
if (whereClauses.length === 0)
|
|
2472
|
+
return;
|
|
2473
|
+
// Single RunView with all records OR'd together
|
|
2474
|
+
const rv = new RunView();
|
|
2475
|
+
const result = await rv.RunView({
|
|
2476
|
+
EntityName: group.EntityName,
|
|
2477
|
+
ExtraFilter: whereClauses.join(' OR '),
|
|
2478
|
+
ResultType: 'simple',
|
|
2479
|
+
});
|
|
2480
|
+
if (result.Success && result.Results) {
|
|
2481
|
+
// Get entity info to know primary key field name
|
|
2482
|
+
const md = new Metadata();
|
|
2483
|
+
const entityInfo = md.Entities.find(e => e.Name === group.EntityName);
|
|
2484
|
+
const pkFieldName = entityInfo?.FirstPrimaryKey?.Name || 'ID';
|
|
2485
|
+
for (const record of result.Results) {
|
|
2486
|
+
const pkValue = String(record[pkFieldName] || '');
|
|
2487
|
+
// Store keyed by both raw PK value and the composite key string format
|
|
2488
|
+
this.comparisonRecords.set(pkValue, record);
|
|
2489
|
+
this.comparisonRecords.set(`${pkFieldName}|${pkValue}`, record);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
/**
|
|
2494
|
+
* Load dependencies for all records (source + matches) in parallel.
|
|
2495
|
+
* Each record's deps are stored in ComparisonDependencies keyed by composite key string.
|
|
2496
|
+
*/
|
|
2497
|
+
async loadComparisonDependencies(group) {
|
|
2498
|
+
const provider = Metadata.Provider;
|
|
2499
|
+
const keyStrings = [group.RecordId];
|
|
2500
|
+
for (const m of group.Matches) {
|
|
2501
|
+
if (m.MatchRecordID) {
|
|
2502
|
+
keyStrings.push(m.MatchRecordID);
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
const promises = keyStrings.map(async (keyStr) => {
|
|
2506
|
+
const ck = new CompositeKey();
|
|
2507
|
+
ck.SimpleLoadFromURLSegment(keyStr);
|
|
2508
|
+
if (ck.KeyValuePairs.length === 0)
|
|
2509
|
+
return;
|
|
2510
|
+
try {
|
|
2511
|
+
const deps = await provider.GetRecordDependencies(group.EntityName, ck);
|
|
2512
|
+
this.ComparisonDependencies.set(keyStr, deps);
|
|
2513
|
+
}
|
|
2514
|
+
catch (error) {
|
|
2515
|
+
console.warn(`[DuplicateDetection] Failed to load deps for ${keyStr}:`, error);
|
|
2516
|
+
this.ComparisonDependencies.set(keyStr, []);
|
|
2517
|
+
}
|
|
2518
|
+
});
|
|
2519
|
+
await Promise.all(promises);
|
|
2520
|
+
}
|
|
2521
|
+
/** Get the field value for a record from the loaded entity records map */
|
|
2522
|
+
getRecordFieldValue(recordId, fieldName) {
|
|
2523
|
+
// Try the composite key string directly (e.g., "ID|uuid")
|
|
2524
|
+
let record = this.comparisonRecords.get(recordId);
|
|
2525
|
+
if (!record) {
|
|
2526
|
+
// Try extracting just the value from "FieldName|Value" format
|
|
2527
|
+
const parts = recordId.split('|');
|
|
2528
|
+
const id = parts.length >= 2 ? parts[1] : parts[0];
|
|
2529
|
+
record = this.comparisonRecords.get(id);
|
|
2530
|
+
}
|
|
2531
|
+
if (!record) {
|
|
2532
|
+
// Case-insensitive search as a fallback (UUIDs may differ in case)
|
|
2533
|
+
const lower = recordId.toLowerCase();
|
|
2534
|
+
for (const [key, val] of this.comparisonRecords.entries()) {
|
|
2535
|
+
if (key.toLowerCase() === lower || key.toLowerCase().endsWith(lower)) {
|
|
2536
|
+
record = val;
|
|
2537
|
+
break;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
if (!record)
|
|
2542
|
+
return undefined;
|
|
2543
|
+
const val = record[fieldName];
|
|
2544
|
+
return val != null ? String(val) : undefined;
|
|
2545
|
+
}
|
|
2546
|
+
/** Build comparison data from loaded entity records */
|
|
2547
|
+
buildComparisonData() {
|
|
2548
|
+
if (!this.ComparisonGroup)
|
|
2549
|
+
return;
|
|
2550
|
+
// Build match infos (use loaded record data for names)
|
|
2551
|
+
this.ComparisonMatches = this.ComparisonGroup.Matches
|
|
2552
|
+
.sort((a, b) => b.MatchProbability - a.MatchProbability)
|
|
2553
|
+
.map(m => {
|
|
2554
|
+
const meta = this.parseRecordMetadata(m.RecordMetadata);
|
|
2555
|
+
// Resolve display name using IsNameField fields from entity metadata
|
|
2556
|
+
const entityName = this.ComparisonGroup.EntityName;
|
|
2557
|
+
const resolvedName = this.resolveMatchName(entityName, m.MatchRecordID, meta);
|
|
2558
|
+
return {
|
|
2559
|
+
Match: m,
|
|
2560
|
+
Name: resolvedName,
|
|
2561
|
+
Score: m.MatchProbability,
|
|
2562
|
+
Metadata: meta,
|
|
2563
|
+
DiffCount: 0,
|
|
2564
|
+
};
|
|
2565
|
+
});
|
|
2566
|
+
// Get entity field info for display names and ordering
|
|
2567
|
+
const md = new Metadata();
|
|
2568
|
+
const entityInfo = md.Entities.find(e => e.Name === this.ComparisonGroup.EntityName);
|
|
2569
|
+
const entityFields = entityInfo?.Fields ?? [];
|
|
2570
|
+
const skip = new Set(['ID', '__mj_CreatedAt', '__mj_UpdatedAt']);
|
|
2571
|
+
const rows = [];
|
|
2572
|
+
// Use entity fields in sequence order — values come from loaded entity records
|
|
2573
|
+
const sourceId = this.ComparisonGroup.RecordId;
|
|
2574
|
+
const matchRecordIds = this.ComparisonGroup.Matches
|
|
2575
|
+
.sort((a, b) => b.MatchProbability - a.MatchProbability)
|
|
2576
|
+
.map(m => m.MatchRecordID);
|
|
2577
|
+
// Sort fields: IsNameField first, then DefaultInView, then by Sequence
|
|
2578
|
+
const sortedFields = [...entityFields].sort((a, b) => {
|
|
2579
|
+
if (a.IsNameField !== b.IsNameField)
|
|
2580
|
+
return a.IsNameField ? -1 : 1;
|
|
2581
|
+
if (a.DefaultInView !== b.DefaultInView)
|
|
2582
|
+
return a.DefaultInView ? -1 : 1;
|
|
2583
|
+
return (a.Sequence ?? 9999) - (b.Sequence ?? 9999);
|
|
2584
|
+
});
|
|
2585
|
+
for (const field of sortedFields) {
|
|
2586
|
+
if (skip.has(field.Name) || field.IsPrimaryKey)
|
|
2587
|
+
continue;
|
|
2588
|
+
const sourceVal = this.getRecordFieldValue(sourceId, field.Name);
|
|
2589
|
+
const matchVals = matchRecordIds.map(rid => this.getRecordFieldValue(rid, field.Name));
|
|
2590
|
+
const hasDiff = matchVals.some(mv => !this.AreValuesEqual(sourceVal, mv));
|
|
2591
|
+
const hasData = sourceVal != null || matchVals.some(v => v != null);
|
|
2592
|
+
if (!hasData)
|
|
2593
|
+
continue;
|
|
2594
|
+
rows.push({
|
|
2595
|
+
FieldName: field.Name,
|
|
2596
|
+
DisplayName: field.DisplayName || field.Name,
|
|
2597
|
+
Category: field.Category || null,
|
|
2598
|
+
SourceValue: sourceVal,
|
|
2599
|
+
MatchValues: matchVals,
|
|
2600
|
+
HasDifference: hasDiff,
|
|
2601
|
+
SelectedColumnIndex: 0,
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
this.ComparisonFields = rows;
|
|
2605
|
+
// Compute diff counts per match
|
|
2606
|
+
for (let mi = 0; mi < this.ComparisonMatches.length; mi++) {
|
|
2607
|
+
this.ComparisonMatches[mi].DiffCount = rows.filter(r => !this.AreValuesEqual(r.SourceValue, r.MatchValues[mi])).length;
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
/** Parse RecordMetadata JSON from a detail or match entity */
|
|
2611
|
+
parseRecordMetadata(json) {
|
|
2612
|
+
if (!json)
|
|
2613
|
+
return {};
|
|
2614
|
+
try {
|
|
2615
|
+
return JSON.parse(json);
|
|
2616
|
+
}
|
|
2617
|
+
catch {
|
|
2618
|
+
return {};
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
/** Build top N match summaries with parsed names and scores */
|
|
2622
|
+
buildTopMatchSummaries(matches, limit) {
|
|
2623
|
+
return matches
|
|
2624
|
+
.slice(0, limit)
|
|
2625
|
+
.map(m => {
|
|
2626
|
+
const meta = this.parseRecordMetadata(m.RecordMetadata);
|
|
2627
|
+
return {
|
|
2628
|
+
Name: this.resolveRecordName(meta, this.SelectedEntityFilter || 'Unknown', m.MatchRecordID ?? ''),
|
|
2629
|
+
Score: m.MatchProbability,
|
|
2630
|
+
};
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Resolve match record name from loaded entity records using IsNameField fields.
|
|
2635
|
+
* Falls back to metadata, then truncated record ID.
|
|
2636
|
+
*/
|
|
2637
|
+
resolveMatchName(entityName, matchRecordID, meta) {
|
|
2638
|
+
try {
|
|
2639
|
+
const md = new Metadata();
|
|
2640
|
+
const entityInfo = md.Entities.find(e => e.Name === entityName);
|
|
2641
|
+
if (entityInfo) {
|
|
2642
|
+
const nameFields = entityInfo.Fields
|
|
2643
|
+
.filter(f => f.IsNameField)
|
|
2644
|
+
.sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));
|
|
2645
|
+
if (nameFields.length > 0) {
|
|
2646
|
+
// Try loaded entity record data first
|
|
2647
|
+
const parts = nameFields
|
|
2648
|
+
.map(f => this.getRecordFieldValue(matchRecordID, f.Name))
|
|
2649
|
+
.filter(v => v != null && String(v).trim() !== '')
|
|
2650
|
+
.map(v => String(v));
|
|
2651
|
+
if (parts.length > 0)
|
|
2652
|
+
return parts.join(' ');
|
|
2653
|
+
// Fall back to vector metadata
|
|
2654
|
+
const metaParts = nameFields
|
|
2655
|
+
.map(f => meta[f.Name])
|
|
2656
|
+
.filter(v => v != null && String(v).trim() !== '')
|
|
2657
|
+
.map(v => String(v));
|
|
2658
|
+
if (metaParts.length > 0)
|
|
2659
|
+
return metaParts.join(' ');
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
catch { /* fall through */ }
|
|
2664
|
+
return meta.Name || this.FormatRecordID(matchRecordID ?? '');
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* Resolve record display name from metadata using entity IsNameField fields.
|
|
2668
|
+
* Combines multiple name fields (e.g., FirstName + LastName → "Sarah Chen").
|
|
2669
|
+
* Falls back to metadata.Name, then recordID.
|
|
2670
|
+
*/
|
|
2671
|
+
resolveRecordName(metadata, entityName, recordID) {
|
|
2672
|
+
try {
|
|
2673
|
+
const md = new Metadata();
|
|
2674
|
+
const entityInfo = md.Entities.find(e => e.Name === entityName);
|
|
2675
|
+
if (entityInfo) {
|
|
2676
|
+
const nameFields = entityInfo.Fields
|
|
2677
|
+
.filter(f => f.IsNameField)
|
|
2678
|
+
.sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));
|
|
2679
|
+
if (nameFields.length > 0) {
|
|
2680
|
+
// 1. Try individual name fields from metadata (new rich metadata)
|
|
2681
|
+
const metaParts = nameFields
|
|
2682
|
+
.map(f => metadata[f.Name])
|
|
2683
|
+
.filter(v => v != null && String(v).trim() !== '')
|
|
2684
|
+
.map(v => String(v));
|
|
2685
|
+
if (metaParts.length > 0)
|
|
2686
|
+
return metaParts.join(' ');
|
|
2687
|
+
// 2. Try loaded entity records (available in comparison panel)
|
|
2688
|
+
const recordParts = nameFields
|
|
2689
|
+
.map(f => this.getRecordFieldValue(recordID, f.Name))
|
|
2690
|
+
.filter(v => v != null && String(v).trim() !== '')
|
|
2691
|
+
.map(v => String(v));
|
|
2692
|
+
if (recordParts.length > 0)
|
|
2693
|
+
return recordParts.join(' ');
|
|
2694
|
+
}
|
|
2695
|
+
// 3. Single NameField fallback
|
|
2696
|
+
if (entityInfo.NameField && metadata[entityInfo.NameField.Name]) {
|
|
2697
|
+
return String(metadata[entityInfo.NameField.Name]);
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
catch { /* fall through */ }
|
|
2702
|
+
// 4. Heuristic — skip single-char or initial-only names from old sparse metadata
|
|
2703
|
+
const metaName = metadata.Name;
|
|
2704
|
+
if (metaName && metaName.length > 2)
|
|
2705
|
+
return metaName;
|
|
2706
|
+
return metadata.Title || this.FormatRecordID(recordID);
|
|
2707
|
+
}
|
|
575
2708
|
/**
|
|
576
2709
|
* Determine the dominant approval status for a group of matches.
|
|
577
2710
|
* If any match is Pending, the group is Pending.
|
|
@@ -616,15 +2749,17 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
616
2749
|
if (this.Filters.MinScore > 0) {
|
|
617
2750
|
filtered = filtered.filter(g => g.HighestScore >= this.Filters.MinScore);
|
|
618
2751
|
}
|
|
619
|
-
if (this.Filters.MaxScore < 1) {
|
|
2752
|
+
if (this.Filters.MaxScore > 0 && this.Filters.MaxScore < 1) {
|
|
620
2753
|
filtered = filtered.filter(g => g.HighestScore <= this.Filters.MaxScore);
|
|
621
2754
|
}
|
|
622
2755
|
if (this.Filters.DateFrom) {
|
|
623
|
-
const
|
|
2756
|
+
const parts = this.Filters.DateFrom.split('-');
|
|
2757
|
+
const from = new Date(+parts[0], +parts[1] - 1, +parts[2], 0, 0, 0, 0);
|
|
624
2758
|
filtered = filtered.filter(g => new Date(g.MatchedAt) >= from);
|
|
625
2759
|
}
|
|
626
2760
|
if (this.Filters.DateTo) {
|
|
627
|
-
const
|
|
2761
|
+
const parts = this.Filters.DateTo.split('-');
|
|
2762
|
+
const to = new Date(+parts[0], +parts[1] - 1, +parts[2], 23, 59, 59, 999);
|
|
628
2763
|
filtered = filtered.filter(g => new Date(g.MatchedAt) <= to);
|
|
629
2764
|
}
|
|
630
2765
|
this.PendingGroups = filtered.filter(g => g.ApprovalStatus === 'Pending');
|
|
@@ -632,10 +2767,7 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
632
2767
|
this.RejectedGroups = filtered.filter(g => g.ApprovalStatus === 'Rejected');
|
|
633
2768
|
this.cdr.detectChanges();
|
|
634
2769
|
}
|
|
635
|
-
/** Update the ApprovalStatus of all matches within a group and re-sort.
|
|
636
|
-
* Since this.Matches already contains entity_object instances, we update and save them directly
|
|
637
|
-
* instead of loading each one individually from the database.
|
|
638
|
-
*/
|
|
2770
|
+
/** Update the ApprovalStatus of all matches within a group and re-sort. */
|
|
639
2771
|
async updateGroupApprovalStatus(group, status) {
|
|
640
2772
|
this.IsSaving = true;
|
|
641
2773
|
this.cdr.detectChanges();
|
|
@@ -657,78 +2789,107 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
657
2789
|
}
|
|
658
2790
|
}
|
|
659
2791
|
static ɵfac = /*@__PURE__*/ (() => { let ɵDuplicateDetectionResourceComponent_BaseFactory; return function DuplicateDetectionResourceComponent_Factory(__ngFactoryType__) { return (ɵDuplicateDetectionResourceComponent_BaseFactory || (ɵDuplicateDetectionResourceComponent_BaseFactory = i0.ɵɵgetInheritedFactory(DuplicateDetectionResourceComponent)))(__ngFactoryType__ || DuplicateDetectionResourceComponent); }; })();
|
|
660
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DuplicateDetectionResourceComponent, selectors: [["app-duplicate-detection-resource"]],
|
|
2792
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DuplicateDetectionResourceComponent, selectors: [["app-duplicate-detection-resource"]], hostBindings: function DuplicateDetectionResourceComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
2793
|
+
i0.ɵɵlistener("keydown.escape", function DuplicateDetectionResourceComponent_keydown_escape_HostBindingHandler() { return ctx.OnEscapeKey(); }, i0.ɵɵresolveDocument);
|
|
2794
|
+
} }, inputs: { EmbeddedMode: "EmbeddedMode" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 70, vars: 29, 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, "merge-warning-banner"], [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"], [1, "fa-solid", "fa-triangle-exclamation"], ["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", "title"], [1, "merge-disabled-hint"], ["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-approval-actions"], ["title", "Skip this match (exclude from merge)", 1, "match-action-btn", "match-skip-btn"], ["title", "Skip this match (exclude from merge)", 1, "match-action-btn", "match-skip-btn", 3, "click"], [1, "match-status-badge"], ["title", "Undo skip", 1, "match-action-btn", "match-undo-btn"], ["title", "Undo skip", 1, "match-action-btn", "match-undo-btn", 3, "click"], [1, "fa-solid", "fa-undo"], [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, "fa-solid", "fa-info-circle"], [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) {
|
|
661
2795
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "h2", 3);
|
|
662
2796
|
i0.ɵɵelement(4, "i", 4);
|
|
663
2797
|
i0.ɵɵtext(5, " Duplicate Detection ");
|
|
664
|
-
i0.ɵɵelementEnd()()
|
|
665
|
-
i0.ɵɵelementStart(6, "div", 5)(7, "div", 6)(8, "
|
|
666
|
-
i0.ɵɵ
|
|
2798
|
+
i0.ɵɵelementEnd()();
|
|
2799
|
+
i0.ɵɵelementStart(6, "div", 5)(7, "div", 6)(8, "select", 7);
|
|
2800
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_select_ngModelChange_8_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.SelectedEntityDocumentID, $event) || (ctx.SelectedEntityDocumentID = $event); return $event; });
|
|
2801
|
+
i0.ɵɵelementStart(9, "option", 8);
|
|
2802
|
+
i0.ɵɵtext(10, "Select entity document...");
|
|
2803
|
+
i0.ɵɵelementEnd();
|
|
2804
|
+
i0.ɵɵrepeaterCreate(11, DuplicateDetectionResourceComponent_For_12_Template, 2, 3, "option", 9, _forTrack0);
|
|
667
2805
|
i0.ɵɵelementEnd();
|
|
668
|
-
i0.ɵɵelementStart(
|
|
669
|
-
i0.ɵɵ
|
|
2806
|
+
i0.ɵɵelementStart(13, "button", 10);
|
|
2807
|
+
i0.ɵɵlistener("click", function DuplicateDetectionResourceComponent_Template_button_click_13_listener() { return ctx.RunDetection(); });
|
|
2808
|
+
i0.ɵɵconditionalCreate(14, DuplicateDetectionResourceComponent_Conditional_14_Template, 2, 0)(15, DuplicateDetectionResourceComponent_Conditional_15_Template, 2, 0);
|
|
2809
|
+
i0.ɵɵelementEnd()()()();
|
|
2810
|
+
i0.ɵɵconditionalCreate(16, DuplicateDetectionResourceComponent_Conditional_16_Template, 10, 5, "div", 11);
|
|
2811
|
+
i0.ɵɵconditionalCreate(17, DuplicateDetectionResourceComponent_Conditional_17_Template, 19, 12, "div", 12);
|
|
2812
|
+
i0.ɵɵelementStart(18, "div", 13)(19, "div", 14)(20, "div", 15);
|
|
2813
|
+
i0.ɵɵtext(21);
|
|
2814
|
+
i0.ɵɵelementEnd();
|
|
2815
|
+
i0.ɵɵelementStart(22, "div", 16);
|
|
2816
|
+
i0.ɵɵtext(23, "Total Groups");
|
|
670
2817
|
i0.ɵɵelementEnd()();
|
|
671
|
-
i0.ɵɵelementStart(
|
|
672
|
-
i0.ɵɵtext(
|
|
2818
|
+
i0.ɵɵelementStart(24, "div", 17)(25, "div", 15);
|
|
2819
|
+
i0.ɵɵtext(26);
|
|
673
2820
|
i0.ɵɵelementEnd();
|
|
674
|
-
i0.ɵɵelementStart(
|
|
675
|
-
i0.ɵɵtext(
|
|
2821
|
+
i0.ɵɵelementStart(27, "div", 16);
|
|
2822
|
+
i0.ɵɵtext(28, "Pending");
|
|
676
2823
|
i0.ɵɵelementEnd()();
|
|
677
|
-
i0.ɵɵelementStart(
|
|
678
|
-
i0.ɵɵtext(
|
|
2824
|
+
i0.ɵɵelementStart(29, "div", 18)(30, "div", 15);
|
|
2825
|
+
i0.ɵɵtext(31);
|
|
679
2826
|
i0.ɵɵelementEnd();
|
|
680
|
-
i0.ɵɵelementStart(
|
|
681
|
-
i0.ɵɵtext(
|
|
2827
|
+
i0.ɵɵelementStart(32, "div", 16);
|
|
2828
|
+
i0.ɵɵtext(33, "Approved");
|
|
682
2829
|
i0.ɵɵelementEnd()();
|
|
683
|
-
i0.ɵɵelementStart(
|
|
684
|
-
i0.ɵɵtext(
|
|
2830
|
+
i0.ɵɵelementStart(34, "div", 19)(35, "div", 15);
|
|
2831
|
+
i0.ɵɵtext(36);
|
|
685
2832
|
i0.ɵɵelementEnd();
|
|
686
|
-
i0.ɵɵelementStart(
|
|
687
|
-
i0.ɵɵtext(
|
|
2833
|
+
i0.ɵɵelementStart(37, "div", 16);
|
|
2834
|
+
i0.ɵɵtext(38, "Rejected");
|
|
688
2835
|
i0.ɵɵelementEnd()()();
|
|
689
|
-
i0.ɵɵelementStart(
|
|
690
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
691
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
692
|
-
i0.ɵɵ
|
|
693
|
-
i0.ɵɵ
|
|
694
|
-
i0.ɵɵelementEnd();
|
|
695
|
-
i0.ɵɵrepeaterCreate(32, DuplicateDetectionResourceComponent_For_33_Template, 2, 2, "option", 16, i0.ɵɵrepeaterTrackByIdentity);
|
|
2836
|
+
i0.ɵɵelementStart(39, "div", 20)(40, "div", 21)(41, "select", 22);
|
|
2837
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_select_ngModelChange_41_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.EntityName, $event) || (ctx.Filters.EntityName = $event); return $event; });
|
|
2838
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_select_ngModelChange_41_listener() { return ctx.OnFilterChange(); });
|
|
2839
|
+
i0.ɵɵconditionalCreate(42, DuplicateDetectionResourceComponent_Conditional_42_Template, 2, 0, "option", 8);
|
|
2840
|
+
i0.ɵɵrepeaterCreate(43, DuplicateDetectionResourceComponent_For_44_Template, 2, 2, "option", 9, i0.ɵɵrepeaterTrackByIdentity);
|
|
696
2841
|
i0.ɵɵelementEnd();
|
|
697
|
-
i0.ɵɵelementStart(
|
|
698
|
-
i0.ɵɵtext(
|
|
2842
|
+
i0.ɵɵelementStart(45, "div", 23)(46, "label", 24);
|
|
2843
|
+
i0.ɵɵtext(47, "Min Score");
|
|
699
2844
|
i0.ɵɵelementEnd();
|
|
700
|
-
i0.ɵɵelementStart(
|
|
701
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
702
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
2845
|
+
i0.ɵɵelementStart(48, "input", 25);
|
|
2846
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_48_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.MinScore, $event) || (ctx.Filters.MinScore = $event); return $event; });
|
|
2847
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_48_listener() { return ctx.OnFilterChange(); });
|
|
703
2848
|
i0.ɵɵelementEnd()();
|
|
704
|
-
i0.ɵɵelementStart(
|
|
705
|
-
i0.ɵɵtext(
|
|
2849
|
+
i0.ɵɵelementStart(49, "div", 23)(50, "label", 24);
|
|
2850
|
+
i0.ɵɵtext(51, "Max Score");
|
|
706
2851
|
i0.ɵɵelementEnd();
|
|
707
|
-
i0.ɵɵelementStart(
|
|
708
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
709
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
2852
|
+
i0.ɵɵelementStart(52, "input", 25);
|
|
2853
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_52_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.MaxScore, $event) || (ctx.Filters.MaxScore = $event); return $event; });
|
|
2854
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_52_listener() { return ctx.OnFilterChange(); });
|
|
710
2855
|
i0.ɵɵelementEnd()();
|
|
711
|
-
i0.ɵɵelementStart(
|
|
712
|
-
i0.ɵɵtext(
|
|
2856
|
+
i0.ɵɵelementStart(53, "div", 23)(54, "label", 24);
|
|
2857
|
+
i0.ɵɵtext(55, "From");
|
|
713
2858
|
i0.ɵɵelementEnd();
|
|
714
|
-
i0.ɵɵelementStart(
|
|
715
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
716
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
2859
|
+
i0.ɵɵelementStart(56, "input", 26);
|
|
2860
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_56_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.DateFrom, $event) || (ctx.Filters.DateFrom = $event); return $event; });
|
|
2861
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_56_listener() { return ctx.OnFilterChange(); });
|
|
717
2862
|
i0.ɵɵelementEnd()();
|
|
718
|
-
i0.ɵɵelementStart(
|
|
719
|
-
i0.ɵɵtext(
|
|
2863
|
+
i0.ɵɵelementStart(57, "div", 23)(58, "label", 24);
|
|
2864
|
+
i0.ɵɵtext(59, "To");
|
|
720
2865
|
i0.ɵɵelementEnd();
|
|
721
|
-
i0.ɵɵelementStart(
|
|
722
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
723
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
2866
|
+
i0.ɵɵelementStart(60, "input", 26);
|
|
2867
|
+
i0.ɵɵtwoWayListener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_60_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.Filters.DateTo, $event) || (ctx.Filters.DateTo = $event); return $event; });
|
|
2868
|
+
i0.ɵɵlistener("ngModelChange", function DuplicateDetectionResourceComponent_Template_input_ngModelChange_60_listener() { return ctx.OnFilterChange(); });
|
|
724
2869
|
i0.ɵɵelementEnd()()();
|
|
725
|
-
i0.ɵɵconditionalCreate(
|
|
2870
|
+
i0.ɵɵconditionalCreate(61, DuplicateDetectionResourceComponent_Conditional_61_Template, 3, 0, "button", 27);
|
|
726
2871
|
i0.ɵɵelementEnd();
|
|
727
|
-
i0.ɵɵconditionalCreate(
|
|
728
|
-
i0.ɵɵconditionalCreate(
|
|
2872
|
+
i0.ɵɵconditionalCreate(62, DuplicateDetectionResourceComponent_Conditional_62_Template, 3, 0, "div", 28);
|
|
2873
|
+
i0.ɵɵconditionalCreate(63, DuplicateDetectionResourceComponent_Conditional_63_Template, 2, 0, "div", 29)(64, DuplicateDetectionResourceComponent_Conditional_64_Template, 2, 0, "div", 29)(65, DuplicateDetectionResourceComponent_Conditional_65_Template, 6, 0, "div", 30)(66, DuplicateDetectionResourceComponent_Conditional_66_Template, 34, 12, "div", 31);
|
|
2874
|
+
i0.ɵɵconditionalCreate(67, DuplicateDetectionResourceComponent_Conditional_67_Template, 2, 0, "div", 32);
|
|
2875
|
+
i0.ɵɵconditionalCreate(68, DuplicateDetectionResourceComponent_Conditional_68_Template, 68, 42);
|
|
2876
|
+
i0.ɵɵconditionalCreate(69, DuplicateDetectionResourceComponent_Conditional_69_Template, 37, 7, "div", 33);
|
|
729
2877
|
i0.ɵɵelementEnd();
|
|
730
2878
|
} if (rf & 2) {
|
|
731
|
-
i0.ɵɵadvance(
|
|
2879
|
+
i0.ɵɵadvance(8);
|
|
2880
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.SelectedEntityDocumentID);
|
|
2881
|
+
i0.ɵɵproperty("disabled", ctx.IsDetecting);
|
|
2882
|
+
i0.ɵɵadvance(3);
|
|
2883
|
+
i0.ɵɵrepeater(ctx.EntityDocuments);
|
|
2884
|
+
i0.ɵɵadvance(2);
|
|
2885
|
+
i0.ɵɵproperty("disabled", ctx.IsDetecting || !ctx.SelectedEntityDocumentID);
|
|
2886
|
+
i0.ɵɵadvance();
|
|
2887
|
+
i0.ɵɵconditional(ctx.IsDetecting ? 14 : 15);
|
|
2888
|
+
i0.ɵɵadvance(2);
|
|
2889
|
+
i0.ɵɵconditional(ctx.IsDetecting ? 16 : -1);
|
|
2890
|
+
i0.ɵɵadvance();
|
|
2891
|
+
i0.ɵɵconditional(ctx.SelectedDocumentThresholds ? 17 : -1);
|
|
2892
|
+
i0.ɵɵadvance(4);
|
|
732
2893
|
i0.ɵɵtextInterpolate(ctx.TotalGroupCount);
|
|
733
2894
|
i0.ɵɵadvance(5);
|
|
734
2895
|
i0.ɵɵtextInterpolate(ctx.PendingCount);
|
|
@@ -738,23 +2899,36 @@ let DuplicateDetectionResourceComponent = class DuplicateDetectionResourceCompon
|
|
|
738
2899
|
i0.ɵɵtextInterpolate(ctx.RejectedCount);
|
|
739
2900
|
i0.ɵɵadvance(5);
|
|
740
2901
|
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.EntityName);
|
|
741
|
-
i0.ɵɵ
|
|
2902
|
+
i0.ɵɵproperty("disabled", ctx.EntityNames.length <= 1);
|
|
2903
|
+
i0.ɵɵadvance();
|
|
2904
|
+
i0.ɵɵconditional(ctx.EntityNames.length > 1 ? 42 : -1);
|
|
2905
|
+
i0.ɵɵadvance();
|
|
742
2906
|
i0.ɵɵrepeater(ctx.EntityNames);
|
|
743
2907
|
i0.ɵɵadvance(5);
|
|
2908
|
+
i0.ɵɵproperty("placeholder", ctx.DataMinScore);
|
|
744
2909
|
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.MinScore);
|
|
745
2910
|
i0.ɵɵadvance(4);
|
|
2911
|
+
i0.ɵɵproperty("placeholder", ctx.DataMaxScore);
|
|
746
2912
|
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.MaxScore);
|
|
747
2913
|
i0.ɵɵadvance(4);
|
|
2914
|
+
i0.ɵɵproperty("min", ctx.DataMinDate)("max", ctx.DataMaxDate);
|
|
748
2915
|
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.DateFrom);
|
|
749
2916
|
i0.ɵɵadvance(4);
|
|
2917
|
+
i0.ɵɵproperty("min", ctx.DataMinDate)("max", ctx.DataMaxDate);
|
|
750
2918
|
i0.ɵɵtwoWayProperty("ngModel", ctx.Filters.DateTo);
|
|
751
2919
|
i0.ɵɵadvance();
|
|
752
|
-
i0.ɵɵconditional(ctx.HasActiveFilters ?
|
|
2920
|
+
i0.ɵɵconditional(ctx.HasActiveFilters ? 61 : -1);
|
|
753
2921
|
i0.ɵɵadvance();
|
|
754
|
-
i0.ɵɵconditional(ctx.
|
|
755
|
-
i0.ɵɵadvance(
|
|
756
|
-
i0.ɵɵconditional(ctx.
|
|
757
|
-
} }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.NumberValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.MinValidator, i1.MaxValidator, i1.NgModel, i2.LoadingComponent], styles: ["\n\n\n\n\n\n.duplicate-detection-container[_ngcontent-%COMP%] {\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\n\n\n.page-header[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n\n\n\n.kpi-strip[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n margin-bottom: 16px;\n flex-shrink: 0;\n}\n\n.kpi-card[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n font-size: 28px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.kpi-label[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] .kpi-value[_ngcontent-%COMP%] {\n color: var(--mj-status-warning);\n}\n\n.kpi-approved[_ngcontent-%COMP%] .kpi-value[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n.kpi-rejected[_ngcontent-%COMP%] .kpi-value[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n\n\n\n.filter-bar[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-end;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.filter-select[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]: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[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.filter-label[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]: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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n min-height: 200px;\n}\n\n.empty-state[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--mj-text-disabled);\n}\n\n.empty-text[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.empty-subtext[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin: 0;\n}\n\n\n\n\n.kanban-board[_ngcontent-%COMP%] {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n overflow-x: auto;\n}\n\n.kanban-column[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n}\n\n.column-header-pending[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-warning);\n}\n\n.column-header-approved[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n}\n\n.column-header-approved[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n.column-header-rejected[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n}\n\n.column-header-rejected[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.column-title[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.column-count[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n}\n\n\n\n\n.kanban-card[_ngcontent-%COMP%] {\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}\n\n.kanban-card[_ngcontent-%COMP%]: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.card-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.entity-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n max-width: 160px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n\n\n.score-indicator[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.card-field[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.field-icon[_ngcontent-%COMP%] {\n width: 14px;\n text-align: center;\n font-size: 12px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n.field-value[_ngcontent-%COMP%] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.record-id[_ngcontent-%COMP%] {\n font-family: monospace;\n font-size: 12px;\n max-width: 180px;\n}\n\n.card-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n padding: 10px 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.action-btn[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.approve-btn[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-success) 20%, var(--mj-bg-surface));\n}\n\n.reject-btn[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-bg-surface));\n}\n\n\n\n\n@media (max-width: 768px) {\n .kanban-board[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .kanban-column[_ngcontent-%COMP%] {\n min-width: auto;\n max-height: 400px;\n }\n}\n\n@media (max-width: 480px) {\n .kpi-strip[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .filter-bar[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n }\n\n .filter-group[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n }\n\n .filter-select[_ngcontent-%COMP%] {\n min-width: auto;\n width: 100%;\n }\n\n .filter-input[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .card-actions[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .action-btn[_ngcontent-%COMP%] {\n width: 100%;\n }\n}\n\n\n\n\n.saving-overlay[_ngcontent-%COMP%] {\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}"] });
|
|
2922
|
+
i0.ɵɵconditional(ctx.ShowMergeWarningBanner ? 62 : -1);
|
|
2923
|
+
i0.ɵɵadvance();
|
|
2924
|
+
i0.ɵɵconditional(ctx.IsLoading ? 63 : ctx.IsLoadingResults ? 64 : ctx.TotalGroupCount === 0 ? 65 : 66);
|
|
2925
|
+
i0.ɵɵadvance(4);
|
|
2926
|
+
i0.ɵɵconditional(ctx.IsSaving ? 67 : -1);
|
|
2927
|
+
i0.ɵɵadvance();
|
|
2928
|
+
i0.ɵɵconditional(ctx.ComparisonGroup ? 68 : -1);
|
|
2929
|
+
i0.ɵɵadvance();
|
|
2930
|
+
i0.ɵɵconditional(ctx.ShowMergeConfirm && ctx.ComparisonGroup ? 69 : -1);
|
|
2931
|
+
} }, 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-approval-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n}\n\n.match-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 10px;\n border-radius: 6px;\n font-size: 0.7rem;\n font-weight: 500;\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n\n.match-skip-btn {\n background: var(--mj-bg-surface);\n color: var(--mj-status-error);\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-border-default));\n}\n\n.match-skip-btn:hover {\n background: var(--mj-status-error-bg);\n border-color: var(--mj-status-error);\n}\n\n.match-undo-btn {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n}\n\n.match-undo-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\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\n/* \u2550\u2550\u2550\u2550\u2550\u2550 Merge Disabled Hint \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.merge-disabled-hint {\n font-size: 11.5px;\n color: var(--mj-status-warning-text, #e65100);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-left: 4px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 Merge Warning Banner (inline, non-blocking) \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.merge-warning-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n margin-bottom: 12px;\n border-radius: 6px;\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n font-size: 13px;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.merge-warning-banner i {\n font-size: 14px;\n flex-shrink: 0;\n}\n"], encapsulation: 2 });
|
|
758
2932
|
};
|
|
759
2933
|
DuplicateDetectionResourceComponent = __decorate([
|
|
760
2934
|
RegisterClass(BaseResourceComponent, 'DuplicateDetectionResource')
|
|
@@ -762,11 +2936,14 @@ DuplicateDetectionResourceComponent = __decorate([
|
|
|
762
2936
|
export { DuplicateDetectionResourceComponent };
|
|
763
2937
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DuplicateDetectionResourceComponent, [{
|
|
764
2938
|
type: Component,
|
|
765
|
-
args: [{ standalone: false, selector: 'app-duplicate-detection-resource', 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>\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 <option value=\"\">All Entities</option>\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\"\n min=\"0\" max=\"1\" step=\"0.05\"\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\"\n min=\"0\" max=\"1\" step=\"0.05\"\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\"\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\"\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\">Run a duplicate detection scan to see results here.</p>\n </div>\n } @else {\n <!-- Kanban Board -->\n <div class=\"kanban-board\">\n <!-- Pending Column -->\n <div class=\"kanban-column\">\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 @for (group of PendingGroups; track group.DetailId) {\n <div class=\"kanban-card\">\n <div class=\"card-header\">\n <span class=\"entity-badge\">{{ group.EntityName }}</span>\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 <div class=\"card-field\">\n <i class=\"fa-solid fa-fingerprint field-icon\"></i>\n <span class=\"field-value record-id\" [title]=\"group.RecordId\">{{ group.RecordId }}</span>\n </div>\n <div class=\"card-field\">\n <i class=\"fa-solid fa-layer-group field-icon\"></i>\n <span class=\"field-value\">{{ group.MatchCount }} match{{ group.MatchCount !== 1 ? 'es' : '' }}</span>\n </div>\n <div class=\"card-field\">\n <i class=\"fa-solid fa-calendar field-icon\"></i>\n <span class=\"field-value\">{{ FormatDate(group.MatchedAt) }}</span>\n </div>\n </div>\n <div class=\"card-actions\">\n <button class=\"action-btn approve-btn\"\n [disabled]=\"IsSaving\"\n (click)=\"ApproveMatch(group)\">\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)\">\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\">\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 @for (group of ApprovedGroups; track group.DetailId) {\n <div class=\"kanban-card\">\n <div class=\"card-header\">\n <span class=\"entity-badge\">{{ group.EntityName }}</span>\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 <div class=\"card-field\">\n <i class=\"fa-solid fa-fingerprint field-icon\"></i>\n <span class=\"field-value record-id\" [title]=\"group.RecordId\">{{ group.RecordId }}</span>\n </div>\n <div class=\"card-field\">\n <i class=\"fa-solid fa-layer-group field-icon\"></i>\n <span class=\"field-value\">{{ group.MatchCount }} match{{ group.MatchCount !== 1 ? 'es' : '' }}</span>\n </div>\n <div class=\"card-field\">\n <i class=\"fa-solid fa-calendar field-icon\"></i>\n <span class=\"field-value\">{{ FormatDate(group.MatchedAt) }}</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\">\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 @for (group of RejectedGroups; track group.DetailId) {\n <div class=\"kanban-card\">\n <div class=\"card-header\">\n <span class=\"entity-badge\">{{ group.EntityName }}</span>\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 <div class=\"card-field\">\n <i class=\"fa-solid fa-fingerprint field-icon\"></i>\n <span class=\"field-value record-id\" [title]=\"group.RecordId\">{{ group.RecordId }}</span>\n </div>\n <div class=\"card-field\">\n <i class=\"fa-solid fa-layer-group field-icon\"></i>\n <span class=\"field-value\">{{ group.MatchCount }} match{{ group.MatchCount !== 1 ? 'es' : '' }}</span>\n </div>\n <div class=\"card-field\">\n <i class=\"fa-solid fa-calendar field-icon\"></i>\n <span class=\"field-value\">{{ FormatDate(group.MatchedAt) }}</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</div>\n", styles: ["/* ============================================================\n Duplicate Detection Kanban Board - Resource Component Styles\n All colors use MJ design tokens (--mj-*) exclusively.\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/* ---- 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: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}\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.card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.entity-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n max-width: 160px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\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: 6px;\n}\n\n.card-field {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.field-icon {\n width: 14px;\n text-align: center;\n font-size: 12px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n.field-value {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.record-id {\n font-family: monospace;\n font-size: 12px;\n max-width: 180px;\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"] }]
|
|
766
|
-
}], null, {
|
|
2939
|
+
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 <!-- Merge Warning Banner (non-blocking, shown when entity lacks AllowRecordMerge) -->\n @if (ShowMergeWarningBanner) {\n <div class=\"merge-warning-banner\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n Merging is not available for this entity. Detection results are read-only.\n </div>\n }\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 (IsLoadingResults) {\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 <!-- Per-match approval actions -->\n <div class=\"match-approval-actions\">\n @if (match.Match.ApprovalStatus === 'Pending') {\n <button class=\"match-action-btn match-skip-btn\"\n title=\"Skip this match (exclude from merge)\"\n (click)=\"RejectIndividualMatch(match)\">\n <i class=\"fa-solid fa-ban\"></i> Skip\n </button>\n } @else {\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 === 'Rejected' ? 'Skipped' : match.Match.ApprovalStatus }}\n </span>\n @if (match.Match.ApprovalStatus === 'Rejected') {\n <button class=\"match-action-btn match-undo-btn\"\n title=\"Undo skip\"\n (click)=\"UndoRejectIndividualMatch(match)\">\n <i class=\"fa-solid fa-undo\"></i> Undo\n </button>\n }\n }\n </div>\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 || !HasMergeableMatches || !MergeEnabled\"\n (click)=\"OpenMergeConfirm()\"\n [title]=\"!MergeEnabled ? 'Merging is not enabled for this entity' : HasMergeableMatches ? 'Merge non-skipped records' : 'All matches have been skipped'\">\n <i class=\"fa-solid fa-code-merge\"></i> Merge Records\n </button>\n @if (!MergeEnabled) {\n <span class=\"merge-disabled-hint\"><i class=\"fa-solid fa-info-circle\"></i> Merging disabled for this entity</span>\n }\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\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-approval-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n}\n\n.match-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 10px;\n border-radius: 6px;\n font-size: 0.7rem;\n font-weight: 500;\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n\n.match-skip-btn {\n background: var(--mj-bg-surface);\n color: var(--mj-status-error);\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-border-default));\n}\n\n.match-skip-btn:hover {\n background: var(--mj-status-error-bg);\n border-color: var(--mj-status-error);\n}\n\n.match-undo-btn {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n}\n\n.match-undo-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\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\n/* \u2550\u2550\u2550\u2550\u2550\u2550 Merge Disabled Hint \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.merge-disabled-hint {\n font-size: 11.5px;\n color: var(--mj-status-warning-text, #e65100);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-left: 4px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 Merge Warning Banner (inline, non-blocking) \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.merge-warning-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n margin-bottom: 12px;\n border-radius: 6px;\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n font-size: 13px;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.merge-warning-banner i {\n font-size: 14px;\n flex-shrink: 0;\n}\n"] }]
|
|
2940
|
+
}], null, { OnEscapeKey: [{
|
|
2941
|
+
type: HostListener,
|
|
2942
|
+
args: ['document:keydown.escape']
|
|
2943
|
+
}], EmbeddedMode: [{
|
|
767
2944
|
type: Input
|
|
768
2945
|
}] }); })();
|
|
769
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DuplicateDetectionResourceComponent, { className: "DuplicateDetectionResourceComponent", filePath: "src/AI/components/duplicates/duplicate-detection-resource.component.ts", lineNumber:
|
|
2946
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DuplicateDetectionResourceComponent, { className: "DuplicateDetectionResourceComponent", filePath: "src/AI/components/duplicates/duplicate-detection-resource.component.ts", lineNumber: 105 }); })();
|
|
770
2947
|
export function LoadDuplicateDetectionResource() {
|
|
771
2948
|
// Prevents tree-shaking
|
|
772
2949
|
}
|