From a6850c8cbca4e357993ccc9487953e972a33391e Mon Sep 17 00:00:00 2001 From: Fringg Date: Fri, 24 Apr 2026 15:19:20 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20FAQ=20answer=20editor=20=E2=80=94=20stab?= =?UTF-8?q?le=20keys,=20safe=20setContent,=20drop=20handler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use emitUpdate: false instead of fragile suppressUpdate ref - Stable React keys for FAQ items — survive reorder/delete without editor desync - Fix onDrop to check file type before preventDefault (non-media drops were swallowed) --- src/pages/AdminInfoPageEditor.tsx | 50 +++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/src/pages/AdminInfoPageEditor.tsx b/src/pages/AdminInfoPageEditor.tsx index 5132bab..d92ec5c 100644 --- a/src/pages/AdminInfoPageEditor.tsx +++ b/src/pages/AdminInfoPageEditor.tsx @@ -259,7 +259,9 @@ function FaqAnswerEditor({ value, onChange }: { value: string; onChange: (html: const isUploading = uploadCount > 0; const [isDragging, setIsDragging] = useState(false); const activeUploadsRef = useRef(new Set()); - const suppressUpdate = useRef(false); + + const onChangeRef = useRef(onChange); + onChangeRef.current = onChange; const editor = useEditor({ extensions: FAQ_EDITOR_EXTENSIONS, @@ -270,10 +272,9 @@ function FaqAnswerEditor({ value, onChange }: { value: string; onChange: (html: }, }, onUpdate: ({ editor: e }) => { - if (suppressUpdate.current) return; const html = e.getHTML(); const isEmpty = html === '

' || html === ''; - onChange(isEmpty ? '' : html); + onChangeRef.current(isEmpty ? '' : html); }, }); @@ -283,9 +284,7 @@ function FaqAnswerEditor({ value, onChange }: { value: string; onChange: (html: const currentHtml = editor.getHTML(); const normalizedCurrent = currentHtml === '

' ? '' : currentHtml; if (normalizedCurrent !== value) { - suppressUpdate.current = true; - editor.commands.setContent(value || ''); - suppressUpdate.current = false; + editor.commands.setContent(value || '', { emitUpdate: false }); } }, [value, editor]); @@ -499,9 +498,11 @@ function FaqAnswerEditor({ value, onChange }: { value: string; onChange: (html: onDrop={(e) => { setIsDragging(false); if (e.defaultPrevented) return; - e.preventDefault(); const file = e.dataTransfer.files[0]; - if (file) handleMediaUpload(file); + if (file && (file.type.startsWith('image/') || file.type.startsWith('video/'))) { + e.preventDefault(); + handleMediaUpload(file); + } }} > @@ -535,6 +536,25 @@ interface FaqBuilderProps { function FaqBuilder({ items, onChange, locale, localeLabel }: FaqBuilderProps) { const { t } = useTranslation(); + // Stable keys for React — travel with items through reorder/delete + const keyCounter = useRef(0); + const [itemKeys, setItemKeys] = useState(() => items.map(() => keyCounter.current++)); + + // Sync key count when items change externally (e.g., locale switch resets items) + useEffect(() => { + setItemKeys((prev) => { + if (prev.length === items.length) return prev; + if (items.length > prev.length) { + const newKeys = [...prev]; + for (let i = prev.length; i < items.length; i++) { + newKeys.push(keyCounter.current++); + } + return newKeys; + } + return prev.slice(0, items.length); + }); + }, [items.length]); + const handleQuestionChange = useCallback( (index: number, value: string) => { const updated = items.map((item, i) => (i === index ? { ...item, q: value } : item)); @@ -553,12 +573,14 @@ function FaqBuilder({ items, onChange, locale, localeLabel }: FaqBuilderProps) { const handleRemove = useCallback( (index: number) => { + setItemKeys((prev) => prev.filter((_, i) => i !== index)); onChange(items.filter((_, i) => i !== index)); }, [items, onChange], ); const handleAdd = useCallback(() => { + setItemKeys((prev) => [...prev, keyCounter.current++]); onChange([...items, { q: '', a: '' }]); }, [items, onChange]); @@ -567,6 +589,11 @@ function FaqBuilder({ items, onChange, locale, localeLabel }: FaqBuilderProps) { if (index === 0) return; const updated = [...items]; [updated[index - 1], updated[index]] = [updated[index], updated[index - 1]]; + setItemKeys((prev) => { + const k = [...prev]; + [k[index - 1], k[index]] = [k[index], k[index - 1]]; + return k; + }); onChange(updated); }, [items, onChange], @@ -577,6 +604,11 @@ function FaqBuilder({ items, onChange, locale, localeLabel }: FaqBuilderProps) { if (index >= items.length - 1) return; const updated = [...items]; [updated[index], updated[index + 1]] = [updated[index + 1], updated[index]]; + setItemKeys((prev) => { + const k = [...prev]; + [k[index], k[index + 1]] = [k[index + 1], k[index]]; + return k; + }); onChange(updated); }, [items, onChange], @@ -603,7 +635,7 @@ function FaqBuilder({ items, onChange, locale, localeLabel }: FaqBuilderProps) { {items.map((item, index) => (