133 lines
5.0 KiB
TypeScript
133 lines
5.0 KiB
TypeScript
"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>
|
||
)
|
||
},
|
||
},
|
||
]
|