diff --git a/src/ui/src/components/configuration-layout/ConfigurationLayout.jsx b/src/ui/src/components/configuration-layout/ConfigurationLayout.jsx
index eb3d6a56..379722c4 100644
--- a/src/ui/src/components/configuration-layout/ConfigurationLayout.jsx
+++ b/src/ui/src/components/configuration-layout/ConfigurationLayout.jsx
@@ -54,9 +54,21 @@ const ConfigurationLayout = () => {
const [exportFileName, setExportFileName] = useState('configuration');
const [importError, setImportError] = useState(null);
const [extractionSchema, setExtractionSchema] = useState(null);
+ const [showMigrationModal, setShowMigrationModal] = useState(false);
+ const [pendingImportConfig, setPendingImportConfig] = useState(null);
const editorRef = useRef(null);
+ // Helper function to detect legacy format
+ const isLegacyFormat = (config) => {
+ if (!config || !config.classes || !Array.isArray(config.classes)) return false;
+ if (config.classes.length === 0) return false;
+
+ // Check if first class has legacy attributes format (array instead of object with properties)
+ const firstClass = config.classes[0];
+ return firstClass.attributes && Array.isArray(firstClass.attributes);
+ };
+
// Initialize form values from merged config
useEffect(() => {
if (mergedConfig) {
@@ -897,24 +909,25 @@ const ConfigurationLayout = () => {
reader.onload = (e) => {
try {
setImportError(null);
- let importedConfig;
const content = e.target.result;
- if (file.name.endsWith('.yaml') || file.name.endsWith('.yml')) {
- importedConfig = yaml.load(content);
- } else {
- importedConfig = JSON.parse(content);
- }
+ const importedConfig = file.name.endsWith('.yaml') || file.name.endsWith('.yml') ? yaml.load(content) : JSON.parse(content);
if (importedConfig && typeof importedConfig === 'object') {
- // If the imported config has classes, use them (should be JSON Schema format)
- if (importedConfig.classes) {
- setExtractionSchema(importedConfig.classes);
+ // Check if config is in legacy format
+ if (isLegacyFormat(importedConfig)) {
+ // Show migration modal and store config for later
+ setPendingImportConfig(importedConfig);
+ setShowMigrationModal(true);
+ } else {
+ // Modern format - load directly into form
+ if (importedConfig.classes) {
+ setExtractionSchema(importedConfig.classes);
+ }
+ handleFormChange(importedConfig);
+ setSaveSuccess(false);
+ setSaveError(null);
}
-
- handleFormChange(importedConfig);
- setSaveSuccess(false);
- setSaveError(null);
} else {
setImportError('Invalid configuration file format');
}
@@ -924,8 +937,36 @@ const ConfigurationLayout = () => {
};
reader.readAsText(file);
// Clear the input value to allow re-importing the same file
- const input = event.target;
- input.value = '';
+ event.target.value = '';
+ };
+
+ const handleMigrationConfirm = async () => {
+ if (!pendingImportConfig) return;
+
+ setIsSaving(true);
+ try {
+ // Send to backend for migration, then reload to get migrated version
+ const success = await updateConfiguration(pendingImportConfig);
+
+ if (success) {
+ await fetchConfiguration();
+ setShowMigrationModal(false);
+ setPendingImportConfig(null);
+ } else {
+ setImportError('Failed to import configuration');
+ setShowMigrationModal(false);
+ }
+ } catch (err) {
+ setImportError(`Import failed: ${err.message}`);
+ setShowMigrationModal(false);
+ } finally {
+ setIsSaving(false);
+ }
+ };
+
+ const handleMigrationCancel = () => {
+ setShowMigrationModal(false);
+ setPendingImportConfig(null);
};
if (loading) {
@@ -1058,6 +1099,41 @@ const ConfigurationLayout = () => {
+
+
+
+
+
+
+ }
+ >
+
+
+ The configuration file you are importing uses a legacy format that needs to be migrated to the current JSON Schema format.
+
+
+
+ - The configuration will be automatically converted to the new format
+ - All your settings and document classes will be preserved
+ - The migrated configuration will be saved to the database
+ - You can review the changes after migration
+
+
+
+ Click "Save and Migrate" to proceed with the migration, or "Cancel" to abort the import.
+
+
+
+