use server response for local state updates

This commit is contained in:
Anton Tranelis 2025-08-18 20:25:41 +02:00
parent 94fa6321ba
commit 89ff940b74

View File

@ -1,11 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/no-misused-promises */
/* eslint-disable @typescript-eslint/prefer-optional-chain */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable security/detect-object-injection */
import { useContext, useEffect, useRef, useState } from 'react' /* 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 { Popup as LeafletPopup, useMap } from 'react-leaflet'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
@ -50,95 +48,170 @@ export function ItemFormPopup(props: Props) {
const { user } = useAuth() const { user } = useAuth()
const handleSubmit = async (evt: any) => { // Extract form data into Item object
if (!popupForm) { const parseFormData = useCallback(
throw new Error('Popup form is not defined') (evt: React.FormEvent<HTMLFormElement>): Item => {
} if (!popupForm) {
const formItem: Item = {} as Item throw new Error('Popup form is not defined')
Array.from(evt.target).forEach((input: HTMLInputElement) => {
if (input.name) {
formItem[input.name] = input.value
} }
})
formItem.position = { const formItem: Item = {} as Item
type: 'Point', const formData = new FormData(evt.currentTarget)
coordinates: [popupForm.position.lng, popupForm.position.lat],
} for (const [key, value] of formData.entries()) {
if (key && typeof value === 'string') {
;(formItem as unknown as Record<string, unknown>)[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<Item>,
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 }
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 uuid = crypto.randomUUID()
const operation = isUserProfileUpdate
? () =>
popupForm.layer.api?.updateItem!({ ...formItem, id: existingUserItem.id }) ??
Promise.resolve({} as Item)
: () =>
popupForm.layer.api?.createItem!({ ...formItem, name: itemName, id: uuid }) ??
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 }
if (isUserProfileUpdate) {
updateItem(itemWithLayer)
} else {
addItem(itemWithLayer)
}
resetFilterTags()
}
return result.success
},
[popupForm, items, user, handleApiOperation, updateItem, addItem, resetFilterTags],
)
const handleSubmit = async (evt: React.FormEvent<HTMLFormElement>) => {
evt.preventDefault() evt.preventDefault()
const name = formItem.name ? formItem.name : user?.first_name if (!popupForm) {
if (!name) { throw new Error('Popup form is not defined')
toast.error('Name is must be defined')
return
} }
setSpinner(true) setSpinner(true)
formItem.text && try {
formItem.text const formItem = parseFormData(evt)
.toLocaleLowerCase()
.match(hashTagRegex)
?.map((tag) => {
if (!tags.find((t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase())) {
addTag({ id: crypto.randomUUID(), name: tag.slice(1), color: randomColor() })
}
return null
})
if (popupForm.item) { // Process hashtags if text exists
let success = false if (formItem.text) {
try { processHashtags(formItem.text)
await popupForm.layer.api?.updateItem!({ ...formItem, id: popupForm.item.id })
success = true
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error) {
toast.error(error.toString())
} }
if (success) {
updateItem({ ...popupForm.item, ...formItem })
toast.success('Item updated')
}
setSpinner(false)
map.closePopup()
} else {
const item = items.find((i) => i.user_created?.id === user?.id && i.layer === popupForm.layer)
const uuid = crypto.randomUUID() let success: boolean
let success = false if (popupForm.item) {
try { success = await handleUpdateItem(formItem)
popupForm.layer.userProfileLayer && } else {
item && success = await handleCreateItem(formItem)
(await popupForm.layer.api?.updateItem!({ ...formItem, id: item.id }))
;(!popupForm.layer.userProfileLayer || !item) &&
(await popupForm.layer.api?.createItem!({
...formItem,
name,
id: uuid,
}))
success = true
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error) {
toast.error(error.toString())
} }
if (success) { if (success) {
if (popupForm.layer.userProfileLayer && item) updateItem({ ...item, ...formItem }) map.closePopup()
if (!popupForm.layer.userProfileLayer || !item) { setPopupForm(null)
addItem({
...formItem,
name: (formItem.name ? formItem.name : user?.first_name) ?? '',
user_created: user ?? undefined,
id: uuid,
layer: popupForm.layer,
public_edit: !user,
})
}
toast.success('New item created')
resetFilterTags()
} }
} finally {
setSpinner(false) setSpinner(false)
map.closePopup()
} }
setPopupForm(null)
} }
const resetPopup = () => { const resetPopup = () => {