/** * 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 => { 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(BIKES_KEY, (bikes) => bikes?.map((bike) => (bike.id === id ? { ...bike, status } : bike)), ) }, }) }