maj url photo + page 404 + gestion programmation d'article
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { useTransition, useState } from 'react';
|
||||
import { useTransition, useState, useEffect } 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 { Loader2, ArrowLeft, Save, Calendar, MapPin, Link as LinkIcon, Send } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import RichTextEditor from './RichTextEditor';
|
||||
import ImageUploader from './ImageUploader';
|
||||
import TagInput from './TagInput';
|
||||
import { ContentStatus } from '@prisma/client';
|
||||
import { getAllTags } from '@/app/actions/taxonomies';
|
||||
|
||||
interface Props {
|
||||
initialData?: any;
|
||||
@@ -16,23 +20,46 @@ export default function EventForm({ initialData }: Props) {
|
||||
const router = useRouter();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [description, setDescription] = useState(initialData?.description || '');
|
||||
const [thumbnailUrl, setThumbnailUrl] = useState(initialData?.thumbnailUrl || '');
|
||||
const [tags, setTags] = useState<string[]>(initialData?.tags || []);
|
||||
const [allExistingTags, setAllExistingTags] = useState<string[]>([]);
|
||||
const [publishedAtValue, setPublishedAtValue] = useState(
|
||||
initialData?.publishedAt
|
||||
? new Date(initialData.publishedAt).toISOString().slice(0, 16)
|
||||
: new Date().toISOString().slice(0, 16)
|
||||
);
|
||||
|
||||
const isPublished = new Date(publishedAtValue) <= new Date();
|
||||
|
||||
useEffect(() => {
|
||||
getAllTags().then(setAllExistingTags);
|
||||
}, []);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const publishedAtStr = formData.get('publishedAt') as string;
|
||||
|
||||
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,
|
||||
thumbnailUrl: thumbnailUrl, // Use state value
|
||||
link: formData.get('link') as string || null,
|
||||
description: description || '',
|
||||
tags: (formData.get('tags') as string)?.split(',').map(t => t.trim()).filter(t => t !== ''),
|
||||
tags: tags, // Use state
|
||||
metaTitle: formData.get('metaTitle') as string,
|
||||
metaDescription: formData.get('metaDescription') as string,
|
||||
publishedAt: publishedAtStr ? new Date(publishedAtStr) : undefined,
|
||||
status: formData.get('status') as ContentStatus,
|
||||
};
|
||||
|
||||
if (!data.thumbnailUrl) {
|
||||
toast.error("Une image est requise");
|
||||
return;
|
||||
}
|
||||
|
||||
startTransition(async () => {
|
||||
const result = initialData
|
||||
? await updateEvent(initialData.id, data)
|
||||
@@ -63,7 +90,21 @@ export default function EventForm({ initialData }: Props) {
|
||||
|
||||
<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="flex items-center justify-between border-b border-slate-800 pb-4">
|
||||
<h2 className="text-xl font-semibold text-white">Informations Générales</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
<label className="text-xs font-bold text-slate-500 uppercase tracking-wider">Statut :</label>
|
||||
<select
|
||||
name="status"
|
||||
defaultValue={initialData?.status || 'PUBLISHED'}
|
||||
className="bg-slate-900 border border-slate-700 text-white text-xs rounded-full px-4 py-1.5 focus:outline-none focus:border-brand-500 appearance-none cursor-pointer hover:bg-slate-800 transition-colors"
|
||||
>
|
||||
<option value="DRAFT">Brouillon</option>
|
||||
<option value="PUBLISHED">Publié</option>
|
||||
<option value="ARCHIVED">Archivé</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
@@ -77,7 +118,7 @@ export default function EventForm({ initialData }: Props) {
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-400">Date de l'événement</label>
|
||||
<label className="text-sm font-medium text-slate-400">Date de l'événement (Début)</label>
|
||||
<div className="relative">
|
||||
<Calendar className="absolute left-3 top-3.5 w-5 h-5 text-slate-500" />
|
||||
<input
|
||||
@@ -105,6 +146,32 @@ export default function EventForm({ initialData }: Props) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-400">
|
||||
Date de publication {isPublished ? '(Publiée)' : '(Programmation)'}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Send className="absolute left-3 top-3.5 w-5 h-5 text-slate-500" />
|
||||
<input
|
||||
name="publishedAt"
|
||||
type="datetime-local"
|
||||
value={publishedAtValue}
|
||||
onChange={(e) => setPublishedAtValue(e.target.value)}
|
||||
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">
|
||||
<ImageUploader
|
||||
label="Image (Affiche/Miniature)"
|
||||
value={thumbnailUrl}
|
||||
onChange={setThumbnailUrl}
|
||||
name="thumbnailUrl"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-400">Lien d'inscription / info (Optionnel)</label>
|
||||
<div className="relative">
|
||||
@@ -119,17 +186,6 @@ export default function EventForm({ initialData }: Props) {
|
||||
</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
|
||||
@@ -156,12 +212,12 @@ export default function EventForm({ initialData }: Props) {
|
||||
<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"
|
||||
<TagInput
|
||||
value={tags}
|
||||
onChange={setTags}
|
||||
suggestions={allExistingTags}
|
||||
label="Mots-clés (tags)"
|
||||
placeholder="evenement, networking, afrique..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user