@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,355 @@
|
|
|
1
|
+
import React, { useState, useCallback } from 'react';
|
|
2
|
+
import { XMarkIcon, InformationCircleIcon } from '@heroicons/react/24/outline';
|
|
3
|
+
import { Qualifiers, QualifierTypes } from '@fgv/ts-res';
|
|
4
|
+
|
|
5
|
+
export interface QualifierEditFormProps {
|
|
6
|
+
qualifier?: Qualifiers.IQualifierDecl;
|
|
7
|
+
qualifierTypes: QualifierTypes.Config.ISystemQualifierTypeConfig[];
|
|
8
|
+
onSave: (qualifier: Qualifiers.IQualifierDecl) => void;
|
|
9
|
+
onCancel: () => void;
|
|
10
|
+
existingNames?: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface FormData {
|
|
14
|
+
name: string;
|
|
15
|
+
typeName: string;
|
|
16
|
+
defaultPriority: number;
|
|
17
|
+
token: string;
|
|
18
|
+
tokenIsOptional: boolean;
|
|
19
|
+
defaultValue: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const QualifierEditForm: React.FC<QualifierEditFormProps> = ({
|
|
23
|
+
qualifier,
|
|
24
|
+
qualifierTypes,
|
|
25
|
+
onSave,
|
|
26
|
+
onCancel,
|
|
27
|
+
existingNames = []
|
|
28
|
+
}) => {
|
|
29
|
+
const [formData, setFormData] = useState<FormData>(() => {
|
|
30
|
+
if (qualifier) {
|
|
31
|
+
return {
|
|
32
|
+
name: qualifier.name,
|
|
33
|
+
typeName: qualifier.typeName,
|
|
34
|
+
defaultPriority: qualifier.defaultPriority,
|
|
35
|
+
token: qualifier.token || '',
|
|
36
|
+
tokenIsOptional: qualifier.tokenIsOptional || false,
|
|
37
|
+
defaultValue: qualifier.defaultValue || ''
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
name: '',
|
|
42
|
+
typeName: qualifierTypes[0]?.name || '',
|
|
43
|
+
defaultPriority: 50,
|
|
44
|
+
token: '',
|
|
45
|
+
tokenIsOptional: false,
|
|
46
|
+
defaultValue: ''
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
51
|
+
|
|
52
|
+
// Get the selected qualifier type for context
|
|
53
|
+
const selectedQualifierType = qualifierTypes.find((qt) => qt.name === formData.typeName);
|
|
54
|
+
const allowsContextList =
|
|
55
|
+
selectedQualifierType?.configuration && (selectedQualifierType.configuration as any).allowContextList;
|
|
56
|
+
|
|
57
|
+
// Validation
|
|
58
|
+
const validateForm = useCallback((): boolean => {
|
|
59
|
+
const newErrors: Record<string, string> = {};
|
|
60
|
+
|
|
61
|
+
if (!formData.name.trim()) {
|
|
62
|
+
newErrors.name = 'Name is required';
|
|
63
|
+
} else if (existingNames.includes(formData.name) && formData.name !== qualifier?.name) {
|
|
64
|
+
newErrors.name = 'Name must be unique';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!formData.typeName) {
|
|
68
|
+
newErrors.typeName = 'Qualifier type is required';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (formData.defaultPriority < 0 || formData.defaultPriority > 1000) {
|
|
72
|
+
newErrors.defaultPriority = 'Priority must be between 0 and 1000';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (formData.token && !/^[a-zA-Z][a-zA-Z0-9_]*$/.test(formData.token)) {
|
|
76
|
+
newErrors.token = 'Token must start with a letter and contain only letters, numbers, and underscores';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setErrors(newErrors);
|
|
80
|
+
return Object.keys(newErrors).length === 0;
|
|
81
|
+
}, [formData, existingNames, qualifier?.name]);
|
|
82
|
+
|
|
83
|
+
const handleSave = useCallback(() => {
|
|
84
|
+
if (!validateForm()) return;
|
|
85
|
+
|
|
86
|
+
const result: Qualifiers.IQualifierDecl = {
|
|
87
|
+
name: formData.name,
|
|
88
|
+
typeName: formData.typeName,
|
|
89
|
+
defaultPriority: formData.defaultPriority,
|
|
90
|
+
...(formData.token && { token: formData.token }),
|
|
91
|
+
...(formData.token && formData.tokenIsOptional && { tokenIsOptional: true }),
|
|
92
|
+
...(formData.defaultValue && { defaultValue: formData.defaultValue })
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
onSave(result);
|
|
96
|
+
}, [formData, validateForm, onSave]);
|
|
97
|
+
|
|
98
|
+
const updateField = useCallback(
|
|
99
|
+
(field: keyof FormData, value: any) => {
|
|
100
|
+
setFormData((prev) => {
|
|
101
|
+
const updated = { ...prev, [field]: value };
|
|
102
|
+
|
|
103
|
+
// Auto-generate token from name if no custom token is set
|
|
104
|
+
if (field === 'name' && !prev.token) {
|
|
105
|
+
updated.token = value.toLowerCase().replace(/[^a-zA-Z0-9]/g, '');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Clear tokenIsOptional if token is cleared
|
|
109
|
+
if (field === 'token' && !value) {
|
|
110
|
+
updated.tokenIsOptional = false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return updated;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (errors[field]) {
|
|
117
|
+
setErrors((prev) => ({ ...prev, [field]: '' }));
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
[errors]
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const getDefaultValuePlaceholder = (): string => {
|
|
124
|
+
if (!selectedQualifierType) return 'Enter default value';
|
|
125
|
+
|
|
126
|
+
switch (selectedQualifierType.systemType) {
|
|
127
|
+
case 'language':
|
|
128
|
+
return allowsContextList ? 'e.g., en-US or en-US,en' : 'e.g., en-US';
|
|
129
|
+
case 'territory':
|
|
130
|
+
return allowsContextList ? 'e.g., US or US,CA' : 'e.g., US';
|
|
131
|
+
case 'literal':
|
|
132
|
+
const enumValues = (selectedQualifierType.configuration as any)?.enumeratedValues;
|
|
133
|
+
if (enumValues && enumValues.length > 0) {
|
|
134
|
+
return allowsContextList
|
|
135
|
+
? `e.g., ${enumValues[0]} or ${enumValues.slice(0, 2).join(',')}`
|
|
136
|
+
: `e.g., ${enumValues[0]}`;
|
|
137
|
+
}
|
|
138
|
+
return allowsContextList ? 'e.g., value or value1,value2' : 'e.g., value';
|
|
139
|
+
default:
|
|
140
|
+
return 'Enter default value';
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50 p-4">
|
|
146
|
+
<div className="bg-white rounded-lg shadow-xl max-w-2xl w-full h-full max-h-[calc(100vh-2rem)] flex flex-col">
|
|
147
|
+
{/* Fixed Header */}
|
|
148
|
+
<div className="flex items-center justify-between p-6 border-b flex-shrink-0">
|
|
149
|
+
<h3 className="text-lg font-medium text-gray-900">
|
|
150
|
+
{qualifier ? 'Edit Qualifier' : 'Add Qualifier'}
|
|
151
|
+
</h3>
|
|
152
|
+
<button onClick={onCancel} className="text-gray-400 hover:text-gray-600">
|
|
153
|
+
<XMarkIcon className="w-6 h-6" />
|
|
154
|
+
</button>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
{/* Scrollable Content */}
|
|
158
|
+
<div className="p-6 space-y-6 overflow-y-auto flex-1 min-h-0">
|
|
159
|
+
{/* Basic Properties */}
|
|
160
|
+
<div className="grid grid-cols-2 gap-4">
|
|
161
|
+
<div>
|
|
162
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">Name *</label>
|
|
163
|
+
<input
|
|
164
|
+
type="text"
|
|
165
|
+
value={formData.name}
|
|
166
|
+
onChange={(e) => updateField('name', e.target.value)}
|
|
167
|
+
className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 ${
|
|
168
|
+
errors.name ? 'border-red-300' : 'border-gray-300'
|
|
169
|
+
}`}
|
|
170
|
+
placeholder="Enter qualifier name"
|
|
171
|
+
/>
|
|
172
|
+
{errors.name && <p className="mt-1 text-sm text-red-600">{errors.name}</p>}
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div>
|
|
176
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">Qualifier Type *</label>
|
|
177
|
+
<select
|
|
178
|
+
value={formData.typeName}
|
|
179
|
+
onChange={(e) => updateField('typeName', e.target.value)}
|
|
180
|
+
className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 ${
|
|
181
|
+
errors.typeName ? 'border-red-300' : 'border-gray-300'
|
|
182
|
+
}`}
|
|
183
|
+
>
|
|
184
|
+
{qualifierTypes.length === 0 ? (
|
|
185
|
+
<option value="">No qualifier types available</option>
|
|
186
|
+
) : (
|
|
187
|
+
qualifierTypes.map((type) => (
|
|
188
|
+
<option key={type.name} value={type.name}>
|
|
189
|
+
{type.name} ({type.systemType})
|
|
190
|
+
</option>
|
|
191
|
+
))
|
|
192
|
+
)}
|
|
193
|
+
</select>
|
|
194
|
+
{errors.typeName && <p className="mt-1 text-sm text-red-600">{errors.typeName}</p>}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<div className="grid grid-cols-2 gap-4">
|
|
199
|
+
<div>
|
|
200
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">Default Priority *</label>
|
|
201
|
+
<input
|
|
202
|
+
type="number"
|
|
203
|
+
min="0"
|
|
204
|
+
max="1000"
|
|
205
|
+
value={formData.defaultPriority}
|
|
206
|
+
onChange={(e) => updateField('defaultPriority', parseInt(e.target.value) || 0)}
|
|
207
|
+
className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 ${
|
|
208
|
+
errors.defaultPriority ? 'border-red-300' : 'border-gray-300'
|
|
209
|
+
}`}
|
|
210
|
+
placeholder="50"
|
|
211
|
+
/>
|
|
212
|
+
{errors.defaultPriority && (
|
|
213
|
+
<p className="mt-1 text-sm text-red-600">{errors.defaultPriority}</p>
|
|
214
|
+
)}
|
|
215
|
+
<p className="mt-1 text-xs text-gray-500">Higher numbers have higher priority (0-1000)</p>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div>
|
|
219
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
220
|
+
Token
|
|
221
|
+
<span className="ml-1 text-gray-500">(optional)</span>
|
|
222
|
+
</label>
|
|
223
|
+
<input
|
|
224
|
+
type="text"
|
|
225
|
+
value={formData.token}
|
|
226
|
+
onChange={(e) => updateField('token', e.target.value)}
|
|
227
|
+
className={`w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 ${
|
|
228
|
+
errors.token ? 'border-red-300' : 'border-gray-300'
|
|
229
|
+
}`}
|
|
230
|
+
placeholder="e.g., lang, locale"
|
|
231
|
+
/>
|
|
232
|
+
{errors.token && <p className="mt-1 text-sm text-red-600">{errors.token}</p>}
|
|
233
|
+
<p className="mt-1 text-xs text-gray-500">Used to identify this qualifier in resource names</p>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
{/* Token Options */}
|
|
238
|
+
{formData.token && (
|
|
239
|
+
<div className="flex items-center">
|
|
240
|
+
<input
|
|
241
|
+
type="checkbox"
|
|
242
|
+
id="tokenIsOptional"
|
|
243
|
+
checked={formData.tokenIsOptional}
|
|
244
|
+
onChange={(e) => updateField('tokenIsOptional', e.target.checked)}
|
|
245
|
+
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
|
246
|
+
/>
|
|
247
|
+
<label htmlFor="tokenIsOptional" className="ml-2 text-sm text-gray-700">
|
|
248
|
+
Token is optional in resource names
|
|
249
|
+
</label>
|
|
250
|
+
<div className="ml-2 group relative">
|
|
251
|
+
<InformationCircleIcon className="w-4 h-4 text-gray-400" />
|
|
252
|
+
<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 z-10">
|
|
253
|
+
Allow resources without this qualifier token
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
|
|
259
|
+
{/* Default Value */}
|
|
260
|
+
<div>
|
|
261
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
262
|
+
Default Value
|
|
263
|
+
<span className="ml-1 text-gray-500">(optional)</span>
|
|
264
|
+
</label>
|
|
265
|
+
<input
|
|
266
|
+
type="text"
|
|
267
|
+
value={formData.defaultValue}
|
|
268
|
+
onChange={(e) => updateField('defaultValue', e.target.value)}
|
|
269
|
+
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"
|
|
270
|
+
placeholder={getDefaultValuePlaceholder()}
|
|
271
|
+
/>
|
|
272
|
+
<div className="mt-1 text-xs text-gray-500">
|
|
273
|
+
{selectedQualifierType && (
|
|
274
|
+
<div>
|
|
275
|
+
<p>
|
|
276
|
+
Qualifier type: <span className="font-medium">{selectedQualifierType.systemType}</span>
|
|
277
|
+
</p>
|
|
278
|
+
{allowsContextList && (
|
|
279
|
+
<p className="text-blue-600">
|
|
280
|
+
This qualifier type supports multiple values (comma-separated)
|
|
281
|
+
</p>
|
|
282
|
+
)}
|
|
283
|
+
</div>
|
|
284
|
+
)}
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
{/* Qualifier Type Information */}
|
|
289
|
+
{selectedQualifierType && (
|
|
290
|
+
<div className="p-4 bg-gray-50 rounded-lg">
|
|
291
|
+
<h4 className="font-medium text-gray-900 mb-2">Qualifier Type Information</h4>
|
|
292
|
+
<div className="text-sm text-gray-600 space-y-1">
|
|
293
|
+
<p>
|
|
294
|
+
<span className="font-medium">System Type:</span> {selectedQualifierType.systemType}
|
|
295
|
+
</p>
|
|
296
|
+
<p>
|
|
297
|
+
<span className="font-medium">Supports Context List:</span>{' '}
|
|
298
|
+
{allowsContextList ? 'Yes' : 'No'}
|
|
299
|
+
</p>
|
|
300
|
+
{selectedQualifierType.systemType === 'literal' && selectedQualifierType.configuration && (
|
|
301
|
+
<>
|
|
302
|
+
{(selectedQualifierType.configuration as any).caseSensitive !== undefined && (
|
|
303
|
+
<p>
|
|
304
|
+
<span className="font-medium">Case Sensitive:</span>{' '}
|
|
305
|
+
{(selectedQualifierType.configuration as any).caseSensitive ? 'Yes' : 'No'}
|
|
306
|
+
</p>
|
|
307
|
+
)}
|
|
308
|
+
{(selectedQualifierType.configuration as any).enumeratedValues && (
|
|
309
|
+
<p>
|
|
310
|
+
<span className="font-medium">Allowed Values:</span>{' '}
|
|
311
|
+
{(selectedQualifierType.configuration as any).enumeratedValues.join(', ')}
|
|
312
|
+
</p>
|
|
313
|
+
)}
|
|
314
|
+
</>
|
|
315
|
+
)}
|
|
316
|
+
{selectedQualifierType.systemType === 'territory' && selectedQualifierType.configuration && (
|
|
317
|
+
<>
|
|
318
|
+
{(selectedQualifierType.configuration as any).acceptLowercase !== undefined && (
|
|
319
|
+
<p>
|
|
320
|
+
<span className="font-medium">Accept Lowercase:</span>{' '}
|
|
321
|
+
{(selectedQualifierType.configuration as any).acceptLowercase ? 'Yes' : 'No'}
|
|
322
|
+
</p>
|
|
323
|
+
)}
|
|
324
|
+
{(selectedQualifierType.configuration as any).allowedTerritories && (
|
|
325
|
+
<p>
|
|
326
|
+
<span className="font-medium">Allowed Territories:</span>{' '}
|
|
327
|
+
{(selectedQualifierType.configuration as any).allowedTerritories.join(', ')}
|
|
328
|
+
</p>
|
|
329
|
+
)}
|
|
330
|
+
</>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
)}
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
{/* Fixed Footer */}
|
|
338
|
+
<div className="flex justify-end space-x-3 px-6 py-4 border-t bg-gray-50 flex-shrink-0">
|
|
339
|
+
<button
|
|
340
|
+
onClick={onCancel}
|
|
341
|
+
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"
|
|
342
|
+
>
|
|
343
|
+
Cancel
|
|
344
|
+
</button>
|
|
345
|
+
<button
|
|
346
|
+
onClick={handleSave}
|
|
347
|
+
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"
|
|
348
|
+
>
|
|
349
|
+
{qualifier ? 'Save Changes' : 'Add Qualifier'}
|
|
350
|
+
</button>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
);
|
|
355
|
+
};
|