@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,374 @@
1
+ import React, { useState, useCallback, useMemo, useRef } from 'react';
2
+ import { Config, QualifierTypes, Qualifiers, ResourceTypes } from '@fgv/ts-res';
3
+ import { Result, succeed, fail } from '@fgv/ts-utils';
4
+ import {
5
+ getDefaultConfiguration,
6
+ validateConfiguration,
7
+ cloneConfiguration,
8
+ compareConfigurations,
9
+ trackConfigurationChanges,
10
+ exportConfiguration,
11
+ importConfiguration,
12
+ getConfigurationTemplates,
13
+ generateConfigurationFilename,
14
+ ConfigurationChanges,
15
+ ConfigurationValidationResult,
16
+ ConfigurationExportOptions,
17
+ ConfigurationTemplate
18
+ } from '../utils/configurationUtils';
19
+
20
+ export interface ConfigurationState {
21
+ currentConfiguration: Config.Model.ISystemConfiguration;
22
+ originalConfiguration: Config.Model.ISystemConfiguration;
23
+ hasUnsavedChanges: boolean;
24
+ changes: ConfigurationChanges;
25
+ validation: ConfigurationValidationResult;
26
+ activeTab: 'qualifierTypes' | 'qualifiers' | 'resourceTypes' | 'json';
27
+ isJsonView: boolean;
28
+ jsonString: string;
29
+ jsonError: string | null;
30
+ }
31
+
32
+ export interface ConfigurationActions {
33
+ // Configuration management
34
+ loadConfiguration: (config: Config.Model.ISystemConfiguration) => void;
35
+ resetConfiguration: () => void;
36
+ applyConfiguration: () => void;
37
+
38
+ // Editing operations
39
+ updateQualifierTypes: (qualifierTypes: QualifierTypes.Config.ISystemQualifierTypeConfig[]) => void;
40
+ updateQualifiers: (qualifiers: Qualifiers.IQualifierDecl[]) => void;
41
+ updateResourceTypes: (resourceTypes: ResourceTypes.Config.IResourceTypeConfig[]) => void;
42
+
43
+ // Individual item operations
44
+ addQualifierType: (qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig) => void;
45
+ updateQualifierType: (
46
+ index: number,
47
+ qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig
48
+ ) => void;
49
+ removeQualifierType: (index: number) => void;
50
+
51
+ addQualifier: (qualifier: Qualifiers.IQualifierDecl) => void;
52
+ updateQualifier: (index: number, qualifier: Qualifiers.IQualifierDecl) => void;
53
+ removeQualifier: (index: number) => void;
54
+
55
+ addResourceType: (resourceType: ResourceTypes.Config.IResourceTypeConfig) => void;
56
+ updateResourceType: (index: number, resourceType: ResourceTypes.Config.IResourceTypeConfig) => void;
57
+ removeResourceType: (index: number) => void;
58
+
59
+ // View management
60
+ setActiveTab: (tab: ConfigurationState['activeTab']) => void;
61
+ toggleJsonView: () => void;
62
+ updateJsonString: (json: string) => void;
63
+ applyJsonChanges: () => Result<void>;
64
+
65
+ // Import/Export
66
+ exportToJson: (options?: ConfigurationExportOptions) => Result<string>;
67
+ importFromJson: (jsonData: string) => Result<void>;
68
+ loadTemplate: (templateId: string) => Result<void>;
69
+
70
+ // Validation
71
+ validateCurrent: () => ConfigurationValidationResult;
72
+ }
73
+
74
+ export interface UseConfigurationStateReturn {
75
+ state: ConfigurationState;
76
+ actions: ConfigurationActions;
77
+ templates: ConfigurationTemplate[];
78
+ }
79
+
80
+ export function useConfigurationState(
81
+ initialConfiguration?: Config.Model.ISystemConfiguration,
82
+ onConfigurationChange?: (config: Config.Model.ISystemConfiguration) => void,
83
+ onUnsavedChanges?: (hasChanges: boolean) => void
84
+ ): UseConfigurationStateReturn {
85
+ const defaultConfig = useMemo(
86
+ () => initialConfiguration || getDefaultConfiguration(),
87
+ [initialConfiguration]
88
+ );
89
+ const originalConfigRef = useRef(defaultConfig);
90
+
91
+ // State
92
+ const [currentConfiguration, setCurrentConfiguration] = useState<Config.Model.ISystemConfiguration>(
93
+ cloneConfiguration(defaultConfig)
94
+ );
95
+ const [activeTab, setActiveTab] = useState<ConfigurationState['activeTab']>('qualifiers');
96
+ const [isJsonView, setIsJsonView] = useState(false);
97
+ const [jsonString, setJsonString] = useState('');
98
+ const [jsonError, setJsonError] = useState<string | null>(null);
99
+
100
+ // Computed state
101
+ const hasUnsavedChanges = useMemo(() => {
102
+ return !compareConfigurations(originalConfigRef.current, currentConfiguration);
103
+ }, [currentConfiguration]);
104
+
105
+ const changes = useMemo(() => {
106
+ return trackConfigurationChanges(originalConfigRef.current, currentConfiguration);
107
+ }, [currentConfiguration]);
108
+
109
+ const validation = useMemo(() => {
110
+ return validateConfiguration(currentConfiguration);
111
+ }, [currentConfiguration]);
112
+
113
+ // Update JSON string when configuration changes and in JSON view
114
+ React.useEffect(() => {
115
+ if (isJsonView) {
116
+ const result = exportConfiguration(currentConfiguration, { format: 'json', pretty: true });
117
+ if (result.isSuccess()) {
118
+ setJsonString(result.value);
119
+ setJsonError(null);
120
+ } else {
121
+ setJsonError(result.message);
122
+ }
123
+ }
124
+ }, [currentConfiguration, isJsonView]);
125
+
126
+ // Notify parent of configuration changes
127
+ const isFirstMount = useRef(true);
128
+ const lastNotifiedConfig = useRef(currentConfiguration);
129
+
130
+ React.useEffect(() => {
131
+ // Skip notification on first mount to avoid loops
132
+ if (isFirstMount.current) {
133
+ isFirstMount.current = false;
134
+ return;
135
+ }
136
+
137
+ // Only notify if configuration actually changed
138
+ if (!compareConfigurations(lastNotifiedConfig.current, currentConfiguration)) {
139
+ lastNotifiedConfig.current = currentConfiguration;
140
+ onConfigurationChange?.(currentConfiguration);
141
+ }
142
+ }, [currentConfiguration, onConfigurationChange]);
143
+
144
+ // Notify parent of unsaved changes
145
+ React.useEffect(() => {
146
+ onUnsavedChanges?.(hasUnsavedChanges);
147
+ }, [hasUnsavedChanges, onUnsavedChanges]);
148
+
149
+ // Actions
150
+ const loadConfiguration = useCallback((config: Config.Model.ISystemConfiguration) => {
151
+ const cloned = cloneConfiguration(config);
152
+ setCurrentConfiguration(cloned);
153
+ originalConfigRef.current = cloneConfiguration(config);
154
+ }, []);
155
+
156
+ const resetConfiguration = useCallback(() => {
157
+ setCurrentConfiguration(cloneConfiguration(originalConfigRef.current));
158
+ }, []);
159
+
160
+ const applyConfiguration = useCallback(() => {
161
+ originalConfigRef.current = cloneConfiguration(currentConfiguration);
162
+ }, [currentConfiguration]);
163
+
164
+ // Qualifier Types operations
165
+ const updateQualifierTypes = useCallback(
166
+ (qualifierTypes: QualifierTypes.Config.ISystemQualifierTypeConfig[]) => {
167
+ setCurrentConfiguration((prev) => ({
168
+ ...prev,
169
+ qualifierTypes
170
+ }));
171
+ },
172
+ []
173
+ );
174
+
175
+ const addQualifierType = useCallback((qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig) => {
176
+ setCurrentConfiguration((prev) => ({
177
+ ...prev,
178
+ qualifierTypes: [...(prev.qualifierTypes || []), qualifierType]
179
+ }));
180
+ }, []);
181
+
182
+ const updateQualifierType = useCallback(
183
+ (index: number, qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig) => {
184
+ setCurrentConfiguration((prev) => ({
185
+ ...prev,
186
+ qualifierTypes: prev.qualifierTypes?.map((qt, i) => (i === index ? qualifierType : qt)) || []
187
+ }));
188
+ },
189
+ []
190
+ );
191
+
192
+ const removeQualifierType = useCallback((index: number) => {
193
+ setCurrentConfiguration((prev) => ({
194
+ ...prev,
195
+ qualifierTypes: prev.qualifierTypes?.filter((_, i) => i !== index) || []
196
+ }));
197
+ }, []);
198
+
199
+ // Qualifiers operations
200
+ const updateQualifiers = useCallback((qualifiers: Qualifiers.IQualifierDecl[]) => {
201
+ setCurrentConfiguration((prev) => ({
202
+ ...prev,
203
+ qualifiers
204
+ }));
205
+ }, []);
206
+
207
+ const addQualifier = useCallback((qualifier: Qualifiers.IQualifierDecl) => {
208
+ setCurrentConfiguration((prev) => ({
209
+ ...prev,
210
+ qualifiers: [...(prev.qualifiers || []), qualifier]
211
+ }));
212
+ }, []);
213
+
214
+ const updateQualifier = useCallback((index: number, qualifier: Qualifiers.IQualifierDecl) => {
215
+ setCurrentConfiguration((prev) => ({
216
+ ...prev,
217
+ qualifiers: prev.qualifiers?.map((q, i) => (i === index ? qualifier : q)) || []
218
+ }));
219
+ }, []);
220
+
221
+ const removeQualifier = useCallback((index: number) => {
222
+ setCurrentConfiguration((prev) => ({
223
+ ...prev,
224
+ qualifiers: prev.qualifiers?.filter((_, i) => i !== index) || []
225
+ }));
226
+ }, []);
227
+
228
+ // Resource Types operations
229
+ const updateResourceTypes = useCallback((resourceTypes: ResourceTypes.Config.IResourceTypeConfig[]) => {
230
+ setCurrentConfiguration((prev) => ({
231
+ ...prev,
232
+ resourceTypes
233
+ }));
234
+ }, []);
235
+
236
+ const addResourceType = useCallback((resourceType: ResourceTypes.Config.IResourceTypeConfig) => {
237
+ setCurrentConfiguration((prev) => ({
238
+ ...prev,
239
+ resourceTypes: [...(prev.resourceTypes || []), resourceType]
240
+ }));
241
+ }, []);
242
+
243
+ const updateResourceType = useCallback(
244
+ (index: number, resourceType: ResourceTypes.Config.IResourceTypeConfig) => {
245
+ setCurrentConfiguration((prev) => ({
246
+ ...prev,
247
+ resourceTypes: prev.resourceTypes?.map((rt, i) => (i === index ? resourceType : rt)) || []
248
+ }));
249
+ },
250
+ []
251
+ );
252
+
253
+ const removeResourceType = useCallback((index: number) => {
254
+ setCurrentConfiguration((prev) => ({
255
+ ...prev,
256
+ resourceTypes: prev.resourceTypes?.filter((_, i) => i !== index) || []
257
+ }));
258
+ }, []);
259
+
260
+ // View management
261
+ const toggleJsonView = useCallback(() => {
262
+ if (!isJsonView) {
263
+ // Switching to JSON view - export current config
264
+ const result = exportConfiguration(currentConfiguration, { format: 'json', pretty: true });
265
+ if (result.isSuccess()) {
266
+ setJsonString(result.value);
267
+ setJsonError(null);
268
+ } else {
269
+ setJsonError(result.message);
270
+ }
271
+ }
272
+ setIsJsonView(!isJsonView);
273
+ }, [isJsonView, currentConfiguration]);
274
+
275
+ const updateJsonString = useCallback((json: string) => {
276
+ setJsonString(json);
277
+ setJsonError(null);
278
+ }, []);
279
+
280
+ const applyJsonChanges = useCallback((): Result<void> => {
281
+ const result = importConfiguration(jsonString);
282
+ if (result.isSuccess()) {
283
+ setCurrentConfiguration(result.value);
284
+ setJsonError(null);
285
+ return succeed(undefined);
286
+ } else {
287
+ setJsonError(result.message);
288
+ return fail(result.message);
289
+ }
290
+ }, [jsonString]);
291
+
292
+ // Import/Export
293
+ const exportToJson = useCallback(
294
+ (options?: ConfigurationExportOptions): Result<string> => {
295
+ return exportConfiguration(currentConfiguration, options);
296
+ },
297
+ [currentConfiguration]
298
+ );
299
+
300
+ const importFromJson = useCallback(
301
+ (jsonData: string): Result<void> => {
302
+ const result = importConfiguration(jsonData);
303
+ if (result.isSuccess()) {
304
+ loadConfiguration(result.value);
305
+ return succeed(undefined);
306
+ }
307
+ return fail(result.message);
308
+ },
309
+ [loadConfiguration]
310
+ );
311
+
312
+ const loadTemplate = useCallback(
313
+ (templateId: string): Result<void> => {
314
+ const templates = getConfigurationTemplates();
315
+ const template = templates.find((t) => t.id === templateId);
316
+
317
+ if (!template) {
318
+ return fail(`Template '${templateId}' not found`);
319
+ }
320
+
321
+ loadConfiguration(template.configuration);
322
+ return succeed(undefined);
323
+ },
324
+ [loadConfiguration]
325
+ );
326
+
327
+ const validateCurrent = useCallback((): ConfigurationValidationResult => {
328
+ return validateConfiguration(currentConfiguration);
329
+ }, [currentConfiguration]);
330
+
331
+ const state: ConfigurationState = {
332
+ currentConfiguration,
333
+ originalConfiguration: originalConfigRef.current,
334
+ hasUnsavedChanges,
335
+ changes,
336
+ validation,
337
+ activeTab,
338
+ isJsonView,
339
+ jsonString,
340
+ jsonError
341
+ };
342
+
343
+ const actions: ConfigurationActions = {
344
+ loadConfiguration,
345
+ resetConfiguration,
346
+ applyConfiguration,
347
+ updateQualifierTypes,
348
+ updateQualifiers,
349
+ updateResourceTypes,
350
+ addQualifierType,
351
+ updateQualifierType,
352
+ removeQualifierType,
353
+ addQualifier,
354
+ updateQualifier,
355
+ removeQualifier,
356
+ addResourceType,
357
+ updateResourceType,
358
+ removeResourceType,
359
+ setActiveTab,
360
+ toggleJsonView,
361
+ updateJsonString,
362
+ applyJsonChanges,
363
+ exportToJson,
364
+ importFromJson,
365
+ loadTemplate,
366
+ validateCurrent
367
+ };
368
+
369
+ return {
370
+ state,
371
+ actions,
372
+ templates: getConfigurationTemplates()
373
+ };
374
+ }
@@ -0,0 +1,97 @@
1
+ import { useState, useCallback } from 'react';
2
+ import { FilterState, FilterActions } from '../types';
3
+
4
+ export interface UseFilterStateReturn {
5
+ state: FilterState;
6
+ actions: FilterActions;
7
+ }
8
+
9
+ const initialFilterState: FilterState = {
10
+ enabled: false,
11
+ values: {},
12
+ appliedValues: {},
13
+ hasPendingChanges: false,
14
+ reduceQualifiers: false
15
+ };
16
+
17
+ // Normalize values by filtering out undefined and empty strings for consistent comparison
18
+ const normalizeValues = (vals: Record<string, string | undefined>): Record<string, string> => {
19
+ const normalized: Record<string, string> = {};
20
+ Object.entries(vals).forEach(([key, value]) => {
21
+ if (value !== undefined && value !== '') {
22
+ normalized[key] = value;
23
+ }
24
+ });
25
+ return normalized;
26
+ };
27
+
28
+ export function useFilterState(initialState?: Partial<FilterState>): UseFilterStateReturn {
29
+ const [state, setState] = useState<FilterState>({
30
+ ...initialFilterState,
31
+ ...initialState
32
+ });
33
+
34
+ const updateFilterEnabled = useCallback((enabled: boolean) => {
35
+ setState((prev) => {
36
+ const normalizedValues = normalizeValues(prev.values);
37
+ const normalizedApplied = normalizeValues(prev.appliedValues);
38
+ const hasChanges = JSON.stringify(normalizedValues) !== JSON.stringify(normalizedApplied);
39
+
40
+ return {
41
+ ...prev,
42
+ enabled,
43
+ // Only consider filter values when determining pending changes, not the enabled state itself
44
+ hasPendingChanges: enabled && hasChanges
45
+ };
46
+ });
47
+ }, []);
48
+
49
+ const updateFilterValues = useCallback((values: Record<string, string | undefined>) => {
50
+ setState((prev) => {
51
+ const normalizedValues = normalizeValues(values);
52
+ const normalizedApplied = normalizeValues(prev.appliedValues);
53
+ const hasChanges = JSON.stringify(normalizedValues) !== JSON.stringify(normalizedApplied);
54
+
55
+ return {
56
+ ...prev,
57
+ values,
58
+ hasPendingChanges: prev.enabled && hasChanges
59
+ };
60
+ });
61
+ }, []);
62
+
63
+ const applyFilterValues = useCallback(() => {
64
+ setState((prev) => ({
65
+ ...prev,
66
+ appliedValues: { ...prev.values },
67
+ hasPendingChanges: false
68
+ }));
69
+ }, []);
70
+
71
+ const resetFilterValues = useCallback(() => {
72
+ setState((prev) => ({
73
+ ...prev,
74
+ values: {},
75
+ appliedValues: {},
76
+ hasPendingChanges: false,
77
+ enabled: false
78
+ }));
79
+ }, []);
80
+
81
+ const updateReduceQualifiers = useCallback((reduceQualifiers: boolean) => {
82
+ setState((prev) => ({
83
+ ...prev,
84
+ reduceQualifiers
85
+ }));
86
+ }, []);
87
+
88
+ const actions: FilterActions = {
89
+ updateFilterEnabled,
90
+ updateFilterValues,
91
+ applyFilterValues,
92
+ resetFilterValues,
93
+ updateReduceQualifiers
94
+ };
95
+
96
+ return { state, actions };
97
+ }