148 lines
5.0 KiB
Vue
148 lines
5.0 KiB
Vue
<template>
|
|
<section
|
|
class="rounded-2xl p-6"
|
|
style="background: var(--nav-bg-alt); border: 1px solid rgba(26,34,56,0.1);"
|
|
>
|
|
<h3 class="font-semibold mb-4" style="color: var(--nav-text);">Ajouter un commentaire</h3>
|
|
|
|
<!-- Succès -->
|
|
<div
|
|
v-if="success"
|
|
class="rounded-xl p-4 text-sm"
|
|
style="background: var(--nav-surface); color: var(--nav-text);"
|
|
>
|
|
<strong>Merci !</strong>
|
|
{{ successMessage }}
|
|
</div>
|
|
|
|
<!-- Formulaire -->
|
|
<form v-else @submit.prevent="submit" class="space-y-4" novalidate>
|
|
|
|
<!-- Commentaire -->
|
|
<div>
|
|
<label
|
|
for="comment-contenu"
|
|
class="block text-sm font-medium mb-1"
|
|
style="color: var(--nav-text);"
|
|
>
|
|
Commentaire <span aria-hidden="true">*</span>
|
|
</label>
|
|
<textarea
|
|
id="comment-contenu"
|
|
v-model="form.contenu"
|
|
required
|
|
rows="4"
|
|
minlength="10"
|
|
maxlength="500"
|
|
placeholder="Partage ton expérience avec cette organisation…"
|
|
class="w-full px-3 py-2 rounded-lg text-sm resize-none focus:outline-none focus:ring-2"
|
|
style="background: var(--nav-surface); color: var(--nav-text); border: 1px solid rgba(26,34,56,0.2); focus-ring-color: var(--nav-accent);"
|
|
:class="{ 'border-red-400': errors.contenu }"
|
|
/>
|
|
<div class="flex justify-between mt-1">
|
|
<span v-if="errors.contenu" class="text-xs text-red-500">{{ errors.contenu }}</span>
|
|
<span class="text-xs ml-auto" style="color: var(--nav-text-muted);">{{ form.contenu.length }}/500</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pseudo (optionnel) -->
|
|
<div>
|
|
<label
|
|
for="comment-pseudo"
|
|
class="block text-sm font-medium mb-1"
|
|
style="color: var(--nav-text);"
|
|
>Pseudo <span class="font-normal" style="color: var(--nav-text-muted);">(optionnel)</span></label>
|
|
<input
|
|
id="comment-pseudo"
|
|
v-model="form.auteur_pseudo"
|
|
type="text"
|
|
maxlength="80"
|
|
placeholder="Marie A."
|
|
class="w-full px-3 py-2 rounded-lg text-sm focus:outline-none focus:ring-2"
|
|
style="background: var(--nav-surface); color: var(--nav-text); border: 1px solid rgba(26,34,56,0.2);"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Note modération -->
|
|
<p class="text-xs" style="color: var(--nav-text-muted);">
|
|
Vos commentaires sont filtrés par une IA avant publication.
|
|
Les critiques professionnelles factuelles sont les bienvenues.
|
|
</p>
|
|
|
|
<!-- Erreur serveur -->
|
|
<p v-if="serverError" class="text-xs text-red-500">{{ serverError }}</p>
|
|
|
|
<!-- Bouton -->
|
|
<button
|
|
type="submit"
|
|
:disabled="submitting"
|
|
class="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg text-sm font-medium transition-colors disabled:opacity-50"
|
|
style="background: var(--nav-primary); color: var(--nav-text-on-primary);"
|
|
@mouseenter="(e: MouseEvent) => { if (!submitting) (e.currentTarget as HTMLElement).style.background = 'rgba(26,34,56,0.75)' }"
|
|
@mouseleave="(e: MouseEvent) => { if (!submitting) (e.currentTarget as HTMLElement).style.background = 'var(--nav-primary)' }"
|
|
>
|
|
<svg v-if="submitting" class="animate-spin" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
|
|
</svg>
|
|
{{ submitting ? 'Envoi…' : 'Envoyer' }}
|
|
</button>
|
|
|
|
</form>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const props = defineProps<{ orgId: number }>()
|
|
const emit = defineEmits<{ submitted: [] }>()
|
|
|
|
const form = reactive({
|
|
contenu: '',
|
|
auteur_pseudo: '',
|
|
})
|
|
|
|
const submitting = ref(false)
|
|
const success = ref(false)
|
|
const successMessage = ref('')
|
|
const serverError = ref('')
|
|
const errors = reactive({ contenu: '' })
|
|
|
|
function validate(): boolean {
|
|
errors.contenu = ''
|
|
const c = form.contenu.trim()
|
|
if (!c) { errors.contenu = 'Le commentaire est requis.'; return false }
|
|
if (c.length < 10) { errors.contenu = 'Minimum 10 caractères.'; return false }
|
|
if (c.length > 500) { errors.contenu = 'Maximum 500 caractères.'; return false }
|
|
return true
|
|
}
|
|
|
|
async function submit() {
|
|
serverError.value = ''
|
|
if (!validate()) return
|
|
|
|
submitting.value = true
|
|
try {
|
|
const res = await $fetch<{ ok: boolean; status: string; message: string }>('/api/comment', {
|
|
method: 'POST',
|
|
body: {
|
|
orga_id: props.orgId,
|
|
contenu: form.contenu.trim(),
|
|
auteur_pseudo: form.auteur_pseudo.trim() || undefined,
|
|
},
|
|
})
|
|
|
|
success.value = true
|
|
successMessage.value = res.message || 'Commentaire reçu.'
|
|
emit('submitted')
|
|
} catch (err: any) {
|
|
const status = err?.response?.status
|
|
if (status === 429) {
|
|
serverError.value = 'Trop de commentaires aujourd\'hui. Réessaie demain.'
|
|
} else {
|
|
serverError.value = 'Erreur lors de l\'envoi. Réessaie dans un moment.'
|
|
}
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
</script>
|