2 Commits

Author SHA1 Message Date
f39b6205d1 add basic print controller + view 2025-04-10 12:54:03 +02:00
5a7565663d update 2025-04-10 11:52:34 +02:00
8 changed files with 328 additions and 185 deletions

View File

@@ -0,0 +1,75 @@
package controllers
import (
"fmt"
"net/http"
"example.com/gin/test/models"
"example.com/gin/test/repositories"
"github.com/gin-gonic/gin"
)
type PrintController interface {
PrintVariantView(*gin.Context)
PrintVariantHandler(*gin.Context)
}
type printController struct{}
func NewPrintController() PrintController {
return &printController{}
}
func (rc *printController) PrintVariantView(c *gin.Context) {
variant, err := repositories.ShopItems.GetVariantById(c.Param("id"))
if err != nil {
c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}})
}
shopItem, err := repositories.ShopItems.GetById(fmt.Sprintf("%v", variant.ShopItemID))
if err != nil {
c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}})
}
type ShopItemVariantPair struct {
ShopItem models.ShopItem
ItemVariant models.ItemVariant
}
data := CreateSessionData(c, gin.H{
"itemVariants": []ShopItemVariantPair{
{ShopItem: shopItem, ItemVariant: variant},
},
})
fmt.Println(data)
c.HTML(http.StatusOK, "printvariant.html", data)
}
func (rc *printController) PrintVariantHandler(c *gin.Context) {
err := repositories.ShopItems.DeleteById(c.Param("id"))
if err != nil {
data := CreateSessionData(c, gin.H{
"error": err,
"success": "",
})
c.HTML(http.StatusOK, "deleteitem.html", data)
}
shopItems, _ := repositories.ShopItems.GetAll()
fmt.Println(len(shopItems))
data := CreateSessionData(c, gin.H{
"title": "shopItem Page",
"shopItems": shopItems,
})
fmt.Println(data)
c.HTML(http.StatusOK, "index.html", data)
}

View File

@@ -3,9 +3,9 @@ package controllers
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"path/filepath"
"os/exec" "os/exec"
"path/filepath"
"strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -198,7 +198,6 @@ func (rc *shopItemController) Create(c *gin.Context) {
ReplyOK(c, "shopItem was created") ReplyOK(c, "shopItem was created")
} }
func (rc *shopItemController) Update(c *gin.Context) { func (rc *shopItemController) Update(c *gin.Context) {
shopItemId, err := strconv.Atoi(c.Param("id")) shopItemId, err := strconv.Atoi(c.Param("id"))
@@ -279,7 +278,6 @@ func (rc *shopItemController) AddItemView(c *gin.Context) {
c.HTML(http.StatusOK, "additem.html", data) c.HTML(http.StatusOK, "additem.html", data)
} }
func (rc *shopItemController) AddItemHandler(c *gin.Context) { func (rc *shopItemController) AddItemHandler(c *gin.Context) {
errorHandler := func(err error, tags []models.Tag) { errorHandler := func(err error, tags []models.Tag) {
data := CreateSessionData(c, gin.H{ data := CreateSessionData(c, gin.H{
@@ -318,7 +316,6 @@ func (rc *shopItemController) AddItemHandler(c *gin.Context) {
c.HTML(http.StatusOK, "additem.html", data) c.HTML(http.StatusOK, "additem.html", data)
} }
func (rc *shopItemController) EditItemView(c *gin.Context) { func (rc *shopItemController) EditItemView(c *gin.Context) {
shopItem, err := repositories.ShopItems.GetById(c.Param("id")) shopItem, err := repositories.ShopItems.GetById(c.Param("id"))
tags, err := repositories.Tags.GetAll() tags, err := repositories.Tags.GetAll()
@@ -339,7 +336,6 @@ func (rc *shopItemController) EditItemView(c *gin.Context) {
c.HTML(http.StatusOK, "edititem.html", data) c.HTML(http.StatusOK, "edititem.html", data)
} }
func (rc *shopItemController) EditItemHandler(c *gin.Context) { func (rc *shopItemController) EditItemHandler(c *gin.Context) {
shopItem, err := rc.NewShopItemFromForm(c) shopItem, err := rc.NewShopItemFromForm(c)
@@ -410,7 +406,6 @@ func (rc *shopItemController) DeleteItemView(c *gin.Context) {
c.HTML(http.StatusOK, "deleteitem.html", data) c.HTML(http.StatusOK, "deleteitem.html", data)
} }
func (rc *shopItemController) DeleteItemHandler(c *gin.Context) { func (rc *shopItemController) DeleteItemHandler(c *gin.Context) {
err := repositories.ShopItems.DeleteById(c.Param("id")) err := repositories.ShopItems.DeleteById(c.Param("id"))

View File

@@ -18,6 +18,7 @@ var (
shopItemController controllers.ShopItemController = controllers.NewShopItemController() shopItemController controllers.ShopItemController = controllers.NewShopItemController()
userController controllers.UserController = controllers.UserController{} userController controllers.UserController = controllers.UserController{}
cartItemController controllers.CartItemController = controllers.NewCartItemController() cartItemController controllers.CartItemController = controllers.NewCartItemController()
printController controllers.PrintController = controllers.NewPrintController()
authValidator middlewares.AuthValidator = middlewares.AuthValidator{} authValidator middlewares.AuthValidator = middlewares.AuthValidator{}
) )
@@ -81,6 +82,8 @@ func main() {
viewRoutes.POST("/shopitems/:id/edit", authValidator.RequireAuth, shopItemController.EditItemHandler) viewRoutes.POST("/shopitems/:id/edit", authValidator.RequireAuth, shopItemController.EditItemHandler)
viewRoutes.GET("/shopitems/:id/delete", authValidator.RequireAuth, shopItemController.DeleteItemView) viewRoutes.GET("/shopitems/:id/delete", authValidator.RequireAuth, shopItemController.DeleteItemView)
viewRoutes.POST("/shopitems/:id/delete", authValidator.RequireAuth, shopItemController.DeleteItemHandler) viewRoutes.POST("/shopitems/:id/delete", authValidator.RequireAuth, shopItemController.DeleteItemHandler)
viewRoutes.GET("/variant/:id/print", authValidator.RequireAuth, printController.PrintVariantView)
viewRoutes.POST("/variant/:id/print", authValidator.RequireAuth, printController.PrintVariantHandler)
viewRoutes.GET("/tags", authValidator.RequireAuth, shopItemController.TagView) viewRoutes.GET("/tags", authValidator.RequireAuth, shopItemController.TagView)
viewRoutes.POST("/tags/:id", authValidator.RequireAuth, shopItemController.TagHandler) viewRoutes.POST("/tags/:id", authValidator.RequireAuth, shopItemController.TagHandler)
viewRoutes.POST("/tags", authValidator.RequireAuth, shopItemController.AddTagHandler) viewRoutes.POST("/tags", authValidator.RequireAuth, shopItemController.AddTagHandler)

View File

@@ -8,10 +8,13 @@ import (
/* /*
Sticker Sticker
- name, abstr, descr, price, tag - name, abstr, descr, price, tag
Poster Poster
- name, abstr, descr, price bw/colored, tag - name, abstr, descr, price bw/colored, tag
Zines Zines
- name, abstr, descr, price bw/colored/coloredcoveronly, tag - name, abstr, descr, price bw/colored/coloredcoveronly, tag
Books Books
- name, abstr, descr, price, tag - name, abstr, descr, price, tag
*/ */

View File

@@ -587,22 +587,6 @@ video {
right: 0px; right: 0px;
} }
.-bottom-\[1\.75rem\] {
bottom: -1.75rem;
}
.end-0 {
inset-inline-end: 0px;
}
.left-1\/2 {
left: 50%;
}
.start-0 {
inset-inline-start: 0px;
}
.col-span-12 { .col-span-12 {
grid-column: span 12 / span 12; grid-column: span 12 / span 12;
} }
@@ -630,11 +614,6 @@ video {
margin-right: auto; margin-right: auto;
} }
.-mx-5 {
margin-left: -1.25rem;
margin-right: -1.25rem;
}
.mb-2 { .mb-2 {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
@@ -719,16 +698,6 @@ video {
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
} }
.size-5 {
width: 1.25rem;
height: 1.25rem;
}
.size-6 {
width: 1.5rem;
height: 1.5rem;
}
.h-10 { .h-10 {
height: 2.5rem; height: 2.5rem;
} }
@@ -793,14 +762,6 @@ video {
width: 100%; width: 100%;
} }
.w-1\/4 {
width: 25%;
}
.w-2\/4 {
width: 50%;
}
.max-w-2xl { .max-w-2xl {
max-width: 42rem; max-width: 42rem;
} }
@@ -837,11 +798,6 @@ video {
flex-grow: 1; flex-grow: 1;
} }
.-translate-x-1\/2 {
--tw-translate-x: -50%;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.cursor-pointer { .cursor-pointer {
cursor: pointer; cursor: pointer;
} }
@@ -854,10 +810,6 @@ video {
grid-template-columns: repeat(12, minmax(0, 1fr)); grid-template-columns: repeat(12, minmax(0, 1fr));
} }
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.flex-col { .flex-col {
flex-direction: column; flex-direction: column;
} }
@@ -870,14 +822,6 @@ video {
align-items: center; align-items: center;
} }
.justify-start {
justify-content: flex-start;
}
.justify-end {
justify-content: flex-end;
}
.justify-center { .justify-center {
justify-content: center; justify-content: center;
} }
@@ -894,10 +838,6 @@ video {
gap: 1.25rem; gap: 1.25rem;
} }
.gap-2 {
gap: 0.5rem;
}
.gap-x-6 { .gap-x-6 {
-moz-column-gap: 1.5rem; -moz-column-gap: 1.5rem;
column-gap: 1.5rem; column-gap: 1.5rem;
@@ -1084,11 +1024,6 @@ video {
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
} }
.bg-gray-600 {
--tw-bg-opacity: 1;
background-color: rgb(75 85 99 / var(--tw-bg-opacity, 1));
}
.fill-red-50 { .fill-red-50 {
fill: #fef2f2; fill: #fef2f2;
} }
@@ -1366,9 +1301,9 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity, 1)); color: rgb(255 255 255 / var(--tw-text-opacity, 1));
} }
.text-blue-600 { .text-red-900 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1)); color: rgb(127 29 29 / var(--tw-text-opacity, 1));
} }
.underline { .underline {
@@ -1427,37 +1362,6 @@ video {
color: rgb(156 163 175 / var(--tw-text-opacity, 1)); color: rgb(156 163 175 / var(--tw-text-opacity, 1));
} }
.after\:mt-4::after {
content: var(--tw-content);
margin-top: 1rem;
}
.after\:block::after {
content: var(--tw-content);
display: block;
}
.after\:h-1::after {
content: var(--tw-content);
height: 0.25rem;
}
.after\:w-full::after {
content: var(--tw-content);
width: 100%;
}
.after\:rounded-lg::after {
content: var(--tw-content);
border-radius: 0.5rem;
}
.after\:bg-gray-200::after {
content: var(--tw-content);
--tw-bg-opacity: 1;
background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));
}
.focus-within\:outline-none:focus-within { .focus-within\:outline-none:focus-within {
outline: 2px solid transparent; outline: 2px solid transparent;
outline-offset: 2px; outline-offset: 2px;

93
views/orderpreview.html Normal file
View File

@@ -0,0 +1,93 @@
{{ template "header.html" . }}
<section class="bg-white py-8 antialiased dark:bg-gray-900 md:py-16">
<form action="/order" method="POST" class="mx-auto max-w-screen-xl px-4 2xl:px-0">
<input type="hidden" name="confirm-order" value="true" required>
<div class="mx-auto max-w-3xl">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl">Order summary</h2>
<div class="mt-6 space-y-4 border-b border-t border-gray-200 py-8 dark:border-gray-700 sm:mt-8">
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Delivery information</h4>
<dl>
<dd class="mt-1 text-base font-normal text-gray-500 dark:text-gray-400">
<p><b>Shipping:</b> {{ .data.shipping.Name }}</p>
{{ if .data.askAddress }}
<p><b>First Name:</b> {{ .data.order.FirstName }}</p>
<p><b>Last Name:</b> {{ .data.order.LastName }}</p>
<p><b>Address:</b> {{ .data.order.Address }}</p>
<p><b>Postal Code:</b> {{ .data.order.PostalCode }}</p>
<p><b>City:</b> {{ .data.order.City }}</p>
<p><b>Country:</b> {{ .data.order.Country }}</p>
{{ end }}
<p><b>Email:</b> {{ .data.order.Email }}</p>
<p><b>Comment:</b> {{ .data.order.Comment }}</p>
</dd>
</dl>
<a href="/checkout?shippingMethod={{ .data.order.Shipping }}" data-modal-target="billingInformationModal" data-modal-toggle="billingInformationModal" class="text-base font-medium text-primary-700 hover:underline dark:text-primary-500">Edit</a>
</div>
<div class="mt-6 sm:mt-8">
<div class="relative overflow-x-auto border-b border-gray-200 dark:border-gray-800">
<table class="w-full text-left font-medium text-gray-900 dark:text-white ">
<tbody class="divide-y divide-gray-200 dark:divide-gray-800">
{{ range .data.order.CartItems }}
<tr>
<td class="whitespace-nowrap py-4">
<div class="flex items-center gap-4">
<a href="#" class="flex items-center aspect-square w-8 h-10 shrink-0">
<img class="h-auto w-full max-h-full dark:hidden" src="/{{ .ShopItem.Image }}" alt="imac image" />
</a>
<a href="/shopitems/{{ .ShopItem.ID }}" class="hover:underline">{{ .ShopItem.Name }} - {{ .ItemVariant.Name }}</a>
</div>
</td>
<td class="p-4 text-base font-normal text-gray-900 dark:text-white">x{{ .Quantity }}</td>
<td class="p-4 text-right text-base font-bold text-gray-900 dark:text-white">{{ .ItemVariant.Price }}€</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
<div class="mt-4 space-y-6">
<h4 class="text-xl font-semibold text-gray-900 dark:text-white">Order summary</h4>
<div class="space-y-4">
<div class="space-y-2">
<dl class="flex items-center justify-between gap-4">
<dt class="text-gray-500 dark:text-gray-400">Original price</dt>
<dd class="text-base font-medium text-gray-900 dark:text-white">{{ .data.priceProducts }}€</dd>
</dl>
<dl class="flex items-center justify-between gap-4">
<dt class="text-gray-500 dark:text-gray-400">Shipping</dt>
<dd class="text-base font-medium text-gray-900 dark:text-white">{{ .data.shipping.Price }}€</dd>
</dl>
</div>
<dl class="flex items-center justify-between gap-4 border-t border-gray-200 pt-2 dark:border-gray-700">
<dt class="text-lg font-bold text-gray-900 dark:text-white">Total</dt>
<dd class="text-lg font-bold text-gray-900 dark:text-white">{{ .data.priceTotal }}€</dd>
</dl>
</div>
<div class="flex items-start sm:items-center">
<input id="terms-checkbox-2" type="checkbox" value="" class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-primary-600 focus:ring-2 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-primary-600" />
<label for="terms-checkbox-2" class="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300"> I agree with the <a href="#" title="" class="text-primary-700 underline hover:no-underline dark:text-primary-500">Terms and Conditions</a> of use of the Flowbite marketplace </label>
</div>
<div class="gap-4 sm:flex sm:items-center">
<button type="button" class="w-full rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"><a href="/">Return to Shopping</a></button>
<button type="submit" class="w-full bg-gray-900 dark:bg-gray-600 rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700">Place binding order</button>
</div>
</div>
</div>
</div>
</form>
</section>
{{ template "footer.html" . }}

72
views/printvariant.html Normal file
View File

@@ -0,0 +1,72 @@
{{ template "header.html" . }}
<section class="bg-white py-8 antialiased dark:bg-gray-900 md:py-16">
<div class="mx-auto max-w-3xl">
<div class="mt-6 sm:mt-8">
<div class="relative overflow-x-auto border-b border-gray-200 dark:border-gray-800">
<p class="font-normal text-base leading-7 text-gray-500 text-left mb-5 mt-6">
Welcome to the Zineshop Printservice.<br>
Pressing Print will automatically print the given Zines for you.
All you have to do is set an Amount for each Zine you want to print.<br><br>
<bold>CoverPage</bold>: If selected, the Printer will take Paper from the BypassTray for the first page. For
example you can put colored paper there to have a nice looking front page, and the rest will be normal paper.
Makue sure you put paper in that tray when selecting this option.<br><br>
Print Order: The Zines will be printed from top to bottom as seen in this list.
</p>
<form action="#" method="POST">
<table class="w-full text-left font-medium text-gray-900 dark:text-white ">
<tbody class="divide-y divide-gray-200 dark:divide-gray-800">
{{ range .data.itemVariants }}
<tr>
<td class="whitespace-nowrap py-4">
<div class="flex items-center gap-4">
<a href="/shopitems/{{ .ItemVariant.ShopItemID }}" class="flex items-center aspect-square w-8 h-10 shrink-0">
<img class="h-auto w-full max-h-full dark:hidden" src="/{{ .ShopItem.Image }}" alt="imac image" />
</a>
<a href="/shopitems/{{ .ItemVariant.ShopItemID }}" class="hover:underline">{{ .ShopItem.Name }} - {{ .ItemVariant.Name }}</a>
</div>
</td>
<td class="whitespace-nowrap py-4">
<label class="flex text-sm/6 items-center">
<input type="checkbox" class="form-checkbox h-4 w-4 text-gray-900" value="CoverPage" name="CoverPage">
<span class="ml-2 text-sm/6 text-gray-900">CoverPage</span>
</label>
</td>
<td class="whitespace-nowrap py-4">
Amount:
</td>
<td class="p-4 text-right text-base font-bold text-gray-900 dark:text-white">
<div>
<input type="hidden" id="variant-name1" name="variant-name[]" value="B/W" required>
<div class="mt-2">
<input type="number" name="variant-value[]" id="variant-value1" step="1" min="1" 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>
</td>
</tr>
{{ end }}
</tbody>
</table>
<div class="max-lg:max-w-lg max-lg:mx-auto">
<p class="font-normal text-base leading-7 text-gray-500 text-center mb-5 mt-6">If CoverPage
selected, make sure you put paper in the BypassTray</p>
<button type="submit"
class="rounded-full py-4 px-6 bg-indigo-600 text-white font-semibold text-lg w-full text-center
transition-all duration-500 hover:bg-indigo-700 ">Print</button>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
{{ template "footer.html" . }}

View File

@@ -9,23 +9,21 @@
<img class="w-full h-full object-cover" src="/{{ .data.shopItem.Image}}" alt="Product Image"> <img class="w-full h-full object-cover" src="/{{ .data.shopItem.Image}}" alt="Product Image">
</div> </div>
<div class="flex -mx-2 mb-4"> <div class="flex -mx-2 mb-4">
<input type="hidden" id="{{ .data.shopItem.ID}}" name="ShopItemId" value="{{ .data.shopItem.ID }}"> <input type="hidden" id="{{ .data.shopItem.ID}}" name="ShopItemId" value="{{ .data.shopItem.ID }}">
<div class="w-1/3 px-2"> <div class="w-1/3 px-2">
<button type="submit" class="w-full bg-gray-900 dark:bg-gray-600 text-white py-2 px-4 rounded-full font-bold hover:bg-gray-800 dark:hover:bg-gray-700">Add to Cart</button> <button type="submit" class="w-full bg-gray-900 dark:bg-gray-600 text-white py-2 px-4 rounded-lg font-bold hover:bg-gray-800 dark:hover:bg-gray-700">Add to Cart</button>
</div> </div>
<div class="w-1/3 px-2"> <div class="w-1/3 px-2">
<a href="/{{ .data.shopItem.Pdf }}"><button type="button" class="w-full bg-blue-900 dark:bg-gray-600 text-white py-2 px-4 rounded-full font-bold hover:bg-gray-800 dark:hover:bg-gray-700">View</button></a> <button type="button" class="w-full bg-blue-900 dark:bg-gray-600 text-white py-2 px-4 rounded-lg font-bold hover:bg-gray-800 dark:hover:bg-gray-700"><a href="/{{ .data.shopItem.Pdf }}">View</a></button>
</div> </div>
{{ if .loggedIn }} {{ if .loggedIn }}
<div class="w-1/3 px-2"> <div class="w-1/3 px-2">
<a href="{{ .data.shopItem.ID }}/edit"><button type="button" class="w-full bg-blue-900 dark:bg-gray-600 text-white py-2 px-4 rounded-full font-bold hover:bg-gray-800 dark:hover:bg-gray-700">Edit</button></a> <button type="button" class="w-full bg-blue-900 dark:bg-gray-600 text-white py-2 px-4 rounded-lg font-bold hover:bg-gray-800 dark:hover:bg-gray-700"><a href="{{ .data.shopItem.ID }}/edit">Edit</a></button>
</div> </div>
<div class="w-1/3 px-2"> <div class="w-1/3 px-2">
<a href="{{ .data.shopItem.ID }}/delete"><button type="button" class="w-full bg-red-900 dark:bg-red-600 text-white py-2 px-4 rounded-full font-bold hover:bg-gray-800 dark:hover:bg-gray-700">Delete</button></a> <button type="button" class="w-full bg-red-900 dark:bg-red-600 text-white py-2 px-4 rounded-lg font-bold hover:bg-gray-800 dark:hover:bg-gray-700"><a href="{{ .data.shopItem.ID }}/delete">Delete</a></button>
</div> </div>
{{ end }} {{ end }}
</div> </div>