@@ -54,9 +54,21 @@ const ConfigurationLayout = () => {
5454 const [ exportFileName , setExportFileName ] = useState ( 'configuration' ) ;
5555 const [ importError , setImportError ] = useState ( null ) ;
5656 const [ extractionSchema , setExtractionSchema ] = useState ( null ) ;
57+ const [ showMigrationModal , setShowMigrationModal ] = useState ( false ) ;
58+ const [ pendingImportConfig , setPendingImportConfig ] = useState ( null ) ;
5759
5860 const editorRef = useRef ( null ) ;
5961
62+ // Helper function to detect legacy format
63+ const isLegacyFormat = ( config ) => {
64+ if ( ! config || ! config . classes || ! Array . isArray ( config . classes ) ) return false ;
65+ if ( config . classes . length === 0 ) return false ;
66+
67+ // Check if first class has legacy attributes format (array instead of object with properties)
68+ const firstClass = config . classes [ 0 ] ;
69+ return firstClass . attributes && Array . isArray ( firstClass . attributes ) ;
70+ } ;
71+
6072 // Initialize form values from merged config
6173 useEffect ( ( ) => {
6274 if ( mergedConfig ) {
@@ -897,24 +909,25 @@ const ConfigurationLayout = () => {
897909 reader . onload = ( e ) => {
898910 try {
899911 setImportError ( null ) ;
900- let importedConfig ;
901912 const content = e . target . result ;
902913
903- if ( file . name . endsWith ( '.yaml' ) || file . name . endsWith ( '.yml' ) ) {
904- importedConfig = yaml . load ( content ) ;
905- } else {
906- importedConfig = JSON . parse ( content ) ;
907- }
914+ const importedConfig = file . name . endsWith ( '.yaml' ) || file . name . endsWith ( '.yml' ) ? yaml . load ( content ) : JSON . parse ( content ) ;
908915
909916 if ( importedConfig && typeof importedConfig === 'object' ) {
910- // If the imported config has classes, use them (should be JSON Schema format)
911- if ( importedConfig . classes ) {
912- setExtractionSchema ( importedConfig . classes ) ;
917+ // Check if config is in legacy format
918+ if ( isLegacyFormat ( importedConfig ) ) {
919+ // Show migration modal and store config for later
920+ setPendingImportConfig ( importedConfig ) ;
921+ setShowMigrationModal ( true ) ;
922+ } else {
923+ // Modern format - load directly into form
924+ if ( importedConfig . classes ) {
925+ setExtractionSchema ( importedConfig . classes ) ;
926+ }
927+ handleFormChange ( importedConfig ) ;
928+ setSaveSuccess ( false ) ;
929+ setSaveError ( null ) ;
913930 }
914-
915- handleFormChange ( importedConfig ) ;
916- setSaveSuccess ( false ) ;
917- setSaveError ( null ) ;
918931 } else {
919932 setImportError ( 'Invalid configuration file format' ) ;
920933 }
@@ -924,8 +937,36 @@ const ConfigurationLayout = () => {
924937 } ;
925938 reader . readAsText ( file ) ;
926939 // Clear the input value to allow re-importing the same file
927- const input = event . target ;
928- input . value = '' ;
940+ event . target . value = '' ;
941+ } ;
942+
943+ const handleMigrationConfirm = async ( ) => {
944+ if ( ! pendingImportConfig ) return ;
945+
946+ setIsSaving ( true ) ;
947+ try {
948+ // Send to backend for migration, then reload to get migrated version
949+ const success = await updateConfiguration ( pendingImportConfig ) ;
950+
951+ if ( success ) {
952+ await fetchConfiguration ( ) ;
953+ setShowMigrationModal ( false ) ;
954+ setPendingImportConfig ( null ) ;
955+ } else {
956+ setImportError ( 'Failed to import configuration' ) ;
957+ setShowMigrationModal ( false ) ;
958+ }
959+ } catch ( err ) {
960+ setImportError ( `Import failed: ${ err . message } ` ) ;
961+ setShowMigrationModal ( false ) ;
962+ } finally {
963+ setIsSaving ( false ) ;
964+ }
965+ } ;
966+
967+ const handleMigrationCancel = ( ) => {
968+ setShowMigrationModal ( false ) ;
969+ setPendingImportConfig ( null ) ;
929970 } ;
930971
931972 if ( loading ) {
@@ -1058,6 +1099,41 @@ const ConfigurationLayout = () => {
10581099 </ SpaceBetween >
10591100 </ Modal >
10601101
1102+ < Modal
1103+ visible = { showMigrationModal }
1104+ onDismiss = { handleMigrationCancel }
1105+ header = "Configuration Migration Required"
1106+ footer = {
1107+ < Box float = "right" >
1108+ < SpaceBetween direction = "horizontal" size = "xs" >
1109+ < Button variant = "link" onClick = { handleMigrationCancel } >
1110+ Cancel
1111+ </ Button >
1112+ < Button variant = "primary" onClick = { handleMigrationConfirm } loading = { isSaving } >
1113+ Save and Migrate
1114+ </ Button >
1115+ </ SpaceBetween >
1116+ </ Box >
1117+ }
1118+ >
1119+ < SpaceBetween direction = "vertical" size = "m" >
1120+ < Box variant = "span" >
1121+ The configuration file you are importing uses a legacy format that needs to be migrated to the current JSON Schema format.
1122+ </ Box >
1123+ < Alert type = "info" header = "What will happen" >
1124+ < ul >
1125+ < li > The configuration will be automatically converted to the new format</ li >
1126+ < li > All your settings and document classes will be preserved</ li >
1127+ < li > The migrated configuration will be saved to the database</ li >
1128+ < li > You can review the changes after migration</ li >
1129+ </ ul >
1130+ </ Alert >
1131+ < Box variant = "span" >
1132+ Click "Save and Migrate" to proceed with the migration, or "Cancel" to abort the import.
1133+ </ Box >
1134+ </ SpaceBetween >
1135+ </ Modal >
1136+
10611137 < Container
10621138 header = {
10631139 < Header
0 commit comments