1
1
import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query" ;
2
2
import { Check , Hash , Loader2 , Plus , X } from "lucide-react" ;
3
- import { useEffect , useState } from "react" ;
3
+ import { useEffect , useMemo , useState } from "react" ;
4
4
import { useTranslation } from "react-i18next" ;
5
5
import { toast } from "sonner" ;
6
6
@@ -56,18 +56,19 @@ export default function ManageQuestionnaireTagsSheet({
56
56
const queryClient = useQueryClient ( ) ;
57
57
const { t } = useTranslation ( ) ;
58
58
const [ open , setOpen ] = useState ( false ) ;
59
+ const [ popOverOpen , setPopOverOpen ] = useState ( false ) ;
59
60
const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
60
- const [ selectedSlugs , setSelectedSlugs ] = useState < string [ ] > ( [ ] ) ;
61
61
const [ isCreateOpen , setIsCreateOpen ] = useState ( false ) ;
62
62
const [ newTagName , setNewTagName ] = useState ( "" ) ;
63
63
const [ newTagSlug , setNewTagSlug ] = useState ( "" ) ;
64
+ const [ selectedTags , setSelectedTags ] = useState < QuestionnaireTagModel [ ] > ( [ ] ) ;
64
65
65
66
const { data : availableTags , isLoading } = useQuery ( {
66
67
queryKey : [ "questionnaire_tags" , searchQuery ] ,
67
68
queryFn : query . debounced ( questionnaireApi . tags . list , {
68
69
queryParams : searchQuery !== "" ? { name : searchQuery } : undefined ,
69
70
} ) ,
70
- enabled : open ,
71
+ enabled : popOverOpen ,
71
72
} ) ;
72
73
73
74
const { mutate : setTags , isPending : isUpdating } = useMutation ( {
@@ -78,7 +79,7 @@ export default function ManageQuestionnaireTagsSheet({
78
79
queryClient . invalidateQueries ( {
79
80
queryKey : [ "questionnaireDetail" , questionnaire . slug ] ,
80
81
} ) ;
81
- toast . success ( "Tags updated successfully" ) ;
82
+ toast . success ( t ( "tag_updated_successfully" ) ) ;
82
83
setOpen ( false ) ;
83
84
} ,
84
85
} ) ;
@@ -90,36 +91,56 @@ export default function ManageQuestionnaireTagsSheet({
90
91
queryClient . invalidateQueries ( {
91
92
queryKey : [ "questionnaire_tags" ] ,
92
93
} ) ;
93
- setSelectedSlugs ( ( current ) => [ ...current , tagData . slug ] ) ;
94
+ setSelectedTags ( ( current ) => [ ...current , tagData ] ) ;
94
95
setNewTagName ( "" ) ;
95
96
setNewTagSlug ( "" ) ;
96
97
setIsCreateOpen ( false ) ;
97
- toast . success ( "Tag created successfully" ) ;
98
+ toast . success ( t ( "tag_created_successfully" ) ) ;
98
99
} ,
99
100
} ) ;
100
101
101
- // Initialize selected slugs from questionnaire tags
102
+ // Initialize selected tags from questionnaire tags
102
103
useEffect ( ( ) => {
103
104
if ( questionnaire . tags ) {
104
- setSelectedSlugs ( questionnaire . tags . map ( ( tag ) => tag . slug ) ) ;
105
+ setSelectedTags ( questionnaire . tags ) ;
105
106
}
106
107
} , [ questionnaire . tags ] ) ;
107
108
108
- const handleToggleTag = ( tagSlug : string ) => {
109
- setSelectedSlugs ( ( current ) =>
110
- current . includes ( tagSlug )
111
- ? current . filter ( ( slug ) => slug !== tagSlug )
112
- : [ ...current , tagSlug ] ,
109
+ // Simple merge of selected tags with available tags
110
+ const tagOptions = useMemo ( ( ) => {
111
+ if ( ! availableTags ?. results ) return selectedTags ;
112
+ if ( searchQuery ) return availableTags . results ;
113
+
114
+ const availableSlugs = new Set (
115
+ availableTags . results . map ( ( tag ) => tag . slug ) ,
116
+ ) ;
117
+
118
+ // Add selected tags that aren't in availableTags
119
+ const selectedNotInAvailable = selectedTags . filter (
120
+ ( selectedTag ) => ! availableSlugs . has ( selectedTag . slug ) ,
113
121
) ;
122
+
123
+ return [ ...availableTags . results , ...selectedNotInAvailable ] ;
124
+ } , [ availableTags , selectedTags , searchQuery ] ) ;
125
+
126
+ const handleToggleTag = ( tagSlug : string ) => {
127
+ setSelectedTags ( ( current ) => {
128
+ const newTag = tagOptions ?. find ( ( tag ) => tag . slug === tagSlug ) ;
129
+ return current . some ( ( tag ) => tag . slug === tagSlug )
130
+ ? current . filter ( ( tag ) => tag . slug !== tagSlug )
131
+ : newTag
132
+ ? [ ...current , newTag ]
133
+ : current ;
134
+ } ) ;
114
135
} ;
115
136
116
137
const handleSave = ( ) => {
117
- setTags ( { tags : selectedSlugs } ) ;
138
+ setTags ( { tags : selectedTags . map ( ( tag ) => tag . slug ) } ) ;
118
139
} ;
119
140
120
141
const handleCreateTag = ( ) => {
121
142
if ( ! newTagName . trim ( ) || ! newTagSlug . trim ( ) ) {
122
- toast . error ( "Name and slug are required" ) ;
143
+ toast . error ( t ( "name_and_slug_are_required" ) ) ;
123
144
return ;
124
145
}
125
146
@@ -129,14 +150,12 @@ export default function ManageQuestionnaireTagsSheet({
129
150
} ) ;
130
151
} ;
131
152
132
- const selectedTags = availableTags ?. results . filter ( ( tag ) =>
133
- selectedSlugs . includes ( tag . slug ) ,
134
- ) ;
135
-
136
153
const hasChanges =
137
154
new Set ( questionnaire . tags . map ( ( tag ) => tag . slug ) ) . size !==
138
- new Set ( selectedSlugs ) . size ||
139
- ! questionnaire . tags . every ( ( tag ) => selectedSlugs . includes ( tag . slug ) ) ;
155
+ new Set ( selectedTags ) . size ||
156
+ ! questionnaire . tags . every ( ( tag ) =>
157
+ selectedTags . some ( ( st ) => st . slug === tag . slug ) ,
158
+ ) ;
140
159
141
160
return (
142
161
< Sheet open = { open } onOpenChange = { setOpen } >
@@ -186,7 +205,16 @@ export default function ManageQuestionnaireTagsSheet({
186
205
{ /* Tag Selector */ }
187
206
< div className = "space-y-4" >
188
207
< h3 className = "text-sm font-medium" > { t ( "add_tags" ) } </ h3 >
189
- < Popover modal = { true } >
208
+ < Popover
209
+ modal = { true }
210
+ open = { popOverOpen }
211
+ onOpenChange = { ( open ) => {
212
+ if ( ! open ) {
213
+ setSearchQuery ( "" ) ;
214
+ }
215
+ setPopOverOpen ( open ) ;
216
+ } }
217
+ >
190
218
< PopoverTrigger asChild >
191
219
< Button
192
220
variant = "outline"
@@ -214,7 +242,7 @@ export default function ManageQuestionnaireTagsSheet({
214
242
< Loader2 className = "h-6 w-6 animate-spin" />
215
243
</ div >
216
244
) : (
217
- availableTags ?. results ?. map ( ( tag ) => (
245
+ tagOptions ?. map ( ( tag ) => (
218
246
< CommandItem
219
247
key = { tag . slug }
220
248
value = { tag . slug }
@@ -224,7 +252,7 @@ export default function ManageQuestionnaireTagsSheet({
224
252
< Hash className = "h-4 w-4" />
225
253
< span > { tag . name } </ span >
226
254
</ div >
227
- { selectedSlugs . includes ( tag . slug ) && (
255
+ { selectedTags . some ( ( t ) => t . slug === tag . slug ) && (
228
256
< Check className = "h-4 w-4" />
229
257
) }
230
258
</ CommandItem >
@@ -301,7 +329,7 @@ export default function ManageQuestionnaireTagsSheet({
301
329
< Button
302
330
variant = "outline"
303
331
onClick = { ( ) => {
304
- setSelectedSlugs ( questionnaire . tags . map ( ( tag ) => tag . slug ) ) ;
332
+ setSelectedTags ( questionnaire . tags ) ;
305
333
setOpen ( false ) ;
306
334
} }
307
335
>
0 commit comments