- api/auth.ts: OAuth2 password flow via form-urlencoded POST /api/auth/login (not mechanic prefix)
- api/me.ts: GET /api/v1/mechanic/me with Me interface
- api/vehicles.ts: listVehicles (unwraps {items:[]}), getVehicle with VehicleDetail + InspectionSummary
- api/inspections.ts: createInspection returning full InspectionDetail; 9-value InspectionType union
- Login.tsx: full form with Логин/Пароль labels, useMutation, toast feedback, navigate on success
- Home.tsx: vehicle list with search, /me header with user name, logout, TanStack Query
- VehicleCard.tsx: vehicle info card, all 9 inspection type buttons (ru labels), history list
Co-Authored-By: claude-flow <ruv@ruv.net>
33 lines
815 B
TypeScript
33 lines
815 B
TypeScript
import { useAuth } from "@/store/auth";
|
|
|
|
export interface LoginInput {
|
|
username: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface LoginOutput {
|
|
access_token: string;
|
|
token_type: string;
|
|
}
|
|
|
|
export async function login(input: LoginInput): Promise<LoginOutput> {
|
|
// OAuth2 password flow — form-urlencoded, NOT JSON, NOT under /api/v1/mechanic/
|
|
const body = new URLSearchParams();
|
|
body.set("username", input.username);
|
|
body.set("password", input.password);
|
|
|
|
const res = await fetch("/api/auth/login", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body,
|
|
});
|
|
if (!res.ok) {
|
|
throw new Error(`Login failed: ${res.status}`);
|
|
}
|
|
return res.json() as Promise<LoginOutput>;
|
|
}
|
|
|
|
export function logout(): void {
|
|
useAuth.getState().setToken(null);
|
|
}
|