Files
marmarislocal/app/[locale]/admin/events/[id]/EventFormClient.tsx
T

286 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState, useTransition } from 'react'
import { useRouter } from '@/i18n/routing'
import { createOrUpdateEventAction } from '@/app/actions'
import { ArrowLeft, Save, Loader2, Sparkles } from 'lucide-react'
import { Link } from '@/i18n/routing'
interface Props {
event: any | null
listings: any[]
}
export default function EventFormClient({ event, listings }: Props) {
const router = useRouter()
const [isPending, startTransition] = useTransition()
const [error, setError] = useState<string | null>(null)
// Format dates for datetime-local input (YYYY-MM-DDTHH:MM)
const formatDateForInput = (dateVal: any) => {
if (!dateVal) return ''
const d = new Date(dateVal)
const pad = (num: number) => String(num).padStart(2, '0')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`
}
const [formData, setFormData] = useState({
id: event?.id || 'new',
slug: event?.slug || '',
listingId: event?.listingId || '',
titleTr: event?.titleTr || '',
titleEn: event?.titleEn || '',
titleRu: event?.titleRu || '',
descriptionTr: event?.descriptionTr || '',
descriptionEn: event?.descriptionEn || '',
descriptionRu: event?.descriptionRu || '',
startDate: formatDateForInput(event?.startDate),
endDate: formatDateForInput(event?.endDate),
isSponsored: event?.isSponsored ? 'true' : 'false',
coverImageUrl: event?.coverImage || ''
})
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const generateSlug = (text: string) => {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '')
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const { name, value } = e.target
setFormData(prev => {
if (name === 'titleTr' && !event) {
return { ...prev, [name]: value, slug: generateSlug(value) }
}
return { ...prev, [name]: value }
})
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError(null)
if (!formData.slug || !formData.titleTr || !formData.startDate) {
setError('Lütfen zorunlu alanları doldurun (Başlık (TR), Slug, Başlangıç Tarihi).')
return
}
startTransition(async () => {
const data = new FormData()
Object.entries(formData).forEach(([key, val]) => {
data.append(key, val)
})
if (selectedFile) {
data.append('coverImageFile', selectedFile)
}
const res = await createOrUpdateEventAction(data)
if (res.success) {
router.push('/admin/events')
router.refresh()
} else {
setError(res.error || 'Kaydetme işlemi başarısız.')
}
})
}
return (
<form onSubmit={handleSubmit} className="space-y-6">
{error && (
<div className="bg-bougainvillea/5 border border-bougainvillea/20 text-bougainvillea text-xs font-semibold p-4 rounded-xl">
{error}
</div>
)}
<div className="bg-paper border border-pine/8 rounded-3xl p-6 sm:p-8 shadow-sm space-y-6">
{/* Core fields */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Başlık (Türkçe) *</label>
<input
type="text"
name="titleTr"
value={formData.titleTr}
onChange={handleChange}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none"
required
/>
</div>
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Slug *</label>
<input
type="text"
name="slug"
value={formData.slug}
onChange={handleChange}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none"
required
/>
</div>
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Başlık (İngilizce)</label>
<input
type="text"
name="titleEn"
value={formData.titleEn}
onChange={handleChange}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none"
/>
</div>
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Başlık (Rusça)</label>
<input
type="text"
name="titleRu"
value={formData.titleRu}
onChange={handleChange}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none"
/>
</div>
</div>
{/* Association & Sponsored */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Düzenleyen Mekan</label>
<select
name="listingId"
value={formData.listingId}
onChange={handleChange}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none"
>
<option value="">Seçilmedi</option>
{listings.map(l => (
<option key={l.id} value={l.id}>{l.nameTr}</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Öne Çıkar / Sponsorlu</label>
<select
name="isSponsored"
value={formData.isSponsored}
onChange={handleChange}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none"
>
<option value="false">Hayır</option>
<option value="true">Evet (Sponsorlu Etiketli)</option>
</select>
</div>
</div>
{/* Dates */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Başlangıç Tarihi *</label>
<input
type="datetime-local"
name="startDate"
value={formData.startDate}
onChange={handleChange}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none"
required
/>
</div>
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Bitiş Tarihi (Opsiyonel)</label>
<input
type="datetime-local"
name="endDate"
value={formData.endDate}
onChange={handleChange}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none"
/>
</div>
</div>
{/* Descriptions */}
<div className="space-y-4">
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Açıklama (Türkçe)</label>
<textarea
name="descriptionTr"
value={formData.descriptionTr}
onChange={handleChange}
rows={3}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none resize-none"
/>
</div>
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Açıklama (İngilizce)</label>
<textarea
name="descriptionEn"
value={formData.descriptionEn}
onChange={handleChange}
rows={3}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none resize-none"
/>
</div>
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Açıklama (Rusça)</label>
<textarea
name="descriptionRu"
value={formData.descriptionRu}
onChange={handleChange}
rows={3}
className="w-full text-xs font-semibold px-4 py-3 bg-stone/50 border border-pine/8 rounded-xl focus:border-turquoise focus:outline-none resize-none"
/>
</div>
</div>
{/* Cover Image */}
<div className="space-y-4">
<div>
<label className="block text-xs font-bold text-shutter uppercase tracking-wider mb-2">Kapak Görseli</label>
<input
type="file"
accept="image/*"
onChange={(e) => setSelectedFile(e.target.files?.[0] || null)}
className="w-full text-xs font-semibold file:mr-4 file:py-2 file:px-4 file:rounded-xl file:border-0 file:bg-turquoise/10 file:text-turquoise file:font-bold hover:file:bg-turquoise/20 cursor-pointer"
/>
</div>
{formData.coverImageUrl && !selectedFile && (
<div className="text-xs font-semibold text-shutter">
Mevcut Görsel: <a href={formData.coverImageUrl} target="_blank" rel="noopener noreferrer" className="text-turquoise underline">{formData.coverImageUrl}</a>
</div>
)}
</div>
</div>
{/* Form Actions */}
<div className="flex items-center justify-between">
<Link
href="/admin/events"
className="inline-flex items-center gap-1.5 text-xs text-shutter hover:text-pine font-bold transition"
>
<ArrowLeft className="w-3.5 h-3.5" />
Vazgeç
</Link>
<button
type="submit"
disabled={isPending}
className="inline-flex items-center gap-1.5 bg-turquoise hover:bg-turquoise/90 text-paper text-xs font-bold py-3.5 px-6 rounded-xl shadow-sm transition active:scale-95 duration-150 disabled:opacity-50 disabled:pointer-events-none"
>
{isPending ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
Kaydediliyor...
</>
) : (
<>
<Save className="w-4 h-4" />
Etkinliği Kaydet
</>
)}
</button>
</div>
</form>
)
}