cargagep-v2/frontend/src/services/api/bikes.ts
Antoine Pelletier 2d6ef8de6c feat: add linka
2026-08-25 12:10:53 +02:00

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