/* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable no-catch-all/no-catch-all */ import { useCallback, useContext, useEffect, useRef, useState } from 'react' import { Popup as LeafletPopup, useMap } from 'react-leaflet' import { toast } from 'react-toastify' import { useAuth } from '#components/Auth/useAuth' import { TextAreaInput } from '#components/Input/TextAreaInput' import { TextInput } from '#components/Input/TextInput' import TemplateItemContext from '#components/Item/TemplateItemContext' import { useResetFilterTags } from '#components/Map/hooks/useFilter' import { useAddItem, useItems, useUpdateItem } from '#components/Map/hooks/useItems' import { usePopupForm } from '#components/Map/hooks/usePopupForm' import { useAddTag, useTags } from '#components/Map/hooks/useTags' import LayerContext from '#components/Map/LayerContext' import { hashTagRegex } from '#utils/HashTagRegex' import { randomColor } from '#utils/RandomColor' import type { Item } from '#types/Item' interface Props { children?: React.ReactNode } export function ItemFormPopup(props: Props) { const layerContext = useContext(LayerContext) const { menuText, name: activeLayerName } = layerContext const { popupForm, setPopupForm } = usePopupForm() const [spinner, setSpinner] = useState(false) const formRef = useRef(null) const map = useMap() const addItem = useAddItem() const updateItem = useUpdateItem() const items = useItems() const tags = useTags() const addTag = useAddTag() const resetFilterTags = useResetFilterTags() const { user } = useAuth() // Extract form data into Item object const parseFormData = useCallback( (evt: React.FormEvent): Item => { if (!popupForm) { throw new Error('Popup form is not defined') } const formItem: Item = {} as Item const formData = new FormData(evt.currentTarget) for (const [key, value] of formData.entries()) { if (key && typeof value === 'string') { // eslint-disable-next-line security/detect-object-injection ;(formItem as unknown as Record)[key] = value } } formItem.position = { type: 'Point', coordinates: [popupForm.position.lng, popupForm.position.lat], } return formItem }, [popupForm], ) // Process hashtags in text and create new tags if needed const processHashtags = useCallback( (text: string) => { if (!text) return text .toLocaleLowerCase() .match(hashTagRegex) ?.forEach((tag) => { const tagName = tag.slice(1).toLocaleLowerCase() if (!tags.find((t) => t.name.toLocaleLowerCase() === tagName)) { addTag({ id: crypto.randomUUID(), name: tag.slice(1), color: randomColor() }) } }) }, [tags, addTag], ) // Handle API operations with consistent error handling and return response data const handleApiOperation = useCallback( async ( operation: () => Promise, successMessage: string, ): Promise<{ success: boolean; data?: Item }> => { try { const data = await operation() toast.success(successMessage) return { success: true, data } } catch (error) { toast.error(error instanceof Error ? error.message : String(error)) return { success: false } } }, [], ) // Update existing item const handleUpdateItem = useCallback( async (formItem: Item) => { if (!popupForm?.item) return false const result = await handleApiOperation( () => popupForm.layer.api?.updateItem!({ ...formItem, id: popupForm.item!.id }) ?? Promise.resolve({} as Item), 'Item updated', ) if (result.success && result.data) { // Ensure the item has the layer object attached const itemWithLayer = { ...result.data, layer: popupForm.layer, user_created: formItem.user_created, } updateItem(itemWithLayer) } return result.success }, [popupForm, handleApiOperation, updateItem], ) // Create new item or update existing user profile const handleCreateItem = useCallback( async (formItem: Item) => { if (!popupForm) return false const existingUserItem = items.find( (i) => i.user_created?.id === user?.id && i.layer === popupForm.layer, ) const itemName = formItem.name || user?.first_name if (!itemName) { toast.error('Name must be defined') return false } const isUserProfileUpdate = popupForm.layer.userProfileLayer && existingUserItem const operation = isUserProfileUpdate ? () => popupForm.layer.api?.updateItem!({ ...formItem, id: existingUserItem.id }) ?? Promise.resolve({} as Item) : () => popupForm.layer.api?.createItem!({ ...formItem, name: itemName, id: crypto.randomUUID(), }) ?? Promise.resolve({} as Item) const result = await handleApiOperation( operation, isUserProfileUpdate ? 'Profile updated' : 'New item created', ) if (result.success && result.data) { // Ensure the item has the layer object attached const itemWithLayer = { ...result.data, layer: popupForm.layer, user_created: user ?? undefined, } if (isUserProfileUpdate) { updateItem(itemWithLayer) } else { addItem(itemWithLayer) } resetFilterTags() } return result.success }, [popupForm, items, user, handleApiOperation, updateItem, addItem, resetFilterTags], ) const handleSubmit = async (evt: React.FormEvent) => { evt.preventDefault() if (!popupForm) { throw new Error('Popup form is not defined') } setSpinner(true) try { const formItem = parseFormData(evt) // Process hashtags if text exists if (formItem.text) { processHashtags(formItem.text) } let success: boolean if (popupForm.item) { success = await handleUpdateItem(formItem) } else { success = await handleCreateItem(formItem) } if (success) { map.closePopup() setPopupForm(null) } } finally { setSpinner(false) } } const resetPopup = () => { if (formRef.current) { formRef.current.reset() } } useEffect(() => { resetPopup() }, [popupForm?.position]) return ( popupForm && popupForm.layer.name === activeLayerName && ( { setTimeout(function () { resetPopup() }, 100) }, }} position={popupForm.position} >
handleSubmit(e)} > {popupForm.item ? (
) : (
{menuText}
)} {props.children ? ( {props.children} ) : ( <> )}
) ) }