10 Commits

Author SHA1 Message Date
861343b338 prefil prices on edititem
All checks were successful
Go / build (push) Successful in 12m32s
2025-04-16 02:10:11 +02:00
68c8654bf3 set selected printmode in edithtml 2025-04-16 01:56:16 +02:00
e62a45372f move error/success to top on edithmtl 2025-04-16 01:56:00 +02:00
d17c33f6ee set selected tags in edititem 2025-04-16 01:55:40 +02:00
ae36903e73 add cups to path
All checks were successful
Go / build (push) Successful in 12m26s
2025-04-15 16:41:23 +02:00
1a5df21fa8 lazy load images
All checks were successful
Go / build (push) Successful in 12m52s
2025-04-15 15:54:41 +02:00
9c15514758 rename to zines
All checks were successful
Go / build (push) Successful in 12m28s
2025-04-15 14:10:00 +02:00
27cf7c37cf resize preview images on upload
Some checks failed
Go / build (push) Has been cancelled
2025-04-15 13:57:28 +02:00
03f1ce361a add missing return in registerhandler
All checks were successful
Go / build (push) Successful in 12m35s
2025-04-15 10:46:46 +02:00
c55cf4480b Merge pull request 'userhandling' (#16) from userhandling into master
All checks were successful
Go / build (push) Successful in 12m35s
Reviewed-on: #16
2025-04-15 01:06:11 +02:00
7 changed files with 125 additions and 31 deletions

View File

@@ -68,6 +68,9 @@ func (rc *shopItemController) GetById(c *gin.Context) {
ReplyOK(c, shopItem) ReplyOK(c, shopItem)
} }
// this currently creates quite big preview images
// workaround is running the following command in the uploads folder:
// for file in *.png; do convert "$file" -resize 35% "$file"; done
func (rc *shopItemController) NewShopItemFromForm(ctx *gin.Context) (models.ShopItem, error) { func (rc *shopItemController) NewShopItemFromForm(ctx *gin.Context) (models.ShopItem, error) {
defaultImagePath := "static/img/zine.jpg" defaultImagePath := "static/img/zine.jpg"
name := ctx.PostForm("name") name := ctx.PostForm("name")
@@ -106,6 +109,14 @@ func (rc *shopItemController) NewShopItemFromForm(ctx *gin.Context) (models.Shop
if err != nil { if err != nil {
fmt.Println("Error during pdftoppm: ", err.Error()) fmt.Println("Error during pdftoppm: ", err.Error())
} }
cmd2 := exec.Command("convert", dstImage, "-resize", "35%", dstImage)
_, err = cmd2.Output()
if err != nil {
fmt.Println("Error during resizing preview image: ", err.Error())
}
} }
} else { } else {
fmt.Println(err) fmt.Println(err)
@@ -365,6 +376,13 @@ func (rc *shopItemController) AddItemsHandler(c *gin.Context) {
fmt.Println("Error during pdftoppm: ", err.Error()) fmt.Println("Error during pdftoppm: ", err.Error())
} }
cmd2 := exec.Command("convert", dstImage, "-resize", "35%", dstImage)
_, err = cmd2.Output()
if err != nil {
fmt.Println("Error during resizing preview image: ", err.Error())
}
category, err := models.ParseCategory("Zine") category, err := models.ParseCategory("Zine")
if err != nil { if err != nil {
errorHandler(err) errorHandler(err)
@@ -414,21 +432,65 @@ func (rc *shopItemController) AddItemsHandler(c *gin.Context) {
c.HTML(http.StatusOK, "batchupload.html", data) c.HTML(http.StatusOK, "batchupload.html", data)
} }
func (rc *shopItemController) EditItemView(c *gin.Context) { func GetCheckedTags(item models.ShopItem) ([]models.CheckedTag, error) {
shopItem, err := repositories.ShopItems.GetById(c.Param("id")) allTags, err := repositories.Tags.GetAll()
tags, err := repositories.Tags.GetAll()
if err != nil { if err != nil {
c.HTML(http.StatusBadRequest, "edititem.html", gin.H{"error": err}) return nil, err
} }
fmt.Println(shopItem) var tags []models.CheckedTag
for _, tag := range allTags {
tmpTag := models.CheckedTag{
Tag: tag,
Checked: "",
}
for _, itemTag := range item.Tags {
if itemTag.Name == tmpTag.Name {
tmpTag.Checked = "checked"
break
}
}
tags = append(tags, tmpTag)
}
return tags, nil
}
func (rc *shopItemController) EditItemView(c *gin.Context) {
shopItem, err := repositories.ShopItems.GetById(c.Param("id"))
if err != nil {
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err})
}
tags, err := GetCheckedTags(shopItem)
if err != nil {
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err})
}
priceBW := ""
priceColored := ""
for _, variant := range shopItem.Variants {
if variant.Name == "B/W" {
priceBW = fmt.Sprintf("%.2f", variant.Price)
}
if variant.Name == "Colored" {
priceColored = fmt.Sprintf("%.2f", variant.Price)
}
}
data := CreateSessionData(c, gin.H{ data := CreateSessionData(c, gin.H{
"error": "", "error": "",
"success": "", "success": "",
"shopItem": shopItem, "shopItem": shopItem,
"tags": tags, "tags": tags,
"priceBW": priceBW,
"priceColored": priceColored,
}) })
c.HTML(http.StatusOK, "edititem.html", data) c.HTML(http.StatusOK, "edititem.html", data)
@@ -471,18 +533,34 @@ func (rc *shopItemController) EditItemHandler(c *gin.Context) {
newShopItem.PrintMode = shopItem.PrintMode newShopItem.PrintMode = shopItem.PrintMode
tags, err := repositories.Tags.GetAll() tags, err := GetCheckedTags(newShopItem)
if err != nil { if err != nil {
c.HTML(http.StatusBadRequest, "edititem.html", gin.H{"error": err}) c.HTML(http.StatusBadRequest, "edititem.html", gin.H{"error": err})
return return
} }
priceBW := ""
priceColored := ""
for _, variant := range newShopItem.Variants {
if variant.Name == "B/W" {
priceBW = fmt.Sprintf("%.2f", variant.Price)
}
if variant.Name == "Colored" {
priceColored = fmt.Sprintf("%.2f", variant.Price)
}
}
_, err = repositories.ShopItems.Update(newShopItem) _, err = repositories.ShopItems.Update(newShopItem)
if err != nil { if err != nil {
data := CreateSessionData(c, gin.H{ data := CreateSessionData(c, gin.H{
"error": err, "error": err,
"success": "", "success": "",
"shopItem": newShopItem,
"tags": tags, "tags": tags,
"priceBW": priceBW,
"priceColored": priceColored,
}) })
c.HTML(http.StatusOK, "edititem.html", data) c.HTML(http.StatusOK, "edititem.html", data)
@@ -492,7 +570,10 @@ func (rc *shopItemController) EditItemHandler(c *gin.Context) {
data := CreateSessionData(c, gin.H{ data := CreateSessionData(c, gin.H{
"error": "", "error": "",
"success": fmt.Sprintf("Item '%s' Updated", newShopItem.Name), "success": fmt.Sprintf("Item '%s' Updated", newShopItem.Name),
"shopItem": newShopItem,
"tags": tags, "tags": tags,
"priceBW": priceBW,
"priceColored": priceColored,
}) })
c.HTML(http.StatusOK, "edititem.html", data) c.HTML(http.StatusOK, "edititem.html", data)

View File

@@ -188,6 +188,7 @@ func (rc *UserController) RegisterHandler(c *gin.Context) {
} }
c.HTML(http.StatusOK, "register.html", data) c.HTML(http.StatusOK, "register.html", data)
return
} }
tokenExists, err := repositories.Tokens.Exists(token) tokenExists, err := repositories.Tokens.Exists(token)

View File

@@ -20,6 +20,8 @@
go go
gotools gotools
poppler_utils #get first pdf page to png poppler_utils #get first pdf page to png
cups
imagemagick
tailwindcss tailwindcss
]; ];
}; };
@@ -71,6 +73,8 @@
environment.systemPackages = [ environment.systemPackages = [
zineshop-pkg zineshop-pkg
pkgs.poppler_utils #get first pdf page to png pkgs.poppler_utils #get first pdf page to png
pkgs.cups
pkgs.imagemagick
]; ];
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
@@ -100,7 +104,7 @@
WorkingDirectory = "/var/lib/zineshop"; WorkingDirectory = "/var/lib/zineshop";
ExecStart = pkgs.writeScript "start-zineshop" '' ExecStart = pkgs.writeScript "start-zineshop" ''
#! ${pkgs.bash}/bin/bash #! ${pkgs.bash}/bin/bash
PATH="$PATH:${lib.makeBinPath [ pkgs.poppler_utils ]}" PATH="$PATH:${lib.makeBinPath [ pkgs.poppler_utils pkgs.cups pkgs.imagemagick ]}"
${zineshop-pkg}/bin/zineshop ${zineshop-pkg}/bin/zineshop
''; '';
Restart = "on-failure"; Restart = "on-failure";

View File

@@ -14,6 +14,11 @@ type Tag struct {
ShopItems []ShopItem `gorm:"many2many:item_tags;"` ShopItems []ShopItem `gorm:"many2many:item_tags;"`
} }
type CheckedTag struct {
Tag
Checked string
}
func NewTag(ctx *gin.Context) (Tag, error) { func NewTag(ctx *gin.Context) (Tag, error) {
colors := []string{ colors := []string{
"red", "red",

View File

@@ -5,9 +5,19 @@
<img class="mx-auto h-10 w-auto" src="/static/img/logo-black.png" alt="Your Company"> <img class="mx-auto h-10 w-auto" src="/static/img/logo-black.png" alt="Your Company">
<h2 class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">Edit Item</h2> <h2 class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">Edit Item</h2>
<p class="mt-10 text-center text-sm/6 text-red-500">
{{ .data.error }}
</p>
<p class="mt-10 text-center text-sm/6 text-green-500">
{{ .data.success }}
</p>
<a href="/shopitems/{{ .data.shopItem.ID }}"> <a href="/shopitems/{{ .data.shopItem.ID }}">
<img src="/{{ .data.shopItem.Image }}" alt="Product Image" class="aspect-4/5 mx-auto rounded-md bg-gray-200 object-cover group-hover:opacity-75 lg:aspect-auto lg:h-80"> <img src="/{{ .data.shopItem.Image }}" alt="Product Image" class="aspect-4/5 mx-auto rounded-md bg-gray-200 object-cover group-hover:opacity-75 lg:aspect-auto lg:h-80">
</a> </a>
</div> </div>
@@ -37,7 +47,7 @@
<div class="space-y-2"> <div class="space-y-2">
{{ range .data.tags }} {{ range .data.tags }}
<label class="flex text-sm/6 items-center"> <label class="flex text-sm/6 items-center">
<input type="checkbox" class="form-checkbox h-4 w-4 text-gray-900" value="{{ .ID }}" name="tags[]"> <input type="checkbox" {{ .Checked }} class="form-checkbox h-4 w-4 text-gray-900" value="{{ .ID }}" name="tags[]">
<span class="ml-2 text-sm/6 text-gray-900">{{ .Name }}</span> <span class="ml-2 text-sm/6 text-gray-900">{{ .Name }}</span>
</label> </label>
{{ end }} {{ end }}
@@ -65,7 +75,7 @@
<label for="variant-value1" class="block text-sm/6 font-medium text-gray-900">Price B/W</label> <label for="variant-value1" class="block text-sm/6 font-medium text-gray-900">Price B/W</label>
</div> </div>
<div class="mt-2"> <div class="mt-2">
<input type="number" name="variant-value[]" id="variant-value1" step="0.01" min="0.00" required class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6"> <input type="number" value="{{ .data.priceBW }}" name="variant-value[]" id="variant-value1" step="0.01" min="0.00" required class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6">
</div> </div>
</div> </div>
@@ -76,7 +86,7 @@
<label for="variant-value2" class="block text-sm/6 font-medium text-gray-900">Price Colored</label> <label for="variant-value2" class="block text-sm/6 font-medium text-gray-900">Price Colored</label>
</div> </div>
<div class="mt-2"> <div class="mt-2">
<input type="number" name="variant-value[]" id="variant-value2" step="0.01" min="0.00" class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6"> <input type="number" value="{{ .data.priceColored }}" name="variant-value[]" id="variant-value2" step="0.01" min="0.00" class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6">
</div> </div>
</div> </div>
@@ -131,20 +141,13 @@
Print Mode Print Mode
</label> </label>
<select name="print-mode" required class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"> <select name="print-mode" required class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option selected value="CreateBooklet">Create Booklet</option> <option selected value="{{ .data.shopItem.PrintMode }}">{{ .data.shopItem.PrintMode }}</option>
<option value="CreateBooklet">CreateBooklet</option>
<option value="LongEdge">Long Edge</option> <option value="LongEdge">Long Edge</option>
<option value="ShortEdge">Short Edge</option> <option value="ShortEdge">Short Edge</option>
</select> </select>
<p class="mt-10 text-center text-sm/6 text-red-500">
{{ .data.error }}
</p>
<p class="mt-10 text-center text-sm/6 text-green-500">
{{ .data.success }}
</p>
<div> <div>
<button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Update Item</button> <button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Update Item</button>
</div> </div>

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<title>Zine Shop</title> <title>Zines</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/static/output.css" rel="stylesheet"> <link href="/static/output.css" rel="stylesheet">

View File

@@ -8,7 +8,7 @@
<div class="myClass group relative"> <div class="myClass group relative">
<a href="/shopitems/{{ .ID }}"> <a href="/shopitems/{{ .ID }}">
<img src="/{{ .Image }}" alt="Product Image" class="aspect-4/5 mx-auto rounded bg-gray-200 object-cover group-hover:opacity-75 lg:aspect-auto lg:h-80"> <img loading="lazy" src="/{{ .Image }}" alt="Product Image" class="aspect-4/5 mx-auto rounded bg-gray-200 object-cover group-hover:opacity-75 lg:aspect-auto lg:h-80">
</a> </a>
<div class="mt-4 flex justify-between"> <div class="mt-4 flex justify-between">
<div> <div>