feat: initialize Prisma schema and scaffold core application modules including auth, blogs, events, and business directories
Some checks failed
Build and Push App / build (push) Failing after 47s

This commit is contained in:
2026-04-22 22:59:12 +02:00
parent b01b2f6476
commit 2e7cb65a29
32 changed files with 1392 additions and 310 deletions

View File

@@ -0,0 +1,208 @@
"use client";
import { useTransition, useState } from 'react';
import { useRouter } from 'next/navigation';
import { createEvent, updateEvent } from '@/app/actions/event';
import { Loader2, ArrowLeft, Save, Calendar, MapPin, Link as LinkIcon } from 'lucide-react';
import Link from 'next/link';
import { toast } from 'react-hot-toast';
import RichTextEditor from './RichTextEditor';
interface Props {
initialData?: any;
}
export default function EventForm({ initialData }: Props) {
const router = useRouter();
const [isPending, startTransition] = useTransition();
const [description, setDescription] = useState(initialData?.description || '');
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: any = {
title: formData.get('title') as string,
slug: formData.get('slug') as string,
location: formData.get('location') as string,
date: new Date(formData.get('date') as string),
thumbnailUrl: formData.get('thumbnailUrl') as string,
link: formData.get('link') as string || null,
description: description || '',
tags: (formData.get('tags') as string)?.split(',').map(t => t.trim()).filter(t => t !== ''),
metaTitle: formData.get('metaTitle') as string,
metaDescription: formData.get('metaDescription') as string,
};
startTransition(async () => {
const result = initialData
? await updateEvent(initialData.id, data)
: await createEvent(data);
if (result.success) {
toast.success(initialData ? "Événement mis à jour" : "Événement créé avec succès");
router.push('/blog');
router.refresh();
} else {
toast.error(result.error || "Une erreur est survenue");
}
});
};
return (
<div className="max-w-4xl mx-auto pb-20">
<div className="mb-8 flex items-center justify-between">
<div className="flex items-center gap-4">
<Link href="/blog" className="p-2 bg-slate-800 rounded-lg text-slate-400 hover:text-white transition-colors">
<ArrowLeft className="w-5 h-5" />
</Link>
<h1 className="text-3xl font-bold text-white">
{initialData ? 'Modifier l\'événement' : 'Nouvel Événement'}
</h1>
</div>
</div>
<form onSubmit={handleSubmit} className="space-y-8">
<div className="card space-y-6">
<h2 className="text-xl font-semibold text-white border-b border-slate-800 pb-4">Informations Générales</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Titre de l'événement</label>
<input
name="title"
defaultValue={initialData?.title}
required
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white focus:outline-none focus:border-indigo-500"
placeholder="Ex: Conférence AfroHub 2026"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Date de l'événement</label>
<div className="relative">
<Calendar className="absolute left-3 top-3.5 w-5 h-5 text-slate-500" />
<input
name="date"
type="datetime-local"
defaultValue={initialData?.date ? new Date(initialData.date).toISOString().slice(0, 16) : ''}
required
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 pl-10 text-white focus:outline-none focus:border-indigo-500"
/>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Lieu</label>
<div className="relative">
<MapPin className="absolute left-3 top-3.5 w-5 h-5 text-slate-500" />
<input
name="location"
defaultValue={initialData?.location}
required
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 pl-10 text-white focus:outline-none focus:border-indigo-500"
placeholder="Ex: Abidjan, Côte-d'Ivoire / Zoom"
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Lien d'inscription / info (Optionnel)</label>
<div className="relative">
<LinkIcon className="absolute left-3 top-3.5 w-5 h-5 text-slate-500" />
<input
name="link"
defaultValue={initialData?.link}
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 pl-10 text-white focus:outline-none focus:border-indigo-500"
placeholder="https://..."
/>
</div>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">URL de l'image (Affiche/Miniature)</label>
<input
name="thumbnailUrl"
defaultValue={initialData?.thumbnailUrl}
required
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white focus:outline-none focus:border-indigo-500"
placeholder="https://..."
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Description de l'événement</label>
<RichTextEditor
value={description}
onChange={setDescription}
placeholder="Détails sur l'événement, programme, intervenants..."
/>
</div>
</div>
{/* SEO SECTION */}
<div className="card space-y-6">
<h2 className="text-xl font-semibold text-white border-b border-slate-800 pb-4">Optimisation SEO</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Slug (URL personnalisée)</label>
<input
name="slug"
defaultValue={initialData?.slug || ''}
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white focus:outline-none focus:border-indigo-500"
placeholder="ex: conference-afrohub-2026"
/>
<p className="text-xs text-slate-500 italic">Laissez vide pour générer automatiquement à partir du titre.</p>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Mots-clés (tags, séparés par des virgules)</label>
<input
name="tags"
defaultValue={initialData?.tags?.join(', ') || ''}
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white focus:outline-none focus:border-indigo-500"
placeholder="evenement, networking, afrique"
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Meta Titre (SEO)</label>
<input
name="metaTitle"
defaultValue={initialData?.metaTitle || ''}
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white focus:outline-none focus:border-indigo-500"
placeholder="Titre optimisé pour Google"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-400">Meta Description (SEO)</label>
<textarea
name="metaDescription"
defaultValue={initialData?.metaDescription || ''}
rows={3}
className="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white focus:outline-none focus:border-indigo-500"
placeholder="Description courte pour les résultats de recherche..."
/>
</div>
</div>
<div className="pt-4">
<button
type="submit"
disabled={isPending}
className="w-full btn-verify flex items-center justify-center gap-2 py-4 text-lg bg-indigo-600"
>
{isPending ? (
<Loader2 className="w-6 h-6 animate-spin" />
) : (
<Save className="w-6 h-6" />
)}
{initialData ? 'Enregistrer les modifications' : 'Créer l\'événement'}
</button>
</div>
</form>
</div>
);
}