Files
kitebeachakyaka/app/admin/activities/ActivitiesClient.tsx
T
2026-06-09 15:15:40 +03:00

256 lines
12 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 { Activity } from "@prisma/client";
import { Plus, Edit2, Trash2, X, Save } from "lucide-react";
import { loadMockActivities, addActivity, updateActivity, deleteActivity } from "@/app/actions/activities";
export default function ActivitiesClient({ activities }: { activities: Activity[] }) {
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingActivity, setEditingActivity] = useState<Activity | null>(null);
const [isPending, startTransition] = useTransition();
const [error, setError] = useState<string | null>(null);
const openAddModal = () => {
setEditingActivity(null);
setError(null);
setIsModalOpen(true);
};
const openEditModal = (activity: Activity) => {
setEditingActivity(activity);
setError(null);
setIsModalOpen(true);
};
const closeModal = () => {
setIsModalOpen(false);
setEditingActivity(null);
setError(null);
};
const handleDelete = (id: number) => {
if (confirm("Bu aktiviteyi silmek istediğinize emin misiniz?")) {
startTransition(async () => {
const result = await deleteActivity(id);
if (result.error) {
alert(result.error);
}
});
}
};
const handleSubmit = async (formData: FormData) => {
setError(null);
if (editingActivity) {
formData.append("id", editingActivity.id.toString());
const result = await updateActivity(null, formData);
if (result.error) {
setError(result.error);
} else {
closeModal();
}
} else {
const result = await addActivity(null, formData);
if (result.error) {
setError(result.error);
} else {
closeModal();
}
}
};
return (
<>
<div className="flex justify-between items-center mb-10">
<h1 className="text-4xl font-black font-serif text-dark tracking-tight">Aktiviteler</h1>
<div className="flex items-center gap-3">
<form action={loadMockActivities}>
<button type="submit" className="bg-white text-dark border border-gray-200 px-6 py-2.5 rounded-xl font-bold tracking-wide hover:bg-gray-50 hover:shadow-lg transition-all text-sm flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-accent animate-pulse" />
Örnek Verileri Yükle
</button>
</form>
<button
onClick={openAddModal}
className="bg-dark text-white px-6 py-2.5 rounded-xl font-bold tracking-wide hover:bg-dark/90 hover:shadow-lg transition-all text-sm flex items-center gap-2"
>
<Plus className="w-4 h-4" />
Yeni Ekle
</button>
</div>
</div>
{activities.length === 0 ? (
<div className="bg-white rounded-3xl shadow-xl border border-gray-100 p-12 text-center text-gray-500 font-medium">
Henüz aktivite eklenmemiş. Sağ üstten yeni ekleyebilir veya örnek verileri yükleyebilirsiniz.
</div>
) : (
<div className="bg-white rounded-3xl shadow-xl border border-gray-100 overflow-hidden animate-fade-up">
<table className="w-full text-left text-sm text-gray-600">
<thead className="bg-gray-50/50 border-b border-gray-100 text-gray-500 uppercase tracking-widest text-[10px] font-bold">
<tr>
<th className="px-8 py-5">Başlık</th>
<th className="px-8 py-5">Dil</th>
<th className="px-8 py-5">Fiyat</th>
<th className="px-8 py-5 text-right">İşlemler</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-50">
{activities.map(act => (
<tr key={act.id} className="hover:bg-gray-50/80 transition-colors group">
<td className="px-8 py-5 font-bold text-dark">{act.title}</td>
<td className="px-8 py-5">
<span className="bg-gray-100 text-gray-600 px-3 py-1 rounded-full text-[10px] uppercase font-black tracking-widest border border-gray-200">{act.lang}</span>
</td>
<td className="px-8 py-5 font-medium">{act.price}</td>
<td className="px-8 py-5 text-right">
<div className="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={() => openEditModal(act)}
className="p-2 bg-primary/10 text-primary rounded-lg hover:bg-primary/20 transition-colors"
title="Düzenle"
>
<Edit2 className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete(act.id)}
disabled={isPending}
className="p-2 bg-red-500/10 text-red-500 rounded-lg hover:bg-red-500/20 transition-colors disabled:opacity-50"
title="Sil"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{/* Modal */}
{isModalOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="absolute inset-0 bg-dark/60 backdrop-blur-sm" onClick={closeModal} />
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-2xl relative z-10 animate-fade-up overflow-hidden max-h-[90vh] flex flex-col">
<div className="flex justify-between items-center p-6 md:p-8 border-b border-gray-100 bg-gray-50/50">
<h2 className="text-2xl font-bold font-serif text-dark">
{editingActivity ? "Aktivite Düzenle" : "Yeni Aktivite Ekle"}
</h2>
<button onClick={closeModal} className="p-2 text-gray-400 hover:text-dark hover:bg-gray-200 rounded-full transition-colors">
<X className="w-5 h-5" />
</button>
</div>
<div className="p-6 md:p-8 overflow-y-auto">
<form action={handleSubmit} className="space-y-6">
{error && (
<div className="p-4 bg-red-50 border border-red-200 rounded-xl text-red-600 text-sm">
{error}
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-xs font-bold text-gray-500 tracking-wider uppercase mb-2">Dil</label>
<select
name="lang"
defaultValue={editingActivity?.lang || "tr"}
className="w-full px-5 py-3.5 bg-gray-50 border border-gray-200 text-dark rounded-xl focus:ring-4 focus:ring-primary/20 focus:border-primary outline-none transition-all hover:border-gray-300"
>
<option value="tr">Türkçe</option>
<option value="en">English</option>
</select>
</div>
<div>
<label className="block text-xs font-bold text-gray-500 tracking-wider uppercase mb-2">Başlık</label>
<input
type="text"
name="title"
defaultValue={editingActivity?.title || ""}
required
className="w-full px-5 py-3.5 bg-gray-50 border border-gray-200 text-dark rounded-xl focus:ring-4 focus:ring-primary/20 focus:border-primary outline-none transition-all hover:border-gray-300"
/>
</div>
<div className="col-span-1 md:col-span-2">
<label className="block text-xs font-bold text-gray-500 tracking-wider uppercase mb-2">Açıklama</label>
<textarea
name="description"
defaultValue={editingActivity?.description || ""}
required
rows={3}
className="w-full px-5 py-3.5 bg-gray-50 border border-gray-200 text-dark rounded-xl focus:ring-4 focus:ring-primary/20 focus:border-primary outline-none transition-all hover:border-gray-300 resize-none"
/>
</div>
<div>
<label className="block text-xs font-bold text-gray-500 tracking-wider uppercase mb-2">Fiyat (Metin)</label>
<input
type="text"
name="price"
defaultValue={editingActivity?.price || ""}
required
className="w-full px-5 py-3.5 bg-gray-50 border border-gray-200 text-dark rounded-xl focus:ring-4 focus:ring-primary/20 focus:border-primary outline-none transition-all hover:border-gray-300"
placeholder="Örn: ₺2.000/Saat"
/>
</div>
<div>
<label className="block text-xs font-bold text-gray-500 tracking-wider uppercase mb-2">Rozet (Opsiyonel)</label>
<input
type="text"
name="badge"
defaultValue={editingActivity?.badge || ""}
className="w-full px-5 py-3.5 bg-gray-50 border border-gray-200 text-dark rounded-xl focus:ring-4 focus:ring-primary/20 focus:border-primary outline-none transition-all hover:border-gray-300"
placeholder="Örn: Çok Satan"
/>
</div>
<div className="col-span-1 md:col-span-2">
<label className="block text-xs font-bold text-gray-500 tracking-wider uppercase mb-2">Görsel URL</label>
<input
type="url"
name="image"
defaultValue={editingActivity?.image || ""}
required
className="w-full px-5 py-3.5 bg-gray-50 border border-gray-200 text-dark rounded-xl focus:ring-4 focus:ring-primary/20 focus:border-primary outline-none transition-all hover:border-gray-300"
placeholder="https://..."
/>
</div>
<div>
<label className="block text-xs font-bold text-gray-500 tracking-wider uppercase mb-2">Sıra (Görünüm Sırası)</label>
<input
type="number"
name="order"
defaultValue={editingActivity?.order || 0}
className="w-full px-5 py-3.5 bg-gray-50 border border-gray-200 text-dark rounded-xl focus:ring-4 focus:ring-primary/20 focus:border-primary outline-none transition-all hover:border-gray-300"
/>
</div>
</div>
<div className="flex justify-end pt-6 mt-6 border-t border-gray-100">
<button
type="submit"
className="flex items-center gap-2 bg-accent text-white font-bold tracking-widest uppercase py-4 px-8 rounded-xl hover:bg-accent/90 transition-all shadow-lg shadow-accent/20 text-sm"
>
<Save className="w-4 h-4" />
Kaydet
</button>
</div>
</form>
</div>
</div>
</div>
)}
</>
);
}