mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
* use server response for local state updates * fix formatting * refactor: comprehensive server-response-first pattern implementation ## Major Changes ### LayerProps ID Required - Made `LayerProps.id` required (was optional) - All layers now guaranteed to have server-provided UUID - Enables reliable layer ID mapping from server responses ### useSelectPosition Hook Refactored - Added reusable `handleApiOperation` helper function - Refactored `itemUpdatePosition`, `itemUpdateParent`, `linkItem` - All functions now use server response + layer ID mapping - Consistent error handling and toast management ### itemFunctions.ts Complete Refactor - **submitNewItem**: Server response with layer mapping - **linkItem**: Server response preserves layer object - **unlinkItem**: Same pattern as linkItem - **handleDelete**: Simplified error handling - **onUpdateItem**: Complex function refactored for both update/create branches ### Benefits - ✅ Eliminates race conditions from manual state construction - ✅ Server response as single source of truth for all updates - ✅ Consistent error handling across all API operations - ✅ Items no longer disappear from map after updates - ✅ Type-safe layer ID mapping ### Testing - Updated ItemFunctions.spec.tsx with new toast patterns - Added required layer IDs to test objects - All 19 tests passing (3 skipped) - ESLint clean 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix linting * fix: resolve TypeScript undefined data errors - Add non-null assertions for result.data in conditional blocks - TypeScript now properly recognizes data is defined after success check - All linting and TypeScript errors resolved 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fixed examples * remove unneccessary uuid generation --------- Co-authored-by: Claude <noreply@anthropic.com>
125 lines
2.8 KiB
TypeScript
125 lines
2.8 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
|
|
import { useSetItemsApi, useSetItemsData } from './hooks/useItems'
|
|
import { useAddTag } from './hooks/useTags'
|
|
import LayerContext from './LayerContext'
|
|
|
|
import type { LayerProps } from '#types/LayerProps'
|
|
import type { Tag } from '#types/Tag'
|
|
|
|
export type { Point } from 'geojson'
|
|
export type { Item } from '#types/Item'
|
|
export type { LayerProps } from '#types/LayerProps'
|
|
export type { Tag } from '#types/Tag'
|
|
export type { Popup } from 'leaflet'
|
|
|
|
/**
|
|
* @category Map
|
|
*/
|
|
export const Layer = ({
|
|
id,
|
|
data,
|
|
children,
|
|
name = 'places',
|
|
menuIcon = 'MapPinIcon',
|
|
menuText = 'add new place',
|
|
menuColor = '#2E7D32',
|
|
markerIcon,
|
|
markerShape = 'circle',
|
|
markerDefaultColor = '#777',
|
|
markerDefaultColor2 = 'RGBA(35, 31, 32, 0.2)',
|
|
api,
|
|
itemType,
|
|
userProfileLayer = false,
|
|
customEditLink,
|
|
customEditParameter,
|
|
// eslint-disable-next-line camelcase
|
|
public_edit_items,
|
|
listed = true,
|
|
}: LayerProps) => {
|
|
const setItemsApi = useSetItemsApi()
|
|
const setItemsData = useSetItemsData()
|
|
|
|
const addTag = useAddTag()
|
|
const [newTagsToAdd] = useState<Tag[]>([])
|
|
const [tagsReady] = useState<boolean>(false)
|
|
|
|
useEffect(() => {
|
|
data &&
|
|
setItemsData({
|
|
id,
|
|
data,
|
|
children,
|
|
name,
|
|
menuIcon,
|
|
menuText,
|
|
menuColor,
|
|
markerIcon,
|
|
markerShape,
|
|
markerDefaultColor,
|
|
markerDefaultColor2,
|
|
api,
|
|
itemType,
|
|
userProfileLayer,
|
|
// Can we just use editCallback for all cases?
|
|
customEditLink,
|
|
customEditParameter,
|
|
// eslint-disable-next-line camelcase
|
|
public_edit_items,
|
|
listed,
|
|
})
|
|
api &&
|
|
setItemsApi({
|
|
id,
|
|
data,
|
|
children,
|
|
name,
|
|
menuIcon,
|
|
menuText,
|
|
menuColor,
|
|
markerIcon,
|
|
markerShape,
|
|
markerDefaultColor,
|
|
markerDefaultColor2,
|
|
api,
|
|
itemType,
|
|
userProfileLayer,
|
|
customEditLink,
|
|
customEditParameter,
|
|
// eslint-disable-next-line camelcase
|
|
public_edit_items,
|
|
listed,
|
|
})
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [data, api])
|
|
|
|
useEffect(() => {
|
|
if (tagsReady) {
|
|
const processedTags = {}
|
|
newTagsToAdd.map((newtag) => {
|
|
if (!processedTags[newtag.name]) {
|
|
processedTags[newtag.name] = true
|
|
addTag(newtag)
|
|
}
|
|
return null
|
|
})
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [tagsReady])
|
|
|
|
return (
|
|
<LayerContext.Provider
|
|
value={{
|
|
name,
|
|
markerDefaultColor,
|
|
markerDefaultColor2,
|
|
markerShape,
|
|
markerIcon,
|
|
menuText,
|
|
}}
|
|
>
|
|
{children}
|
|
</LayerContext.Provider>
|
|
)
|
|
}
|