Files
Beyhan Oğur 2a5b661443 first commit
2026-04-26 21:46:42 +03:00

230 lines
8.4 KiB
TypeScript
Raw Permalink 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 { useEffect, useState, useCallback } from "react"
import { useSession } from "next-auth/react"
import { DataTable } from "@/components/ui/data-table"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Plus, Edit, Trash, RotateCcw } from "lucide-react"
import { settingService } from "@/services/settingService"
import { Setting } from "@/types/setting"
import { SettingDialog } from "./_components/setting-dialog"
import { toast } from "sonner"
import Swal from "sweetalert2"
import withReactContent from "sweetalert2-react-content"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
const MySwal = withReactContent(Swal)
export default function SettingsPage() {
const { data: session } = useSession()
const [settings, setSettings] = useState<Setting[]>([])
const [total, setTotal] = useState(0)
const [page, setPage] = useState(1)
const [perPage] = useState(10)
const [search, setSearch] = useState("")
const [statusFilter, setStatusFilter] = useState("with") // "with" | "active" | "only"
const [dialogOpen, setDialogOpen] = useState(false)
const [selectedSetting, setSelectedSetting] = useState<Setting | null>(null)
const fetchSettings = useCallback(async () => {
try {
// "active" -> "" (backend default?), "with" -> "with", "only" -> "only"
const apiSoftFilter = statusFilter === "active" ? "" : statusFilter
const res = await settingService.getSettings(page, perPage, search, apiSoftFilter)
setSettings(res.items || [])
setTotal(res.total)
} catch (error) {
toast.error("Ayarlar yüklenirken hata oluştu")
console.error(error)
}
}, [page, perPage, search, statusFilter])
useEffect(() => {
if (session) {
// eslint-disable-next-line react-hooks/set-state-in-effect
fetchSettings()
}
}, [session, fetchSettings])
const handleDelete = async (id: number) => {
const result = await MySwal.fire({
title: "Emin misiniz?",
text: "Bu ayarı silmek istediğinize emin misiniz?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "Evet, Sil",
cancelButtonText: "İptal",
customClass: {
popup: "dark:bg-gray-800 dark:text-white",
title: "dark:text-white",
},
})
if (result.isConfirmed) {
try {
await settingService.deleteSetting(id)
toast.success("Ayar başarıyla silindi")
fetchSettings()
} catch (error) {
toast.error("Silme işlemi başarısız oldu")
console.error(error)
}
}
}
const handleRestore = async (id: number) => {
const result = await MySwal.fire({
title: "Geri Yükle?",
text: "Bu ayarı geri yüklemek istediğinize emin misiniz?",
icon: "question",
showCancelButton: true,
confirmButtonText: "Evet, Geri Yükle",
cancelButtonText: "İptal",
customClass: {
popup: "dark:bg-gray-800 dark:text-white",
title: "dark:text-white",
},
})
if (result.isConfirmed) {
try {
await settingService.restoreSetting(id)
toast.success("Ayar başarıyla geri yüklendi")
fetchSettings()
} catch (error) {
toast.error("Geri yükleme başarısız oldu")
console.error(error)
}
}
}
const columns = [
{
accessorKey: "title",
header: "Başlık",
},
{
accessorKey: "is_active",
header: "Durum",
cell: ({ row }: { row: { original: Setting } }) => (
<span
className={`px-2 py-1 rounded-full text-xs ${row.original.is_active ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"
}`}
>
{row.original.is_active ? "Aktif" : "Pasif"}
</span>
),
},
{
accessorKey: "UpdatedAt",
header: "Son Güncelleme",
cell: ({ row }: { row: { original: Setting } }) => {
return new Date(row.original.UpdatedAt).toLocaleDateString("tr-TR")
},
},
{
id: "actions",
cell: ({ row }: { row: { original: Setting } }) => {
const isDeleted = !!row.original.DeletedAt
return (
<div className="flex gap-2 justify-end">
{isDeleted ? (
<Button
variant="outline"
size="sm"
onClick={() => handleRestore(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={() => {
setSelectedSetting(row.original)
setDialogOpen(true)
}}
className="h-8 w-8 p-0"
>
<Edit className="h-4 w-4" />
</Button>
<Button
variant="destructive"
size="sm"
onClick={() => handleDelete(row.original.ID)}
className="h-8 w-8 p-0"
>
<Trash className="h-4 w-4" />
</Button>
</>
)}
</div>
)
},
},
]
return (
<div className="p-8">
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-3xl font-bold tracking-tight">Site Ayarları</h1>
<p className="text-muted-foreground">
Genel site ayarlarını ve SEO yapılandırmalarını yönetin.
</p>
</div>
<Button onClick={() => {
setSelectedSetting(null)
setDialogOpen(true)
}}>
<Plus className="mr-2 h-4 w-4" /> Yeni Ayar
</Button>
</div>
<div className="flex items-center py-4 gap-4">
<Input
placeholder="Başlık ara..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="max-w-sm"
/>
<Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Durum" />
</SelectTrigger>
<SelectContent>
<SelectItem value="with">Tümü (Dahil)</SelectItem>
<SelectItem value="active">Sadece Aktif</SelectItem>
<SelectItem value="only">Sadece Silinenler</SelectItem>
</SelectContent>
</Select>
</div>
<DataTable
columns={columns}
data={settings}
pageCount={Math.ceil(total / perPage)}
page={page}
onPageChange={setPage}
/>
<SettingDialog
open={dialogOpen}
onOpenChange={setDialogOpen}
setting={selectedSetting}
onSuccess={fetchSettings}
/>
</div>
)
}