Files
Yvv b30e54a8f7 Initial commit: SejeteralO water tarification platform
Full-stack app for participatory water pricing using Bezier curves.
- Backend: FastAPI + SQLAlchemy + SQLite with JWT auth
- Frontend: Nuxt 4 + TypeScript with interactive SVG editor
- Math engine: cubic Bezier tarification with Cardano solver
- Admin: commune management, household import, vote monitoring, CMS
- Citizen: interactive curve editor, vote submission
- Docker-compose deployment ready

Includes fixes for:
- Impact table snake_case/camelCase property mismatch
- CMS content backend API + frontend editor (was stub)
- Admin route protection middleware
- Public content display on commune page
- Vote confirmation page link fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 15:26:02 +01:00

68 lines
1.7 KiB
TypeScript

/**
* Composable for API calls to the FastAPI backend.
*/
export function useApi() {
const config = useRuntimeConfig()
const baseURL = config.public.apiBase as string
function getToken(): string | null {
if (import.meta.client) {
return localStorage.getItem('sejeteralo_token')
}
return null
}
async function apiFetch<T>(
path: string,
options: RequestInit = {},
): Promise<T> {
const headers: Record<string, string> = {
...(options.headers as Record<string, string> || {}),
}
const token = getToken()
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
if (options.body && !(options.body instanceof FormData)) {
headers['Content-Type'] = 'application/json'
}
let response: Response
try {
response = await fetch(`${baseURL}${path}`, {
...options,
headers,
})
} catch (err) {
throw new Error(`Impossible de contacter le serveur (${baseURL}). Vérifiez que le backend est lancé.`)
}
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: response.statusText }))
throw new Error(error.detail || `Erreur API ${response.status}`)
}
const text = await response.text()
if (!text) return {} as T
return JSON.parse(text)
}
return {
get: <T>(path: string) => apiFetch<T>(path),
post: <T>(path: string, body?: unknown) =>
apiFetch<T>(path, {
method: 'POST',
body: body instanceof FormData ? body : JSON.stringify(body),
}),
put: <T>(path: string, body?: unknown) =>
apiFetch<T>(path, {
method: 'PUT',
body: JSON.stringify(body),
}),
delete: <T>(path: string) =>
apiFetch<T>(path, { method: 'DELETE' }),
}
}