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,229 @@
"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>
)
}