feat: setup iniziale sistema controllo accessi Focolari
Struttura progetto: - Backend mock Python (FastAPI) con API per gestione varchi - Frontend React + TypeScript + Vite + Tailwind CSS - Documentazione e piani di sviluppo Backend (backend-mock/): - API REST: /info-room, /login-validate, /anagrafica, /entry-request - Dati mock: 7 utenti, validatore (999999/focolari) - CORS abilitato, docs OpenAPI automatiche - Configurazione pipenv per ambiente virtuale Frontend (frontend/): - State machine completa per flusso accesso varco - Hook useRFIDScanner per lettura badge (pattern singolo) - Componenti UI: Logo, Button, Input, Modal, UserCard, Timer - Schermate: Loading, Login, ActiveGate, Success/Error Modal - Design system con colori Focolari - Ottimizzato per tablet touch Documentazione (ai-prompts/): - Welcome guide per futuri agenti - Piano sviluppo backend e frontend con checklist DA COMPLETARE: - Hook RFID multi-pattern (US/IT/altri layout tastiera) - Pagina /debug per diagnostica in loco - Logging console strutturato
This commit is contained in:
135
frontend/src/services/api.ts
Normal file
135
frontend/src/services/api.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Focolari Voting System - API Service
|
||||
*/
|
||||
|
||||
import type {
|
||||
RoomInfo,
|
||||
User,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
EntryRequest,
|
||||
EntryResponse
|
||||
} from '../types';
|
||||
|
||||
const API_BASE_URL = 'http://localhost:8000';
|
||||
|
||||
/**
|
||||
* Custom error class for API errors
|
||||
*/
|
||||
export class ApiError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public statusCode: number,
|
||||
public detail?: string
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'ApiError';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic fetch wrapper with error handling
|
||||
*/
|
||||
async function apiFetch<T>(
|
||||
endpoint: string,
|
||||
options?: RequestInit
|
||||
): Promise<T> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new ApiError(
|
||||
errorData.detail || `HTTP Error ${response.status}`,
|
||||
response.status,
|
||||
errorData.detail
|
||||
);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) {
|
||||
throw error;
|
||||
}
|
||||
throw new ApiError(
|
||||
'Errore di connessione al server',
|
||||
0,
|
||||
'Verifica che il server sia attivo'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// API Endpoints
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* GET /info-room
|
||||
* Ottiene le informazioni sulla sala e la riunione
|
||||
*/
|
||||
export async function getRoomInfo(): Promise<RoomInfo> {
|
||||
return apiFetch<RoomInfo>('/info-room');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /login-validate
|
||||
* Autentica il validatore con badge e password
|
||||
*/
|
||||
export async function loginValidator(
|
||||
badge: string,
|
||||
password: string
|
||||
): Promise<LoginResponse> {
|
||||
const payload: LoginRequest = { badge, password };
|
||||
return apiFetch<LoginResponse>('/login-validate', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /anagrafica/{badge_code}
|
||||
* Ottiene i dati anagrafici di un utente tramite badge
|
||||
*/
|
||||
export async function getUserByBadge(badgeCode: string): Promise<User> {
|
||||
return apiFetch<User>(`/anagrafica/${encodeURIComponent(badgeCode)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /entry-request
|
||||
* Registra l'ingresso di un utente
|
||||
*/
|
||||
export async function requestEntry(
|
||||
userBadge: string,
|
||||
validatorPassword: string
|
||||
): Promise<EntryResponse> {
|
||||
const payload: EntryRequest = {
|
||||
user_badge: userBadge,
|
||||
validator_password: validatorPassword,
|
||||
};
|
||||
return apiFetch<EntryResponse>('/entry-request', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Utility Functions
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Check if the API server is reachable
|
||||
*/
|
||||
export async function checkServerHealth(): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/`);
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user