"use client"
import { useState, useEffect } from "react"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Post } from "@/types/post"
import { categoryService } from "@/services/categoryService"
import { postService } from "@/services/postService"
import { tagService } from "@/services/tagService"
import { Category } from "@/types/category"
import { Tag } from "@/types/tag"
import { toast } from "sonner"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { useSlug } from "@/hooks/useSlug"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
// MultiSelect component specifically for Shadcn UI
// Since Shadcn doesn't have a native MultiSelect, we'll use a simple implementation or standard select with multiple
// For better UI, using a basic select list for now, ideally should use a proper MultiSelect component
const MultiSelect = ({
options,
selected,
onChange
}: {
options: { label: string; value: string }[]
selected: string[]
onChange: (values: string[]) => void
}) => {
return (
{options.map((option) => (
{
if (e.target.checked) {
onChange([...selected, option.value])
} else {
onChange(selected.filter((v) => v !== option.value))
}
}}
className="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
/>
))}
{options.length === 0 &&
Veri bulunamadı.
}
)
}
const formSchema = z.object({
title: z.string().min(2, "Başlık en az 2 karakter olmalı"),
slug: z.string().min(2, "Slug en az 2 karakter olmalı"),
content: z.string().min(10, "İçerik en az 10 karakter olmalı"),
category_ids: z.array(z.string()).min(1, "En az bir kategori seçilmelidir"),
tag_names: z.array(z.string()).optional(), // Changed to array for MultiSelect
// Image Config
images: z.any().optional(),
width: z.coerce.number().min(1).default(800),
height: z.coerce.number().min(1).default(600),
quality: z.coerce.number().min(1).max(100).default(85),
format: z.string().default("webp"),
})
interface PostDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
post?: Post | null
onSuccess?: () => void
}
export function PostDialog({ open, onOpenChange, post, onSuccess }: PostDialogProps) {
const [loading, setLoading] = useState(false)
const [categories, setCategories] = useState([])
const [tags, setTags] = useState([])
const [preview, setPreview] = useState(null)
const { slugify } = useSlug()
const form = useForm>({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
resolver: zodResolver(formSchema) as any,
defaultValues: {
title: "",
slug: "",
content: "",
category_ids: [],
tag_names: [],
width: 800,
height: 600,
quality: 85,
format: "webp",
},
})
useEffect(() => {
const loadData = async () => {
try {
const catRes = await categoryService.getCategories(1, 100)
setCategories(catRes.items || [])
const tagRes = await tagService.getTags(1, 100)
setTags(tagRes.items || [])
} catch (error) {
console.error("Failed to load categories/tags", error)
}
}
if (open) {
loadData()
}
}, [open])
useEffect(() => {
if (post) {
const categoryIds =
post.categories
?.map(c => {
const id = c.id ?? c.ID
return id != null ? id.toString() : null
})
.filter((id): id is string => !!id) || []
form.reset({
title: post.title,
slug: post.slug,
content: post.content,
category_ids: categoryIds,
tag_names: post.tags?.map(t => t.name) || [],
width: post.width || 800,
height: post.height || 600,
quality: post.quality || 85,
format: post.format || "webp",
})
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080"
if (post.images) {
// Backend \"images\" alanı birden fazla path'i virgülle birleştirebiliyor.
// Dialog önizlemesi için ilk path'i kullan.
const firstImage = post.images
.split(",")
.map(p => p.trim())
.filter(Boolean)[0]
if (firstImage) {
setPreview(firstImage.startsWith("http") ? firstImage : `${apiUrl}${firstImage}`)
}
}
} else {
form.reset({
title: "",
slug: "",
content: "",
category_ids: [],
tag_names: [],
width: 800,
height: 600,
quality: 85,
format: "webp",
})
setPreview(null)
}
}, [post, form, open])
const handleTitleChange = (e: React.ChangeEvent) => {
const title = e.target.value
form.setValue("title", title)
if (!post) { // Only auto-slug on create
form.setValue("slug", slugify(title))
}
}
const handleImageChange = (e: React.ChangeEvent) => {
const file = e.target.files?.[0]
if (file) {
form.setValue("images", file)
const reader = new FileReader()
reader.onloadend = () => {
setPreview(reader.result as string)
}
reader.readAsDataURL(file)
}
}
const onSubmit = async (values: z.infer) => {
setLoading(true)
const formData = new FormData()
formData.append("title", values.title)
formData.append("slug", values.slug)
formData.append("content", values.content)
// Append categories - Backend likely expects multiple fields with same name or comma separated
// Based on curl example: -F 'category_ids=1'
// If multiple, standard is usually repeating the field
values.category_ids.forEach(id => {
formData.append("category_ids", id)
})
// Tags - Backend, dokümana göre tekrar eden 'tag_names' alanlarını bekliyor:
// -F 'tag_names=tag1' -F 'tag_names=tag2'
if (values.tag_names && values.tag_names.length > 0) {
values.tag_names.forEach(name => {
formData.append("tag_names", name)
})
}
// Image config
formData.append("width", values.width.toString())
formData.append("height", values.height.toString())
formData.append("quality", values.quality.toString())
formData.append("format", values.format)
if (values.images instanceof File) {
formData.append("images", values.images)
}
try {
if (post) {
await postService.updatePost(post.id || post.ID!, formData)
toast.success("Yazı başarıyla güncellendi")
} else {
await postService.createPost(formData)
toast.success("Yazı başarıyla oluşturuldu")
}
onOpenChange(false)
if (onSuccess) onSuccess()
} catch (error: unknown) {
console.error("Post save error:", error)
toast.error((error as Error).message || "Bir hata oluştu")
} finally {
setLoading(false)
}
}
return (
)
}