first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:46:42 +03:00
commit 2a5b661443
202 changed files with 49770 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { Post } from "@/types/post"
import { Button } from "@/components/ui/button"
import { Edit, Trash, RotateCcw } from "lucide-react"
interface PostColumnsProps {
onEdit: (post: Post) => void
onDelete: (id: number) => void
onRestore: (id: number) => void
statusFilter: string
deletedIds: number[]
}
export const getPostColumns = ({ onEdit, onDelete, onRestore, statusFilter, deletedIds }: PostColumnsProps): ColumnDef<Post>[] => [
{
accessorKey: "images",
header: "Görsel",
cell: ({ row }) => {
const rawImages = row.original.images
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080"
// Backend tarafında "images" alanı virgülle ayrılmış birden fazla path içerebilir.
// Liste görünümünde ilk path'i küçük görsel için kullanalım.
const firstImage = rawImages
? rawImages
.split(",")
.map(p => p.trim())
.filter(Boolean)[0]
: null
const fullUrl = firstImage
? (firstImage.startsWith("http") ? firstImage : `${apiUrl}${firstImage}`)
: null
return (
<div className="w-16 h-10 bg-gray-100 rounded overflow-hidden flex items-center justify-center">
{fullUrl ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={fullUrl} alt={row.original.title} className="w-full h-full object-cover" />
) : (
<span className="text-xs text-gray-400">Yok</span>
)}
</div>
)
},
},
{
accessorKey: "title",
header: "Başlık",
},
{
accessorKey: "categories",
header: "Kategoriler",
cell: ({ row }) => (
<div className="flex flex-wrap gap-1">
{row.original.categories?.map((cat, index) => (
<span key={cat.id || cat.title || index} className="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full">
{cat.title}
</span>
))}
</div>
),
},
{
accessorKey: "tags",
header: "Etiketler",
cell: ({ row }) => (
<div className="flex flex-wrap gap-1">
{row.original.tags?.map((tag, index) => (
<span key={tag.id || tag.name || index} className="px-2 py-1 bg-gray-100 text-gray-800 text-xs rounded-full">
{tag.name}
</span>
))}
</div>
),
},
{
accessorKey: "updated_at",
header: "Son Güncelleme",
cell: ({ row }) => {
const updatedAt = row.original.updated_at || row.original.UpdatedAt
return updatedAt ? new Date(updatedAt).toLocaleDateString("tr-TR") : "-"
},
},
{
id: "actions",
cell: ({ row }) => {
const id = row.original.id || row.original.ID
const inDeletedList = typeof id === "number" && deletedIds.includes(id)
// "Sadece Silinenler" filtresinde hepsi silinmiş kabul edilir.
// "Tümü (Dahil)" filtresinde ise deletedIds listesine bakılır.
const isDeleted = statusFilter === "only" || inDeletedList
return (
<div className="flex gap-2 justify-end">
{isDeleted ? (
<Button
variant="outline"
size="sm"
onClick={() => onRestore(row.original.id || row.original.ID!)}
className="h-8 w-8 p-0 text-green-600 hover:text-green-700 hover:bg-green-50"
title="Geri Yükle"
>
<RotateCcw className="h-4 w-4" />
</Button>
) : (
<>
<Button
variant="outline"
size="sm"
onClick={() => onEdit(row.original)}
className="h-8 w-8 p-0"
>
<Edit className="h-4 w-4" />
</Button>
<Button
variant="destructive"
size="sm"
onClick={() => onDelete(row.original.id || row.original.ID!)}
className="h-8 w-8 p-0"
>
<Trash className="h-4 w-4" />
</Button>
</>
)}
</div>
)
},
},
]