@@ -11,10 +11,11 @@ import CustomizedDialog from "../../common/CustomizedDialog";
1111import ArrowForwardIcon from '@mui/icons-material/ArrowForward' ;
1212import EditOutlinedIcon from '@mui/icons-material/EditOutlined' ;
1313import SearchTermsData from "../../../static/SearchTermsData.json" ;
14+ import { patchTerm } from "../../../api/endpoints" ;
1415
1516const initialSearchConditions = { attribute : '' , value : '' , condition : 'where' , relation : SearchTermsData . objectOptions [ 0 ] . value }
1617
17- const HeaderRightSideContent = ( { handleClose, activeStep, handleNext, handleBack, setActiveStep, isAllFieldsFilled, selectedOntology } ) => {
18+ const HeaderRightSideContent = ( { handleClose, activeStep, handleNext, handleBack, setActiveStep, isAllFieldsFilled, selectedOntology, isUpdating } ) => {
1819 return (
1920 < Box display = 'flex' alignItems = 'center' gap = '.75rem' >
2021 < MobileStepper
@@ -41,8 +42,14 @@ const HeaderRightSideContent = ({ handleClose, activeStep, handleNext, handleBac
4142 Previous
4243 </ Button >
4344 }
44- < Button endIcon = { < ArrowForwardIcon /> } variant = 'contained' color = 'primary' onClick = { handleNext } disabled = { activeStep === 0 && ! isAllFieldsFilled && ! selectedOntology } >
45- Continue
45+ < Button
46+ endIcon = { ! isUpdating && < ArrowForwardIcon /> }
47+ variant = 'contained'
48+ color = 'primary'
49+ onClick = { handleNext }
50+ disabled = { ( activeStep === 0 && ! isAllFieldsFilled && ! selectedOntology ) || isUpdating }
51+ >
52+ { isUpdating ? 'Saving Changes...' : 'Continue' }
4653 </ Button >
4754 </ >
4855 }
@@ -65,7 +72,117 @@ const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) =
6572 const [ ontologyTerms , setOntologyTerms ] = useState ( [ ] ) ;
6673 const [ ontologyAttributes , setOntologyAttributes ] = useState ( [ ] ) ;
6774 const [ selectedOntology , setSelectedOntology ] = useState ( null ) ;
68- const handleNext = ( ) => {
75+ const [ originalTerms , setOriginalTerms ] = useState ( [ ] ) ;
76+ const [ batchUpdateResults , setBatchUpdateResults ] = useState ( null ) ;
77+ const [ isUpdating , setIsUpdating ] = useState ( false ) ;
78+ const performBatchUpdate = async ( termsToUpdate = null ) => {
79+ setIsUpdating ( true ) ;
80+
81+ // Use provided terms or all ontology terms
82+ const terms = termsToUpdate || ontologyTerms ;
83+
84+ const results = {
85+ successful : [ ] ,
86+ failed : [ ] ,
87+ total : terms . length
88+ } ;
89+
90+ try {
91+ // Store original terms before starting updates (only if not retrying)
92+ if ( ! termsToUpdate ) {
93+ setOriginalTerms ( [ ...ontologyTerms ] ) ;
94+ }
95+
96+ // Process each term
97+ for ( const term of terms ) {
98+ try {
99+ // Extract the group and term ID from the term
100+ let termId = term . id || term [ '@id' ] ;
101+
102+ // If termId is in full URI format, extract just the ID part
103+ if ( termId && termId . includes ( '/' ) ) {
104+ termId = termId . split ( '/' ) . pop ( ) ;
105+ }
106+
107+ const group = 'base' ; // Default group, can be made configurable
108+
109+ // Create the JSON-LD payload with the current term data
110+ const jsonLdPayload = {
111+ "@context" : term [ "@context" ] || {
112+ "@vocab" : "http://uri.interlex.org/base/" ,
113+ "owl" : "http://www.w3.org/2002/07/owl#" ,
114+ "rdfs" : "http://www.w3.org/2000/01/rdf-schema#"
115+ } ,
116+ "@id" : term [ '@id' ] || termId ,
117+ ...term
118+ } ;
119+
120+ // Send PATCH request
121+ const response = await patchTerm ( group , termId , jsonLdPayload ) ;
122+
123+ if ( response . status === 200 || response . status === 201 ) {
124+ results . successful . push ( {
125+ termId,
126+ term : response . term ,
127+ status : response . status
128+ } ) ;
129+ } else {
130+ results . failed . push ( {
131+ termId,
132+ error : `HTTP ${ response . status } ` ,
133+ term
134+ } ) ;
135+ }
136+ } catch ( error ) {
137+ console . error ( `Failed to update term ${ term . id } :` , error ) ;
138+ results . failed . push ( {
139+ termId : term . id ,
140+ error : error . message || 'Unknown error' ,
141+ term
142+ } ) ;
143+ }
144+ }
145+
146+ // If retrying, merge with existing results
147+ if ( termsToUpdate && batchUpdateResults ) {
148+ setBatchUpdateResults ( {
149+ successful : [ ...batchUpdateResults . successful , ...results . successful ] ,
150+ failed : results . failed , // Replace failed list with new attempt results
151+ total : batchUpdateResults . total
152+ } ) ;
153+ } else {
154+ setBatchUpdateResults ( results ) ;
155+ }
156+ } catch ( error ) {
157+ console . error ( 'Batch update failed:' , error ) ;
158+ const failureResults = {
159+ successful : termsToUpdate && batchUpdateResults ? batchUpdateResults . successful : [ ] ,
160+ failed : terms . map ( term => ( {
161+ termId : term . id ,
162+ error : error . message || 'Batch operation failed' ,
163+ term
164+ } ) ) ,
165+ total : termsToUpdate && batchUpdateResults ? batchUpdateResults . total : terms . length
166+ } ;
167+ setBatchUpdateResults ( failureResults ) ;
168+ } finally {
169+ setIsUpdating ( false ) ;
170+ }
171+ } ;
172+
173+ const handleTryAgain = async ( ) => {
174+ if ( batchUpdateResults && batchUpdateResults . failed . length > 0 ) {
175+ // Extract failed terms for retry
176+ const failedTerms = batchUpdateResults . failed . map ( failedItem => failedItem . term ) ;
177+ await performBatchUpdate ( failedTerms ) ;
178+ }
179+ } ;
180+
181+ const handleNext = async ( ) => {
182+ // If we're moving from step 1 (EditTerms) to step 2 (Status), perform batch update
183+ if ( activeStep === 1 ) {
184+ await performBatchUpdate ( ) ;
185+ }
69186 setActiveStep ( ( prevActiveStep ) => prevActiveStep + 1 ) ;
70187 } ;
71188
@@ -83,7 +200,7 @@ const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) =
83200 } ;
84201
85202 // put success by default, should be changed later
86- const statusProps = getStatusProps ( { success : true } ) ;
203+
87204
88205 return (
89206 < CustomizedDialog
@@ -99,6 +216,7 @@ const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) =
99216 setActiveStep = { setActiveStep }
100217 isAllFieldsFilled = { isAllFieldsFilled ( searchConditions ) }
101218 selectedOntology = { selectedOntology }
219+ isUpdating = { isUpdating }
102220 />
103221 }
104222 sx = { {
@@ -119,6 +237,7 @@ const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) =
119237 setOntologyAttributes = { setOntologyAttributes }
120238 selectedOntology = { selectedOntology }
121239 setSelectedOntology = { setSelectedOntology }
240+ setOriginalTerms = { setOriginalTerms }
122241 />
123242 }
124243 {
@@ -130,7 +249,12 @@ const EditBulkTermsDialog = ({ open, handleClose, activeStep, setActiveStep }) =
130249 />
131250 }
132251 {
133- activeStep === 2 && < StatusStep statusProps = { statusProps } onAction = { ( ) => setActiveStep ( 0 ) } actionButtonStartIcon = { < EditOutlinedIcon /> } />
252+ activeStep === 2 && < StatusStep
253+ statusProps = { getStatusProps ( batchUpdateResults , isUpdating ) }
254+ onAction = { ( ) => setActiveStep ( 0 ) }
255+ actionButtonStartIcon = { < EditOutlinedIcon /> }
256+ onTryAgain = { handleTryAgain }
257+ />
134258 }
135259 </ >
136260 </ CustomizedDialog >
@@ -143,7 +267,9 @@ HeaderRightSideContent.propTypes = {
143267 handleNext : PropTypes . func . isRequired ,
144268 handleBack : PropTypes . func . isRequired ,
145269 setActiveStep : PropTypes . func . isRequired ,
146- isAllFieldsFilled : PropTypes . func . isRequired ,
270+ isAllFieldsFilled : PropTypes . bool . isRequired ,
271+ selectedOntology : PropTypes . object ,
272+ isUpdating : PropTypes . bool . isRequired ,
147273} ;
148274
149275EditBulkTermsDialog . propTypes = {
0 commit comments