@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.
Files changed (231) hide show
  1. package/.rush/temp/03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log +271 -0
  2. package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +9 -0
  3. package/.rush/temp/operation/build/all.log +9 -0
  4. package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
  5. package/.rush/temp/operation/build/state.json +3 -0
  6. package/.rush/temp/shrinkwrap-deps.json +1111 -0
  7. package/README.md +18 -0
  8. package/REFACTORING_PLAN.md +171 -0
  9. package/config/jest.config.json +16 -0
  10. package/config/jest.setup.js +64 -0
  11. package/config/rig.json +16 -0
  12. package/lib/components/common/QualifierContextControl.d.ts +14 -0
  13. package/lib/components/common/QualifierContextControl.d.ts.map +1 -0
  14. package/lib/components/common/QualifierContextControl.js +78 -0
  15. package/lib/components/common/QualifierContextControl.js.map +1 -0
  16. package/lib/components/common/ResourceListView.d.ts +11 -0
  17. package/lib/components/common/ResourceListView.d.ts.map +1 -0
  18. package/lib/components/common/ResourceListView.js +20 -0
  19. package/lib/components/common/ResourceListView.js.map +1 -0
  20. package/lib/components/common/ResourceTreeView.d.ts +12 -0
  21. package/lib/components/common/ResourceTreeView.d.ts.map +1 -0
  22. package/lib/components/common/ResourceTreeView.js +162 -0
  23. package/lib/components/common/ResourceTreeView.js.map +1 -0
  24. package/lib/components/forms/HierarchyEditor.d.ts +10 -0
  25. package/lib/components/forms/HierarchyEditor.d.ts.map +1 -0
  26. package/lib/components/forms/HierarchyEditor.js +106 -0
  27. package/lib/components/forms/HierarchyEditor.js.map +1 -0
  28. package/lib/components/forms/QualifierEditForm.d.ts +11 -0
  29. package/lib/components/forms/QualifierEditForm.d.ts.map +1 -0
  30. package/lib/components/forms/QualifierEditForm.js +181 -0
  31. package/lib/components/forms/QualifierEditForm.js.map +1 -0
  32. package/lib/components/forms/QualifierTypeEditForm.d.ts +10 -0
  33. package/lib/components/forms/QualifierTypeEditForm.d.ts.map +1 -0
  34. package/lib/components/forms/QualifierTypeEditForm.js +172 -0
  35. package/lib/components/forms/QualifierTypeEditForm.js.map +1 -0
  36. package/lib/components/forms/ResourceTypeEditForm.d.ts +10 -0
  37. package/lib/components/forms/ResourceTypeEditForm.d.ts.map +1 -0
  38. package/lib/components/forms/ResourceTypeEditForm.js +188 -0
  39. package/lib/components/forms/ResourceTypeEditForm.js.map +1 -0
  40. package/lib/components/forms/index.d.ts +9 -0
  41. package/lib/components/forms/index.d.ts.map +1 -0
  42. package/lib/components/forms/index.js +5 -0
  43. package/lib/components/forms/index.js.map +1 -0
  44. package/lib/components/orchestrator/ResourceOrchestrator.d.ts +14 -0
  45. package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -0
  46. package/lib/components/orchestrator/ResourceOrchestrator.js +278 -0
  47. package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -0
  48. package/lib/components/views/CompiledView/index.d.ts +5 -0
  49. package/lib/components/views/CompiledView/index.d.ts.map +1 -0
  50. package/lib/components/views/CompiledView/index.js +595 -0
  51. package/lib/components/views/CompiledView/index.js.map +1 -0
  52. package/lib/components/views/ConfigurationView/index.d.ts +5 -0
  53. package/lib/components/views/ConfigurationView/index.d.ts.map +1 -0
  54. package/lib/components/views/ConfigurationView/index.js +363 -0
  55. package/lib/components/views/ConfigurationView/index.js.map +1 -0
  56. package/lib/components/views/FilterView/index.d.ts +5 -0
  57. package/lib/components/views/FilterView/index.d.ts.map +1 -0
  58. package/lib/components/views/FilterView/index.js +463 -0
  59. package/lib/components/views/FilterView/index.js.map +1 -0
  60. package/lib/components/views/ImportView/index.d.ts +5 -0
  61. package/lib/components/views/ImportView/index.d.ts.map +1 -0
  62. package/lib/components/views/ImportView/index.js +514 -0
  63. package/lib/components/views/ImportView/index.js.map +1 -0
  64. package/lib/components/views/ResolutionView/EditableJsonView.d.ts +21 -0
  65. package/lib/components/views/ResolutionView/EditableJsonView.d.ts.map +1 -0
  66. package/lib/components/views/ResolutionView/EditableJsonView.js +109 -0
  67. package/lib/components/views/ResolutionView/EditableJsonView.js.map +1 -0
  68. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +19 -0
  69. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts.map +1 -0
  70. package/lib/components/views/ResolutionView/ResolutionEditControls.js +82 -0
  71. package/lib/components/views/ResolutionView/ResolutionEditControls.js.map +1 -0
  72. package/lib/components/views/ResolutionView/index.d.ts +5 -0
  73. package/lib/components/views/ResolutionView/index.d.ts.map +1 -0
  74. package/lib/components/views/ResolutionView/index.js +255 -0
  75. package/lib/components/views/ResolutionView/index.js.map +1 -0
  76. package/lib/components/views/SourceView/index.d.ts +5 -0
  77. package/lib/components/views/SourceView/index.d.ts.map +1 -0
  78. package/lib/components/views/SourceView/index.js +316 -0
  79. package/lib/components/views/SourceView/index.js.map +1 -0
  80. package/lib/components/views/ZipLoaderView/index.d.ts +5 -0
  81. package/lib/components/views/ZipLoaderView/index.d.ts.map +1 -0
  82. package/lib/components/views/ZipLoaderView/index.js +313 -0
  83. package/lib/components/views/ZipLoaderView/index.js.map +1 -0
  84. package/lib/hooks/useConfigurationState.d.ts +46 -0
  85. package/lib/hooks/useConfigurationState.d.ts.map +1 -0
  86. package/lib/hooks/useConfigurationState.js +239 -0
  87. package/lib/hooks/useConfigurationState.js.map +1 -0
  88. package/lib/hooks/useFilterState.d.ts +7 -0
  89. package/lib/hooks/useFilterState.d.ts.map +1 -0
  90. package/lib/hooks/useFilterState.js +80 -0
  91. package/lib/hooks/useFilterState.js.map +1 -0
  92. package/lib/hooks/useResolutionState.d.ts +8 -0
  93. package/lib/hooks/useResolutionState.d.ts.map +1 -0
  94. package/lib/hooks/useResolutionState.js +253 -0
  95. package/lib/hooks/useResolutionState.js.map +1 -0
  96. package/lib/hooks/useResourceData.d.ts +19 -0
  97. package/lib/hooks/useResourceData.d.ts.map +1 -0
  98. package/lib/hooks/useResourceData.js +368 -0
  99. package/lib/hooks/useResourceData.js.map +1 -0
  100. package/lib/hooks/useViewState.d.ts +10 -0
  101. package/lib/hooks/useViewState.d.ts.map +1 -0
  102. package/lib/hooks/useViewState.js +29 -0
  103. package/lib/hooks/useViewState.js.map +1 -0
  104. package/lib/index.d.ts +27 -0
  105. package/lib/index.d.ts.map +1 -0
  106. package/lib/index.js +34 -0
  107. package/lib/index.js.map +1 -0
  108. package/lib/test/helpers/testDataLoader.d.ts +37 -0
  109. package/lib/test/helpers/testDataLoader.d.ts.map +1 -0
  110. package/lib/test/helpers/testDataLoader.js +171 -0
  111. package/lib/test/helpers/testDataLoader.js.map +1 -0
  112. package/lib/test/unit/utils/configurationUtils.test.d.ts +2 -0
  113. package/lib/test/unit/utils/configurationUtils.test.d.ts.map +1 -0
  114. package/lib/test/unit/utils/configurationUtils.test.js +497 -0
  115. package/lib/test/unit/utils/configurationUtils.test.js.map +1 -0
  116. package/lib/test/unit/utils/fileProcessing.test.d.ts +2 -0
  117. package/lib/test/unit/utils/fileProcessing.test.d.ts.map +1 -0
  118. package/lib/test/unit/utils/fileProcessing.test.js +321 -0
  119. package/lib/test/unit/utils/fileProcessing.test.js.map +1 -0
  120. package/lib/test/unit/utils/filterResources.test.d.ts +2 -0
  121. package/lib/test/unit/utils/filterResources.test.d.ts.map +1 -0
  122. package/lib/test/unit/utils/filterResources.test.js +403 -0
  123. package/lib/test/unit/utils/filterResources.test.js.map +1 -0
  124. package/lib/test/unit/utils/resolutionEditing.test.d.ts +2 -0
  125. package/lib/test/unit/utils/resolutionEditing.test.d.ts.map +1 -0
  126. package/lib/test/unit/utils/resolutionEditing.test.js +439 -0
  127. package/lib/test/unit/utils/resolutionEditing.test.js.map +1 -0
  128. package/lib/test/unit/utils/resolutionUtils.test.d.ts +2 -0
  129. package/lib/test/unit/utils/resolutionUtils.test.d.ts.map +1 -0
  130. package/lib/test/unit/utils/resolutionUtils.test.js +397 -0
  131. package/lib/test/unit/utils/resolutionUtils.test.js.map +1 -0
  132. package/lib/test/unit/utils/tsResIntegration.test.d.ts +2 -0
  133. package/lib/test/unit/utils/tsResIntegration.test.d.ts.map +1 -0
  134. package/lib/test/unit/utils/tsResIntegration.test.js +376 -0
  135. package/lib/test/unit/utils/tsResIntegration.test.js.map +1 -0
  136. package/lib/types/index.d.ts +251 -0
  137. package/lib/types/index.d.ts.map +1 -0
  138. package/lib/types/index.js +2 -0
  139. package/lib/types/index.js.map +1 -0
  140. package/lib/utils/configurationUtils.d.ts +74 -0
  141. package/lib/utils/configurationUtils.d.ts.map +1 -0
  142. package/lib/utils/configurationUtils.js +359 -0
  143. package/lib/utils/configurationUtils.js.map +1 -0
  144. package/lib/utils/fileProcessing.d.ts +18 -0
  145. package/lib/utils/fileProcessing.d.ts.map +1 -0
  146. package/lib/utils/fileProcessing.js +142 -0
  147. package/lib/utils/fileProcessing.js.map +1 -0
  148. package/lib/utils/filterResources.d.ts +38 -0
  149. package/lib/utils/filterResources.d.ts.map +1 -0
  150. package/lib/utils/filterResources.js +153 -0
  151. package/lib/utils/filterResources.js.map +1 -0
  152. package/lib/utils/resolutionEditing.d.ts +58 -0
  153. package/lib/utils/resolutionEditing.d.ts.map +1 -0
  154. package/lib/utils/resolutionEditing.js +246 -0
  155. package/lib/utils/resolutionEditing.js.map +1 -0
  156. package/lib/utils/resolutionUtils.d.ts +28 -0
  157. package/lib/utils/resolutionUtils.d.ts.map +1 -0
  158. package/lib/utils/resolutionUtils.js +216 -0
  159. package/lib/utils/resolutionUtils.js.map +1 -0
  160. package/lib/utils/tsResIntegration.d.ts +71 -0
  161. package/lib/utils/tsResIntegration.d.ts.map +1 -0
  162. package/lib/utils/tsResIntegration.js +294 -0
  163. package/lib/utils/tsResIntegration.js.map +1 -0
  164. package/lib/utils/zipLoader/browserZipLoader.d.ts +48 -0
  165. package/lib/utils/zipLoader/browserZipLoader.d.ts.map +1 -0
  166. package/lib/utils/zipLoader/browserZipLoader.js +247 -0
  167. package/lib/utils/zipLoader/browserZipLoader.js.map +1 -0
  168. package/lib/utils/zipLoader/index.d.ts +8 -0
  169. package/lib/utils/zipLoader/index.d.ts.map +1 -0
  170. package/lib/utils/zipLoader/index.js +13 -0
  171. package/lib/utils/zipLoader/index.js.map +1 -0
  172. package/lib/utils/zipLoader/nodeZipBuilder.d.ts +55 -0
  173. package/lib/utils/zipLoader/nodeZipBuilder.d.ts.map +1 -0
  174. package/lib/utils/zipLoader/nodeZipBuilder.js +98 -0
  175. package/lib/utils/zipLoader/nodeZipBuilder.js.map +1 -0
  176. package/lib/utils/zipLoader/types.d.ts +139 -0
  177. package/lib/utils/zipLoader/types.d.ts.map +1 -0
  178. package/lib/utils/zipLoader/types.js +2 -0
  179. package/lib/utils/zipLoader/types.js.map +1 -0
  180. package/lib/utils/zipLoader/zipUtils.d.ts +53 -0
  181. package/lib/utils/zipLoader/zipUtils.d.ts.map +1 -0
  182. package/lib/utils/zipLoader/zipUtils.js +229 -0
  183. package/lib/utils/zipLoader/zipUtils.js.map +1 -0
  184. package/package.json +69 -0
  185. package/rush-logs/ts-res-ui-components.build.cache.log +3 -0
  186. package/rush-logs/ts-res-ui-components.build.log +9 -0
  187. package/src/components/common/QualifierContextControl.tsx +151 -0
  188. package/src/components/common/ResourceListView.tsx +63 -0
  189. package/src/components/common/ResourceTreeView.tsx +271 -0
  190. package/src/components/forms/HierarchyEditor.tsx +204 -0
  191. package/src/components/forms/QualifierEditForm.tsx +355 -0
  192. package/src/components/forms/QualifierTypeEditForm.tsx +347 -0
  193. package/src/components/forms/ResourceTypeEditForm.tsx +331 -0
  194. package/src/components/forms/index.ts +11 -0
  195. package/src/components/orchestrator/ResourceOrchestrator.tsx +372 -0
  196. package/src/components/views/CompiledView/index.tsx +922 -0
  197. package/src/components/views/ConfigurationView/index.tsx +800 -0
  198. package/src/components/views/FilterView/index.tsx +825 -0
  199. package/src/components/views/ImportView/index.tsx +717 -0
  200. package/src/components/views/ResolutionView/EditableJsonView.tsx +214 -0
  201. package/src/components/views/ResolutionView/ResolutionEditControls.tsx +170 -0
  202. package/src/components/views/ResolutionView/index.tsx +591 -0
  203. package/src/components/views/SourceView/index.tsx +536 -0
  204. package/src/components/views/ZipLoaderView/index.tsx +485 -0
  205. package/src/hooks/useConfigurationState.ts +374 -0
  206. package/src/hooks/useFilterState.ts +97 -0
  207. package/src/hooks/useResolutionState.ts +355 -0
  208. package/src/hooks/useResourceData.ts +467 -0
  209. package/src/hooks/useViewState.ts +44 -0
  210. package/src/index.ts +45 -0
  211. package/src/test/helpers/testDataLoader.ts +195 -0
  212. package/src/test/unit/utils/configurationUtils.test.ts +630 -0
  213. package/src/test/unit/utils/fileProcessing.test.ts +391 -0
  214. package/src/test/unit/utils/filterResources.test.ts +574 -0
  215. package/src/test/unit/utils/resolutionEditing.test.ts +556 -0
  216. package/src/test/unit/utils/resolutionUtils.test.ts +521 -0
  217. package/src/test/unit/utils/tsResIntegration.test.ts +433 -0
  218. package/src/types/index.ts +322 -0
  219. package/src/utils/configurationUtils.ts +424 -0
  220. package/src/utils/fileProcessing.ts +160 -0
  221. package/src/utils/filterResources.ts +206 -0
  222. package/src/utils/resolutionEditing.ts +319 -0
  223. package/src/utils/resolutionUtils.ts +289 -0
  224. package/src/utils/tsResIntegration.ts +440 -0
  225. package/src/utils/zipLoader/browserZipLoader.ts +319 -0
  226. package/src/utils/zipLoader/index.ts +26 -0
  227. package/src/utils/zipLoader/nodeZipBuilder.ts +153 -0
  228. package/src/utils/zipLoader/types.ts +175 -0
  229. package/src/utils/zipLoader/zipUtils.ts +266 -0
  230. package/temp/build/typescript/ts_gZid87Hu.json +1 -0
  231. 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;