İmage upload,sıkıştırma

This commit is contained in:
2026-01-13 23:36:40 +03:00
parent 6bbae0de21
commit 70f61a76b0
11 changed files with 795 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
import { SliderForm } from "@/components/dashboard/slider-form"
import { createClient } from "@/lib/supabase-server"
import { notFound } from "next/navigation"
interface EditSliderPageProps {
params: {
id: string
}
}
export default async function EditSliderPage({ params }: EditSliderPageProps) {
const supabase = createClient()
const { data: slider } = await supabase
.from('sliders')
.select('*')
.eq('id', params.id)
.single()
if (!slider) {
notFound()
}
return (
<div className="flex-1 space-y-4 p-8 pt-6">
<div className="flex items-center justify-between space-y-2">
<h2 className="text-3xl font-bold tracking-tight">Slider Düzenle</h2>
</div>
<div className="max-w-2xl">
<SliderForm initialData={slider} />
</div>
</div>
)
}

View File

@@ -0,0 +1,124 @@
"use server"
import { createClient } from "@/lib/supabase-server"
import { createClient as createSupabaseClient } from "@supabase/supabase-js"
import { revalidatePath } from "next/cache"
// Admin client for privileged operations
const supabaseAdmin = createSupabaseClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{
auth: {
autoRefreshToken: false,
persistSession: false
}
}
)
async function assertAdmin() {
const supabase = createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) throw new Error("Oturum açmanız gerekiyor.")
const { data: profile } = await supabase.from('profiles').select('role').eq('id', user.id).single()
if (profile?.role !== 'admin') throw new Error("Yetkisiz işlem.")
return user
}
export async function getSliders() {
const supabase = createClient()
// Everyone can read, so normal client is fine
const { data, error } = await supabase
.from('sliders')
.select('*')
.order('order', { ascending: true })
.order('created_at', { ascending: false })
if (error) return { error: error.message }
return { data }
}
export async function createSlider(data: {
title: string
description?: string
image_url: string
link?: string
order?: number
is_active?: boolean
}) {
try {
await assertAdmin()
const { error } = await supabaseAdmin.from('sliders').insert({
title: data.title,
description: data.description,
image_url: data.image_url,
link: data.link,
order: data.order || 0,
is_active: data.is_active ?? true
})
if (error) throw error
revalidatePath("/dashboard/sliders")
revalidatePath("/") // Homepage cache update
return { success: true }
} catch (error) {
return { error: (error as Error).message }
}
}
export async function updateSlider(id: string, data: {
title: string
description?: string
image_url: string
link?: string
order?: number
is_active?: boolean
}) {
try {
await assertAdmin()
const { error } = await supabaseAdmin
.from('sliders')
.update({
title: data.title,
description: data.description,
image_url: data.image_url,
link: data.link,
order: data.order,
is_active: data.is_active,
// updated_at trigger usually handles time, but we don't have it in schema yet, so maybe add later
})
.eq('id', id)
if (error) throw error
revalidatePath("/dashboard/sliders")
revalidatePath("/")
return { success: true }
} catch (error) {
return { error: (error as Error).message }
}
}
export async function deleteSlider(id: string) {
try {
await assertAdmin()
const { error } = await supabaseAdmin
.from('sliders')
.delete()
.eq('id', id)
if (error) throw error
revalidatePath("/dashboard/sliders")
revalidatePath("/")
return { success: true }
} catch (error) {
return { error: (error as Error).message }
}
}

View File

@@ -0,0 +1,14 @@
import { SliderForm } from "@/components/dashboard/slider-form"
export default function NewSliderPage() {
return (
<div className="flex-1 space-y-4 p-8 pt-6">
<div className="flex items-center justify-between space-y-2">
<h2 className="text-3xl font-bold tracking-tight">Yeni Slider Oluştur</h2>
</div>
<div className="max-w-2xl">
<SliderForm />
</div>
</div>
)
}

View File

@@ -0,0 +1,85 @@
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Plus, Pencil, Trash2, GripVertical } from "lucide-react"
import { getSliders, deleteSlider } from "./actions"
import { Card, CardContent } from "@/components/ui/card"
import Image from "next/image"
import { Badge } from "@/components/ui/badge"
export default async function SlidersPage() {
const { data: sliders, error } = await getSliders()
if (error) {
return <div className="p-8 text-red-500">Hata: {error}</div>
}
return (
<div className="flex-1 space-y-4 p-8 pt-6">
<div className="flex items-center justify-between space-y-2">
<h2 className="text-3xl font-bold tracking-tight">Slider Yönetimi</h2>
<div className="flex items-center space-x-2">
<Link href="/dashboard/sliders/new">
<Button>
<Plus className="mr-2 h-4 w-4" /> Yeni Slider Ekle
</Button>
</Link>
</div>
</div>
<div className="grid gap-4">
{sliders?.length === 0 ? (
<Card>
<CardContent className="flex flex-col items-center justify-center p-12 text-muted-foreground">
<p>Henüz hiç slider eklenmemiş.</p>
</CardContent>
</Card>
) : (
sliders?.map((slider) => (
<Card key={slider.id} className="overflow-hidden">
<div className="flex flex-col sm:flex-row items-center p-2 gap-4">
<div className="p-2 cursor-move text-muted-foreground">
<GripVertical className="h-5 w-5" />
</div>
<div className="relative w-full sm:w-48 h-32 sm:h-24 rounded-md overflow-hidden bg-slate-100 flex-shrink-0">
<Image
src={slider.image_url}
alt={slider.title}
fill
className="object-cover"
/>
</div>
<div className="flex-1 min-w-0 grid gap-1 text-center sm:text-left">
<div className="flex items-center gap-2 justify-center sm:justify-start">
<h3 className="font-semibold truncate">{slider.title}</h3>
{!slider.is_active && (
<Badge variant="secondary">Pasif</Badge>
)}
<Badge variant="outline">Sıra: {slider.order}</Badge>
</div>
<p className="text-sm text-muted-foreground truncate">
{slider.description || "Açıklama yok"}
</p>
</div>
<div className="flex items-center gap-2 p-2">
<Link href={`/dashboard/sliders/${slider.id}`}>
<Button variant="ghost" size="icon">
<Pencil className="h-4 w-4" />
</Button>
</Link>
{/* Delete functionality usually needs a client component or form action,
for simplicity here we will just link to edit,
or we can add a delete button with server action in a separate client component if needed.
Ideally, list items should be client components to handle delete easily.
*/}
</div>
</div>
</Card>
))
)}
</div>
</div>
)
}