Scrap-Dji / frontend_example.html
joel
Initial deployment: Scrap-Dji with API
dfdddb1
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Scrap-Dji - Exemple Frontend</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 20px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
h1 {
color: #333;
margin-bottom: 10px;
font-size: 2.5em;
}
.subtitle {
color: #666;
margin-bottom: 30px;
font-size: 1.1em;
}
.search-box {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
input[type="text"] {
flex: 1;
padding: 15px 20px;
border: 2px solid #ddd;
border-radius: 10px;
font-size: 16px;
transition: border-color 0.3s;
}
input[type="text"]:focus {
outline: none;
border-color: #667eea;
}
button {
padding: 15px 30px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 10px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s;
}
button:hover {
transform: translateY(-2px);
}
button:active {
transform: translateY(0);
}
.filters {
display: flex;
gap: 15px;
margin-bottom: 30px;
flex-wrap: wrap;
}
select,
.checkbox-label {
padding: 10px 15px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 14px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
}
.stat-value {
font-size: 2em;
font-weight: bold;
margin-bottom: 5px;
}
.stat-label {
font-size: 0.9em;
opacity: 0.9;
}
.results {
margin-top: 30px;
}
.result-item {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-bottom: 15px;
border-left: 4px solid #667eea;
transition: transform 0.2s;
}
.result-item:hover {
transform: translateX(5px);
}
.result-title {
font-size: 1.3em;
color: #333;
margin-bottom: 10px;
font-weight: bold;
}
.result-meta {
color: #666;
font-size: 0.9em;
margin-bottom: 10px;
}
.result-excerpt {
color: #444;
line-height: 1.6;
margin-bottom: 10px;
}
.result-link {
color: #667eea;
text-decoration: none;
font-weight: bold;
}
.result-link:hover {
text-decoration: underline;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.error {
background: #fee;
color: #c33;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.badge {
display: inline-block;
padding: 4px 10px;
background: #667eea;
color: white;
border-radius: 12px;
font-size: 0.8em;
margin-right: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1>🌍 Scrap-Dji</h1>
<p class="subtitle">Base de Connaissance Panafricaine - Recherche Intelligente</p>
<!-- Statistiques -->
<div class="stats" id="stats">
<div class="stat-card">
<div class="stat-value" id="total-docs">-</div>
<div class="stat-label">Documents</div>
</div>
<div class="stat-card">
<div class="stat-value" id="total-pays">-</div>
<div class="stat-label">Pays</div>
</div>
<div class="stat-card">
<div class="stat-value" id="total-sources">-</div>
<div class="stat-label">Sources</div>
</div>
</div>
<!-- Recherche -->
<div class="search-box">
<input type="text" id="search-input"
placeholder="Recherchez des articles (ex: économie togo, politique bénin...)"
onkeypress="if(event.key==='Enter') search()">
<button onclick="search()">🔍 Rechercher</button>
</div>
<!-- Filtres -->
<div class="filters">
<select id="pays-filter">
<option value="">Tous les pays</option>
<option value="Togo">Togo</option>
<option value="Bénin">Bénin</option>
<option value="Afrique">Afrique</option>
</select>
<select id="langue-filter">
<option value="">Toutes les langues</option>
<option value="fr">Français</option>
<option value="en">English</option>
</select>
<label class="checkbox-label">
<input type="checkbox" id="fuzzy-checkbox" checked>
Recherche permissive (tolérance aux fautes)
</label>
</div>
<!-- Résultats -->
<div class="results" id="results"></div>
</div>
<script>
// ⚠️ REMPLACEZ PAR L'URL DE VOTRE SPACE HUGGING FACE
const API_URL = 'http://localhost:7860'; // Pour test local
// const API_URL = 'https://VOTRE_USERNAME-scrap-dji.hf.space'; // Pour production
// Charger les statistiques au démarrage
loadStats();
async function loadStats() {
try {
const response = await fetch(`${API_URL}/api/stats`);
const data = await response.json();
document.getElementById('total-docs').textContent = data.total_documents;
document.getElementById('total-pays').textContent = Object.keys(data.pays).length;
document.getElementById('total-sources').textContent = Object.keys(data.sources).length;
} catch (error) {
console.error('Erreur chargement stats:', error);
}
}
async function search() {
const query = document.getElementById('search-input').value.trim();
if (!query) {
alert('Veuillez entrer une requête de recherche');
return;
}
const pays = document.getElementById('pays-filter').value;
const langue = document.getElementById('langue-filter').value;
const fuzzy = document.getElementById('fuzzy-checkbox').checked;
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = '<div class="loading">🔍 Recherche en cours...</div>';
try {
const response = await fetch(`${API_URL}/api/search`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: query,
pays: pays || null,
langue: langue || null,
limit: 20,
fuzzy: fuzzy
})
});
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`);
}
const data = await response.json();
displayResults(data);
} catch (error) {
resultsDiv.innerHTML = `
<div class="error">
❌ Erreur lors de la recherche: ${error.message}
<br><br>
Vérifiez que l'API est accessible à l'adresse: ${API_URL}
</div>
`;
}
}
function displayResults(data) {
const resultsDiv = document.getElementById('results');
if (data.total === 0) {
resultsDiv.innerHTML = `
<div class="loading">
❌ Aucun résultat trouvé pour "${data.query}"
<br><br>
Essayez avec d'autres mots-clés ou activez la recherche permissive
</div>
`;
return;
}
let html = `
<h2>📊 ${data.total} résultat(s) trouvé(s) en ${data.execution_time_ms}ms</h2>
<p style="color: #666; margin-bottom: 20px;">Requête: "${data.query}"</p>
`;
data.results.forEach((result, index) => {
const titre = result.titre || 'Sans titre';
const texte = result.texte || '';
const excerpt = texte.substring(0, 200) + (texte.length > 200 ? '...' : '');
const pays = result.pays || 'Inconnu';
const langue = result.langue || 'fr';
const source = result.source_url || '#';
const score = result._score || 0;
html += `
<div class="result-item">
<div class="result-title">${index + 1}. ${titre}</div>
<div class="result-meta">
<span class="badge">${pays}</span>
<span class="badge">${langue.toUpperCase()}</span>
<span class="badge">Score: ${score.toFixed(1)}</span>
</div>
<div class="result-excerpt">${excerpt}</div>
<a href="${source}" target="_blank" class="result-link">🔗 Lire l'article complet</a>
</div>
`;
});
resultsDiv.innerHTML = html;
}
</script>
</body>
</html>