import { useEffect, useState } from "react";
const API_BASE_URL = (import.meta.env.VITE_API_BASE_URL || "").replace(/\/$/, "");
const EXAMPLES = [
"Un film d'une elegance rare, chaque scene est precise et emouvante.",
"La mise en scene est confuse et les dialogues sonnent faux du debut a la fin.",
"J'etais sceptique, mais le rythme et les acteurs m'ont completement embarque."
];
function getApiUrl(path) {
return API_BASE_URL ? `${API_BASE_URL}${path}` : path;
}
function clampConfidence(value) {
return Math.max(0, Math.min(100, Number((value * 100).toFixed(1))));
}
export default function App() {
const [text, setText] = useState(EXAMPLES[0]);
const [result, setResult] = useState(null);
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [health, setHealth] = useState({ loading: true, ready: false, message: "" });
useEffect(() => {
let isMounted = true;
async function fetchHealth() {
try {
const response = await fetch(getApiUrl("/health"));
if (!response.ok) {
throw new Error("API indisponible");
}
const data = await response.json();
if (!isMounted) {
return;
}
setHealth({
loading: false,
ready: Boolean(data.model_loaded),
message: data.model_loaded
? "Modele pret pour l'inference"
: "API joignable, modele encore en chargement"
});
} catch (fetchError) {
if (!isMounted) {
return;
}
setHealth({
loading: false,
ready: false,
message: "Impossible de joindre l'API"
});
}
}
fetchHealth();
return () => {
isMounted = false;
};
}, []);
async function handleSubmit(event) {
event.preventDefault();
const trimmed = text.trim();
if (!trimmed) {
setError("Saisis un avis avant de lancer l'analyse.");
setResult(null);
return;
}
setLoading(true);
setError("");
try {
const response = await fetch(getApiUrl("/predict"), {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ text: trimmed })
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.detail || "Prediction impossible");
}
setResult(data);
} catch (requestError) {
setResult(null);
setError(requestError.message || "Prediction impossible");
} finally {
setLoading(false);
}
}
const positive = result?.label === "positif";
const confidence = result ? clampConfidence(result.confidence) : 0;
return (
AvisSense / Sentiment cinema francais
Une interface front qui vend le modele
sans cacher l'incertitude.
Ce front React parle a une API FastAPI hebergee sur Hugging Face et
renvoie un verdict lisible, un niveau de confiance et un etat de service.
{health.loading ? "Verification de l'API..." : health.message}
Voir la doc API
{EXAMPLES.map((example) => (
setText(example)}
>
{example}
))}
Verdict
{result ? (
{positive ? "Positif" : "Negatif"}
) : (
En attente
)}
{error ? {error}
: null}
{result ? (
<>
Confiance du modele
{confidence}%
Positif
{clampConfidence(result.probabilities.positif)}%
Negatif
{clampConfidence(result.probabilities.negatif)}%
Temps de reponse
{result.processing_time_ms} ms
>
) : (
Colle un avis, lance l'analyse, puis utilise la confiance pour juger si le
verdict est net ou ambigu.
)}
);
}