@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.
- package/README.md +401 -155
- package/config/jest.setup.js +10 -0
- package/dist/ts-res-ui-components.d.ts +1657 -76
- package/lib/components/common/QualifierContextControl.js +4 -1
- package/lib/components/common/ResourceTreeView.js +4 -1
- package/lib/components/forms/GenericQualifierTypeEditForm.d.ts +26 -0
- package/lib/components/forms/GenericQualifierTypeEditForm.js +166 -0
- package/lib/components/forms/QualifierEditForm.d.ts +1 -1
- package/lib/components/forms/index.d.ts +2 -0
- package/lib/components/forms/index.js +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts +3 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js +118 -51
- package/lib/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
- package/lib/components/pickers/ResourcePicker/index.js +4 -2
- package/lib/components/views/CompiledView/index.js +75 -16
- package/lib/components/views/ConfigurationView/index.js +94 -35
- package/lib/components/views/FilterView/index.js +7 -4
- package/lib/components/views/GridView/EditableGridCell.d.ts +76 -0
- package/lib/components/views/GridView/EditableGridCell.js +224 -0
- package/lib/components/views/GridView/GridSelector.d.ts +43 -0
- package/lib/components/views/GridView/GridSelector.js +89 -0
- package/lib/components/views/GridView/MultiGridView.d.ts +85 -0
- package/lib/components/views/GridView/MultiGridView.js +196 -0
- package/lib/components/views/GridView/ResourceGrid.d.ts +38 -0
- package/lib/components/views/GridView/ResourceGrid.js +232 -0
- package/lib/components/views/GridView/SharedContextControls.d.ts +47 -0
- package/lib/components/views/GridView/SharedContextControls.js +95 -0
- package/lib/components/views/GridView/cells/BooleanCell.d.ts +44 -0
- package/lib/components/views/GridView/cells/BooleanCell.js +49 -0
- package/lib/components/views/GridView/cells/DropdownCell.d.ts +58 -0
- package/lib/components/views/GridView/cells/DropdownCell.js +182 -0
- package/lib/components/views/GridView/cells/StringCell.d.ts +57 -0
- package/lib/components/views/GridView/cells/StringCell.js +106 -0
- package/lib/components/views/GridView/cells/TriStateCell.d.ts +54 -0
- package/lib/components/views/GridView/cells/TriStateCell.js +112 -0
- package/lib/components/views/GridView/cells/index.d.ts +15 -0
- package/lib/components/views/GridView/cells/index.js +11 -0
- package/lib/components/views/GridView/index.d.ts +53 -0
- package/lib/components/views/GridView/index.js +212 -0
- package/lib/components/views/ImportView/index.js +22 -19
- package/lib/components/views/MessagesWindow/index.js +4 -1
- package/lib/components/views/ResolutionView/index.js +8 -5
- package/lib/contexts/ObservabilityContext.d.ts +85 -0
- package/lib/contexts/ObservabilityContext.js +98 -0
- package/lib/contexts/index.d.ts +2 -0
- package/lib/contexts/index.js +24 -0
- package/lib/hooks/useConfigurationState.d.ts +3 -3
- package/lib/hooks/useResolutionState.js +850 -246
- package/lib/hooks/useResourceData.d.ts +7 -4
- package/lib/hooks/useResourceData.js +185 -184
- package/lib/index.d.ts +5 -1
- package/lib/index.js +8 -1
- package/lib/namespaces/GridTools.d.ts +136 -0
- package/lib/namespaces/GridTools.js +138 -0
- package/lib/namespaces/ObservabilityTools.d.ts +3 -0
- package/lib/namespaces/ObservabilityTools.js +23 -0
- package/lib/namespaces/ResolutionTools.d.ts +2 -1
- package/lib/namespaces/ResolutionTools.js +2 -0
- package/lib/namespaces/index.d.ts +2 -0
- package/lib/namespaces/index.js +2 -0
- package/lib/test/integration/observability.integration.test.d.ts +2 -0
- package/lib/test/unit/hooks/useResourceData.test.d.ts +2 -0
- package/lib/test/unit/utils/downloadHelper.test.d.ts +2 -0
- package/lib/test/unit/workflows/resolutionWorkflows.test.d.ts +2 -0
- package/lib/test/unit/workflows/resourceCreation.test.d.ts +2 -0
- package/lib/test/unit/workflows/resultPatternExtensions.test.d.ts +2 -0
- package/lib/test/unit/workflows/validation.test.d.ts +2 -0
- package/lib/types/index.d.ts +387 -20
- package/lib/types/index.js +2 -1
- package/lib/utils/cellValidation.d.ts +113 -0
- package/lib/utils/cellValidation.js +248 -0
- package/lib/utils/downloadHelper.d.ts +66 -0
- package/lib/utils/downloadHelper.js +195 -0
- package/lib/utils/observability/factories.d.ts +29 -0
- package/lib/utils/observability/factories.js +58 -0
- package/lib/utils/observability/implementations.d.ts +61 -0
- package/lib/utils/observability/implementations.js +103 -0
- package/lib/utils/observability/index.d.ts +4 -0
- package/lib/utils/observability/index.js +26 -0
- package/lib/utils/observability/interfaces.d.ts +30 -0
- package/lib/utils/observability/interfaces.js +23 -0
- package/lib/utils/resolutionEditing.js +2 -1
- package/lib/utils/resourceSelector.d.ts +97 -0
- package/lib/utils/resourceSelector.js +195 -0
- package/lib/utils/resourceSelectors.d.ts +146 -0
- package/lib/utils/resourceSelectors.js +233 -0
- package/lib/utils/tsResIntegration.d.ts +6 -41
- package/lib/utils/tsResIntegration.js +20 -16
- package/lib/utils/zipLoader/zipProcessingHelpers.d.ts +3 -2
- package/lib/utils/zipLoader/zipProcessingHelpers.js +6 -5
- package/lib-commonjs/components/common/QualifierContextControl.js +4 -1
- package/lib-commonjs/components/common/ResourceTreeView.js +4 -1
- package/lib-commonjs/components/forms/GenericQualifierTypeEditForm.js +171 -0
- package/lib-commonjs/components/forms/index.js +3 -1
- package/lib-commonjs/components/orchestrator/ResourceOrchestrator.js +118 -51
- package/lib-commonjs/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
- package/lib-commonjs/components/pickers/ResourcePicker/index.js +4 -2
- package/lib-commonjs/components/views/CompiledView/index.js +75 -16
- package/lib-commonjs/components/views/ConfigurationView/index.js +93 -34
- package/lib-commonjs/components/views/FilterView/index.js +7 -4
- package/lib-commonjs/components/views/GridView/EditableGridCell.js +232 -0
- package/lib-commonjs/components/views/GridView/GridSelector.js +94 -0
- package/lib-commonjs/components/views/GridView/MultiGridView.js +201 -0
- package/lib-commonjs/components/views/GridView/ResourceGrid.js +237 -0
- package/lib-commonjs/components/views/GridView/SharedContextControls.js +100 -0
- package/lib-commonjs/components/views/GridView/cells/BooleanCell.js +54 -0
- package/lib-commonjs/components/views/GridView/cells/DropdownCell.js +187 -0
- package/lib-commonjs/components/views/GridView/cells/StringCell.js +111 -0
- package/lib-commonjs/components/views/GridView/cells/TriStateCell.js +117 -0
- package/lib-commonjs/components/views/GridView/cells/index.js +18 -0
- package/lib-commonjs/components/views/GridView/index.js +217 -0
- package/lib-commonjs/components/views/ImportView/index.js +22 -19
- package/lib-commonjs/components/views/MessagesWindow/index.js +4 -1
- package/lib-commonjs/components/views/ResolutionView/index.js +8 -5
- package/lib-commonjs/contexts/ObservabilityContext.js +104 -0
- package/lib-commonjs/contexts/index.js +30 -0
- package/lib-commonjs/hooks/useResolutionState.js +849 -245
- package/lib-commonjs/hooks/useResourceData.js +184 -215
- package/lib-commonjs/index.js +15 -1
- package/lib-commonjs/namespaces/GridTools.js +161 -0
- package/lib-commonjs/namespaces/ObservabilityTools.js +33 -0
- package/lib-commonjs/namespaces/ResolutionTools.js +10 -1
- package/lib-commonjs/namespaces/index.js +3 -1
- package/lib-commonjs/types/index.js +10 -0
- package/lib-commonjs/utils/cellValidation.js +253 -0
- package/lib-commonjs/utils/downloadHelper.js +198 -0
- package/lib-commonjs/utils/observability/factories.js +63 -0
- package/lib-commonjs/utils/observability/implementations.js +109 -0
- package/lib-commonjs/utils/observability/index.js +36 -0
- package/lib-commonjs/utils/observability/interfaces.js +24 -0
- package/lib-commonjs/utils/resolutionEditing.js +2 -1
- package/lib-commonjs/utils/resourceSelector.js +200 -0
- package/lib-commonjs/utils/resourceSelectors.js +242 -0
- package/lib-commonjs/utils/tsResIntegration.js +21 -16
- package/lib-commonjs/utils/zipLoader/zipProcessingHelpers.js +7 -5
- package/package.json +7 -7
- package/src/components/common/QualifierContextControl.tsx +0 -338
- package/src/components/common/ResolutionContextOptionsControl.tsx +0 -450
- package/src/components/common/ResolutionResults/index.tsx +0 -481
- package/src/components/common/ResourceListView.tsx +0 -167
- package/src/components/common/ResourcePickerOptionsControl.tsx +0 -351
- package/src/components/common/ResourceTreeView.tsx +0 -417
- package/src/components/common/SourceResourceDetail/index.tsx +0 -493
- package/src/components/forms/HierarchyEditor.tsx +0 -285
- package/src/components/forms/QualifierEditForm.tsx +0 -487
- package/src/components/forms/QualifierTypeEditForm.tsx +0 -458
- package/src/components/forms/ResourceTypeEditForm.tsx +0 -437
- package/src/components/forms/index.ts +0 -11
- package/src/components/orchestrator/ResourceOrchestrator.tsx +0 -444
- package/src/components/pickers/ResourcePicker/README.md +0 -570
- package/src/components/pickers/ResourcePicker/ResourceItem.tsx +0 -127
- package/src/components/pickers/ResourcePicker/ResourcePickerList.tsx +0 -114
- package/src/components/pickers/ResourcePicker/ResourcePickerTree.tsx +0 -461
- package/src/components/pickers/ResourcePicker/index.tsx +0 -234
- package/src/components/pickers/ResourcePicker/types.ts +0 -301
- package/src/components/pickers/ResourcePicker/utils/treeNavigation.ts +0 -210
- package/src/components/views/CompiledView/index.tsx +0 -1342
- package/src/components/views/ConfigurationView/index.tsx +0 -848
- package/src/components/views/FilterView/index.tsx +0 -681
- package/src/components/views/ImportView/index.tsx +0 -789
- package/src/components/views/MessagesWindow/index.tsx +0 -325
- package/src/components/views/ResolutionView/EditableJsonView.tsx +0 -386
- package/src/components/views/ResolutionView/NewResourceModal.tsx +0 -158
- package/src/components/views/ResolutionView/UnifiedChangeControls.tsx +0 -163
- package/src/components/views/ResolutionView/index.tsx +0 -751
- package/src/components/views/SourceView/index.tsx +0 -291
- package/src/hooks/useConfigurationState.ts +0 -436
- package/src/hooks/useFilterState.ts +0 -150
- package/src/hooks/useResolutionState.ts +0 -893
- package/src/hooks/useResourceData.ts +0 -596
- package/src/hooks/useViewState.ts +0 -97
- package/src/index.ts +0 -68
- package/src/namespaces/ConfigurationTools.ts +0 -59
- package/src/namespaces/FilterTools.ts +0 -47
- package/src/namespaces/ImportTools.ts +0 -42
- package/src/namespaces/PickerTools.ts +0 -104
- package/src/namespaces/ResolutionTools.ts +0 -68
- package/src/namespaces/ResourceTools.ts +0 -106
- package/src/namespaces/TsResTools.ts +0 -49
- package/src/namespaces/ViewStateTools.ts +0 -91
- package/src/namespaces/ZipTools.ts +0 -49
- package/src/namespaces/index.ts +0 -49
- package/src/types/index.ts +0 -1273
- package/src/utils/configurationUtils.ts +0 -339
- package/src/utils/fileProcessing.ts +0 -164
- package/src/utils/filterResources.ts +0 -356
- package/src/utils/resolutionEditing.ts +0 -346
- package/src/utils/resolutionUtils.ts +0 -740
- package/src/utils/tsResIntegration.ts +0 -475
- package/src/utils/zipLoader/index.ts +0 -5
- package/src/utils/zipLoader/zipProcessingHelpers.ts +0 -46
- 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
|
-
};
|