feat: add admin panel, Openinary media integration and standardized footer backlink

This commit is contained in:
mstfyldz
2026-08-23 01:40:48 +03:00
parent 17915cd3d5
commit b312bc5593
29 changed files with 1405 additions and 41 deletions
+89
View File
@@ -0,0 +1,89 @@
import { prisma } from '@/lib/db';
import { getSession } from '@/lib/auth';
import { redirect } from 'next/navigation';
import { Trash2, CheckCircle } from 'lucide-react';
import { deleteContact, markContactAsRead } from '../actions';
import { revalidatePath } from 'next/cache';
export default async function ContactAdminPage() {
const session = await getSession();
if (!session) redirect('/admin/login');
const messages = await prisma.contactMessage.findMany({
orderBy: { createdAt: 'desc' }
});
return (
<div>
<h1 className="text-3xl font-bold mb-8">İletişim Mesajları</h1>
<div className="bg-white dark:bg-slate-900 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-800 overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full text-left border-collapse">
<thead>
<tr className="bg-slate-50 dark:bg-slate-800/50 border-b border-slate-200 dark:border-slate-800">
<th className="p-4 font-semibold text-sm text-slate-600 dark:text-slate-300">Tarih</th>
<th className="p-4 font-semibold text-sm text-slate-600 dark:text-slate-300">Gönderen</th>
<th className="p-4 font-semibold text-sm text-slate-600 dark:text-slate-300">E-posta</th>
<th className="p-4 font-semibold text-sm text-slate-600 dark:text-slate-300">Konu</th>
<th className="p-4 font-semibold text-sm text-slate-600 dark:text-slate-300">Mesaj</th>
<th className="p-4 font-semibold text-sm text-slate-600 dark:text-slate-300">Durum</th>
<th className="p-4 font-semibold text-sm text-slate-600 dark:text-slate-300">İşlem</th>
</tr>
</thead>
<tbody>
{messages.map(msg => (
<tr key={msg.id} className="border-b border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800/50">
<td className="p-4 text-sm">{msg.createdAt.toLocaleDateString('tr-TR')}</td>
<td className="p-4 text-sm font-medium">{msg.name}</td>
<td className="p-4 text-sm text-slate-500 dark:text-slate-400">{msg.email}</td>
<td className="p-4 text-sm">{msg.subject || '-'}</td>
<td className="p-4 text-sm max-w-xs truncate" title={msg.message}>{msg.message}</td>
<td className="p-4 text-sm">
{msg.isRead ? (
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400">
Okundu
</span>
) : (
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400">
Yeni
</span>
)}
</td>
<td className="p-4 text-sm">
<div className="flex items-center space-x-2">
{!msg.isRead && (
<form action={async () => {
'use server';
await markContactAsRead(msg.id);
revalidatePath('/admin/contact');
}}>
<button type="submit" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300" title="Okundu İşaretle">
<CheckCircle className="w-5 h-5" />
</button>
</form>
)}
<form action={async () => {
'use server';
await deleteContact(msg.id);
revalidatePath('/admin/contact');
}}>
<button type="submit" className="text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300" title="Sil">
<Trash2 className="w-5 h-5" />
</button>
</form>
</div>
</td>
</tr>
))}
{messages.length === 0 && (
<tr>
<td colSpan={7} className="p-8 text-center text-slate-500">Hiç mesaj bulunamadı.</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
);
}