Files
marmarislocal/app/[locale]/admin/listings/[id]/ListingForm.tsx
T
2026-07-12 20:18:55 +03:00

445 lines
18 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 } from 'react'
import { useRouter } from 'next/navigation'
import { createOrUpdateListingAction } from '@/app/actions'
import { ArrowLeft, Save } from 'lucide-react'
import { Link } from '@/i18n/routing'
interface Option {
value: string
label: string
}
interface FormProps {
listing: any | null
categories: Option[]
neighborhoods: Option[]
}
export default function ListingForm({ listing, categories, neighborhoods }: FormProps) {
const router = useRouter()
const [activeTab, setActiveTab] = useState<'tr' | 'en' | 'ru'>('tr')
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState(false)
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setLoading(true)
setError('')
setSuccess(false)
const formData = new FormData(e.currentTarget)
if (listing) {
formData.append('id', listing.id)
}
try {
const res = await createOrUpdateListingAction(formData)
if (res.error) {
setError(res.error)
} else {
setSuccess(true)
setTimeout(() => {
router.push('/admin/listings')
router.refresh()
}, 1500)
}
} catch (err) {
setError('İşlem sırasında bir hata oluştu.')
} finally {
setLoading(false)
}
}
const initialImages = listing?.images || []
const img1 = initialImages[0]?.url || ''
const img2 = initialImages[1]?.url || ''
return (
<form onSubmit={handleSubmit} className="space-y-6">
{error && (
<div className="bg-bougainvillea/10 border border-bougainvillea/25 text-bougainvillea p-4 rounded-xl text-xs font-semibold">
{error}
</div>
)}
{success && (
<div className="bg-turquoise/10 border border-turquoise/25 text-turquoise p-4 rounded-xl text-xs font-bold">
Mekan başarıyla kaydedildi! Yönlendiriliyorsunuz...
</div>
)}
{/* Language tabs */}
<div className="border-b border-pine/8">
<div className="flex gap-2">
{(['tr', 'en', 'ru'] as const).map((lang) => {
const label = lang === 'tr' ? '🇹🇷 Türkçe' : lang === 'en' ? '🇬🇧 English' : '🇷🇺 Русский'
const isTabActive = activeTab === lang
return (
<button
key={lang}
type="button"
onClick={() => setActiveTab(lang)}
className={`py-3 px-4 font-heading font-bold text-xs border-b-2 transition lowercase ${
isTabActive
? 'border-turquoise text-turquoise'
: 'border-transparent text-shutter hover:text-pine'
}`}
>
{label}
</button>
)
})}
</div>
</div>
{/* Multilingual input fields */}
<div className="space-y-4">
{/* TR Tab */}
{activeTab === 'tr' && (
<div className="space-y-4">
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Mekan Adı (TR) *</label>
<input
type="text"
name="nameTr"
required
defaultValue={listing?.nameTr || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
placeholder="Örn: İskele Balık Ocakbaşı"
/>
</div>
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Açıklama (TR) *</label>
<textarea
name="descriptionTr"
required
rows={4}
defaultValue={listing?.descriptionTr || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
placeholder="Türkçe tanıtım metni..."
/>
</div>
</div>
)}
{/* EN Tab */}
{activeTab === 'en' && (
<div className="space-y-4">
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Mekan Adı (EN) *</label>
<input
type="text"
name="nameEn"
required
defaultValue={listing?.nameEn || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
placeholder="Örn: Iskele Fish & Grill"
/>
</div>
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Açıklama (EN) *</label>
<textarea
name="descriptionEn"
required
rows={4}
defaultValue={listing?.descriptionEn || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
placeholder="English description..."
/>
</div>
</div>
)}
{/* RU Tab */}
{activeTab === 'ru' && (
<div className="space-y-4">
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Mekan Adı (RU) *</label>
<input
type="text"
name="nameRu"
required
defaultValue={listing?.nameRu || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
placeholder="Örn: Рыбный Гриль Искеле"
/>
</div>
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Açıklama (RU) *</label>
<textarea
name="descriptionRu"
required
rows={4}
defaultValue={listing?.descriptionRu || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
placeholder="Russian description..."
/>
</div>
</div>
)}
</div>
<hr className="border-dashed border-pine/8" />
{/* Global fields */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{/* Slug */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">URL Slug *</label>
<input
type="text"
name="slug"
required
defaultValue={listing?.slug || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink font-mono"
placeholder="iskele-balik-ocakbasi"
/>
</div>
{/* Categories */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Kategori *</label>
<select
name="categoryId"
required
defaultValue={listing?.categoryId || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink appearance-none"
>
<option value="">Kategori seçin...</option>
{categories.map((c) => (
<option key={c.value} value={c.value}>
{c.label}
</option>
))}
</select>
</div>
{/* Neighborhoods */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Mahalle/Bölge *</label>
<select
name="neighborhoodId"
required
defaultValue={listing?.neighborhoodId || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink appearance-none"
>
<option value="">Mahalle seçin...</option>
{neighborhoods.map((n) => (
<option key={n.value} value={n.value}>
{n.label}
</option>
))}
</select>
</div>
{/* Address */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Açık Adres *</label>
<input
type="text"
name="address"
required
defaultValue={listing?.address || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
placeholder="Yat Limanı No:12, Marmaris"
/>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-4 gap-4">
{/* Phone */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Telefon</label>
<input
type="text"
name="phone"
defaultValue={listing?.phone || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink font-mono"
placeholder="+90 252..."
/>
</div>
{/* Whatsapp */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">WhatsApp</label>
<input
type="text"
name="whatsapp"
defaultValue={listing?.whatsapp || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink font-mono"
placeholder="+90 532..."
/>
</div>
{/* Website */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Web Sitesi</label>
<input
type="text"
name="website"
defaultValue={listing?.website || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink font-mono"
placeholder="https://..."
/>
</div>
{/* Instagram */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Instagram</label>
<input
type="text"
name="instagram"
defaultValue={listing?.instagram || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink font-mono"
placeholder="https://instagram.com/..."
/>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-4 gap-4">
{/* Price range */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Fiyat Aralığı (1-3) *</label>
<select
name="priceRange"
required
defaultValue={listing?.priceRange || 2}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
>
<option value="1"> (Ekonomik)</option>
<option value="2">₺₺ (Orta)</option>
<option value="3">₺₺₺ (Lüks)</option>
</select>
</div>
{/* Rating */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Puan (0.0 - 5.0)</label>
<input
type="number"
name="rating"
step="0.1"
min="0"
max="5"
defaultValue={listing?.rating || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink font-mono"
placeholder="4.8"
/>
</div>
{/* Latitude */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Enlem (Lat)</label>
<input
type="number"
name="latitude"
step="0.0001"
defaultValue={listing?.latitude || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink font-mono"
placeholder="36.8524"
/>
</div>
{/* Longitude */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Boylam (Lng)</label>
<input
type="number"
name="longitude"
step="0.0001"
defaultValue={listing?.longitude || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink font-mono"
placeholder="28.2741"
/>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{/* Opening hours */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Çalışma Saatleri</label>
<input
type="text"
name="openingHours"
defaultValue={listing?.openingHours?.all || ''}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
placeholder="Örn: 12:00 - 00:00"
/>
</div>
{/* Approved toggle */}
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Yerel Onaylı Mührü</label>
<select
name="isLocalApproved"
defaultValue={listing?.isLocalApproved ? 'true' : 'false'}
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink"
>
<option value="false">Normal Mekan</option>
<option value="true">Yerel Onaylı (Mühürlü)</option>
</select>
</div>
</div>
{/* Image Upload fields */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 border-t border-dashed border-pine/8 pt-6">
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Görsel 1 (Dosya Yükle)</label>
<input
type="file"
name="imageFile1"
accept="image/*"
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink file:mr-3 file:py-1 file:px-2.5 file:rounded-lg file:border-0 file:text-xs file:font-semibold file:bg-pine file:text-stone hover:file:opacity-90 cursor-pointer"
/>
{img1 && (
<div className="mt-2 text-xs flex items-center gap-2">
<span className="text-shutter/65">Mevcut Görsel:</span>
<a href={img1} target="_blank" rel="noopener noreferrer" className="text-turquoise hover:underline font-mono truncate max-w-xs">{img1}</a>
<input type="hidden" name="imageUrl1" value={img1} />
</div>
)}
</div>
<div className="space-y-1.5">
<label className="block text-xs font-mono uppercase tracking-wider text-shutter">Görsel 2 (Dosya Yükle)</label>
<input
type="file"
name="imageFile2"
accept="image/*"
className="w-full bg-stone border border-pine/10 rounded-xl px-4 py-3 text-sm focus:ring-1 focus:ring-turquoise outline-none text-ink file:mr-3 file:py-1 file:px-2.5 file:rounded-lg file:border-0 file:text-xs file:font-semibold file:bg-pine file:text-stone hover:file:opacity-90 cursor-pointer"
/>
{img2 && (
<div className="mt-2 text-xs flex items-center gap-2">
<span className="text-shutter/65">Mevcut Görsel:</span>
<a href={img2} target="_blank" rel="noopener noreferrer" className="text-turquoise hover:underline font-mono truncate max-w-xs">{img2}</a>
<input type="hidden" name="imageUrl2" value={img2} />
</div>
)}
</div>
</div>
<div className="flex gap-4 pt-4 border-t border-dashed border-pine/8">
<Link
href="/admin/listings"
className="inline-flex items-center gap-1.5 px-5 py-3 border border-pine/20 rounded-xl text-xs font-bold text-pine hover:bg-stone/30 transition duration-150"
>
<ArrowLeft className="w-4 h-4" />
İptal
</Link>
<button
type="submit"
disabled={loading}
className="inline-flex items-center gap-1.5 px-5 py-3 bg-turquoise hover:bg-turquoise/90 disabled:opacity-75 text-paper rounded-xl text-xs font-bold shadow-sm transition duration-150"
>
<Save className="w-4 h-4" />
{loading ? 'Kaydediliyor...' : 'Kaydet'}
</button>
</div>
</form>
)
}