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,122 @@
"use client"
import { useEffect, useState, useCallback } from "react"
import { DataTable } from "./_components/data-table"
import { getColumns } from "./_components/columns"
import { Hero } from "@/types/hero"
import { heroService } from "@/services/heroService"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Plus } from "lucide-react"
import { HeroDialog } from "./_components/hero-dialog"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
export default function HeroesPage() {
const [data, setData] = useState<Hero[]>([])
const [loading, setLoading] = useState(true)
const [page, setPage] = useState(1)
const [total, setTotal] = useState(0)
const [perPage] = useState(20)
const [search, setSearch] = useState("")
const [statusFilter, setStatusFilter] = useState("with") // "with" shows active + deleted
const [showCreateDialog, setShowCreateDialog] = useState(false)
const fetchData = useCallback(async () => {
setLoading(true)
try {
// API expects "active" logic differently perhaps?
// checking task.md/heroService docs:
// heroService.getHeroes(page, perPage, search, soft)
// soft: 'only' | 'with' | empty (defaults to active in some apis, but checking service impl)
// From service: soft param defaults to "with".
const apiSoftFilter = statusFilter === "active" ? "" : statusFilter
const res = await heroService.getHeroes(page, perPage, search, apiSoftFilter)
setData(res.items || [])
setTotal(res.total || 0)
} catch (error) {
console.error("Heroes fetch error:", error)
setData([])
} finally {
setLoading(false)
}
}, [page, perPage, search, statusFilter])
useEffect(() => {
fetchData()
}, [fetchData])
const totalPages = Math.ceil(total / perPage)
return (
<div className="p-8">
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-3xl font-bold tracking-tight">Hero Banner Yönetimi</h1>
<p className="text-muted-foreground">
Ana sayfa banner alanlarını yönetin.
</p>
</div>
<Button onClick={() => setShowCreateDialog(true)}>
<Plus className="mr-2 h-4 w-4" /> Yeni Hero
</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={getColumns(fetchData)} data={data} />
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => setPage((old) => Math.max(old - 1, 1))}
disabled={page === 1 || loading}
>
Önceki
</Button>
<div className="text-sm text-muted-foreground">
Sayfa {page} / {totalPages || 1}
</div>
<Button
variant="outline"
size="sm"
onClick={() => setPage((old) => (data.length === perPage || page < totalPages ? old + 1 : old))}
disabled={page >= totalPages || loading}
>
Sonraki
</Button>
</div>
<HeroDialog
open={showCreateDialog}
onOpenChange={setShowCreateDialog}
onSuccess={fetchData}
/>
</div>
)
}