Files
aresv2/controllers/admin_product_controller.go
Beyhan Oğur 4362c3b83f first commit
2026-04-26 21:33:39 +03:00

620 lines
18 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package controllers
import (
dbConfig "ares/database/config"
"ares/database/models"
utils "ares/pkg/utis"
"ares/services"
"math"
"strconv"
"github.com/gofiber/fiber/v3"
)
// AdminContentProducts renders the products partial
func AdminContentProducts(c fiber.Ctx) error {
page, _ := strconv.Atoi(c.Query("page", "1"))
limit := 20
offset := (page - 1) * limit
search := c.Query("search", "")
showDeleted := c.Query("deleted") == "true"
var products []models.Product
var total int64
query := dbConfig.DB.Model(&models.Product{})
if showDeleted {
query = query.Unscoped().Where("deleted_at IS NOT NULL")
}
if search != "" {
query = query.Where("title LIKE ? OR slug LIKE ?", "%"+search+"%", "%"+search+"%")
}
query.Count(&total)
query.Preload("Categories").Preload("Tags").Order("created_at desc").Limit(limit).Offset(offset).Find(&products)
imageMap := make(map[uint]string)
for _, p := range products {
if p.Images != "" {
imgs := parseImagesField(p.Images)
if len(imgs) > 0 {
imageMap[p.ID] = imgs[0]
}
}
}
totalPages := int(math.Ceil(float64(total) / float64(limit)))
data := fiber.Map{
"Products": products,
"ImageMap": imageMap,
"Page": page,
"TotalPages": totalPages,
"NextPage": page + 1,
"PrevPage": page - 1,
"Search": search,
"ShowDeleted": showDeleted,
}
if c.Get("HX-Request") == "true" {
return c.Render("admin/partials/products", data)
}
return c.Render("admin/partials/products", data, "admin/layout")
}
// AdminProductNew renders create form
func AdminProductNew(c fiber.Ctx) error {
var cats []models.ProductCategory
var tags []models.ProductTag
dbConfig.DB.Order("title asc").Find(&cats)
dbConfig.DB.Order("name asc").Find(&tags)
return c.Render("admin/pages/product_form", fiber.Map{"IsEdit": false, "Categories": cats, "Tags": tags, "FirstImage": ""}, "admin/layout")
}
// AdminProductCreate handles creation
func AdminProductCreate(c fiber.Ctx) error {
title := c.FormValue("title")
if title == "" {
return c.Redirect().To("/admin/content/products?error=Başlık+gerekli")
}
product := models.Product{Title: title}
product.Content = c.FormValue("content")
// Slug handling
rawSlug := c.FormValue("slug")
if rawSlug == "" {
rawSlug = utils.Slugify(product.Title)
} else {
rawSlug = utils.Slugify(rawSlug)
}
attempt := rawSlug
i := 1
for {
var existing models.Product
err := dbConfig.DB.Unscoped().Where("slug = ?", attempt).First(&existing).Error
if err != nil {
break
}
attempt = rawSlug + "-" + strconv.Itoa(i)
i++
}
product.Slug = attempt
// Image Upload
priceStr := c.FormValue("price")
price, _ := strconv.ParseFloat(priceStr, 64)
width, _ := strconv.Atoi(c.FormValue("width"))
height, _ := strconv.Atoi(c.FormValue("height"))
quality, _ := strconv.Atoi(c.FormValue("quality"))
format := c.FormValue("format")
if format == "" {
format = "avif"
}
// DB fields
product.Price = price
product.Format = format
product.Width = width
product.Height = height
product.Quality = quality
// Use processAndSaveImage for main product image (for simplicity we store 1 image mapped as JSON array or just single string per parseImagesField)
imagePath, err := services.ProcessAndSaveImage(c, "image", services.ImageOptions{
Width: width,
Height: height,
Quality: quality,
Format: format,
Folder: "products",
})
if err == nil && imagePath != "" {
// Tek resim olarak string kaydet
product.Images = imagePath
}
if err := dbConfig.DB.Create(&product).Error; err != nil {
return c.Redirect().To("/admin/content/products?error=Oluşturma+başarısız")
}
// Handline Relations (Categories & Tags)
form, err := c.MultipartForm()
if err == nil && form != nil {
catIDs := form.Value["categories"]
for _, catIDStr := range catIDs {
catID, _ := strconv.Atoi(catIDStr)
if catID > 0 {
var cat models.ProductCategory
if err := dbConfig.DB.First(&cat, catID).Error; err == nil {
dbConfig.DB.Model(&product).Association("Categories").Append(&cat)
}
}
}
tagIDs := form.Value["tags"]
for _, tagIDStr := range tagIDs {
tagID, _ := strconv.Atoi(tagIDStr)
if tagID > 0 {
var tag models.ProductTag
if err := dbConfig.DB.First(&tag, tagID).Error; err == nil {
dbConfig.DB.Model(&product).Association("Tags").Append(&tag)
}
}
}
} else {
// if form data is not multipart, try basic FormValue array
// Though Fiber usually parses multiple values when we get them explicitly or we can just parse the body
}
return c.Redirect().To("/admin/content/products?success=Ürün+oluşturuldu")
}
// AdminProductEdit renders the edit hero form
func AdminProductEdit(c fiber.Ctx) error {
id := c.Params("id")
var product models.Product
if err := dbConfig.DB.Preload("Categories").Preload("Tags").First(&product, id).Error; err != nil {
return c.Status(fiber.StatusNotFound).SendString("Ürün bulunamadı")
}
var cats []models.ProductCategory
var tags []models.ProductTag
dbConfig.DB.Order("title asc").Find(&cats)
dbConfig.DB.Order("name asc").Find(&tags)
firstImage := ""
if product.Images != "" {
imgs := parseImagesField(product.Images)
if len(imgs) > 0 {
firstImage = imgs[0]
}
}
return c.Render("admin/pages/product_form", fiber.Map{
"IsEdit": true,
"Product": product,
"Categories": cats,
"Tags": tags,
"FirstImage": firstImage,
}, "admin/layout")
}
// AdminProductUpdate handles product update
func AdminProductUpdate(c fiber.Ctx) error {
id := c.Params("id")
var product models.Product
if err := dbConfig.DB.First(&product, id).Error; err != nil {
return c.Redirect().To("/admin/content/products?error=Ürün+bulunamadı")
}
product.Title = c.FormValue("title")
product.Content = c.FormValue("content")
// Slug update
rawSlug := c.FormValue("slug")
if rawSlug != "" && rawSlug != product.Slug {
rawSlug = utils.Slugify(rawSlug)
attempt := rawSlug
i := 1
for {
var existing models.Product
err := dbConfig.DB.Unscoped().Where("slug = ? AND id != ?", attempt, product.ID).First(&existing).Error
if err != nil {
break
}
attempt = rawSlug + "-" + strconv.Itoa(i)
i++
}
product.Slug = attempt
}
// Image Upload
priceStr := c.FormValue("price")
price, _ := strconv.ParseFloat(priceStr, 64)
width, _ := strconv.Atoi(c.FormValue("width"))
height, _ := strconv.Atoi(c.FormValue("height"))
quality, _ := strconv.Atoi(c.FormValue("quality"))
format := c.FormValue("format")
if format == "" {
format = "avif"
}
// DB fields
product.Price = price
product.Format = format
product.Width = width
product.Height = height
product.Quality = quality
imagePath, err := services.ProcessAndSaveImage(c, "image", services.ImageOptions{
Width: width,
Height: height,
Quality: quality,
Format: format,
Folder: "products",
})
if err == nil && imagePath != "" {
product.Images = imagePath
}
if err := dbConfig.DB.Save(&product).Error; err != nil {
return c.Redirect().To("/admin/content/products?error=Güncelleme+başarısız")
}
// Handline Relations (Categories & Tags)
dbConfig.DB.Model(&product).Association("Categories").Clear()
dbConfig.DB.Model(&product).Association("Tags").Clear()
form, err := c.MultipartForm()
if err == nil && form != nil {
catIDs := form.Value["categories"]
for _, catIDStr := range catIDs {
catID, _ := strconv.Atoi(catIDStr)
if catID > 0 {
var cat models.ProductCategory
if err := dbConfig.DB.First(&cat, catID).Error; err == nil {
dbConfig.DB.Model(&product).Association("Categories").Append(&cat)
}
}
}
tagIDs := form.Value["tags"]
for _, tagIDStr := range tagIDs {
tagID, _ := strconv.Atoi(tagIDStr)
if tagID > 0 {
var tag models.ProductTag
if err := dbConfig.DB.First(&tag, tagID).Error; err == nil {
dbConfig.DB.Model(&product).Association("Tags").Append(&tag)
}
}
}
}
return c.Redirect().To("/admin/content/products?success=Ürün+güncellendi")
}
// AdminProductDelete handles soft delete
func AdminProductDelete(c fiber.Ctx) error {
id := c.Params("id")
if err := dbConfig.DB.Delete(&models.Product{}, id).Error; err != nil {
return c.Redirect().To("/admin/content/products?error=Silme+hatası")
}
return c.Redirect().To("/admin/content/products?success=Ürün+silindi")
}
// AdminProductRestore handles restore
func AdminProductRestore(c fiber.Ctx) error {
id := c.Params("id")
if err := dbConfig.DB.Unscoped().Model(&models.Product{}).Where("id = ?", id).Update("deleted_at", nil).Error; err != nil {
return c.Redirect().To("/admin/content/products?error=Geri+yükleme+hatası")
}
return c.Redirect().To("/admin/content/products?deleted=true&success=Ürün+geri+yüklendi")
}
// --- ProductCategory Management ---
func AdminContentProductCategories(c fiber.Ctx) error {
var categories []models.ProductCategory
showDeleted := c.Query("deleted") == "true"
query := dbConfig.DB.Model(&models.ProductCategory{})
if showDeleted {
query = query.Unscoped().Where("deleted_at IS NOT NULL")
}
query.Preload("Parent").Order("title asc").Find(&categories)
data := fiber.Map{
"Categories": categories,
"ShowDeleted": showDeleted,
}
if c.Get("HX-Request") == "true" {
return c.Render("admin/partials/product_categories", data)
}
return c.Render("admin/partials/product_categories", data, "admin/layout")
}
func AdminProductCategoryNew(c fiber.Ctx) error {
var parents []models.ProductCategory
dbConfig.DB.Order("title asc").Find(&parents)
return c.Render("admin/pages/product_category_form", fiber.Map{
"IsEdit": false,
"Parents": parents,
}, "admin/layout")
}
func AdminProductCategoryCreate(c fiber.Ctx) error {
cat := new(models.ProductCategory)
if err := c.Bind().Body(cat); err != nil {
return c.Redirect().To("/admin/content/product-categories?error=Geçersiz+istek")
}
if cat.Title == "" {
return c.Redirect().To("/admin/content/product-categories?error=Başlık+zorunlu")
}
rawSlug := c.FormValue("slug")
if rawSlug == "" {
rawSlug = utils.Slugify(cat.Title)
} else {
rawSlug = utils.Slugify(rawSlug)
}
attempt := rawSlug
i := 1
for {
var existing models.ProductCategory
err := dbConfig.DB.Unscoped().Where("slug = ?", attempt).First(&existing).Error
if err != nil {
break
}
attempt = rawSlug + "-" + strconv.Itoa(i)
i++
}
cat.Slug = attempt
// Handle ParentID
parentIDStr := c.FormValue("parent_id")
if parentIDStr != "" {
if pid, err := strconv.ParseUint(parentIDStr, 10, 32); err == nil && pid > 0 {
pidUint := uint(pid)
cat.ParentID = &pidUint
}
} else {
cat.ParentID = nil
}
if err := dbConfig.DB.Create(cat).Error; err != nil {
return c.Redirect().To("/admin/content/product-categories?error=Oluşturma+başarısız")
}
return c.Redirect().To("/admin/content/product-categories?success=Kategori+oluşturuldu")
}
func AdminProductCategoryEdit(c fiber.Ctx) error {
id := c.Params("id")
var cat models.ProductCategory
if err := dbConfig.DB.First(&cat, id).Error; err != nil {
return c.Redirect().To("/admin/content/product-categories?error=Kategori+bulunamadı")
}
var parents []models.ProductCategory
dbConfig.DB.Where("id != ?", id).Order("title asc").Find(&parents)
return c.Render("admin/pages/product_category_form", fiber.Map{
"IsEdit": true,
"Category": cat,
"Parents": parents,
}, "admin/layout")
}
func AdminProductCategoryUpdate(c fiber.Ctx) error {
id := c.Params("id")
var cat models.ProductCategory
if err := dbConfig.DB.First(&cat, id).Error; err != nil {
return c.Redirect().To("/admin/content/product-categories?error=Kategori+bulunamadı")
}
cat.Title = c.FormValue("title")
cat.Description = c.FormValue("description")
cat.Keywords = c.FormValue("keywords")
rawSlug := c.FormValue("slug")
if rawSlug != "" && rawSlug != cat.Slug {
rawSlug = utils.Slugify(rawSlug)
attempt := rawSlug
i := 1
for {
var existing models.ProductCategory
err := dbConfig.DB.Unscoped().Where("slug = ? AND id != ?", attempt, cat.ID).First(&existing).Error
if err != nil {
break
}
attempt = rawSlug + "-" + strconv.Itoa(i)
i++
}
cat.Slug = attempt
}
parentIDStr := c.FormValue("parent_id")
if parentIDStr != "" {
if pid, err := strconv.ParseUint(parentIDStr, 10, 32); err == nil && pid > 0 {
pidUint := uint(pid)
if pidUint != cat.ID {
cat.ParentID = &pidUint
}
}
} else {
cat.ParentID = nil
}
if err := dbConfig.DB.Save(&cat).Error; err != nil {
return c.Redirect().To("/admin/content/product-categories?error=Güncelleme+başarısız")
}
return c.Redirect().To("/admin/content/product-categories?success=Kategori+güncellendi")
}
func AdminProductCategoryDelete(c fiber.Ctx) error {
id := c.Params("id")
if err := dbConfig.DB.Delete(&models.ProductCategory{}, id).Error; err != nil {
return c.Redirect().To("/admin/content/product-categories?error=Silme+başarısız")
}
return c.Redirect().To("/admin/content/product-categories?success=Kategori+silindi")
}
func AdminProductCategoryRestore(c fiber.Ctx) error {
id := c.Params("id")
if err := dbConfig.DB.Unscoped().Model(&models.ProductCategory{}).Where("id = ?", id).Update("deleted_at", nil).Error; err != nil {
return c.Redirect().To("/admin/content/product-categories?error=Geri+yükleme+başarısız")
}
return c.Redirect().To("/admin/content/product-categories?deleted=true&success=Kategori+geri+yüklendi")
}
// --- ProductTag Management ---
func AdminContentProductTags(c fiber.Ctx) error {
var tags []models.ProductTag
showDeleted := c.Query("deleted") == "true"
query := dbConfig.DB.Model(&models.ProductTag{})
if showDeleted {
query = query.Unscoped().Where("deleted_at IS NOT NULL")
}
query.Order("name asc").Find(&tags)
data := fiber.Map{
"Tags": tags,
"ShowDeleted": showDeleted,
}
if c.Get("HX-Request") == "true" {
return c.Render("admin/partials/product_tags", data)
}
return c.Render("admin/partials/product_tags", data, "admin/layout")
}
func AdminProductTagNew(c fiber.Ctx) error {
return c.Render("admin/pages/product_tag_form", fiber.Map{
"IsEdit": false,
}, "admin/layout")
}
func AdminProductTagCreate(c fiber.Ctx) error {
tag := new(models.ProductTag)
if err := c.Bind().Body(tag); err != nil {
return c.Redirect().To("/admin/content/product-tags?error=Geçersiz+istek")
}
if tag.Name == "" {
return c.Redirect().To("/admin/content/product-tags?error=İsim+zorunlu")
}
if err := dbConfig.DB.Create(tag).Error; err != nil {
return c.Redirect().To("/admin/content/product-tags?error=Oluşturma+başarısız")
}
return c.Redirect().To("/admin/content/product-tags?success=Etiket+oluşturuldu")
}
func AdminProductTagEdit(c fiber.Ctx) error {
id := c.Params("id")
var tag models.ProductTag
if err := dbConfig.DB.First(&tag, id).Error; err != nil {
return c.Redirect().To("/admin/content/product-tags?error=Etiket+bulunamadı")
}
return c.Render("admin/pages/product_tag_form", fiber.Map{
"IsEdit": true,
"Tag": tag,
}, "admin/layout")
}
func AdminProductTagUpdate(c fiber.Ctx) error {
id := c.Params("id")
var tag models.ProductTag
if err := dbConfig.DB.First(&tag, id).Error; err != nil {
return c.Redirect().To("/admin/content/product-tags?error=Etiket+bulunamadı")
}
tag.Name = c.FormValue("name")
if err := dbConfig.DB.Save(&tag).Error; err != nil {
return c.Redirect().To("/admin/content/product-tags?error=Güncelleme+başarısız")
}
return c.Redirect().To("/admin/content/product-tags?success=Etiket+güncellendi")
}
func AdminProductTagDelete(c fiber.Ctx) error {
id := c.Params("id")
if err := dbConfig.DB.Delete(&models.ProductTag{}, id).Error; err != nil {
return c.Redirect().To("/admin/content/product-tags?error=Silme+başarısız")
}
return c.Redirect().To("/admin/content/product-tags?success=Etiket+silindi")
}
func AdminProductTagRestore(c fiber.Ctx) error {
id := c.Params("id")
if err := dbConfig.DB.Unscoped().Model(&models.ProductTag{}).Where("id = ?", id).Update("deleted_at", nil).Error; err != nil {
return c.Redirect().To("/admin/content/product-tags?error=Geri+yükleme+başarısız")
}
return c.Redirect().To("/admin/content/product-tags?deleted=true&success=Etiket+geri+yüklendi")
}
// AdminContentProductComments handles rendering the Product Comments list in the admin panel
func AdminContentProductComments(c fiber.Ctx) error {
var comments []models.ProductComment
// Preload the User and Product for display (Wait, user and product relationships are missing in model temporarily)
// We'll just list them out manually
query := dbConfig.DB.Model(&models.ProductComment{})
// Optional filtering by specific product via query string
productID := c.Query("product_id")
if productID != "" {
if pid, err := strconv.Atoi(productID); err == nil {
query = query.Where("product_id = ?", pid)
}
}
query.Order("created_at desc").Find(&comments)
data := fiber.Map{
"ProductComments": comments,
"ProductID": productID,
}
if c.Get("HX-Request") == "true" {
return c.Render("admin/partials/product_comments", data)
}
return c.Render("admin/partials/product_comments", data, "admin/layout")
}
// AdminProductCommentDelete handles hard deleting a product comment
func AdminProductCommentDelete(c fiber.Ctx) error {
id := c.Params("id")
if err := dbConfig.DB.Unscoped().Where("id = ?", id).Delete(&models.ProductComment{}).Error; err != nil {
return c.Redirect().To("/admin/content/product-comments?error=Yorum+silinemedi")
}
return c.Redirect().To("/admin/content/product-comments?deleted=true&success=Yorum+silindi")
}
// AdminContentProductCategoryViews handles rendering the Product Category Views list
func AdminContentProductCategoryViews(c fiber.Ctx) error {
var views []models.ProductCategoryView
query := dbConfig.DB.Model(&models.ProductCategoryView{})
// Filter by Category ID
categoryID := c.Query("category_id")
if categoryID != "" {
if cid, err := strconv.Atoi(categoryID); err == nil {
query = query.Where("category_id = ?", cid)
}
}
query.Order("created_at desc").Find(&views)
data := fiber.Map{
"Views": views,
"CategoryID": categoryID,
}
if c.Get("HX-Request") == "true" {
return c.Render("admin/partials/product_category_views", data)
}
return c.Render("admin/partials/product_category_views", data, "admin/layout")
}