@fgv/ts-res-ui-components 5.0.0-10
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/.rush/temp/03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log +271 -0
- package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +9 -0
- package/.rush/temp/operation/build/all.log +9 -0
- package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
- package/.rush/temp/operation/build/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +1111 -0
- package/README.md +18 -0
- package/REFACTORING_PLAN.md +171 -0
- package/config/jest.config.json +16 -0
- package/config/jest.setup.js +64 -0
- package/config/rig.json +16 -0
- package/lib/components/common/QualifierContextControl.d.ts +14 -0
- package/lib/components/common/QualifierContextControl.d.ts.map +1 -0
- package/lib/components/common/QualifierContextControl.js +78 -0
- package/lib/components/common/QualifierContextControl.js.map +1 -0
- package/lib/components/common/ResourceListView.d.ts +11 -0
- package/lib/components/common/ResourceListView.d.ts.map +1 -0
- package/lib/components/common/ResourceListView.js +20 -0
- package/lib/components/common/ResourceListView.js.map +1 -0
- package/lib/components/common/ResourceTreeView.d.ts +12 -0
- package/lib/components/common/ResourceTreeView.d.ts.map +1 -0
- package/lib/components/common/ResourceTreeView.js +162 -0
- package/lib/components/common/ResourceTreeView.js.map +1 -0
- package/lib/components/forms/HierarchyEditor.d.ts +10 -0
- package/lib/components/forms/HierarchyEditor.d.ts.map +1 -0
- package/lib/components/forms/HierarchyEditor.js +106 -0
- package/lib/components/forms/HierarchyEditor.js.map +1 -0
- package/lib/components/forms/QualifierEditForm.d.ts +11 -0
- package/lib/components/forms/QualifierEditForm.d.ts.map +1 -0
- package/lib/components/forms/QualifierEditForm.js +181 -0
- package/lib/components/forms/QualifierEditForm.js.map +1 -0
- package/lib/components/forms/QualifierTypeEditForm.d.ts +10 -0
- package/lib/components/forms/QualifierTypeEditForm.d.ts.map +1 -0
- package/lib/components/forms/QualifierTypeEditForm.js +172 -0
- package/lib/components/forms/QualifierTypeEditForm.js.map +1 -0
- package/lib/components/forms/ResourceTypeEditForm.d.ts +10 -0
- package/lib/components/forms/ResourceTypeEditForm.d.ts.map +1 -0
- package/lib/components/forms/ResourceTypeEditForm.js +188 -0
- package/lib/components/forms/ResourceTypeEditForm.js.map +1 -0
- package/lib/components/forms/index.d.ts +9 -0
- package/lib/components/forms/index.d.ts.map +1 -0
- package/lib/components/forms/index.js +5 -0
- package/lib/components/forms/index.js.map +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts +14 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js +278 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -0
- package/lib/components/views/CompiledView/index.d.ts +5 -0
- package/lib/components/views/CompiledView/index.d.ts.map +1 -0
- package/lib/components/views/CompiledView/index.js +595 -0
- package/lib/components/views/CompiledView/index.js.map +1 -0
- package/lib/components/views/ConfigurationView/index.d.ts +5 -0
- package/lib/components/views/ConfigurationView/index.d.ts.map +1 -0
- package/lib/components/views/ConfigurationView/index.js +363 -0
- package/lib/components/views/ConfigurationView/index.js.map +1 -0
- package/lib/components/views/FilterView/index.d.ts +5 -0
- package/lib/components/views/FilterView/index.d.ts.map +1 -0
- package/lib/components/views/FilterView/index.js +463 -0
- package/lib/components/views/FilterView/index.js.map +1 -0
- package/lib/components/views/ImportView/index.d.ts +5 -0
- package/lib/components/views/ImportView/index.d.ts.map +1 -0
- package/lib/components/views/ImportView/index.js +514 -0
- package/lib/components/views/ImportView/index.js.map +1 -0
- package/lib/components/views/ResolutionView/EditableJsonView.d.ts +21 -0
- package/lib/components/views/ResolutionView/EditableJsonView.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/EditableJsonView.js +109 -0
- package/lib/components/views/ResolutionView/EditableJsonView.js.map +1 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +19 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.js +82 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.js.map +1 -0
- package/lib/components/views/ResolutionView/index.d.ts +5 -0
- package/lib/components/views/ResolutionView/index.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/index.js +255 -0
- package/lib/components/views/ResolutionView/index.js.map +1 -0
- package/lib/components/views/SourceView/index.d.ts +5 -0
- package/lib/components/views/SourceView/index.d.ts.map +1 -0
- package/lib/components/views/SourceView/index.js +316 -0
- package/lib/components/views/SourceView/index.js.map +1 -0
- package/lib/components/views/ZipLoaderView/index.d.ts +5 -0
- package/lib/components/views/ZipLoaderView/index.d.ts.map +1 -0
- package/lib/components/views/ZipLoaderView/index.js +313 -0
- package/lib/components/views/ZipLoaderView/index.js.map +1 -0
- package/lib/hooks/useConfigurationState.d.ts +46 -0
- package/lib/hooks/useConfigurationState.d.ts.map +1 -0
- package/lib/hooks/useConfigurationState.js +239 -0
- package/lib/hooks/useConfigurationState.js.map +1 -0
- package/lib/hooks/useFilterState.d.ts +7 -0
- package/lib/hooks/useFilterState.d.ts.map +1 -0
- package/lib/hooks/useFilterState.js +80 -0
- package/lib/hooks/useFilterState.js.map +1 -0
- package/lib/hooks/useResolutionState.d.ts +8 -0
- package/lib/hooks/useResolutionState.d.ts.map +1 -0
- package/lib/hooks/useResolutionState.js +253 -0
- package/lib/hooks/useResolutionState.js.map +1 -0
- package/lib/hooks/useResourceData.d.ts +19 -0
- package/lib/hooks/useResourceData.d.ts.map +1 -0
- package/lib/hooks/useResourceData.js +368 -0
- package/lib/hooks/useResourceData.js.map +1 -0
- package/lib/hooks/useViewState.d.ts +10 -0
- package/lib/hooks/useViewState.d.ts.map +1 -0
- package/lib/hooks/useViewState.js +29 -0
- package/lib/hooks/useViewState.js.map +1 -0
- package/lib/index.d.ts +27 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +34 -0
- package/lib/index.js.map +1 -0
- package/lib/test/helpers/testDataLoader.d.ts +37 -0
- package/lib/test/helpers/testDataLoader.d.ts.map +1 -0
- package/lib/test/helpers/testDataLoader.js +171 -0
- package/lib/test/helpers/testDataLoader.js.map +1 -0
- package/lib/test/unit/utils/configurationUtils.test.d.ts +2 -0
- package/lib/test/unit/utils/configurationUtils.test.d.ts.map +1 -0
- package/lib/test/unit/utils/configurationUtils.test.js +497 -0
- package/lib/test/unit/utils/configurationUtils.test.js.map +1 -0
- package/lib/test/unit/utils/fileProcessing.test.d.ts +2 -0
- package/lib/test/unit/utils/fileProcessing.test.d.ts.map +1 -0
- package/lib/test/unit/utils/fileProcessing.test.js +321 -0
- package/lib/test/unit/utils/fileProcessing.test.js.map +1 -0
- package/lib/test/unit/utils/filterResources.test.d.ts +2 -0
- package/lib/test/unit/utils/filterResources.test.d.ts.map +1 -0
- package/lib/test/unit/utils/filterResources.test.js +403 -0
- package/lib/test/unit/utils/filterResources.test.js.map +1 -0
- package/lib/test/unit/utils/resolutionEditing.test.d.ts +2 -0
- package/lib/test/unit/utils/resolutionEditing.test.d.ts.map +1 -0
- package/lib/test/unit/utils/resolutionEditing.test.js +439 -0
- package/lib/test/unit/utils/resolutionEditing.test.js.map +1 -0
- package/lib/test/unit/utils/resolutionUtils.test.d.ts +2 -0
- package/lib/test/unit/utils/resolutionUtils.test.d.ts.map +1 -0
- package/lib/test/unit/utils/resolutionUtils.test.js +397 -0
- package/lib/test/unit/utils/resolutionUtils.test.js.map +1 -0
- package/lib/test/unit/utils/tsResIntegration.test.d.ts +2 -0
- package/lib/test/unit/utils/tsResIntegration.test.d.ts.map +1 -0
- package/lib/test/unit/utils/tsResIntegration.test.js +376 -0
- package/lib/test/unit/utils/tsResIntegration.test.js.map +1 -0
- package/lib/types/index.d.ts +251 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +2 -0
- package/lib/types/index.js.map +1 -0
- package/lib/utils/configurationUtils.d.ts +74 -0
- package/lib/utils/configurationUtils.d.ts.map +1 -0
- package/lib/utils/configurationUtils.js +359 -0
- package/lib/utils/configurationUtils.js.map +1 -0
- package/lib/utils/fileProcessing.d.ts +18 -0
- package/lib/utils/fileProcessing.d.ts.map +1 -0
- package/lib/utils/fileProcessing.js +142 -0
- package/lib/utils/fileProcessing.js.map +1 -0
- package/lib/utils/filterResources.d.ts +38 -0
- package/lib/utils/filterResources.d.ts.map +1 -0
- package/lib/utils/filterResources.js +153 -0
- package/lib/utils/filterResources.js.map +1 -0
- package/lib/utils/resolutionEditing.d.ts +58 -0
- package/lib/utils/resolutionEditing.d.ts.map +1 -0
- package/lib/utils/resolutionEditing.js +246 -0
- package/lib/utils/resolutionEditing.js.map +1 -0
- package/lib/utils/resolutionUtils.d.ts +28 -0
- package/lib/utils/resolutionUtils.d.ts.map +1 -0
- package/lib/utils/resolutionUtils.js +216 -0
- package/lib/utils/resolutionUtils.js.map +1 -0
- package/lib/utils/tsResIntegration.d.ts +71 -0
- package/lib/utils/tsResIntegration.d.ts.map +1 -0
- package/lib/utils/tsResIntegration.js +294 -0
- package/lib/utils/tsResIntegration.js.map +1 -0
- package/lib/utils/zipLoader/browserZipLoader.d.ts +48 -0
- package/lib/utils/zipLoader/browserZipLoader.d.ts.map +1 -0
- package/lib/utils/zipLoader/browserZipLoader.js +247 -0
- package/lib/utils/zipLoader/browserZipLoader.js.map +1 -0
- package/lib/utils/zipLoader/index.d.ts +8 -0
- package/lib/utils/zipLoader/index.d.ts.map +1 -0
- package/lib/utils/zipLoader/index.js +13 -0
- package/lib/utils/zipLoader/index.js.map +1 -0
- package/lib/utils/zipLoader/nodeZipBuilder.d.ts +55 -0
- package/lib/utils/zipLoader/nodeZipBuilder.d.ts.map +1 -0
- package/lib/utils/zipLoader/nodeZipBuilder.js +98 -0
- package/lib/utils/zipLoader/nodeZipBuilder.js.map +1 -0
- package/lib/utils/zipLoader/types.d.ts +139 -0
- package/lib/utils/zipLoader/types.d.ts.map +1 -0
- package/lib/utils/zipLoader/types.js +2 -0
- package/lib/utils/zipLoader/types.js.map +1 -0
- package/lib/utils/zipLoader/zipUtils.d.ts +53 -0
- package/lib/utils/zipLoader/zipUtils.d.ts.map +1 -0
- package/lib/utils/zipLoader/zipUtils.js +229 -0
- package/lib/utils/zipLoader/zipUtils.js.map +1 -0
- package/package.json +69 -0
- package/rush-logs/ts-res-ui-components.build.cache.log +3 -0
- package/rush-logs/ts-res-ui-components.build.log +9 -0
- package/src/components/common/QualifierContextControl.tsx +151 -0
- package/src/components/common/ResourceListView.tsx +63 -0
- package/src/components/common/ResourceTreeView.tsx +271 -0
- package/src/components/forms/HierarchyEditor.tsx +204 -0
- package/src/components/forms/QualifierEditForm.tsx +355 -0
- package/src/components/forms/QualifierTypeEditForm.tsx +347 -0
- package/src/components/forms/ResourceTypeEditForm.tsx +331 -0
- package/src/components/forms/index.ts +11 -0
- package/src/components/orchestrator/ResourceOrchestrator.tsx +372 -0
- package/src/components/views/CompiledView/index.tsx +922 -0
- package/src/components/views/ConfigurationView/index.tsx +800 -0
- package/src/components/views/FilterView/index.tsx +825 -0
- package/src/components/views/ImportView/index.tsx +717 -0
- package/src/components/views/ResolutionView/EditableJsonView.tsx +214 -0
- package/src/components/views/ResolutionView/ResolutionEditControls.tsx +170 -0
- package/src/components/views/ResolutionView/index.tsx +591 -0
- package/src/components/views/SourceView/index.tsx +536 -0
- package/src/components/views/ZipLoaderView/index.tsx +485 -0
- package/src/hooks/useConfigurationState.ts +374 -0
- package/src/hooks/useFilterState.ts +97 -0
- package/src/hooks/useResolutionState.ts +355 -0
- package/src/hooks/useResourceData.ts +467 -0
- package/src/hooks/useViewState.ts +44 -0
- package/src/index.ts +45 -0
- package/src/test/helpers/testDataLoader.ts +195 -0
- package/src/test/unit/utils/configurationUtils.test.ts +630 -0
- package/src/test/unit/utils/fileProcessing.test.ts +391 -0
- package/src/test/unit/utils/filterResources.test.ts +574 -0
- package/src/test/unit/utils/resolutionEditing.test.ts +556 -0
- package/src/test/unit/utils/resolutionUtils.test.ts +521 -0
- package/src/test/unit/utils/tsResIntegration.test.ts +433 -0
- package/src/types/index.ts +322 -0
- package/src/utils/configurationUtils.ts +424 -0
- package/src/utils/fileProcessing.ts +160 -0
- package/src/utils/filterResources.ts +206 -0
- package/src/utils/resolutionEditing.ts +319 -0
- package/src/utils/resolutionUtils.ts +289 -0
- package/src/utils/tsResIntegration.ts +440 -0
- package/src/utils/zipLoader/browserZipLoader.ts +319 -0
- package/src/utils/zipLoader/index.ts +26 -0
- package/src/utils/zipLoader/nodeZipBuilder.ts +153 -0
- package/src/utils/zipLoader/types.ts +175 -0
- package/src/utils/zipLoader/zipUtils.ts +266 -0
- package/temp/build/typescript/ts_gZid87Hu.json +1 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
import React, { useState, useMemo, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
MagnifyingGlassIcon,
|
|
4
|
+
DocumentTextIcon,
|
|
5
|
+
CubeIcon,
|
|
6
|
+
CheckIcon,
|
|
7
|
+
XMarkIcon,
|
|
8
|
+
PencilIcon,
|
|
9
|
+
TrashIcon,
|
|
10
|
+
ListBulletIcon,
|
|
11
|
+
FolderIcon
|
|
12
|
+
} from '@heroicons/react/24/outline';
|
|
13
|
+
import { ResolutionViewProps, CandidateInfo, ResolutionActions, ResolutionState } from '../../../types';
|
|
14
|
+
import { QualifierContextControl } from '../../common/QualifierContextControl';
|
|
15
|
+
import { EditableJsonView } from './EditableJsonView';
|
|
16
|
+
import { ResolutionEditControls } from './ResolutionEditControls';
|
|
17
|
+
import { ResourceTreeView } from '../../common/ResourceTreeView';
|
|
18
|
+
import { ResourceListView } from '../../common/ResourceListView';
|
|
19
|
+
|
|
20
|
+
export const ResolutionView: React.FC<ResolutionViewProps> = ({
|
|
21
|
+
resources,
|
|
22
|
+
filterState,
|
|
23
|
+
filterResult,
|
|
24
|
+
resolutionState,
|
|
25
|
+
resolutionActions,
|
|
26
|
+
availableQualifiers = [],
|
|
27
|
+
onMessage,
|
|
28
|
+
className = ''
|
|
29
|
+
}) => {
|
|
30
|
+
// Local UI state
|
|
31
|
+
const [viewMode, setViewMode] = useState<'tree' | 'list'>('list');
|
|
32
|
+
|
|
33
|
+
// Use filtered resources when filtering is active and successful
|
|
34
|
+
const isFilteringActive = filterState?.enabled && filterResult?.success === true;
|
|
35
|
+
const activeProcessedResources = isFilteringActive ? filterResult?.processedResources : resources;
|
|
36
|
+
|
|
37
|
+
// Available resources for selection
|
|
38
|
+
const availableResources = useMemo(() => {
|
|
39
|
+
if (!activeProcessedResources?.summary?.resourceIds) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return activeProcessedResources.summary.resourceIds.sort();
|
|
43
|
+
}, [activeProcessedResources?.summary?.resourceIds]);
|
|
44
|
+
|
|
45
|
+
// Handle context value changes using the shared component's callback pattern
|
|
46
|
+
const handleQualifierChange = useCallback(
|
|
47
|
+
(qualifierName: string, value: string | undefined) => {
|
|
48
|
+
resolutionActions?.updateContextValue(qualifierName, value);
|
|
49
|
+
},
|
|
50
|
+
[resolutionActions]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Handle resource selection
|
|
54
|
+
const handleResourceSelect = useCallback(
|
|
55
|
+
(resourceId: string) => {
|
|
56
|
+
resolutionActions?.selectResource(resourceId);
|
|
57
|
+
},
|
|
58
|
+
[resolutionActions]
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Handle view mode change
|
|
62
|
+
const handleViewModeChange = useCallback(
|
|
63
|
+
(mode: 'composed' | 'best' | 'all' | 'raw') => {
|
|
64
|
+
resolutionActions?.setViewMode(mode);
|
|
65
|
+
},
|
|
66
|
+
[resolutionActions]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (!resources) {
|
|
70
|
+
return (
|
|
71
|
+
<div className={`p-6 ${className}`}>
|
|
72
|
+
<div className="flex items-center space-x-3 mb-6">
|
|
73
|
+
<MagnifyingGlassIcon className="h-8 w-8 text-blue-600" />
|
|
74
|
+
<h2 className="text-2xl font-bold text-gray-900">Resolution Viewer</h2>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-8 text-center">
|
|
78
|
+
<div className="max-w-2xl mx-auto">
|
|
79
|
+
<h3 className="text-xl font-semibold text-gray-900 mb-4">No Resources Loaded</h3>
|
|
80
|
+
<p className="text-gray-600 mb-6">
|
|
81
|
+
Import resources first to test resource resolution with different contexts.
|
|
82
|
+
</p>
|
|
83
|
+
<div className="bg-blue-50 rounded-lg p-4">
|
|
84
|
+
<p className="text-sm text-blue-800">
|
|
85
|
+
<strong>Resolution Viewer:</strong> Test how resources resolve with different qualifier
|
|
86
|
+
contexts. Set context values and see which candidates match.
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div className={`p-6 ${className}`}>
|
|
97
|
+
<div className="flex items-center space-x-3 mb-6">
|
|
98
|
+
<MagnifyingGlassIcon className="h-8 w-8 text-blue-600" />
|
|
99
|
+
<h2 className="text-2xl font-bold text-gray-900">Resolution Viewer</h2>
|
|
100
|
+
{isFilteringActive && (
|
|
101
|
+
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800">
|
|
102
|
+
Filtered
|
|
103
|
+
</span>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
108
|
+
{/* Context Configuration Panel */}
|
|
109
|
+
<div className="mb-6">
|
|
110
|
+
<h3 className="text-lg font-semibold text-gray-900 mb-4">Context Configuration</h3>
|
|
111
|
+
<div className="bg-gray-50 rounded-lg p-4">
|
|
112
|
+
<div className="mb-4">
|
|
113
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
114
|
+
{availableQualifiers.map((qualifierName) => (
|
|
115
|
+
<QualifierContextControl
|
|
116
|
+
key={qualifierName}
|
|
117
|
+
qualifierName={qualifierName}
|
|
118
|
+
value={resolutionState?.pendingContextValues[qualifierName]}
|
|
119
|
+
onChange={handleQualifierChange}
|
|
120
|
+
placeholder={`Enter ${qualifierName} value`}
|
|
121
|
+
resources={activeProcessedResources}
|
|
122
|
+
/>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<div className="flex items-center justify-between">
|
|
128
|
+
<div className="text-sm text-gray-600">
|
|
129
|
+
Current:{' '}
|
|
130
|
+
{Object.entries(resolutionState?.contextValues || {})
|
|
131
|
+
.map(([key, value]) => `${key}=${value === undefined ? '(undefined)' : value}`)
|
|
132
|
+
.join(', ')}
|
|
133
|
+
</div>
|
|
134
|
+
<div className="flex items-center space-x-2">
|
|
135
|
+
<button
|
|
136
|
+
onClick={resolutionActions?.resetCache}
|
|
137
|
+
className="px-3 py-1 text-xs font-medium text-gray-600 bg-gray-100 rounded hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500"
|
|
138
|
+
title="Clear resolution cache"
|
|
139
|
+
>
|
|
140
|
+
Clear Cache
|
|
141
|
+
</button>
|
|
142
|
+
<button
|
|
143
|
+
onClick={resolutionActions?.applyContext}
|
|
144
|
+
disabled={!resolutionState?.hasPendingChanges}
|
|
145
|
+
className={`px-4 py-2 rounded-md text-sm font-medium ${
|
|
146
|
+
resolutionState?.hasPendingChanges
|
|
147
|
+
? 'bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500'
|
|
148
|
+
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
|
149
|
+
}`}
|
|
150
|
+
>
|
|
151
|
+
{resolutionState?.hasPendingChanges
|
|
152
|
+
? 'Apply Changes'
|
|
153
|
+
: resolutionState?.currentResolver
|
|
154
|
+
? 'Context Applied'
|
|
155
|
+
: 'Apply Context'}
|
|
156
|
+
</button>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{/* Edit Controls - Show when there are unsaved edits */}
|
|
163
|
+
{resolutionState?.hasUnsavedEdits && (
|
|
164
|
+
<div className="mt-6">
|
|
165
|
+
<ResolutionEditControls
|
|
166
|
+
editCount={resolutionState.editedResources.size}
|
|
167
|
+
isApplying={resolutionState.isApplyingEdits}
|
|
168
|
+
hasEdits={resolutionState.hasUnsavedEdits}
|
|
169
|
+
onApplyEdits={resolutionActions?.applyEdits}
|
|
170
|
+
onDiscardEdits={resolutionActions?.discardEdits}
|
|
171
|
+
disabled={!resolutionState.currentResolver}
|
|
172
|
+
/>
|
|
173
|
+
</div>
|
|
174
|
+
)}
|
|
175
|
+
|
|
176
|
+
{/* Main Browser/Details Layout */}
|
|
177
|
+
<div className="flex flex-col lg:flex-row gap-6 h-[600px]">
|
|
178
|
+
{/* Left side: Resource Selection */}
|
|
179
|
+
<div className="lg:w-1/2 flex flex-col">
|
|
180
|
+
<div className="flex items-center justify-between mb-4">
|
|
181
|
+
<div>
|
|
182
|
+
<h3 className="text-lg font-semibold text-gray-900">Resources</h3>
|
|
183
|
+
<div className="text-sm text-gray-500">{availableResources.length} available</div>
|
|
184
|
+
</div>
|
|
185
|
+
{/* View Mode Toggle */}
|
|
186
|
+
<div className="flex items-center space-x-1 bg-gray-100 rounded-lg p-1">
|
|
187
|
+
<button
|
|
188
|
+
onClick={() => setViewMode('list')}
|
|
189
|
+
className={`flex items-center px-2 py-1 text-xs font-medium rounded ${
|
|
190
|
+
viewMode === 'list'
|
|
191
|
+
? 'bg-white text-gray-900 shadow-sm'
|
|
192
|
+
: 'text-gray-600 hover:text-gray-900'
|
|
193
|
+
}`}
|
|
194
|
+
title="List View"
|
|
195
|
+
>
|
|
196
|
+
<ListBulletIcon className="h-4 w-4" />
|
|
197
|
+
<span className="ml-1">List</span>
|
|
198
|
+
</button>
|
|
199
|
+
<button
|
|
200
|
+
onClick={() => setViewMode('tree')}
|
|
201
|
+
className={`flex items-center px-2 py-1 text-xs font-medium rounded ${
|
|
202
|
+
viewMode === 'tree'
|
|
203
|
+
? 'bg-white text-gray-900 shadow-sm'
|
|
204
|
+
: 'text-gray-600 hover:text-gray-900'
|
|
205
|
+
}`}
|
|
206
|
+
title="Tree View"
|
|
207
|
+
>
|
|
208
|
+
<FolderIcon className="h-4 w-4" />
|
|
209
|
+
<span className="ml-1">Tree</span>
|
|
210
|
+
</button>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<div className="flex-1 overflow-y-auto border border-gray-200 rounded-lg bg-gray-50">
|
|
215
|
+
{viewMode === 'tree' && activeProcessedResources?.system.resourceManager ? (
|
|
216
|
+
<ResourceTreeView
|
|
217
|
+
resources={activeProcessedResources.system.resourceManager}
|
|
218
|
+
selectedResourceId={resolutionState?.selectedResourceId || null}
|
|
219
|
+
onResourceSelect={handleResourceSelect}
|
|
220
|
+
searchTerm=""
|
|
221
|
+
className=""
|
|
222
|
+
/>
|
|
223
|
+
) : (
|
|
224
|
+
availableResources.map((resourceId) => (
|
|
225
|
+
<div
|
|
226
|
+
key={resourceId}
|
|
227
|
+
className={`flex items-center px-3 py-2 cursor-pointer hover:bg-gray-100 ${
|
|
228
|
+
resolutionState?.selectedResourceId === resourceId
|
|
229
|
+
? 'bg-blue-50 border-r-2 border-blue-500'
|
|
230
|
+
: ''
|
|
231
|
+
}`}
|
|
232
|
+
onClick={() => handleResourceSelect(resourceId)}
|
|
233
|
+
>
|
|
234
|
+
<DocumentTextIcon className="w-4 h-4 mr-2 text-green-500" />
|
|
235
|
+
<span
|
|
236
|
+
className={`text-sm ${
|
|
237
|
+
resolutionState?.selectedResourceId === resourceId
|
|
238
|
+
? 'font-medium text-blue-900'
|
|
239
|
+
: 'text-gray-700'
|
|
240
|
+
}`}
|
|
241
|
+
>
|
|
242
|
+
{resourceId}
|
|
243
|
+
</span>
|
|
244
|
+
{/* Show edit indicator */}
|
|
245
|
+
{resolutionActions?.hasEdit?.(resourceId) && (
|
|
246
|
+
<span className="ml-auto">
|
|
247
|
+
<PencilIcon className="h-3 w-3 text-blue-500" />
|
|
248
|
+
</span>
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
))
|
|
252
|
+
)}
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
{/* Right side: Resolution Results */}
|
|
257
|
+
<div className="lg:w-1/2 flex flex-col">
|
|
258
|
+
<div className="flex items-center justify-between mb-4">
|
|
259
|
+
<h3 className="text-lg font-semibold text-gray-900">Results</h3>
|
|
260
|
+
{resolutionState?.selectedResourceId && (
|
|
261
|
+
<div className="flex space-x-2">
|
|
262
|
+
<button
|
|
263
|
+
onClick={() => handleViewModeChange('composed')}
|
|
264
|
+
className={`px-3 py-1 text-xs rounded ${
|
|
265
|
+
resolutionState?.viewMode === 'composed'
|
|
266
|
+
? 'bg-blue-600 text-white'
|
|
267
|
+
: 'bg-gray-200 text-gray-700'
|
|
268
|
+
}`}
|
|
269
|
+
>
|
|
270
|
+
Composed
|
|
271
|
+
</button>
|
|
272
|
+
<button
|
|
273
|
+
onClick={() => handleViewModeChange('best')}
|
|
274
|
+
className={`px-3 py-1 text-xs rounded ${
|
|
275
|
+
resolutionState?.viewMode === 'best'
|
|
276
|
+
? 'bg-blue-600 text-white'
|
|
277
|
+
: 'bg-gray-200 text-gray-700'
|
|
278
|
+
}`}
|
|
279
|
+
>
|
|
280
|
+
Best
|
|
281
|
+
</button>
|
|
282
|
+
<button
|
|
283
|
+
onClick={() => handleViewModeChange('all')}
|
|
284
|
+
className={`px-3 py-1 text-xs rounded ${
|
|
285
|
+
resolutionState?.viewMode === 'all'
|
|
286
|
+
? 'bg-blue-600 text-white'
|
|
287
|
+
: 'bg-gray-200 text-gray-700'
|
|
288
|
+
}`}
|
|
289
|
+
>
|
|
290
|
+
All
|
|
291
|
+
</button>
|
|
292
|
+
<button
|
|
293
|
+
onClick={() => handleViewModeChange('raw')}
|
|
294
|
+
className={`px-3 py-1 text-xs rounded ${
|
|
295
|
+
resolutionState?.viewMode === 'raw'
|
|
296
|
+
? 'bg-blue-600 text-white'
|
|
297
|
+
: 'bg-gray-200 text-gray-700'
|
|
298
|
+
}`}
|
|
299
|
+
>
|
|
300
|
+
Raw
|
|
301
|
+
</button>
|
|
302
|
+
</div>
|
|
303
|
+
)}
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<div className="flex-1 overflow-y-auto border border-gray-200 rounded-lg p-4 bg-gray-50">
|
|
307
|
+
{!resolutionState?.selectedResourceId ? (
|
|
308
|
+
<div className="flex items-center justify-center h-full">
|
|
309
|
+
<div className="text-center">
|
|
310
|
+
<CubeIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
|
311
|
+
<p className="text-gray-500">Select a resource to view resolution results</p>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
) : !resolutionState?.currentResolver ? (
|
|
315
|
+
<div className="text-center text-gray-500">
|
|
316
|
+
<p>Apply a context to resolve resources</p>
|
|
317
|
+
</div>
|
|
318
|
+
) : !resolutionState?.resolutionResult ? (
|
|
319
|
+
<div className="text-center text-gray-500">
|
|
320
|
+
<p>Resolving...</p>
|
|
321
|
+
</div>
|
|
322
|
+
) : (
|
|
323
|
+
<ResolutionResults
|
|
324
|
+
result={resolutionState.resolutionResult}
|
|
325
|
+
viewMode={resolutionState.viewMode}
|
|
326
|
+
contextValues={resolutionState.contextValues}
|
|
327
|
+
resolutionActions={resolutionActions}
|
|
328
|
+
resolutionState={resolutionState}
|
|
329
|
+
/>
|
|
330
|
+
)}
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// Resolution Results Component (simplified version)
|
|
340
|
+
interface ResolutionResultsProps {
|
|
341
|
+
result: any;
|
|
342
|
+
viewMode: 'composed' | 'best' | 'all' | 'raw';
|
|
343
|
+
contextValues: Record<string, string | undefined>;
|
|
344
|
+
resolutionActions?: ResolutionActions;
|
|
345
|
+
resolutionState?: ResolutionState;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const ResolutionResults: React.FC<ResolutionResultsProps> = ({
|
|
349
|
+
result,
|
|
350
|
+
viewMode,
|
|
351
|
+
contextValues,
|
|
352
|
+
resolutionActions,
|
|
353
|
+
resolutionState
|
|
354
|
+
}) => {
|
|
355
|
+
if (!result.success) {
|
|
356
|
+
return (
|
|
357
|
+
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
|
|
358
|
+
<h4 className="font-medium text-red-800 mb-2">Resolution Failed</h4>
|
|
359
|
+
<p className="text-sm text-red-600">{result.error}</p>
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (viewMode === 'raw') {
|
|
365
|
+
return (
|
|
366
|
+
<div className="space-y-4">
|
|
367
|
+
<div>
|
|
368
|
+
<h4 className="font-medium text-gray-800 mb-2">Raw Resolution Data</h4>
|
|
369
|
+
<pre className="text-xs bg-white p-3 rounded border overflow-x-auto">
|
|
370
|
+
{JSON.stringify(
|
|
371
|
+
{
|
|
372
|
+
context: contextValues,
|
|
373
|
+
resource: result.resource
|
|
374
|
+
? {
|
|
375
|
+
id: result.resource.id,
|
|
376
|
+
candidateCount: result.resource.candidates.length
|
|
377
|
+
}
|
|
378
|
+
: null,
|
|
379
|
+
bestCandidate: result.bestCandidate?.json,
|
|
380
|
+
allCandidates: result.allCandidates?.map((c: any) => c.json),
|
|
381
|
+
composedValue: result.composedValue,
|
|
382
|
+
error: result.error
|
|
383
|
+
},
|
|
384
|
+
null,
|
|
385
|
+
2
|
|
386
|
+
)}
|
|
387
|
+
</pre>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (viewMode === 'composed') {
|
|
394
|
+
return (
|
|
395
|
+
<div className="space-y-4">
|
|
396
|
+
{result.composedValue ? (
|
|
397
|
+
<EditableJsonView
|
|
398
|
+
value={result.composedValue}
|
|
399
|
+
resourceId={result.resourceId}
|
|
400
|
+
isEdited={resolutionActions?.hasEdit?.(result.resourceId) || false}
|
|
401
|
+
editedValue={resolutionActions?.getEditedValue?.(result.resourceId)}
|
|
402
|
+
onSave={resolutionActions?.saveEdit}
|
|
403
|
+
onCancel={() => {}} // Could add cancel functionality if needed
|
|
404
|
+
disabled={resolutionState?.isApplyingEdits || false}
|
|
405
|
+
/>
|
|
406
|
+
) : (
|
|
407
|
+
<div className="bg-yellow-50 border border-yellow-200 rounded p-3">
|
|
408
|
+
<p className="text-sm text-yellow-800">No composed value available for the current context.</p>
|
|
409
|
+
{result.error && <p className="text-xs text-yellow-600 mt-1">{result.error}</p>}
|
|
410
|
+
</div>
|
|
411
|
+
)}
|
|
412
|
+
|
|
413
|
+
{result.resource && (
|
|
414
|
+
<div>
|
|
415
|
+
<h4 className="font-medium text-gray-800 mb-2">Resource Info</h4>
|
|
416
|
+
<div className="bg-white p-3 rounded border text-sm">
|
|
417
|
+
<div>
|
|
418
|
+
<strong>ID:</strong> {result.resource.id}
|
|
419
|
+
</div>
|
|
420
|
+
<div>
|
|
421
|
+
<strong>Type:</strong> {result.resource.resourceType.key}
|
|
422
|
+
</div>
|
|
423
|
+
<div>
|
|
424
|
+
<strong>Total Candidates:</strong> {result.resource.candidates.length}
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
)}
|
|
429
|
+
</div>
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (viewMode === 'best') {
|
|
434
|
+
return (
|
|
435
|
+
<div className="space-y-4">
|
|
436
|
+
<div>
|
|
437
|
+
<h4 className="font-medium text-gray-800 mb-2">Best Match</h4>
|
|
438
|
+
{result.bestCandidate ? (
|
|
439
|
+
<div className="bg-white p-3 rounded border border-green-200">
|
|
440
|
+
<div className="text-sm font-medium text-gray-700 mb-2">
|
|
441
|
+
Selected candidate for current context
|
|
442
|
+
</div>
|
|
443
|
+
<pre className="text-xs bg-gray-50 p-2 rounded overflow-x-auto">
|
|
444
|
+
{JSON.stringify(result.bestCandidate.json, null, 2)}
|
|
445
|
+
</pre>
|
|
446
|
+
</div>
|
|
447
|
+
) : (
|
|
448
|
+
<div className="bg-yellow-50 border border-yellow-200 rounded p-3">
|
|
449
|
+
<p className="text-sm text-yellow-800">No best candidate found for the current context.</p>
|
|
450
|
+
{result.error && <p className="text-xs text-yellow-600 mt-1">{result.error}</p>}
|
|
451
|
+
</div>
|
|
452
|
+
)}
|
|
453
|
+
</div>
|
|
454
|
+
</div>
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// 'all' view mode
|
|
459
|
+
const regularMatchingCandidates =
|
|
460
|
+
result.candidateDetails?.filter((c: CandidateInfo) => c.matched && !c.isDefaultMatch) || [];
|
|
461
|
+
const defaultMatchingCandidates =
|
|
462
|
+
result.candidateDetails?.filter((c: CandidateInfo) => c.matched && c.isDefaultMatch) || [];
|
|
463
|
+
const nonMatchingCandidates = result.candidateDetails?.filter((c: CandidateInfo) => !c.matched) || [];
|
|
464
|
+
|
|
465
|
+
const getMatchTypeColor = (type: string) => {
|
|
466
|
+
switch (type) {
|
|
467
|
+
case 'match':
|
|
468
|
+
return 'bg-green-100 text-green-800';
|
|
469
|
+
case 'matchAsDefault':
|
|
470
|
+
return 'bg-amber-100 text-amber-800';
|
|
471
|
+
case 'noMatch':
|
|
472
|
+
return 'bg-red-100 text-red-800';
|
|
473
|
+
default:
|
|
474
|
+
return 'bg-gray-100 text-gray-800';
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const getMatchTypeIcon = (type: string) => {
|
|
479
|
+
switch (type) {
|
|
480
|
+
case 'match':
|
|
481
|
+
return '✓';
|
|
482
|
+
case 'matchAsDefault':
|
|
483
|
+
return '≈';
|
|
484
|
+
case 'noMatch':
|
|
485
|
+
return '✗';
|
|
486
|
+
default:
|
|
487
|
+
return '?';
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
return (
|
|
492
|
+
<div className="space-y-4">
|
|
493
|
+
{/* Regular Matching Candidates */}
|
|
494
|
+
{regularMatchingCandidates.length > 0 && (
|
|
495
|
+
<div>
|
|
496
|
+
<h4 className="font-medium text-gray-800 mb-2">Regular Matches</h4>
|
|
497
|
+
<div className="space-y-2">
|
|
498
|
+
{regularMatchingCandidates.map((candidateInfo: CandidateInfo, index: number) => (
|
|
499
|
+
<div
|
|
500
|
+
key={`regular-${candidateInfo.candidateIndex}`}
|
|
501
|
+
className="bg-white p-3 rounded border border-green-200"
|
|
502
|
+
>
|
|
503
|
+
<div className="flex items-center justify-between mb-2">
|
|
504
|
+
<div className="text-sm font-medium text-gray-700 flex items-center space-x-2">
|
|
505
|
+
<span>
|
|
506
|
+
Candidate {candidateInfo.candidateIndex + 1} {index === 0 ? '(Best Match)' : ''}
|
|
507
|
+
</span>
|
|
508
|
+
<span
|
|
509
|
+
className={`px-2 py-1 rounded text-xs ${getMatchTypeColor(candidateInfo.matchType)}`}
|
|
510
|
+
>
|
|
511
|
+
{getMatchTypeIcon(candidateInfo.matchType)} {candidateInfo.matchType}
|
|
512
|
+
</span>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
<pre className="text-xs bg-gray-50 p-2 rounded overflow-x-auto">
|
|
516
|
+
{JSON.stringify(candidateInfo.candidate.json, null, 2)}
|
|
517
|
+
</pre>
|
|
518
|
+
</div>
|
|
519
|
+
))}
|
|
520
|
+
</div>
|
|
521
|
+
</div>
|
|
522
|
+
)}
|
|
523
|
+
|
|
524
|
+
{/* Default Matching Candidates */}
|
|
525
|
+
{defaultMatchingCandidates.length > 0 && (
|
|
526
|
+
<div>
|
|
527
|
+
<h4 className="font-medium text-gray-800 mb-2">Default Matches</h4>
|
|
528
|
+
<div className="space-y-2">
|
|
529
|
+
{defaultMatchingCandidates.map((candidateInfo: CandidateInfo) => (
|
|
530
|
+
<div
|
|
531
|
+
key={`default-${candidateInfo.candidateIndex}`}
|
|
532
|
+
className="bg-white p-3 rounded border border-amber-200"
|
|
533
|
+
>
|
|
534
|
+
<div className="flex items-center justify-between mb-2">
|
|
535
|
+
<div className="text-sm font-medium text-gray-700 flex items-center space-x-2">
|
|
536
|
+
<span>Candidate {candidateInfo.candidateIndex + 1}</span>
|
|
537
|
+
<span
|
|
538
|
+
className={`px-2 py-1 rounded text-xs ${getMatchTypeColor(candidateInfo.matchType)}`}
|
|
539
|
+
>
|
|
540
|
+
{getMatchTypeIcon(candidateInfo.matchType)} {candidateInfo.matchType}
|
|
541
|
+
</span>
|
|
542
|
+
</div>
|
|
543
|
+
</div>
|
|
544
|
+
<pre className="text-xs bg-gray-50 p-2 rounded overflow-x-auto">
|
|
545
|
+
{JSON.stringify(candidateInfo.candidate.json, null, 2)}
|
|
546
|
+
</pre>
|
|
547
|
+
</div>
|
|
548
|
+
))}
|
|
549
|
+
</div>
|
|
550
|
+
</div>
|
|
551
|
+
)}
|
|
552
|
+
|
|
553
|
+
{/* Show message when no matches */}
|
|
554
|
+
{regularMatchingCandidates.length === 0 && defaultMatchingCandidates.length === 0 && (
|
|
555
|
+
<div>
|
|
556
|
+
<h4 className="font-medium text-gray-800 mb-2">Matching Candidates</h4>
|
|
557
|
+
<p className="text-sm text-gray-600">No candidates matched the current context.</p>
|
|
558
|
+
</div>
|
|
559
|
+
)}
|
|
560
|
+
|
|
561
|
+
{/* Non-matching Candidates */}
|
|
562
|
+
{nonMatchingCandidates.length > 0 && (
|
|
563
|
+
<div>
|
|
564
|
+
<h4 className="font-medium text-gray-500 mb-2">Non-matching Candidates</h4>
|
|
565
|
+
<div className="space-y-2">
|
|
566
|
+
{nonMatchingCandidates.slice(0, 3).map((candidateInfo: CandidateInfo) => (
|
|
567
|
+
<div
|
|
568
|
+
key={`non-matching-${candidateInfo.candidateIndex}`}
|
|
569
|
+
className="bg-gray-50 p-3 rounded border border-gray-200 opacity-75"
|
|
570
|
+
>
|
|
571
|
+
<div className="text-sm font-medium text-gray-500 mb-2">
|
|
572
|
+
Candidate {candidateInfo.candidateIndex + 1}
|
|
573
|
+
</div>
|
|
574
|
+
<pre className="text-xs bg-gray-100 p-2 rounded overflow-x-auto text-gray-600">
|
|
575
|
+
{JSON.stringify(candidateInfo.candidate.json, null, 2)}
|
|
576
|
+
</pre>
|
|
577
|
+
</div>
|
|
578
|
+
))}
|
|
579
|
+
{nonMatchingCandidates.length > 3 && (
|
|
580
|
+
<div className="text-center text-sm text-gray-500">
|
|
581
|
+
... and {nonMatchingCandidates.length - 3} more non-matching candidates
|
|
582
|
+
</div>
|
|
583
|
+
)}
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
)}
|
|
587
|
+
</div>
|
|
588
|
+
);
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
export default ResolutionView;
|