first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:33:39 +03:00
commit 4362c3b83f
1991 changed files with 285411 additions and 0 deletions

View File

@@ -0,0 +1,619 @@
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")
}