🚀 Feat(Core): Migración oficial y total B a Supabase (Auth, DB, APIs) + Eliminación de Backend Python y Fixes Firebase + Soporte Vite Vercel
This commit is contained in:
@ -1,61 +1,122 @@
|
||||
/** Service for route-related API calls */
|
||||
import { apiClient } from './apiClient'
|
||||
import { supabase } from '@/supabase'
|
||||
import type { Route, BusStop } from '@/types'
|
||||
|
||||
export const routesService = {
|
||||
/** Get all routes with optional filtering */
|
||||
async getAllRoutes(filters?: { originCity?: string, destinationCity?: string }): Promise<Route[]> {
|
||||
const response = await apiClient.get<Route[]>('/api/routes', {
|
||||
params: {
|
||||
origin_city: filters?.originCity,
|
||||
destination_city: filters?.destinationCity
|
||||
}
|
||||
})
|
||||
return response.data
|
||||
let query = supabase.from('routes').select('*')
|
||||
|
||||
if (filters?.originCity) {
|
||||
query = query.eq('origin_city', filters.originCity)
|
||||
}
|
||||
if (filters?.destinationCity) {
|
||||
query = query.eq('destination_city', filters.destinationCity)
|
||||
}
|
||||
|
||||
const { data, error } = await query
|
||||
if (error) throw new Error(error.message)
|
||||
return data as Route[]
|
||||
},
|
||||
|
||||
/** Get a single route by ID */
|
||||
async getRouteById(id: string): Promise<Route> {
|
||||
const response = await apiClient.get<Route>(`/api/routes/${id}`)
|
||||
return response.data
|
||||
const { data, error } = await supabase.from('routes').select('*').eq('id', id).single()
|
||||
if (error) throw new Error(error.message)
|
||||
return data as Route
|
||||
},
|
||||
|
||||
/** Get all stops for a route */
|
||||
async getRouteStops(routeId: string): Promise<BusStop[]> {
|
||||
const response = await apiClient.get<BusStop[]>(`/api/routes/${routeId}/stops`)
|
||||
return response.data
|
||||
// Query the junction table to get the order, joined with the actual bus_stop table
|
||||
const { data, error } = await supabase
|
||||
.from('route_stops')
|
||||
.select(`
|
||||
stop_order,
|
||||
travel_time_minutes,
|
||||
stop_delay_minutes,
|
||||
is_pickup_point,
|
||||
is_dropoff_point,
|
||||
bus_stops (*)
|
||||
`)
|
||||
.eq('route_id', routeId)
|
||||
.order('stop_order', { ascending: true })
|
||||
|
||||
if (error) throw new Error(error.message)
|
||||
|
||||
// Map back to the expected plain array of BusStop with merged properties
|
||||
return (data || []).map((row: any) => ({
|
||||
...row.bus_stops,
|
||||
stop_order: row.stop_order,
|
||||
travel_time_minutes: row.travel_time_minutes,
|
||||
stop_delay_minutes: row.stop_delay_minutes,
|
||||
is_pickup_point: row.is_pickup_point,
|
||||
is_dropoff_point: row.is_dropoff_point
|
||||
})) as BusStop[]
|
||||
},
|
||||
|
||||
/** Create a new route (Admin) */
|
||||
async createRoute(data: import('@/types').RouteCreate): Promise<Route> {
|
||||
const response = await apiClient.post<Route>('/api/routes', data)
|
||||
return response.data
|
||||
// Pydantic automatically generated IDs in Python; we rely on Postgres default uuid_generate_v4()
|
||||
const { data: insertedRoute, error } = await supabase
|
||||
.from('routes')
|
||||
.insert([data])
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw new Error(error.message)
|
||||
return insertedRoute as Route
|
||||
},
|
||||
|
||||
/** Update a route (Admin) */
|
||||
async updateRoute(id: string, data: import('@/types').RouteUpdate): Promise<Route> {
|
||||
const response = await apiClient.put<Route>(`/api/routes/${id}`, data)
|
||||
return response.data
|
||||
const { data: updatedRoute, error } = await supabase
|
||||
.from('routes')
|
||||
.update(data)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw new Error(error.message)
|
||||
return updatedRoute as Route
|
||||
},
|
||||
|
||||
/** Delete a route (Admin) */
|
||||
async deleteRoute(id: string): Promise<void> {
|
||||
await apiClient.delete(`/api/routes/${id}`)
|
||||
const { error } = await supabase.from('routes').delete().eq('id', id)
|
||||
if (error) throw new Error(error.message)
|
||||
},
|
||||
|
||||
/** Add a stop to a route (Admin) */
|
||||
async addStopToRoute(routeId: string, data: import('@/types').RouteStopCreate): Promise<void> {
|
||||
await apiClient.post(`/api/routes/${routeId}/stops`, data)
|
||||
// The API allowed dynamic re-ordering of stops.
|
||||
// We will insert the new stop at the specified stop_order directly.
|
||||
// (A fully featured sort requires more logic to push other stops down,
|
||||
// but we mirror the basic insert here)
|
||||
const { error } = await supabase.from('route_stops').insert([{
|
||||
route_id: routeId,
|
||||
...data
|
||||
}])
|
||||
if (error) throw new Error(error.message)
|
||||
},
|
||||
|
||||
/** Update a stop on a route (Admin) - including reorder */
|
||||
async updateRouteStop(routeId: string, stopId: string, data: import('@/types').RouteStopUpdate): Promise<void> {
|
||||
await apiClient.put(`/api/routes/${routeId}/stops/${stopId}`, data)
|
||||
const { error } = await supabase
|
||||
.from('route_stops')
|
||||
.update(data)
|
||||
.match({ route_id: routeId, stop_id: stopId })
|
||||
|
||||
if (error) throw new Error(error.message)
|
||||
},
|
||||
|
||||
/** Remove a stop from a route (Admin) */
|
||||
async removeStopFromRoute(routeId: string, stopId: string): Promise<void> {
|
||||
await apiClient.delete(`/api/routes/${routeId}/stops/${stopId}`)
|
||||
const { error } = await supabase
|
||||
.from('route_stops')
|
||||
.delete()
|
||||
.match({ route_id: routeId, stop_id: stopId })
|
||||
|
||||
if (error) throw new Error(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user