73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
/**
|
|
* The fleet. One file per domain area, exposing vue-query hooks: views never
|
|
* call `fetch` themselves.
|
|
*/
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
|
import { HttpStatus } from 'http-status-ts'
|
|
|
|
import type { Bike, BikeStatus, FleetLive } from '@/utils/types'
|
|
import { getClient } from './client'
|
|
|
|
export const BIKES_KEY = ['bikes']
|
|
export const FLEET_LIVE_KEY = ['bikes', 'live']
|
|
|
|
/** How often the platform's picture of the fleet is asked for again */
|
|
const LIVE_REFRESH_MS = 30 * 1000
|
|
|
|
export function useBikes() {
|
|
return useQuery({
|
|
queryKey: BIKES_KEY,
|
|
staleTime: 60 * 1000,
|
|
queryFn: async () => {
|
|
const { data, response } = await getClient().GET('/api/bikes')
|
|
if (response.status === HttpStatus.OK) {
|
|
return data
|
|
}
|
|
throw new Error(`Unexpected status code received: ${response.status}`)
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* What Linka Go last said about the fleet: which bike is locked, which is being
|
|
* ridden, and by whom.
|
|
*
|
|
* The backend answers from a snapshot its own ticker refreshes, so this is a
|
|
* cheap call: polling it costs the platform nothing. Admin only.
|
|
*/
|
|
export function useFleetLive() {
|
|
return useQuery({
|
|
queryKey: FLEET_LIVE_KEY,
|
|
refetchInterval: LIVE_REFRESH_MS,
|
|
queryFn: async (): Promise<FleetLive> => {
|
|
const { data, response } = await getClient().GET('/api/bikes/live')
|
|
if (response.status === HttpStatus.OK && data) {
|
|
return data
|
|
}
|
|
throw new Error(`Unexpected status code received: ${response.status}`)
|
|
},
|
|
})
|
|
}
|
|
|
|
/** Takes a bike in or out of the fleet. Admin only, the backend enforces it. */
|
|
export function useSetBikeStatus() {
|
|
const queryClient = useQueryClient()
|
|
return useMutation({
|
|
retry: 0,
|
|
mutationFn: async ({ id, status }: { id: number; status: BikeStatus }) => {
|
|
// A refusal from the platform comes back as a 502, which the client
|
|
// turns into an `ApiError`: the card must not pretend the lock heard
|
|
// about a change it refused
|
|
await getClient().PUT('/api/bikes/{id}/status', {
|
|
params: { path: { id } },
|
|
body: { status },
|
|
})
|
|
return { id, status }
|
|
},
|
|
onSuccess: ({ id, status }) => {
|
|
queryClient.setQueryData<Bike[]>(BIKES_KEY, (bikes) =>
|
|
bikes?.map((bike) => (bike.id === id ? { ...bike, status } : bike)),
|
|
)
|
|
},
|
|
})
|
|
}
|