Page publique (/p/[slug]): - Design éditorial moderne (Source Serif 4 + Inter, couleurs Momento) - KaTeX rendu côté serveur (plus de 3184089 en brut) - Callouts colorés, toggles, colonnes, code blocks rendus correctement - Barre sticky avec logo + bouton Signaler - Temps de lecture estimé - Footer Momento - Responsive Modération: - content-moderation.service.ts — IA classe safe/flagged/blocked - Sera appelée automatiquement à la publication Signalement: - Page /p/[slug]/report — formulaire de signalement public - API /api/notes/report — stocke notification au propriétaire + admins - 8 motifs: illegal, haine, violence, sexuel, harcèlement, copyright, spam, autre i18n: FR/EN
82 lines
4.4 KiB
TypeScript
82 lines
4.4 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { Flag, X, Loader2, Check } from 'lucide-react'
|
|
|
|
export default function ReportPage({ params }: { params: { slug: string } }) {
|
|
const [reason, setReason] = useState('')
|
|
const [details, setDetails] = useState('')
|
|
const [submitting, setSubmitting] = useState(false)
|
|
const [done, setDone] = useState(false)
|
|
|
|
const handleSubmit = async () => {
|
|
setSubmitting(true)
|
|
try {
|
|
await fetch('/api/notes/report', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ slug: params.slug, reason, details }),
|
|
})
|
|
setDone(true)
|
|
} catch {}
|
|
finally { setSubmitting(false) }
|
|
}
|
|
|
|
return (
|
|
<div style={{ minHeight: '100vh', background: '#FAF9F5', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px' }}>
|
|
<div style={{ maxWidth: '440px', width: '100%', background: 'white', borderRadius: '16px', border: '1px solid rgba(0,0,0,0.08)', padding: '32px' }}>
|
|
{done ? (
|
|
<div style={{ textAlign: 'center' }}>
|
|
<div style={{ width: '48px', height: '48px', borderRadius: '50%', background: '#f0fdf4', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 16px' }}>
|
|
<Check size={24} style={{ color: '#22c55e' }} />
|
|
</div>
|
|
<h2 style={{ fontFamily: 'Georgia, serif', fontSize: '18px', fontWeight: 600, margin: '0 0 8px' }}>Signalement envoyé</h2>
|
|
<p style={{ fontSize: '14px', color: '#666', margin: 0 }}>Merci. Notre équipe va examiner ce contenu dans les plus brefs délais.</p>
|
|
</div>
|
|
) : (
|
|
<>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '20px' }}>
|
|
<div style={{ width: '32px', height: '32px', borderRadius: '8px', background: '#fef2f2', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<Flag size={16} style={{ color: '#ef4444' }} />
|
|
</div>
|
|
<h2 style={{ fontFamily: 'Georgia, serif', fontSize: '17px', fontWeight: 600, margin: 0 }}>Signaler ce contenu</h2>
|
|
</div>
|
|
|
|
<div style={{ marginBottom: '16px' }}>
|
|
<label style={{ fontSize: '12px', fontWeight: 600, color: '#666', textTransform: 'uppercase', letterSpacing: '0.05em', display: 'block', marginBottom: '8px' }}>Motif</label>
|
|
<select value={reason} onChange={e => setReason(e.target.value)} style={{ width: '100%', padding: '10px 12px', borderRadius: '8px', border: '1px solid #ddd', fontSize: '14px', outline: 'none', background: 'white' }}>
|
|
<option value="">Sélectionner...</option>
|
|
<option value="illegal">Contenu illégal</option>
|
|
<option value="hate">Discours de haine</option>
|
|
<option value="violence">Violence / Menaces</option>
|
|
<option value="sexual">Contenu sexuel</option>
|
|
<option value="harassment">Harcèlement</option>
|
|
<option value="copyright">Violation de copyright</option>
|
|
<option value="spam">Spam / Arnaque</option>
|
|
<option value="other">Autre</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div style={{ marginBottom: '20px' }}>
|
|
<label style={{ fontSize: '12px', fontWeight: 600, color: '#666', textTransform: 'uppercase', letterSpacing: '0.05em', display: 'block', marginBottom: '8px' }}>Détails (optionnel)</label>
|
|
<textarea value={details} onChange={e => setDetails(e.target.value)} rows={3} placeholder="Expliquez le problème..." style={{ width: '100%', padding: '10px 12px', borderRadius: '8px', border: '1px solid #ddd', fontSize: '14px', outline: 'none', resize: 'none', background: 'white' }} />
|
|
</div>
|
|
|
|
<button
|
|
onClick={handleSubmit}
|
|
disabled={!reason || submitting}
|
|
style={{
|
|
width: '100%', padding: '12px', borderRadius: '10px', border: 'none',
|
|
background: !reason || submitting ? '#ccc' : '#ef4444',
|
|
color: 'white', fontSize: '14px', fontWeight: 600, cursor: !reason || submitting ? 'not-allowed' : 'pointer',
|
|
}}
|
|
>
|
|
{submitting ? <Loader2 size={16} className="animate-spin" /> : 'Signaler'}
|
|
</button>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|