@fgv/ts-res-ui-components 5.0.0-21 → 5.0.0-23

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 (192) hide show
  1. package/README.md +401 -155
  2. package/config/jest.setup.js +10 -0
  3. package/dist/ts-res-ui-components.d.ts +1657 -76
  4. package/lib/components/common/QualifierContextControl.js +4 -1
  5. package/lib/components/common/ResourceTreeView.js +4 -1
  6. package/lib/components/forms/GenericQualifierTypeEditForm.d.ts +26 -0
  7. package/lib/components/forms/GenericQualifierTypeEditForm.js +166 -0
  8. package/lib/components/forms/QualifierEditForm.d.ts +1 -1
  9. package/lib/components/forms/index.d.ts +2 -0
  10. package/lib/components/forms/index.js +1 -0
  11. package/lib/components/orchestrator/ResourceOrchestrator.d.ts +3 -0
  12. package/lib/components/orchestrator/ResourceOrchestrator.js +118 -51
  13. package/lib/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
  14. package/lib/components/pickers/ResourcePicker/index.js +4 -2
  15. package/lib/components/views/CompiledView/index.js +75 -16
  16. package/lib/components/views/ConfigurationView/index.js +94 -35
  17. package/lib/components/views/FilterView/index.js +7 -4
  18. package/lib/components/views/GridView/EditableGridCell.d.ts +76 -0
  19. package/lib/components/views/GridView/EditableGridCell.js +224 -0
  20. package/lib/components/views/GridView/GridSelector.d.ts +43 -0
  21. package/lib/components/views/GridView/GridSelector.js +89 -0
  22. package/lib/components/views/GridView/MultiGridView.d.ts +85 -0
  23. package/lib/components/views/GridView/MultiGridView.js +196 -0
  24. package/lib/components/views/GridView/ResourceGrid.d.ts +38 -0
  25. package/lib/components/views/GridView/ResourceGrid.js +232 -0
  26. package/lib/components/views/GridView/SharedContextControls.d.ts +47 -0
  27. package/lib/components/views/GridView/SharedContextControls.js +95 -0
  28. package/lib/components/views/GridView/cells/BooleanCell.d.ts +44 -0
  29. package/lib/components/views/GridView/cells/BooleanCell.js +49 -0
  30. package/lib/components/views/GridView/cells/DropdownCell.d.ts +58 -0
  31. package/lib/components/views/GridView/cells/DropdownCell.js +182 -0
  32. package/lib/components/views/GridView/cells/StringCell.d.ts +57 -0
  33. package/lib/components/views/GridView/cells/StringCell.js +106 -0
  34. package/lib/components/views/GridView/cells/TriStateCell.d.ts +54 -0
  35. package/lib/components/views/GridView/cells/TriStateCell.js +112 -0
  36. package/lib/components/views/GridView/cells/index.d.ts +15 -0
  37. package/lib/components/views/GridView/cells/index.js +11 -0
  38. package/lib/components/views/GridView/index.d.ts +53 -0
  39. package/lib/components/views/GridView/index.js +212 -0
  40. package/lib/components/views/ImportView/index.js +22 -19
  41. package/lib/components/views/MessagesWindow/index.js +4 -1
  42. package/lib/components/views/ResolutionView/index.js +8 -5
  43. package/lib/contexts/ObservabilityContext.d.ts +85 -0
  44. package/lib/contexts/ObservabilityContext.js +98 -0
  45. package/lib/contexts/index.d.ts +2 -0
  46. package/lib/contexts/index.js +24 -0
  47. package/lib/hooks/useConfigurationState.d.ts +3 -3
  48. package/lib/hooks/useResolutionState.js +850 -246
  49. package/lib/hooks/useResourceData.d.ts +7 -4
  50. package/lib/hooks/useResourceData.js +185 -184
  51. package/lib/index.d.ts +5 -1
  52. package/lib/index.js +8 -1
  53. package/lib/namespaces/GridTools.d.ts +136 -0
  54. package/lib/namespaces/GridTools.js +138 -0
  55. package/lib/namespaces/ObservabilityTools.d.ts +3 -0
  56. package/lib/namespaces/ObservabilityTools.js +23 -0
  57. package/lib/namespaces/ResolutionTools.d.ts +2 -1
  58. package/lib/namespaces/ResolutionTools.js +2 -0
  59. package/lib/namespaces/index.d.ts +2 -0
  60. package/lib/namespaces/index.js +2 -0
  61. package/lib/test/integration/observability.integration.test.d.ts +2 -0
  62. package/lib/test/unit/hooks/useResourceData.test.d.ts +2 -0
  63. package/lib/test/unit/utils/downloadHelper.test.d.ts +2 -0
  64. package/lib/test/unit/workflows/resolutionWorkflows.test.d.ts +2 -0
  65. package/lib/test/unit/workflows/resourceCreation.test.d.ts +2 -0
  66. package/lib/test/unit/workflows/resultPatternExtensions.test.d.ts +2 -0
  67. package/lib/test/unit/workflows/validation.test.d.ts +2 -0
  68. package/lib/types/index.d.ts +387 -20
  69. package/lib/types/index.js +2 -1
  70. package/lib/utils/cellValidation.d.ts +113 -0
  71. package/lib/utils/cellValidation.js +248 -0
  72. package/lib/utils/downloadHelper.d.ts +66 -0
  73. package/lib/utils/downloadHelper.js +195 -0
  74. package/lib/utils/observability/factories.d.ts +29 -0
  75. package/lib/utils/observability/factories.js +58 -0
  76. package/lib/utils/observability/implementations.d.ts +61 -0
  77. package/lib/utils/observability/implementations.js +103 -0
  78. package/lib/utils/observability/index.d.ts +4 -0
  79. package/lib/utils/observability/index.js +26 -0
  80. package/lib/utils/observability/interfaces.d.ts +30 -0
  81. package/lib/utils/observability/interfaces.js +23 -0
  82. package/lib/utils/resolutionEditing.js +2 -1
  83. package/lib/utils/resourceSelector.d.ts +97 -0
  84. package/lib/utils/resourceSelector.js +195 -0
  85. package/lib/utils/resourceSelectors.d.ts +146 -0
  86. package/lib/utils/resourceSelectors.js +233 -0
  87. package/lib/utils/tsResIntegration.d.ts +6 -41
  88. package/lib/utils/tsResIntegration.js +20 -16
  89. package/lib/utils/zipLoader/zipProcessingHelpers.d.ts +3 -2
  90. package/lib/utils/zipLoader/zipProcessingHelpers.js +6 -5
  91. package/lib-commonjs/components/common/QualifierContextControl.js +4 -1
  92. package/lib-commonjs/components/common/ResourceTreeView.js +4 -1
  93. package/lib-commonjs/components/forms/GenericQualifierTypeEditForm.js +171 -0
  94. package/lib-commonjs/components/forms/index.js +3 -1
  95. package/lib-commonjs/components/orchestrator/ResourceOrchestrator.js +118 -51
  96. package/lib-commonjs/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
  97. package/lib-commonjs/components/pickers/ResourcePicker/index.js +4 -2
  98. package/lib-commonjs/components/views/CompiledView/index.js +75 -16
  99. package/lib-commonjs/components/views/ConfigurationView/index.js +93 -34
  100. package/lib-commonjs/components/views/FilterView/index.js +7 -4
  101. package/lib-commonjs/components/views/GridView/EditableGridCell.js +232 -0
  102. package/lib-commonjs/components/views/GridView/GridSelector.js +94 -0
  103. package/lib-commonjs/components/views/GridView/MultiGridView.js +201 -0
  104. package/lib-commonjs/components/views/GridView/ResourceGrid.js +237 -0
  105. package/lib-commonjs/components/views/GridView/SharedContextControls.js +100 -0
  106. package/lib-commonjs/components/views/GridView/cells/BooleanCell.js +54 -0
  107. package/lib-commonjs/components/views/GridView/cells/DropdownCell.js +187 -0
  108. package/lib-commonjs/components/views/GridView/cells/StringCell.js +111 -0
  109. package/lib-commonjs/components/views/GridView/cells/TriStateCell.js +117 -0
  110. package/lib-commonjs/components/views/GridView/cells/index.js +18 -0
  111. package/lib-commonjs/components/views/GridView/index.js +217 -0
  112. package/lib-commonjs/components/views/ImportView/index.js +22 -19
  113. package/lib-commonjs/components/views/MessagesWindow/index.js +4 -1
  114. package/lib-commonjs/components/views/ResolutionView/index.js +8 -5
  115. package/lib-commonjs/contexts/ObservabilityContext.js +104 -0
  116. package/lib-commonjs/contexts/index.js +30 -0
  117. package/lib-commonjs/hooks/useResolutionState.js +849 -245
  118. package/lib-commonjs/hooks/useResourceData.js +184 -215
  119. package/lib-commonjs/index.js +15 -1
  120. package/lib-commonjs/namespaces/GridTools.js +161 -0
  121. package/lib-commonjs/namespaces/ObservabilityTools.js +33 -0
  122. package/lib-commonjs/namespaces/ResolutionTools.js +10 -1
  123. package/lib-commonjs/namespaces/index.js +3 -1
  124. package/lib-commonjs/types/index.js +10 -0
  125. package/lib-commonjs/utils/cellValidation.js +253 -0
  126. package/lib-commonjs/utils/downloadHelper.js +198 -0
  127. package/lib-commonjs/utils/observability/factories.js +63 -0
  128. package/lib-commonjs/utils/observability/implementations.js +109 -0
  129. package/lib-commonjs/utils/observability/index.js +36 -0
  130. package/lib-commonjs/utils/observability/interfaces.js +24 -0
  131. package/lib-commonjs/utils/resolutionEditing.js +2 -1
  132. package/lib-commonjs/utils/resourceSelector.js +200 -0
  133. package/lib-commonjs/utils/resourceSelectors.js +242 -0
  134. package/lib-commonjs/utils/tsResIntegration.js +21 -16
  135. package/lib-commonjs/utils/zipLoader/zipProcessingHelpers.js +7 -5
  136. package/package.json +7 -7
  137. package/src/components/common/QualifierContextControl.tsx +0 -338
  138. package/src/components/common/ResolutionContextOptionsControl.tsx +0 -450
  139. package/src/components/common/ResolutionResults/index.tsx +0 -481
  140. package/src/components/common/ResourceListView.tsx +0 -167
  141. package/src/components/common/ResourcePickerOptionsControl.tsx +0 -351
  142. package/src/components/common/ResourceTreeView.tsx +0 -417
  143. package/src/components/common/SourceResourceDetail/index.tsx +0 -493
  144. package/src/components/forms/HierarchyEditor.tsx +0 -285
  145. package/src/components/forms/QualifierEditForm.tsx +0 -487
  146. package/src/components/forms/QualifierTypeEditForm.tsx +0 -458
  147. package/src/components/forms/ResourceTypeEditForm.tsx +0 -437
  148. package/src/components/forms/index.ts +0 -11
  149. package/src/components/orchestrator/ResourceOrchestrator.tsx +0 -444
  150. package/src/components/pickers/ResourcePicker/README.md +0 -570
  151. package/src/components/pickers/ResourcePicker/ResourceItem.tsx +0 -127
  152. package/src/components/pickers/ResourcePicker/ResourcePickerList.tsx +0 -114
  153. package/src/components/pickers/ResourcePicker/ResourcePickerTree.tsx +0 -461
  154. package/src/components/pickers/ResourcePicker/index.tsx +0 -234
  155. package/src/components/pickers/ResourcePicker/types.ts +0 -301
  156. package/src/components/pickers/ResourcePicker/utils/treeNavigation.ts +0 -210
  157. package/src/components/views/CompiledView/index.tsx +0 -1342
  158. package/src/components/views/ConfigurationView/index.tsx +0 -848
  159. package/src/components/views/FilterView/index.tsx +0 -681
  160. package/src/components/views/ImportView/index.tsx +0 -789
  161. package/src/components/views/MessagesWindow/index.tsx +0 -325
  162. package/src/components/views/ResolutionView/EditableJsonView.tsx +0 -386
  163. package/src/components/views/ResolutionView/NewResourceModal.tsx +0 -158
  164. package/src/components/views/ResolutionView/UnifiedChangeControls.tsx +0 -163
  165. package/src/components/views/ResolutionView/index.tsx +0 -751
  166. package/src/components/views/SourceView/index.tsx +0 -291
  167. package/src/hooks/useConfigurationState.ts +0 -436
  168. package/src/hooks/useFilterState.ts +0 -150
  169. package/src/hooks/useResolutionState.ts +0 -893
  170. package/src/hooks/useResourceData.ts +0 -596
  171. package/src/hooks/useViewState.ts +0 -97
  172. package/src/index.ts +0 -68
  173. package/src/namespaces/ConfigurationTools.ts +0 -59
  174. package/src/namespaces/FilterTools.ts +0 -47
  175. package/src/namespaces/ImportTools.ts +0 -42
  176. package/src/namespaces/PickerTools.ts +0 -104
  177. package/src/namespaces/ResolutionTools.ts +0 -68
  178. package/src/namespaces/ResourceTools.ts +0 -106
  179. package/src/namespaces/TsResTools.ts +0 -49
  180. package/src/namespaces/ViewStateTools.ts +0 -91
  181. package/src/namespaces/ZipTools.ts +0 -49
  182. package/src/namespaces/index.ts +0 -49
  183. package/src/types/index.ts +0 -1273
  184. package/src/utils/configurationUtils.ts +0 -339
  185. package/src/utils/fileProcessing.ts +0 -164
  186. package/src/utils/filterResources.ts +0 -356
  187. package/src/utils/resolutionEditing.ts +0 -346
  188. package/src/utils/resolutionUtils.ts +0 -740
  189. package/src/utils/tsResIntegration.ts +0 -475
  190. package/src/utils/zipLoader/index.ts +0 -5
  191. package/src/utils/zipLoader/zipProcessingHelpers.ts +0 -46
  192. package/src/utils/zipLoader/zipUtils.ts +0 -7
@@ -1,458 +0,0 @@
1
- import React, { useState, useCallback, useEffect } from 'react';
2
- import { XMarkIcon, InformationCircleIcon } from '@heroicons/react/24/outline';
3
- import { QualifierTypes } from '@fgv/ts-res';
4
- import { HierarchyEditor } from './HierarchyEditor';
5
-
6
- /**
7
- * Props for the QualifierTypeEditForm component.
8
- *
9
- * @public
10
- */
11
- export interface QualifierTypeEditFormProps {
12
- /** Existing qualifier type to edit (undefined for creating new type) */
13
- qualifierType?: QualifierTypes.Config.ISystemQualifierTypeConfig;
14
- /** Callback fired when qualifier type is saved */
15
- onSave: (qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig) => void;
16
- /** Callback fired when editing is cancelled */
17
- onCancel: () => void;
18
- /** Names of existing qualifier types to prevent duplicates */
19
- existingNames?: string[];
20
- }
21
-
22
- interface FormData {
23
- name: string;
24
- systemType: 'language' | 'territory' | 'literal';
25
- allowContextList: boolean;
26
- // Literal type specific
27
- caseSensitive: boolean;
28
- enumeratedValues: string[];
29
- // Territory type specific
30
- acceptLowercase: boolean;
31
- allowedTerritories: string[];
32
- // Hierarchy support (for literal and territory types)
33
- hierarchy: Record<string, string>;
34
- }
35
-
36
- /**
37
- * Modal form component for creating and editing qualifier types in a ts-res system configuration.
38
- *
39
- * The QualifierTypeEditForm provides a comprehensive interface for defining qualifier types that
40
- * control the behavior and validation of qualifiers. It supports all three system types (language,
41
- * territory, literal) with type-specific configuration options, enumerated values, and hierarchical
42
- * relationships between values.
43
- *
44
- * @example
45
- * ```tsx
46
- * import { ConfigurationTools } from '@fgv/ts-res-ui-components';
47
- *
48
- * // Creating a new literal qualifier type with enumerated values
49
- * const [showForm, setShowForm] = useState(false);
50
- * const [qualifierTypes, setQualifierTypes] = useState([]);
51
- *
52
- * const handleSave = (qualifierType) => {
53
- * setQualifierTypes(prev => [...prev, qualifierType]);
54
- * setShowForm(false);
55
- * };
56
- *
57
- * {showForm && (
58
- * <ConfigurationTools.QualifierTypeEditForm
59
- * onSave={handleSave}
60
- * onCancel={() => setShowForm(false)}
61
- * existingNames={qualifierTypes.map(qt => qt.name)}
62
- * />
63
- * )}
64
- * ```
65
- *
66
- * @example
67
- * ```tsx
68
- * // Editing a platform qualifier type with hierarchy
69
- * const platformType = {
70
- * name: 'platform',
71
- * systemType: 'literal',
72
- * configuration: {
73
- * allowContextList: true,
74
- * caseSensitive: false,
75
- * enumeratedValues: ['web', 'mobile', 'desktop', 'smart-tv'],
76
- * hierarchy: {
77
- * 'smart-tv': 'web', // smart-tv inherits from web
78
- * 'tablet': 'mobile' // tablet inherits from mobile
79
- * }
80
- * }
81
- * };
82
- *
83
- * <ConfigurationTools.QualifierTypeEditForm
84
- * qualifierType={platformType}
85
- * onSave={updatePlatformType}
86
- * onCancel={closeEditor}
87
- * existingNames={otherTypeNames}
88
- * />
89
- * ```
90
- *
91
- * @example
92
- * ```tsx
93
- * // Territory qualifier type with restricted territories
94
- * const regionType = {
95
- * name: 'region',
96
- * systemType: 'territory',
97
- * configuration: {
98
- * allowContextList: false,
99
- * acceptLowercase: true,
100
- * allowedTerritories: ['US', 'CA', 'GB', 'AU'],
101
- * hierarchy: {
102
- * 'US': 'AMERICAS',
103
- * 'CA': 'AMERICAS',
104
- * 'GB': 'EUROPE',
105
- * 'AU': 'APAC'
106
- * }
107
- * }
108
- * };
109
- *
110
- * <ConfigurationTools.QualifierTypeEditForm
111
- * qualifierType={regionType}
112
- * onSave={saveRegionType}
113
- * onCancel={cancelEdit}
114
- * />
115
- * ```
116
- *
117
- * @example
118
- * ```tsx
119
- * // Simple language qualifier type
120
- * const languageType = {
121
- * name: 'locale',
122
- * systemType: 'language',
123
- * configuration: {
124
- * allowContextList: true // Allow multiple languages like 'en-US,en'
125
- * }
126
- * };
127
- *
128
- * <ConfigurationTools.QualifierTypeEditForm
129
- * qualifierType={languageType}
130
- * onSave={handleLanguageTypeSave}
131
- * onCancel={handleCancel}
132
- * existingNames={existingTypeNames}
133
- * />
134
- * ```
135
- *
136
- * @public
137
- */
138
- export const QualifierTypeEditForm: React.FC<QualifierTypeEditFormProps> = ({
139
- qualifierType,
140
- onSave,
141
- onCancel,
142
- existingNames = []
143
- }) => {
144
- const [formData, setFormData] = useState<FormData>(() => {
145
- if (qualifierType) {
146
- const config = qualifierType.configuration || {};
147
- // Ensure hierarchy is a plain object with string values
148
- let hierarchy: Record<string, string> = {};
149
- const rawHierarchy = (config as Record<string, unknown>)?.hierarchy;
150
- if (rawHierarchy && typeof rawHierarchy === 'object' && !Array.isArray(rawHierarchy)) {
151
- for (const [key, value] of Object.entries(rawHierarchy)) {
152
- if (typeof value === 'string') {
153
- hierarchy[key] = value;
154
- }
155
- }
156
- }
157
-
158
- return {
159
- name: qualifierType.name,
160
- systemType: qualifierType.systemType as 'language' | 'territory' | 'literal',
161
- allowContextList: ((config as Record<string, unknown>)?.allowContextList as boolean) ?? false,
162
- caseSensitive: ((config as Record<string, unknown>)?.caseSensitive as boolean) ?? true,
163
- enumeratedValues: ((config as Record<string, unknown>)?.enumeratedValues as string[]) || [],
164
- acceptLowercase: ((config as Record<string, unknown>)?.acceptLowercase as boolean) ?? false,
165
- allowedTerritories: ((config as Record<string, unknown>)?.allowedTerritories as string[]) || [],
166
- hierarchy: hierarchy
167
- };
168
- }
169
- return {
170
- name: '',
171
- systemType: 'literal',
172
- allowContextList: false,
173
- caseSensitive: true,
174
- enumeratedValues: [],
175
- acceptLowercase: false,
176
- allowedTerritories: [],
177
- hierarchy: {}
178
- };
179
- });
180
-
181
- const [errors, setErrors] = useState<Record<string, string>>({});
182
- const [enumeratedValuesText, setEnumeratedValuesText] = useState(formData.enumeratedValues.join(', '));
183
- const [allowedTerritoriesText, setAllowedTerritoriesText] = useState(
184
- formData.allowedTerritories.join(', ')
185
- );
186
-
187
- // Validation
188
- const validateForm = useCallback((): boolean => {
189
- const newErrors: Record<string, string> = {};
190
-
191
- if (!formData.name.trim()) {
192
- newErrors.name = 'Name is required';
193
- } else if (existingNames.includes(formData.name) && formData.name !== qualifierType?.name) {
194
- newErrors.name = 'Name must be unique';
195
- }
196
-
197
- if (formData.systemType === 'literal' && formData.enumeratedValues.length === 0) {
198
- newErrors.enumeratedValues = 'Literal types should have enumerated values';
199
- }
200
-
201
- setErrors(newErrors);
202
- return Object.keys(newErrors).length === 0;
203
- }, [formData, existingNames, qualifierType?.name]);
204
-
205
- // Update enumerated values when text changes
206
- useEffect(() => {
207
- const values = enumeratedValuesText
208
- .split(',')
209
- .map((v) => v.trim())
210
- .filter((v) => v.length > 0);
211
- setFormData((prev) => ({ ...prev, enumeratedValues: values }));
212
- }, [enumeratedValuesText]);
213
-
214
- // Update allowed territories when text changes
215
- useEffect(() => {
216
- const territories = allowedTerritoriesText
217
- .split(',')
218
- .map((v) => v.trim().toUpperCase())
219
- .filter((v) => v.length > 0);
220
- setFormData((prev) => ({ ...prev, allowedTerritories: territories }));
221
- }, [allowedTerritoriesText]);
222
-
223
- const handleSave = useCallback(() => {
224
- if (!validateForm()) return;
225
-
226
- let configuration: Record<string, unknown> = {
227
- allowContextList: formData.allowContextList
228
- };
229
-
230
- switch (formData.systemType) {
231
- case 'literal':
232
- configuration = {
233
- ...configuration,
234
- caseSensitive: formData.caseSensitive,
235
- enumeratedValues: formData.enumeratedValues.length > 0 ? formData.enumeratedValues : undefined
236
- };
237
- // Add hierarchy if it has entries
238
- if (Object.keys(formData.hierarchy).length > 0) {
239
- configuration.hierarchy = formData.hierarchy;
240
- }
241
- break;
242
- case 'territory':
243
- configuration = {
244
- ...configuration,
245
- acceptLowercase: formData.acceptLowercase,
246
- allowedTerritories: formData.allowedTerritories.length > 0 ? formData.allowedTerritories : undefined
247
- };
248
- // Add hierarchy if it has entries
249
- if (Object.keys(formData.hierarchy).length > 0) {
250
- configuration.hierarchy = formData.hierarchy;
251
- }
252
- break;
253
- case 'language':
254
- // Language types only have allowContextList
255
- break;
256
- }
257
-
258
- const result: QualifierTypes.Config.ISystemQualifierTypeConfig = {
259
- name: formData.name,
260
- systemType: formData.systemType,
261
- configuration: Object.keys(configuration).length > 1 ? configuration : undefined
262
- };
263
-
264
- onSave(result);
265
- }, [formData, validateForm, onSave]);
266
-
267
- const updateField = useCallback(
268
- (field: keyof FormData, value: FormData[keyof FormData]) => {
269
- setFormData((prev) => ({ ...prev, [field]: value }));
270
- if (errors[field]) {
271
- setErrors((prev) => ({ ...prev, [field]: '' }));
272
- }
273
- },
274
- [errors]
275
- );
276
-
277
- return (
278
- <div className="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50 p-4">
279
- <div className="bg-white rounded-lg shadow-xl max-w-2xl w-full h-full max-h-[calc(100vh-2rem)] flex flex-col">
280
- {/* Fixed Header */}
281
- <div className="flex items-center justify-between p-6 border-b flex-shrink-0">
282
- <h3 className="text-lg font-medium text-gray-900">
283
- {qualifierType ? 'Edit Qualifier Type' : 'Add Qualifier Type'}
284
- </h3>
285
- <button onClick={onCancel} className="text-gray-400 hover:text-gray-600">
286
- <XMarkIcon className="w-6 h-6" />
287
- </button>
288
- </div>
289
-
290
- {/* Scrollable Content */}
291
- <div className="p-6 space-y-6 overflow-y-auto flex-1 min-h-0">
292
- {/* Basic Properties */}
293
- <div className="grid grid-cols-2 gap-4">
294
- <div>
295
- <label className="block text-sm font-medium text-gray-700 mb-1">Name *</label>
296
- <input
297
- type="text"
298
- value={formData.name}
299
- onChange={(e) => updateField('name', e.target.value)}
300
- className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 ${
301
- errors.name ? 'border-red-300' : 'border-gray-300'
302
- }`}
303
- placeholder="Enter qualifier type name"
304
- />
305
- {errors.name && <p className="mt-1 text-sm text-red-600">{errors.name}</p>}
306
- </div>
307
-
308
- <div>
309
- <label className="block text-sm font-medium text-gray-700 mb-1">System Type *</label>
310
- <select
311
- value={formData.systemType}
312
- onChange={(e) =>
313
- updateField('systemType', e.target.value as 'language' | 'territory' | 'literal')
314
- }
315
- className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
316
- >
317
- <option value="literal">Literal</option>
318
- <option value="language">Language</option>
319
- <option value="territory">Territory</option>
320
- </select>
321
- </div>
322
- </div>
323
-
324
- {/* Allow Context List */}
325
- <div className="flex items-center">
326
- <input
327
- type="checkbox"
328
- id="allowContextList"
329
- checked={formData.allowContextList}
330
- onChange={(e) => updateField('allowContextList', e.target.checked)}
331
- className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
332
- />
333
- <label htmlFor="allowContextList" className="ml-2 text-sm text-gray-700">
334
- Allow Context List
335
- </label>
336
- <div className="ml-2 group relative">
337
- <InformationCircleIcon className="w-4 h-4 text-gray-400" />
338
- <div className="absolute left-0 bottom-6 hidden group-hover:block bg-gray-800 text-white text-xs rounded py-1 px-2 whitespace-nowrap">
339
- Allow multiple values separated by commas
340
- </div>
341
- </div>
342
- </div>
343
-
344
- {/* Literal Type Specific */}
345
- {formData.systemType === 'literal' && (
346
- <div className="space-y-4 p-4 bg-blue-50 rounded-lg">
347
- <h4 className="font-medium text-gray-900">Literal Type Configuration</h4>
348
-
349
- <div className="flex items-center">
350
- <input
351
- type="checkbox"
352
- id="caseSensitive"
353
- checked={formData.caseSensitive}
354
- onChange={(e) => updateField('caseSensitive', e.target.checked)}
355
- className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
356
- />
357
- <label htmlFor="caseSensitive" className="ml-2 text-sm text-gray-700">
358
- Case Sensitive
359
- </label>
360
- </div>
361
-
362
- <div>
363
- <label className="block text-sm font-medium text-gray-700 mb-1">Enumerated Values</label>
364
- <textarea
365
- value={enumeratedValuesText}
366
- onChange={(e) => setEnumeratedValuesText(e.target.value)}
367
- className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 ${
368
- errors.enumeratedValues ? 'border-red-300' : 'border-gray-300'
369
- }`}
370
- rows={3}
371
- placeholder="Enter values separated by commas (e.g., dev, test, prod)"
372
- />
373
- {errors.enumeratedValues && (
374
- <p className="mt-1 text-sm text-red-600">{errors.enumeratedValues}</p>
375
- )}
376
- <p className="mt-1 text-xs text-gray-500">Separate multiple values with commas</p>
377
- </div>
378
- </div>
379
- )}
380
-
381
- {/* Territory Type Specific */}
382
- {formData.systemType === 'territory' && (
383
- <div className="space-y-4 p-4 bg-green-50 rounded-lg">
384
- <h4 className="font-medium text-gray-900">Territory Type Configuration</h4>
385
-
386
- <div className="flex items-center">
387
- <input
388
- type="checkbox"
389
- id="acceptLowercase"
390
- checked={formData.acceptLowercase}
391
- onChange={(e) => updateField('acceptLowercase', e.target.checked)}
392
- className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
393
- />
394
- <label htmlFor="acceptLowercase" className="ml-2 text-sm text-gray-700">
395
- Accept Lowercase Territory Codes
396
- </label>
397
- </div>
398
-
399
- <div>
400
- <label className="block text-sm font-medium text-gray-700 mb-1">Allowed Territories</label>
401
- <textarea
402
- value={allowedTerritoriesText}
403
- onChange={(e) => setAllowedTerritoriesText(e.target.value)}
404
- className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
405
- rows={3}
406
- placeholder="Enter territory codes separated by commas (e.g., US, CA, GB)"
407
- />
408
- <p className="mt-1 text-xs text-gray-500">
409
- Separate multiple territory codes with commas. Will be automatically converted to uppercase.
410
- </p>
411
- </div>
412
- </div>
413
- )}
414
-
415
- {/* Hierarchy Editor for Literal and Territory Types */}
416
- {(formData.systemType === 'literal' || formData.systemType === 'territory') && (
417
- <div className="space-y-4">
418
- <HierarchyEditor
419
- hierarchy={formData.hierarchy}
420
- onChange={(hierarchy) => updateField('hierarchy', hierarchy)}
421
- availableValues={
422
- formData.systemType === 'literal' ? formData.enumeratedValues : formData.allowedTerritories
423
- }
424
- />
425
- </div>
426
- )}
427
-
428
- {/* Language Type Specific */}
429
- {formData.systemType === 'language' && (
430
- <div className="p-4 bg-yellow-50 rounded-lg">
431
- <h4 className="font-medium text-gray-900">Language Type Configuration</h4>
432
- <p className="text-sm text-gray-600 mt-2">
433
- Language qualifier types use BCP47 language tags and only support the "Allow Context List"
434
- option above.
435
- </p>
436
- </div>
437
- )}
438
- </div>
439
-
440
- {/* Fixed Footer */}
441
- <div className="flex justify-end space-x-3 px-6 py-4 border-t bg-gray-50 flex-shrink-0">
442
- <button
443
- onClick={onCancel}
444
- className="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"
445
- >
446
- Cancel
447
- </button>
448
- <button
449
- onClick={handleSave}
450
- className="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
451
- >
452
- {qualifierType ? 'Save Changes' : 'Add Qualifier Type'}
453
- </button>
454
- </div>
455
- </div>
456
- </div>
457
- );
458
- };