V1 Fonctionnelle (pas de secu de l'api, ni front quali)
This commit is contained in:
@@ -105,10 +105,10 @@
|
||||
class="bg-white rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow cursor-pointer"
|
||||
@click="$router.push(`/texte/${text.id}`)"
|
||||
>
|
||||
<h3 class="font-semibold mb-2">{{ text.titre_fr }}</h3>
|
||||
<p class="text-sm mb-3" style="color: #6b7280">{{ text.titre_pt }}</p>
|
||||
<h3 class="font-semibold mb-2">{{ text.metadata.titre_fr }}</h3>
|
||||
<p class="text-sm mb-3" style="color: #6b7280">{{ text.metadata.titre_pt }}</p>
|
||||
<div class="flex justify-between items-center text-xs text-gray-500">
|
||||
<span>{{ text.auteur }}</span>
|
||||
<span>{{ text.metadata.auteur }}</span>
|
||||
<span v-if="text.hasAudio" class="flex items-center">
|
||||
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v6.114A4.369 4.369 0 005 11c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.369 4.369 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z"/>
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-50 py-8">
|
||||
<div class="max-w-4xl mx-auto px-4">
|
||||
<!-- En-tête -->
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-4xl font-bold text-black mb-4">Texte au Hasard</h1>
|
||||
<p class="mb-6" style="color: #6b7280">
|
||||
Découvrez un texte choisi aléatoirement dans notre collection
|
||||
</p>
|
||||
<button
|
||||
@click="loadRandomText"
|
||||
:disabled="loading"
|
||||
class="btn-primary"
|
||||
>
|
||||
<span v-if="loading" class="flex items-center">
|
||||
<div class="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
||||
Chargement...
|
||||
</span>
|
||||
<span v-else>
|
||||
Nouveau texte au hasard
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Contenu du texte -->
|
||||
<div v-if="randomText && !loading">
|
||||
<!-- Navigation vers le texte complet -->
|
||||
<div class="text-center mb-6">
|
||||
<router-link
|
||||
:to="`/texte/${randomText.id}`"
|
||||
class="btn-secondary"
|
||||
>
|
||||
Voir le texte complet
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<!-- Affichage du texte -->
|
||||
<TextDisplay
|
||||
:patois-text="randomText.patoisText"
|
||||
:french-text="randomText.frenchText"
|
||||
:metadata="randomText.metadata"
|
||||
/>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="mt-8 flex justify-center space-x-4">
|
||||
<button
|
||||
@click="loadRandomText"
|
||||
class="btn-primary"
|
||||
>
|
||||
Autre texte au hasard
|
||||
</button>
|
||||
<router-link
|
||||
:to="`/texte/${randomText.id}`"
|
||||
class="btn-secondary"
|
||||
>
|
||||
Lire avec audio
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chargement -->
|
||||
<div v-if="loading" class="flex justify-center items-center py-20">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-black"></div>
|
||||
</div>
|
||||
|
||||
<!-- Erreur -->
|
||||
<div v-if="error" class="text-center py-20">
|
||||
<svg class="mx-auto h-12 w-12 text-red-400 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"/>
|
||||
</svg>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Erreur</h3>
|
||||
<p class="text-gray-500 mb-4">{{ error }}</p>
|
||||
<button
|
||||
@click="loadRandomText"
|
||||
class="btn-primary"
|
||||
>
|
||||
Réessayer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Message si aucun texte -->
|
||||
<div v-if="!randomText && !loading && !error" class="text-center py-20">
|
||||
<svg class="mx-auto h-12 w-12 text-gray-400 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucun texte disponible</h3>
|
||||
<p class="text-gray-500">La collection est vide pour le moment.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { textService } from '../services/textService.js'
|
||||
import TextDisplay from '../components/TextDisplay.vue'
|
||||
|
||||
export default {
|
||||
name: 'Random',
|
||||
components: {
|
||||
TextDisplay
|
||||
},
|
||||
setup() {
|
||||
const randomText = ref(null)
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
|
||||
const loadRandomText = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const text = await textService.getRandomText()
|
||||
randomText.value = text
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
console.error('Erreur lors du chargement du texte aléatoire:', err)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadRandomText()
|
||||
})
|
||||
|
||||
return {
|
||||
randomText,
|
||||
loading,
|
||||
error,
|
||||
loadRandomText
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -4,7 +4,7 @@
|
||||
<!-- Navigation de retour -->
|
||||
<div class="mb-6">
|
||||
<button
|
||||
@click="$router.go(-1)"
|
||||
@click="$router.push('/textes')"
|
||||
class="flex items-center hover:text-black transition-colors" style="color: #6b7280"
|
||||
>
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -17,10 +17,10 @@
|
||||
<!-- Titre du texte -->
|
||||
<div v-if="textData" class="text-center mb-8">
|
||||
<h1 class="text-4xl font-bold text-black mb-2">
|
||||
{{ textData.metadata.titre_fr }}
|
||||
{{ textData.metadata.titre_pt }}
|
||||
</h1>
|
||||
<h2 class="text-2xl" style="color: #6b7280">
|
||||
{{ textData.metadata.titre_pt }}
|
||||
{{ textData.metadata.titre_fr }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -107,8 +107,8 @@ export default {
|
||||
|
||||
const audioSrc = computed(() => {
|
||||
if (!textData.value || !textData.value.hasAudio) return null
|
||||
// Le chemin vers le fichier audio sera construit dynamiquement
|
||||
return `/texts/${props.id}/audio.mp3`
|
||||
// Utiliser l'URL de l'API backend pour le fichier audio
|
||||
return textService.getAudioUrl(props.id)
|
||||
})
|
||||
|
||||
const loadText = async () => {
|
||||
|
||||
@@ -200,38 +200,8 @@ export default {
|
||||
})
|
||||
|
||||
const filteredTexts = computed(() => {
|
||||
let filtered = allTexts.value
|
||||
|
||||
// Recherche textuelle
|
||||
if (searchQuery.value.trim()) {
|
||||
const query = searchQuery.value.toLowerCase().trim()
|
||||
filtered = filtered.filter(text => {
|
||||
return (
|
||||
text.metadata.titre_fr?.toLowerCase().includes(query) ||
|
||||
text.metadata.titre_pt?.toLowerCase().includes(query) ||
|
||||
text.metadata.auteur?.toLowerCase().includes(query) ||
|
||||
text.frenchText?.toLowerCase().includes(query) ||
|
||||
text.patoisText?.toLowerCase().includes(query)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Filtre par catégorie
|
||||
if (selectedCategory.value) {
|
||||
filtered = filtered.filter(text => text.metadata.categorie === selectedCategory.value)
|
||||
}
|
||||
|
||||
// Filtre par difficulté
|
||||
if (selectedDifficulty.value) {
|
||||
filtered = filtered.filter(text => text.metadata.difficulte === selectedDifficulty.value)
|
||||
}
|
||||
|
||||
// Filtre audio seulement
|
||||
if (onlyWithAudio.value) {
|
||||
filtered = filtered.filter(text => text.hasAudio)
|
||||
}
|
||||
|
||||
return filtered
|
||||
// Ne pas filtrer localement - l'API backend s'en charge
|
||||
return allTexts.value
|
||||
})
|
||||
|
||||
const totalPages = computed(() => {
|
||||
@@ -246,23 +216,39 @@ export default {
|
||||
|
||||
const loadTexts = async () => {
|
||||
try {
|
||||
const texts = await textService.loadAllTexts()
|
||||
// Utiliser l'API de recherche avec les filtres actuels
|
||||
const texts = await textService.searchTexts(searchQuery.value, {
|
||||
category: selectedCategory.value,
|
||||
difficulty: selectedDifficulty.value,
|
||||
onlyWithAudio: onlyWithAudio.value
|
||||
})
|
||||
allTexts.value = texts
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du chargement des textes:', error)
|
||||
// Fallback : charger tous les textes si la recherche échoue
|
||||
try {
|
||||
const allTextsData = await textService.loadAllTexts()
|
||||
allTexts.value = allTextsData
|
||||
} catch (fallbackError) {
|
||||
console.error('Erreur fallback:', fallbackError)
|
||||
allTexts.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const performSearch = () => {
|
||||
const performSearch = async () => {
|
||||
currentPage.value = 1
|
||||
await loadTexts()
|
||||
}
|
||||
|
||||
const clearFilters = () => {
|
||||
const clearFilters = async () => {
|
||||
searchQuery.value = ''
|
||||
selectedCategory.value = ''
|
||||
selectedDifficulty.value = ''
|
||||
onlyWithAudio.value = false
|
||||
currentPage.value = 1
|
||||
// IMPORTANT: Recharger via l'API après avoir vidé les filtres
|
||||
await loadTexts()
|
||||
}
|
||||
|
||||
const getTextPreview = (text) => {
|
||||
|
||||
Reference in New Issue
Block a user