1
- import React , { cloneElement , type FC , useEffect , useMemo } from 'react' ;
1
+ import React , { cloneElement , type FC , useEffect , useMemo , useState } from 'react' ;
2
2
import { Controller , useForm } from 'react-hook-form' ;
3
3
import { useNavigate } from 'react-router-dom' ;
4
- import { Grid , Typography } from '@mui/material' ;
4
+ import { Grid , Typography , Alert , IconButton } from '@mui/material' ;
5
5
import { useTranslation } from 'react-i18next' ;
6
6
import type { TermDetails , TermFormData } from 'generated-sources' ;
7
7
import {
@@ -12,11 +12,14 @@ import {
12
12
NamespaceAutocomplete ,
13
13
} from 'components/shared/elements' ;
14
14
import { useAppDispatch , useAppSelector } from 'redux/lib/hooks' ;
15
- import { createTerm , updateTerm } from 'redux/thunks' ;
15
+ import { createTerm , updateTerm , fetchTermsSearchResults } from 'redux/thunks' ;
16
16
import {
17
17
getTermCreatingStatuses ,
18
18
getTermDetails ,
19
19
getTermUpdatingStatuses ,
20
+ getTermSearchResults ,
21
+ getTermSearchId ,
22
+ getTermSearchFacetsSynced ,
20
23
} from 'redux/selectors' ;
21
24
import { termDetailsPath , useTermsRouteParams } from 'routes' ;
22
25
import { useQueryClient } from '@tanstack/react-query' ;
@@ -40,6 +43,12 @@ const TermsForm: FC<TermsFormDialogProps> = ({ btnCreateEl }) => {
40
43
getTermUpdatingStatuses
41
44
) ;
42
45
46
+ const searchId = useAppSelector ( getTermSearchId ) ;
47
+ const isTermSearchFacetsSynced = useAppSelector ( getTermSearchFacetsSynced ) ;
48
+ const existingTerms = useAppSelector ( getTermSearchResults ) ;
49
+
50
+ const [ error , setError ] = useState < string | null > ( null ) ;
51
+
43
52
const defaultValues = useMemo (
44
53
( ) => ( {
45
54
name : term ?. name ?? '' ,
@@ -57,13 +66,31 @@ const TermsForm: FC<TermsFormDialogProps> = ({ btnCreateEl }) => {
57
66
58
67
useEffect ( ( ) => {
59
68
reset ( defaultValues ) ;
60
- } , [ defaultValues ] ) ;
69
+ } , [ defaultValues , reset ] ) ;
70
+
71
+ useEffect ( ( ) => {
72
+ if ( searchId && isTermSearchFacetsSynced ) {
73
+ dispatch ( fetchTermsSearchResults ( { searchId, page : 1 , size : 1000 } ) ) ;
74
+ }
75
+ } , [ dispatch , searchId , isTermSearchFacetsSynced ] ) ;
61
76
62
77
const clearState = ( ) => {
63
78
reset ( ) ;
79
+ setError ( null ) ;
64
80
} ;
65
81
66
82
const onSubmit = ( data : TermFormData ) => {
83
+ const duplicateTerm = existingTerms . find (
84
+ ( existingTerm : TermDetails ) =>
85
+ existingTerm . name === data . name &&
86
+ existingTerm . namespace . name === data . namespaceName
87
+ ) ;
88
+
89
+ if ( duplicateTerm ) {
90
+ setError ( 'A term with the same name already exists in this namespace.' ) ;
91
+ return ;
92
+ }
93
+
67
94
const parsedData = { ...data } ;
68
95
( term && term . id
69
96
? dispatch ( updateTerm ( { termId : term . id , termFormData : parsedData } ) )
@@ -81,12 +108,38 @@ const TermsForm: FC<TermsFormDialogProps> = ({ btnCreateEl }) => {
81
108
82
109
const termFormTitle = (
83
110
< Typography variant = 'h4' component = 'span' >
84
- { term . id ? t ( 'Edit' ) : t ( 'Add' ) } { t ( 'term' ) }
111
+ { term ? .id ? t ( 'Edit' ) : t ( 'Add' ) } { t ( 'term' ) }
85
112
</ Typography >
86
113
) ;
87
114
88
115
const termFormContent = ( ) => (
89
116
< form id = 'term-create-form' onSubmit = { handleSubmit ( onSubmit ) } >
117
+ { error && (
118
+ < Alert
119
+ severity = "error"
120
+ sx = { { mb : 2 , alignItems : 'center' } }
121
+ action = {
122
+ < Typography
123
+ component = "button"
124
+ onClick = { ( ) => setError ( null ) }
125
+ sx = { {
126
+ fontSize : '1.1rem' ,
127
+ color : 'inherit' ,
128
+ background : 'none' ,
129
+ border : 'none' ,
130
+ cursor : 'pointer' ,
131
+ padding : 0 ,
132
+ marginLeft : 2 ,
133
+ lineHeight : '1.6rem' ,
134
+ } }
135
+ >
136
+ ×
137
+ </ Typography >
138
+ }
139
+ >
140
+ { error }
141
+ </ Alert >
142
+ ) }
90
143
< Controller
91
144
name = 'name'
92
145
control = { control }
@@ -131,7 +184,7 @@ const TermsForm: FC<TermsFormDialogProps> = ({ btnCreateEl }) => {
131
184
132
185
const termFormActionButtons = ( ) => (
133
186
< Button
134
- text = { term ? t ( 'Save term' ) : t ( 'Add term' ) }
187
+ text = { term ?. id ? t ( 'Save term' ) : t ( 'Add term' ) }
135
188
buttonType = 'main-lg'
136
189
type = 'submit'
137
190
form = 'term-create-form'
@@ -149,8 +202,8 @@ const TermsForm: FC<TermsFormDialogProps> = ({ btnCreateEl }) => {
149
202
title = { termFormTitle }
150
203
renderContent = { termFormContent }
151
204
renderActions = { termFormActionButtons }
152
- handleCloseSubmittedForm = { term ? isTermUpdated : isTermCreated }
153
- isLoading = { term ? isTermUpdating : isTermCreating }
205
+ handleCloseSubmittedForm = { term ?. id ? isTermUpdated : isTermCreated }
206
+ isLoading = { term ?. id ? isTermUpdating : isTermCreating }
154
207
clearState = { clearState }
155
208
formSubmitHandler = { handleSubmit ( onSubmit ) }
156
209
/>
0 commit comments