mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-24 07:41:47 +00:00
feat: always-optimistic write pattern across all repos
All create/update/delete repo methods now write to IndexedDB optimistically and fire mutationQueue.flush() as fire-and-forget, returning immediately without waiting for the network. This eliminates the 8-second UX freeze previously seen when the API was unreachable but navigator.onLine was true. - Repos rewritten: trip, day, place, packing, todo, budget, accommodation, reservation, file — write methods never throw, always return optimistic data - mutationQueue.flush() changed to iterative (one item per loop iteration) so mutations enqueued mid-flush (e.g. bulk check-all) are picked up - fileRepo.toggleStar skips the IDB put when the file is not cached locally - DayDetailPanel passes place_name into accommodationRepo.create so the optimistic accommodation renders the correct hotel label immediately - Test suite updated throughout to reflect optimistic-first semantics: no more rollback assertions, IDB cleared in component test beforeEach hooks, FileManager tests switched from filesApi spy to MSW endpoint assertions
This commit is contained in:
+50
-62
@@ -29,73 +29,61 @@ export const todoRepo = {
|
||||
},
|
||||
|
||||
async create(tripId: number | string, data: Record<string, unknown>): Promise<{ item: TodoItem }> {
|
||||
if (!navigator.onLine) {
|
||||
const tempId = -(Date.now())
|
||||
const tempItem: TodoItem = {
|
||||
...(data as Partial<TodoItem>),
|
||||
id: tempId,
|
||||
trip_id: Number(tripId),
|
||||
name: (data.name as string) ?? 'New todo',
|
||||
checked: 0,
|
||||
sort_order: 0,
|
||||
due_date: null,
|
||||
description: null,
|
||||
assigned_user_id: null,
|
||||
priority: 0,
|
||||
} as TodoItem
|
||||
await offlineDb.todoItems.put(tempItem)
|
||||
await mutationQueue.enqueue({
|
||||
id: generateUUID(),
|
||||
tripId: Number(tripId),
|
||||
method: 'POST',
|
||||
url: `/trips/${tripId}/todo`,
|
||||
body: data,
|
||||
resource: 'todoItems',
|
||||
tempId,
|
||||
})
|
||||
return { item: tempItem }
|
||||
}
|
||||
const result = await todoApi.create(tripId, data)
|
||||
offlineDb.todoItems.put(result.item)
|
||||
return result
|
||||
const tempId = -(Date.now())
|
||||
const tempItem: TodoItem = {
|
||||
...(data as Partial<TodoItem>),
|
||||
id: tempId,
|
||||
trip_id: Number(tripId),
|
||||
name: (data.name as string) ?? 'New todo',
|
||||
checked: 0,
|
||||
sort_order: 0,
|
||||
due_date: null,
|
||||
description: null,
|
||||
assigned_user_id: null,
|
||||
priority: 0,
|
||||
} as TodoItem
|
||||
await offlineDb.todoItems.put(tempItem)
|
||||
await mutationQueue.enqueue({
|
||||
id: generateUUID(),
|
||||
tripId: Number(tripId),
|
||||
method: 'POST',
|
||||
url: `/trips/${tripId}/todo`,
|
||||
body: data,
|
||||
resource: 'todoItems',
|
||||
tempId,
|
||||
})
|
||||
mutationQueue.flush().catch(() => {})
|
||||
return { item: tempItem }
|
||||
},
|
||||
|
||||
async update(tripId: number | string, id: number, data: Record<string, unknown>): Promise<{ item: TodoItem }> {
|
||||
if (!navigator.onLine) {
|
||||
const existing = await offlineDb.todoItems.get(id)
|
||||
const optimistic: TodoItem = { ...(existing ?? {} as TodoItem), ...(data as Partial<TodoItem>), id }
|
||||
await offlineDb.todoItems.put(optimistic)
|
||||
await mutationQueue.enqueue({
|
||||
id: generateUUID(),
|
||||
tripId: Number(tripId),
|
||||
method: 'PUT',
|
||||
url: `/trips/${tripId}/todo/${id}`,
|
||||
body: data,
|
||||
resource: 'todoItems',
|
||||
})
|
||||
return { item: optimistic }
|
||||
}
|
||||
const result = await todoApi.update(tripId, id, data)
|
||||
offlineDb.todoItems.put(result.item)
|
||||
return result
|
||||
const existing = await offlineDb.todoItems.get(id)
|
||||
const optimistic: TodoItem = { ...(existing ?? {} as TodoItem), ...(data as Partial<TodoItem>), id }
|
||||
await offlineDb.todoItems.put(optimistic)
|
||||
await mutationQueue.enqueue({
|
||||
id: generateUUID(),
|
||||
tripId: Number(tripId),
|
||||
method: 'PUT',
|
||||
url: `/trips/${tripId}/todo/${id}`,
|
||||
body: data,
|
||||
resource: 'todoItems',
|
||||
})
|
||||
mutationQueue.flush().catch(() => {})
|
||||
return { item: optimistic }
|
||||
},
|
||||
|
||||
async delete(tripId: number | string, id: number): Promise<unknown> {
|
||||
if (!navigator.onLine) {
|
||||
await offlineDb.todoItems.delete(id)
|
||||
await mutationQueue.enqueue({
|
||||
id: generateUUID(),
|
||||
tripId: Number(tripId),
|
||||
method: 'DELETE',
|
||||
url: `/trips/${tripId}/todo/${id}`,
|
||||
body: undefined,
|
||||
resource: 'todoItems',
|
||||
entityId: id,
|
||||
})
|
||||
return { success: true }
|
||||
}
|
||||
const result = await todoApi.delete(tripId, id)
|
||||
offlineDb.todoItems.delete(id)
|
||||
return result
|
||||
await offlineDb.todoItems.delete(id)
|
||||
await mutationQueue.enqueue({
|
||||
id: generateUUID(),
|
||||
tripId: Number(tripId),
|
||||
method: 'DELETE',
|
||||
url: `/trips/${tripId}/todo/${id}`,
|
||||
body: undefined,
|
||||
resource: 'todoItems',
|
||||
entityId: id,
|
||||
})
|
||||
mutationQueue.flush().catch(() => {})
|
||||
return { success: true }
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user