Files
moybeach-main/app/admin/services/page.tsx
T
2026-07-10 09:30:48 +03:00

140 lines
7.4 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.
import { prisma } from '@/lib/db';
import { getSession } from '@/lib/auth';
import { redirect } from 'next/navigation';
import { Trash2, Plus, Briefcase, Edit, X } from 'lucide-react';
import { addServiceItem, deleteServiceItem, updateServiceItem } from '../actions';
import { revalidatePath } from 'next/cache';
import { uploadImage } from '@/lib/openinary';
import Link from 'next/link';
export default async function ServicesAdminPage({ searchParams }: { searchParams: Promise<{ edit?: string }> }) {
const session = await getSession();
if (!session) redirect('/admin/login');
const { edit } = await searchParams;
const services = await prisma.service.findMany({
orderBy: { createdAt: 'desc' }
});
const editingService = edit ? services.find((s: any) => s.id === edit) : null;
return (
<div>
<div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">Hizmetlerimiz Yönetimi</h1>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Ekleme / Düzenleme Formu */}
<div className="lg:col-span-1">
<div className="bg-white dark:bg-slate-900 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-800 p-6 sticky top-8">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-bold">{editingService ? "Hizmeti Düzenle" : "Yeni Hizmet Ekle"}</h2>
{editingService && (
<Link href="/admin/services" className="text-slate-400 hover:text-slate-600">
<X className="w-5 h-5" />
</Link>
)}
</div>
<form action={async (formData: FormData) => {
'use server';
const id = formData.get('id') as string;
const title = formData.get('title') as string;
const description = formData.get('description') as string;
const iconFile = formData.get('icon') as File;
if (title && description) {
let iconUrl = editingService?.iconUrl || '';
if (iconFile && iconFile.size > 0) {
iconUrl = await uploadImage(iconFile);
}
if (id) {
await updateServiceItem(id, { title, description, iconUrl });
} else {
await addServiceItem({ title, description, iconUrl });
}
revalidatePath('/admin/services');
redirect('/admin/services');
}
}} className="space-y-4">
{editingService && <input type="hidden" name="id" value={editingService.id} />}
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Başlık</label>
<input type="text" name="title" defaultValue={editingService?.title || ""} required className="w-full px-4 py-2 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg outline-none focus:ring-2 focus:ring-blue-500" />
</div>
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Açıklama</label>
<textarea name="description" defaultValue={editingService?.description || ""} required rows={4} className="w-full px-4 py-2 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg outline-none focus:ring-2 focus:ring-blue-500"></textarea>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Resim Yükle {editingService ? "(Değiştirmek için seçin)" : ""}</label>
<input type="file" name="icon" accept="image/*" required={!editingService} className="w-full px-4 py-2 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg outline-none focus:ring-2 focus:ring-blue-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-purple-50 file:text-purple-700 hover:file:bg-purple-100" />
{editingService?.iconUrl && (
<div className="mt-2 text-xs text-slate-500">Mevcut resim korunuyor. Yeni resim seçerseniz değiştirilecektir.</div>
)}
</div>
<button type="submit" className="w-full flex items-center justify-center space-x-2 bg-purple-600 hover:bg-purple-700 text-white py-2.5 rounded-lg transition-colors">
{editingService ? <Edit className="w-5 h-5" /> : <Plus className="w-5 h-5" />}
<span>{editingService ? "Güncelle" : "Ekle"}</span>
</button>
</form>
</div>
</div>
{/* Listeleme */}
<div className="lg:col-span-2">
<div className="bg-white dark:bg-slate-900 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-800 p-6">
<h2 className="text-xl font-bold mb-6">Mevcut Hizmetler ({services.length})</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{services.map((item: any) => (
<div key={item.id} className="group relative rounded-xl overflow-hidden border border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-800">
<div className="aspect-video w-full overflow-hidden bg-slate-200 dark:bg-slate-700">
{item.iconUrl ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={item.iconUrl} alt={item.title} className="w-full h-full object-cover" />
) : (
<div className="w-full h-full flex items-center justify-center">
<Briefcase className="w-8 h-8 text-slate-400" />
</div>
)}
</div>
<div className="p-4">
<h3 className="font-semibold text-slate-800 dark:text-white text-lg">{item.title}</h3>
<p className="text-sm text-slate-500 dark:text-slate-400 mt-1 whitespace-pre-wrap line-clamp-3">{item.description}</p>
</div>
<div className="absolute top-2 right-2 flex space-x-2 opacity-0 group-hover:opacity-100 transition-opacity">
<Link href={`/admin/services?edit=${item.id}`} className="p-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg shadow-lg transition-colors">
<Edit className="w-4 h-4" />
</Link>
<form action={async () => {
'use server';
await deleteServiceItem(item.id);
revalidatePath('/admin/services');
}}>
<button type="submit" className="p-2 bg-red-600 hover:bg-red-700 text-white rounded-lg shadow-lg transition-colors">
<Trash2 className="w-4 h-4" />
</button>
</form>
</div>
</div>
))}
{services.length === 0 && (
<div className="col-span-full py-8 text-center text-slate-500">
Henüz hizmet eklenmemiş.
</div>
)}
</div>
</div>
</div>
</div>
</div>
);
}