230 lines
8.4 KiB
TypeScript
230 lines
8.4 KiB
TypeScript
"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>
|
||
)
|
||
}
|