mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
fix(collections): white screen when editing a place (undefined in places) (#1081)
updatePlace wrote `res.place` into the places list, but the endpoint returns the
updated place directly (not wrapped in { place }, unlike savePlace) — so an
`undefined` slipped into the list and the category-filter's presentCategories()
crashed on `undefined.category_id`, blanking the whole page. The WS echo used to
mask it by refetching; excluding the editor's own socket exposed it.
- Read the updated place directly and guard against a falsy response.
- Fix the api return types to match (updatePlace/setStatus return the place).
- Harden filterPlaces / statusCounts / presentCategories / mappablePlaces against
a stray undefined entry so a single bad row can never white-screen the page.
This commit is contained in:
@@ -62,9 +62,9 @@ export const collectionsApi = {
|
||||
ax.post(`${base}/places`, body satisfies CollectionSavePlaceRequest).then((r: AxiosResponse) => r.data),
|
||||
saveFromTrip: (body: CollectionSaveFromTripRequest): Promise<CollectionSaveResult> =>
|
||||
ax.post(`${base}/places/from-trip`, body satisfies CollectionSaveFromTripRequest).then((r: AxiosResponse) => r.data),
|
||||
updatePlace: (pid: number, body: CollectionPlaceUpdateRequest): Promise<{ place: CollectionPlace }> =>
|
||||
updatePlace: (pid: number, body: CollectionPlaceUpdateRequest): Promise<CollectionPlace> =>
|
||||
ax.patch(`${base}/places/${pid}`, body satisfies CollectionPlaceUpdateRequest).then((r: AxiosResponse) => r.data),
|
||||
setStatus: (pid: number, status: CollectionStatus): Promise<{ place: CollectionPlace }> =>
|
||||
setStatus: (pid: number, status: CollectionStatus): Promise<CollectionPlace> =>
|
||||
ax.post(`${base}/places/${pid}/status`, { status }).then((r: AxiosResponse) => r.data),
|
||||
deletePlace: (pid: number): Promise<unknown> =>
|
||||
ax.delete(`${base}/places/${pid}`).then((r: AxiosResponse) => r.data),
|
||||
|
||||
@@ -53,6 +53,7 @@ export function filterPlaces(
|
||||
): CollectionPlace[] {
|
||||
const q = search.trim().toLowerCase()
|
||||
return places.filter(p => {
|
||||
if (!p) return false
|
||||
if (statusFilter !== 'all' && p.status !== statusFilter) return false
|
||||
if (categoryFilter !== 'all' && (p.category_id ?? null) !== categoryFilter) return false
|
||||
if (!q) return true
|
||||
@@ -66,8 +67,8 @@ export function filterPlaces(
|
||||
|
||||
/** Count places per status for the filter chips. */
|
||||
export function statusCounts(places: CollectionPlace[]): Record<StatusFilter, number> {
|
||||
const counts: Record<StatusFilter, number> = { all: places.length, idea: 0, want: 0, visited: 0 }
|
||||
for (const p of places) counts[p.status] += 1
|
||||
const counts: Record<StatusFilter, number> = { all: 0, idea: 0, want: 0, visited: 0 }
|
||||
for (const p of places) { if (!p) continue; counts.all += 1; counts[p.status] += 1 }
|
||||
return counts
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ export interface CategoryOption { id: number; name: string; color: string | null
|
||||
export function presentCategories(places: CollectionPlace[]): CategoryOption[] {
|
||||
const byId = new Map<number, CategoryOption>()
|
||||
for (const p of places) {
|
||||
if (p.category_id == null || !p.category) continue
|
||||
if (!p || p.category_id == null || !p.category) continue
|
||||
const existing = byId.get(p.category_id)
|
||||
if (existing) existing.count += 1
|
||||
else byId.set(p.category_id, { id: p.category_id, name: p.category.name, color: p.category.color ?? null, icon: p.category.icon ?? null, count: 1 })
|
||||
@@ -87,7 +88,7 @@ export function presentCategories(places: CollectionPlace[]): CategoryOption[] {
|
||||
|
||||
/** Only the places that can render on a map. */
|
||||
export function mappablePlaces(places: CollectionPlace[]): CollectionPlace[] {
|
||||
return places.filter(p => typeof p.lat === 'number' && typeof p.lng === 'number')
|
||||
return places.filter(p => p && typeof p.lat === 'number' && typeof p.lng === 'number')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -203,8 +203,9 @@ export const useCollectionStore = create<CollectionState>((set, get) => ({
|
||||
},
|
||||
|
||||
updatePlace: async (placeId, body) => {
|
||||
const res = await collectionsApi.updatePlace(placeId, body)
|
||||
set({ places: get().places.map(p => (p.id === placeId ? res.place : p)) })
|
||||
// The endpoint returns the updated place directly (not wrapped in { place }).
|
||||
const updated = await collectionsApi.updatePlace(placeId, body)
|
||||
if (updated) set({ places: get().places.map(p => (p.id === placeId ? updated : p)) })
|
||||
},
|
||||
|
||||
deletePlace: async (placeId: number) => {
|
||||
|
||||
Reference in New Issue
Block a user