@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,214 @@
|
|
|
1
|
+
import React, { useState, useCallback, useMemo } from 'react';
|
|
2
|
+
import { JsonEditor } from 'json-edit-react';
|
|
3
|
+
import { PencilIcon, CheckIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
|
4
|
+
import { validateEditedResource } from '../../../utils/resolutionEditing';
|
|
5
|
+
|
|
6
|
+
export interface EditableJsonViewProps {
|
|
7
|
+
/** The original JSON value */
|
|
8
|
+
value: any;
|
|
9
|
+
/** The resource ID for tracking edits */
|
|
10
|
+
resourceId: string;
|
|
11
|
+
/** Whether this resource has been edited */
|
|
12
|
+
isEdited?: boolean;
|
|
13
|
+
/** The current edited value if any */
|
|
14
|
+
editedValue?: any;
|
|
15
|
+
/** Callback when the user saves an edit */
|
|
16
|
+
onSave?: (resourceId: string, editedValue: any, originalValue: any) => void;
|
|
17
|
+
/** Callback when the user cancels an edit */
|
|
18
|
+
onCancel?: (resourceId: string) => void;
|
|
19
|
+
/** Whether editing is currently disabled */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** Additional CSS classes */
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const EditableJsonView: React.FC<EditableJsonViewProps> = ({
|
|
26
|
+
value,
|
|
27
|
+
resourceId,
|
|
28
|
+
isEdited = false,
|
|
29
|
+
editedValue,
|
|
30
|
+
onSave,
|
|
31
|
+
onCancel,
|
|
32
|
+
disabled = false,
|
|
33
|
+
className = ''
|
|
34
|
+
}) => {
|
|
35
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
36
|
+
const [currentEditValue, setCurrentEditValue] = useState<any>(null);
|
|
37
|
+
const [validationErrors, setValidationErrors] = useState<string[]>([]);
|
|
38
|
+
|
|
39
|
+
// The display value is either the edited value or the original value
|
|
40
|
+
const displayValue = useMemo(() => {
|
|
41
|
+
if (isEdited && editedValue !== undefined) {
|
|
42
|
+
return editedValue;
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}, [value, editedValue, isEdited]);
|
|
46
|
+
|
|
47
|
+
// Handle starting an edit
|
|
48
|
+
const handleStartEdit = useCallback(() => {
|
|
49
|
+
if (disabled) return;
|
|
50
|
+
setCurrentEditValue(displayValue);
|
|
51
|
+
setIsEditing(true);
|
|
52
|
+
setValidationErrors([]);
|
|
53
|
+
}, [displayValue, disabled]);
|
|
54
|
+
|
|
55
|
+
// Handle canceling an edit
|
|
56
|
+
const handleCancelEdit = useCallback(() => {
|
|
57
|
+
setIsEditing(false);
|
|
58
|
+
setCurrentEditValue(null);
|
|
59
|
+
setValidationErrors([]);
|
|
60
|
+
onCancel?.(resourceId);
|
|
61
|
+
}, [resourceId, onCancel]);
|
|
62
|
+
|
|
63
|
+
// Handle saving an edit
|
|
64
|
+
const handleSaveEdit = useCallback(() => {
|
|
65
|
+
if (!onSave || currentEditValue === null) return;
|
|
66
|
+
|
|
67
|
+
// Validate the edited value
|
|
68
|
+
const validation = validateEditedResource(currentEditValue);
|
|
69
|
+
if (!validation.isValid) {
|
|
70
|
+
setValidationErrors(validation.errors);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Save the edit
|
|
75
|
+
onSave(resourceId, currentEditValue, value);
|
|
76
|
+
setIsEditing(false);
|
|
77
|
+
setCurrentEditValue(null);
|
|
78
|
+
setValidationErrors([]);
|
|
79
|
+
}, [resourceId, currentEditValue, value, onSave]);
|
|
80
|
+
|
|
81
|
+
// Handle changes in the JSON editor
|
|
82
|
+
const handleJsonChange = useCallback(
|
|
83
|
+
(newValue: any) => {
|
|
84
|
+
setCurrentEditValue(newValue);
|
|
85
|
+
|
|
86
|
+
// Clear validation errors when user starts typing
|
|
87
|
+
if (validationErrors.length > 0) {
|
|
88
|
+
setValidationErrors([]);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
[validationErrors]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// JSON editor configuration
|
|
95
|
+
const jsonEditConfig = useMemo(
|
|
96
|
+
() => ({
|
|
97
|
+
minHeight: '200px',
|
|
98
|
+
maxHeight: '400px',
|
|
99
|
+
style: {
|
|
100
|
+
container: {
|
|
101
|
+
backgroundColor: '#f9fafb',
|
|
102
|
+
border: '1px solid #d1d5db',
|
|
103
|
+
borderRadius: '0.375rem',
|
|
104
|
+
fontFamily:
|
|
105
|
+
'ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
|
106
|
+
},
|
|
107
|
+
key: {
|
|
108
|
+
color: '#1f2937',
|
|
109
|
+
fontWeight: '500'
|
|
110
|
+
},
|
|
111
|
+
string: {
|
|
112
|
+
color: '#059669'
|
|
113
|
+
},
|
|
114
|
+
number: {
|
|
115
|
+
color: '#dc2626'
|
|
116
|
+
},
|
|
117
|
+
boolean: {
|
|
118
|
+
color: '#7c3aed'
|
|
119
|
+
},
|
|
120
|
+
null: {
|
|
121
|
+
color: '#6b7280'
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
enableHighlight: true,
|
|
125
|
+
enableClipboard: true
|
|
126
|
+
}),
|
|
127
|
+
[]
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div className={`bg-white rounded-lg border ${className}`}>
|
|
132
|
+
{/* Header with edit controls */}
|
|
133
|
+
<div className="flex items-center justify-between p-3 border-b bg-gray-50">
|
|
134
|
+
<div className="flex items-center space-x-2">
|
|
135
|
+
<h4 className="text-sm font-semibold text-gray-900">Resource Content</h4>
|
|
136
|
+
{isEdited && (
|
|
137
|
+
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
|
138
|
+
Edited
|
|
139
|
+
</span>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{!isEditing && (
|
|
144
|
+
<button
|
|
145
|
+
onClick={handleStartEdit}
|
|
146
|
+
disabled={disabled}
|
|
147
|
+
className="inline-flex items-center px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
148
|
+
title="Edit resource content"
|
|
149
|
+
>
|
|
150
|
+
<PencilIcon className="h-4 w-4 mr-1" />
|
|
151
|
+
Edit
|
|
152
|
+
</button>
|
|
153
|
+
)}
|
|
154
|
+
|
|
155
|
+
{isEditing && (
|
|
156
|
+
<div className="flex items-center space-x-2">
|
|
157
|
+
<button
|
|
158
|
+
onClick={handleSaveEdit}
|
|
159
|
+
disabled={validationErrors.length > 0}
|
|
160
|
+
className="inline-flex items-center px-3 py-1.5 text-sm font-medium text-white bg-green-600 border border-transparent rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
161
|
+
title="Save changes"
|
|
162
|
+
>
|
|
163
|
+
<CheckIcon className="h-4 w-4 mr-1" />
|
|
164
|
+
Save
|
|
165
|
+
</button>
|
|
166
|
+
<button
|
|
167
|
+
onClick={handleCancelEdit}
|
|
168
|
+
className="inline-flex items-center px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
169
|
+
title="Cancel changes"
|
|
170
|
+
>
|
|
171
|
+
<XMarkIcon className="h-4 w-4 mr-1" />
|
|
172
|
+
Cancel
|
|
173
|
+
</button>
|
|
174
|
+
</div>
|
|
175
|
+
)}
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
{/* Validation errors */}
|
|
179
|
+
{validationErrors.length > 0 && (
|
|
180
|
+
<div className="p-3 border-b bg-red-50">
|
|
181
|
+
<div className="text-sm text-red-800">
|
|
182
|
+
<p className="font-medium mb-1">Validation Errors:</p>
|
|
183
|
+
<ul className="list-disc list-inside space-y-1">
|
|
184
|
+
{validationErrors.map((error, index) => (
|
|
185
|
+
<li key={index}>{error}</li>
|
|
186
|
+
))}
|
|
187
|
+
</ul>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
)}
|
|
191
|
+
|
|
192
|
+
{/* JSON content */}
|
|
193
|
+
<div className="p-3">
|
|
194
|
+
{isEditing ? (
|
|
195
|
+
<JsonEditor data={currentEditValue} setData={handleJsonChange} {...jsonEditConfig} />
|
|
196
|
+
) : (
|
|
197
|
+
<pre className="text-sm font-mono text-gray-800 bg-gray-50 p-3 rounded border overflow-x-auto whitespace-pre-wrap">
|
|
198
|
+
{JSON.stringify(displayValue, null, 2)}
|
|
199
|
+
</pre>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
{/* Help text */}
|
|
204
|
+
{isEditing && (
|
|
205
|
+
<div className="px-3 pb-3">
|
|
206
|
+
<p className="text-xs text-gray-500">
|
|
207
|
+
Edit the JSON content above. Changes will be saved as a new candidate with the current resolution
|
|
208
|
+
context.
|
|
209
|
+
</p>
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
CheckIcon,
|
|
4
|
+
XMarkIcon,
|
|
5
|
+
ExclamationTriangleIcon,
|
|
6
|
+
ArrowPathIcon,
|
|
7
|
+
DocumentTextIcon
|
|
8
|
+
} from '@heroicons/react/24/outline';
|
|
9
|
+
|
|
10
|
+
export interface ResolutionEditControlsProps {
|
|
11
|
+
/** Number of unsaved edits */
|
|
12
|
+
editCount: number;
|
|
13
|
+
/** Whether edit application is currently in progress */
|
|
14
|
+
isApplying: boolean;
|
|
15
|
+
/** Whether any edits exist to operate on */
|
|
16
|
+
hasEdits: boolean;
|
|
17
|
+
/** Callback to apply all pending edits */
|
|
18
|
+
onApplyEdits?: () => Promise<void>;
|
|
19
|
+
/** Callback to discard all pending edits */
|
|
20
|
+
onDiscardEdits?: () => void;
|
|
21
|
+
/** Whether the controls should be disabled */
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** Additional CSS classes */
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ResolutionEditControls: React.FC<ResolutionEditControlsProps> = ({
|
|
28
|
+
editCount,
|
|
29
|
+
isApplying,
|
|
30
|
+
hasEdits,
|
|
31
|
+
onApplyEdits,
|
|
32
|
+
onDiscardEdits,
|
|
33
|
+
disabled = false,
|
|
34
|
+
className = ''
|
|
35
|
+
}) => {
|
|
36
|
+
const [showDiscardConfirm, setShowDiscardConfirm] = useState(false);
|
|
37
|
+
|
|
38
|
+
const handleApplyEdits = async () => {
|
|
39
|
+
if (onApplyEdits && !isApplying && hasEdits) {
|
|
40
|
+
await onApplyEdits();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleDiscardEdits = () => {
|
|
45
|
+
if (onDiscardEdits && hasEdits && !isApplying) {
|
|
46
|
+
onDiscardEdits();
|
|
47
|
+
setShowDiscardConfirm(false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleDiscardClick = () => {
|
|
52
|
+
if (hasEdits) {
|
|
53
|
+
setShowDiscardConfirm(true);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
if (!hasEdits && !isApplying) {
|
|
58
|
+
return (
|
|
59
|
+
<div className={`bg-gray-50 rounded-lg border border-gray-200 p-4 ${className}`}>
|
|
60
|
+
<div className="flex items-center justify-center text-gray-500">
|
|
61
|
+
<DocumentTextIcon className="h-5 w-5 mr-2" />
|
|
62
|
+
<span className="text-sm">No pending edits</span>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className={`bg-white rounded-lg border border-gray-200 shadow-sm ${className}`}>
|
|
70
|
+
<div className="p-4">
|
|
71
|
+
{/* Header */}
|
|
72
|
+
<div className="flex items-center justify-between mb-4">
|
|
73
|
+
<div className="flex items-center">
|
|
74
|
+
<div className="flex items-center space-x-2">
|
|
75
|
+
<h3 className="text-lg font-semibold text-gray-900">Pending Edits</h3>
|
|
76
|
+
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
|
77
|
+
{editCount} edit{editCount !== 1 ? 's' : ''}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{isApplying && (
|
|
83
|
+
<div className="flex items-center text-blue-600">
|
|
84
|
+
<ArrowPathIcon className="h-4 w-4 mr-1 animate-spin" />
|
|
85
|
+
<span className="text-sm font-medium">Applying...</span>
|
|
86
|
+
</div>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
{/* Info text */}
|
|
91
|
+
<p className="text-sm text-gray-600 mb-4">
|
|
92
|
+
You have {editCount} unsaved edit{editCount !== 1 ? 's' : ''}.{' '}
|
|
93
|
+
{editCount === 1 ? 'This edit' : 'These edits'} will be applied as new candidate
|
|
94
|
+
{editCount !== 1 ? 's' : ''} with the current resolution context.
|
|
95
|
+
</p>
|
|
96
|
+
|
|
97
|
+
{/* Action buttons */}
|
|
98
|
+
{!showDiscardConfirm ? (
|
|
99
|
+
<div className="flex items-center space-x-3">
|
|
100
|
+
<button
|
|
101
|
+
onClick={handleApplyEdits}
|
|
102
|
+
disabled={disabled || isApplying || !hasEdits}
|
|
103
|
+
className="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-green-600 border border-transparent rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
104
|
+
>
|
|
105
|
+
{isApplying ? (
|
|
106
|
+
<>
|
|
107
|
+
<ArrowPathIcon className="h-4 w-4 mr-2 animate-spin" />
|
|
108
|
+
Applying Edits...
|
|
109
|
+
</>
|
|
110
|
+
) : (
|
|
111
|
+
<>
|
|
112
|
+
<CheckIcon className="h-4 w-4 mr-2" />
|
|
113
|
+
Apply Edits
|
|
114
|
+
</>
|
|
115
|
+
)}
|
|
116
|
+
</button>
|
|
117
|
+
|
|
118
|
+
<button
|
|
119
|
+
onClick={handleDiscardClick}
|
|
120
|
+
disabled={disabled || isApplying || !hasEdits}
|
|
121
|
+
className="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
122
|
+
>
|
|
123
|
+
<XMarkIcon className="h-4 w-4 mr-2" />
|
|
124
|
+
Discard Edits
|
|
125
|
+
</button>
|
|
126
|
+
</div>
|
|
127
|
+
) : (
|
|
128
|
+
/* Discard confirmation */
|
|
129
|
+
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
|
|
130
|
+
<div className="flex items-start">
|
|
131
|
+
<ExclamationTriangleIcon className="h-5 w-5 text-yellow-400 mt-0.5 mr-3" />
|
|
132
|
+
<div className="flex-1">
|
|
133
|
+
<h4 className="text-sm font-medium text-yellow-800 mb-1">Confirm Discard</h4>
|
|
134
|
+
<p className="text-sm text-yellow-700 mb-3">
|
|
135
|
+
Are you sure you want to discard {editCount} unsaved edit{editCount !== 1 ? 's' : ''}? This
|
|
136
|
+
action cannot be undone.
|
|
137
|
+
</p>
|
|
138
|
+
<div className="flex items-center space-x-3">
|
|
139
|
+
<button
|
|
140
|
+
onClick={handleDiscardEdits}
|
|
141
|
+
className="inline-flex items-center px-3 py-1.5 text-sm font-medium text-white bg-red-600 border border-transparent rounded hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
|
|
142
|
+
>
|
|
143
|
+
<XMarkIcon className="h-4 w-4 mr-1" />
|
|
144
|
+
Yes, Discard
|
|
145
|
+
</button>
|
|
146
|
+
<button
|
|
147
|
+
onClick={() => setShowDiscardConfirm(false)}
|
|
148
|
+
className="inline-flex items-center px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
149
|
+
>
|
|
150
|
+
Cancel
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
)}
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
{/* Warning about system rebuild */}
|
|
160
|
+
{hasEdits && !showDiscardConfirm && (
|
|
161
|
+
<div className="border-t border-gray-200 px-4 py-3 bg-blue-50">
|
|
162
|
+
<p className="text-xs text-blue-700">
|
|
163
|
+
<strong>Note:</strong> Applying edits will rebuild the entire resource system with your changes.
|
|
164
|
+
This may take a moment and will update all resolution results.
|
|
165
|
+
</p>
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
};
|