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") }