Anton Tranelis 649efe551d
refactor(lib): implement server-response-first pattern (#322)
* 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>
2025-08-20 15:03:30 +02:00

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>
)
}