add item image

This commit is contained in:
2025-03-03 15:21:01 +01:00
parent cdfd77bc21
commit 803535daba
12 changed files with 401 additions and 112 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"strconv"
"path/filepath"
"github.com/gin-gonic/gin"
@@ -27,6 +28,10 @@ type ShopItemController interface {
AddItemHandler(*gin.Context)
CreateTag(*gin.Context)
GetAllTags(*gin.Context)
EditItemView(*gin.Context)
EditItemHandler(*gin.Context)
DeleteItemView(*gin.Context)
DeleteItemHandler(*gin.Context)
}
type shopItemController struct {}
@@ -63,6 +68,15 @@ func (rc *shopItemController) NewShopItemFromForm(ctx *gin.Context) (models.Shop
description := ctx.PostForm("description")
priceStr := ctx.PostForm("price")
tagIds := ctx.PostFormArray("tags[]")
image, err := ctx.FormFile("image")
dst := "static/img/zine.jpg"
if err == nil {
dst = filepath.Join("static/uploads", image.Filename)
if err := ctx.SaveUploadedFile(image, dst); err != nil {
return models.ShopItem{}, fmt.Errorf("Could not save image")
}
}
if name == "" || description == "" {
return models.ShopItem{}, fmt.Errorf("Name or description empty")
@@ -80,6 +94,7 @@ func (rc *shopItemController) NewShopItemFromForm(ctx *gin.Context) (models.Shop
Description: description,
Price: price,
IsPublic: true,
Image: dst,
}
for _, tagId := range tagIds {
@@ -179,7 +194,6 @@ func (rc *shopItemController) ShopItemView(c *gin.Context) {
c.HTML(http.StatusOK, "shopitem.html", data)
}
func (rc *shopItemController) AddItemView(c *gin.Context) {
tags, err := repositories.Tags.GetAll()
@@ -201,6 +215,7 @@ func (rc *shopItemController) AddItemHandler(c *gin.Context) {
shopItem, err := rc.NewShopItemFromForm(c)
if err != nil {
fmt.Println(err)
c.HTML(http.StatusBadRequest, "additem.html", gin.H{ "error": err })
return
}
@@ -208,6 +223,7 @@ func (rc *shopItemController) AddItemHandler(c *gin.Context) {
tags, err := repositories.Tags.GetAll()
if err != nil {
fmt.Println(err)
c.HTML(http.StatusBadRequest, "additem.html", gin.H{ "error": err })
return
}
@@ -233,6 +249,123 @@ func (rc *shopItemController) AddItemHandler(c *gin.Context) {
c.HTML(http.StatusOK, "additem.html", data)
}
func (rc *shopItemController) EditItemView(c *gin.Context) {
shopItem, err := repositories.ShopItems.GetById(c.Param("id"))
tags, err := repositories.Tags.GetAll()
if err != nil {
c.HTML(http.StatusBadRequest, "edititem.html", gin.H{ "error": err })
}
fmt.Println(shopItem)
data := CreateSessionData(c, gin.H{
"error": "",
"success": "",
"shopItem": shopItem,
"tags": tags,
})
c.HTML(http.StatusOK, "edititem.html", data)
}
func (rc *shopItemController) EditItemHandler(c *gin.Context) {
shopItem, err := rc.NewShopItemFromForm(c)
if err != nil {
c.HTML(http.StatusBadRequest, "edititem.html", gin.H{ "error": err })
return
}
newShopItem, err := repositories.ShopItems.GetById(c.Param("id"))
if err != nil {
c.HTML(http.StatusBadRequest, "edititem.html", gin.H{ "error": err })
return
}
newShopItem.Name = shopItem.Name
newShopItem.Abstract = shopItem.Abstract
newShopItem.Description = shopItem.Description
newShopItem.Price = shopItem.Price
newShopItem.IsPublic = shopItem.IsPublic
newShopItem.Tags = shopItem.Tags
tags, err := repositories.Tags.GetAll()
if err != nil {
c.HTML(http.StatusBadRequest, "edititem.html", gin.H{ "error": err })
return
}
_, err = repositories.ShopItems.Update(newShopItem)
if err != nil {
data := CreateSessionData(c, gin.H{
"error": err,
"success": "",
"tags": tags,
})
c.HTML(http.StatusOK, "edititem.html", data)
return
}
data := CreateSessionData(c, gin.H{
"error": "",
"success": fmt.Sprintf("Item '%s' Updated", newShopItem.Name),
"tags": tags,
})
c.HTML(http.StatusOK, "edititem.html", data)
}
func (rc *shopItemController) DeleteItemView(c *gin.Context) {
shopItem, err := repositories.ShopItems.GetById(c.Param("id"))
tags, err := repositories.Tags.GetAll()
if err != nil {
c.HTML(http.StatusBadRequest, "deleteitem.html", gin.H{ "error": err })
}
fmt.Println(shopItem)
data := CreateSessionData(c, gin.H{
"error": "",
"success": "",
"shopItem": shopItem,
"tags": tags,
})
c.HTML(http.StatusOK, "deleteitem.html", data)
}
func (rc *shopItemController) DeleteItemHandler(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)
}
func (rc *shopItemController) CreateTag(c *gin.Context) {
tag, err := models.NewTagByJson(c)

View File

@@ -77,6 +77,10 @@ func main() {
{
viewRoutes.GET("/", userController.MainView)
viewRoutes.GET("/shopitems/:id", shopItemController.ShopItemView)
viewRoutes.GET("/shopitems/:id/edit", authValidator.RequireAuth, shopItemController.EditItemView)
viewRoutes.GET("/shopitems/:id/delete", authValidator.RequireAuth, shopItemController.DeleteItemView)
viewRoutes.POST("/shopitems/:id/edit", authValidator.RequireAuth, shopItemController.EditItemHandler)
viewRoutes.POST("/shopitems/:id/delete", authValidator.RequireAuth, shopItemController.DeleteItemHandler)
//write middleware that redirects to homescreen on register/login/reset for logged in users
viewRoutes.GET("/login", userController.LoginView)
viewRoutes.GET("/logout", userController.Logout)

View File

@@ -12,6 +12,6 @@ type ShopItem struct {
Price float64 `json:"price" binding:"required"`
IsPublic bool `json:"isPublic" gorm:"default:true"`
Tags []Tag `gorm:"many2many:item_tags;"`
//Images gin.multipart.FileHeader ``
Image string
}

BIN
static/img/zine.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

View File

@@ -596,18 +596,6 @@ video {
margin-right: auto;
}
.mt-10 {
margin-top: 2.5rem;
}
.mt-2 {
margin-top: 0.5rem;
}
.mt-1 {
margin-top: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
@@ -616,6 +604,18 @@ video {
margin-left: 0.5rem;
}
.mt-1 {
margin-top: 0.25rem;
}
.mt-10 {
margin-top: 2.5rem;
}
.mt-2 {
margin-top: 0.5rem;
}
.block {
display: block;
}
@@ -636,34 +636,38 @@ video {
height: 2.5rem;
}
.h-16 {
height: 4rem;
}
.h-8 {
height: 2rem;
}
.h-12 {
height: 3rem;
}
.h-5 {
height: 1.25rem;
}
.h-3 {
height: 0.75rem;
.h-16 {
height: 4rem;
}
.h-4 {
height: 1rem;
}
.h-48 {
height: 12rem;
}
.h-8 {
height: 2rem;
}
.min-h-full {
min-height: 100%;
}
.w-12 {
width: 3rem;
}
.w-4 {
width: 1rem;
}
.w-auto {
width: auto;
}
@@ -672,30 +676,10 @@ video {
width: 100%;
}
.w-12 {
width: 3rem;
}
.w-5 {
width: 1.25rem;
}
.w-3 {
width: 0.75rem;
}
.w-4 {
width: 1rem;
}
.max-w-7xl {
max-width: 80rem;
}
.max-w-md {
max-width: 28rem;
}
.flex-1 {
flex: 1 1 0%;
}
@@ -740,36 +724,30 @@ video {
margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));
}
.space-y-6 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
}
.space-y-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
}
.space-y-4 > :not([hidden]) ~ :not([hidden]) {
.space-y-6 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
}
.rounded {
border-radius: 0.25rem;
}
.rounded-md {
border-radius: 0.375rem;
}
.rounded-lg {
border-radius: 0.5rem;
}
.rounded-md {
border-radius: 0.375rem;
}
.border {
border-width: 1px;
}
@@ -812,18 +790,19 @@ video {
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
}
.p-4 {
padding: 1rem;
}
.p-6 {
padding: 1.5rem;
.object-cover {
-o-object-fit: cover;
object-fit: cover;
}
.p-2 {
padding: 0.5rem;
}
.p-4 {
padding: 1rem;
}
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
@@ -863,14 +842,6 @@ video {
padding-bottom: 0.75rem;
}
.pr-2 {
padding-right: 0.5rem;
}
.pt-2 {
padding-top: 0.5rem;
}
.pb-6 {
padding-bottom: 1.5rem;
}
@@ -879,6 +850,14 @@ video {
padding-left: 0.25rem;
}
.pr-2 {
padding-right: 0.5rem;
}
.pt-2 {
padding-top: 0.5rem;
}
.pt-5 {
padding-top: 1.25rem;
}
@@ -943,6 +922,16 @@ video {
color: rgb(107 114 128 / var(--tw-text-opacity, 1));
}
.text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity, 1));
}
.text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity, 1));
}
.text-gray-900 {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity, 1));
@@ -973,33 +962,12 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
}
.text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity, 1));
}
.text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity, 1));
}
.text-blue-600 {
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
}
.shadow-sm {
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.shadow-md {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.outline {
outline-style: solid;
}
@@ -1169,6 +1137,14 @@ video {
}
@media (min-width: 768px) {
.md\:h-full {
height: 100%;
}
.md\:w-48 {
width: 12rem;
}
.md\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@@ -1202,16 +1178,16 @@ video {
background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1));
}
.dark\:text-gray-200 {
--tw-text-opacity: 1;
color: rgb(229 231 235 / var(--tw-text-opacity, 1));
}
.dark\:text-gray-300 {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity, 1));
}
.dark\:text-gray-900 {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity, 1));
}
.dark\:focus\:border-blue-500:focus {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity, 1));

16
tailwind.config.js Normal file
View File

@@ -0,0 +1,16 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./views/**/*.{html,js}"],
theme: {
extend: {
gridTemplateRows: {
// Simple 16 row grid
'19': 'repeat(19, minmax(0, 1fr))',
// Complex site-specific row configuration
'layout': '200px minmax(900px, 1fr) 100px',
}
},
},
plugins: [],
}

View File

@@ -8,7 +8,7 @@
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form class="space-y-6" action="#" method="POST">
<form class="space-y-6" action="#" method="POST" enctype="multipart/form-data">
<div>
<label for="name" class="block text-sm/6 font-medium text-gray-900">Name</label>
<div class="mt-2">
@@ -73,9 +73,9 @@
<path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500">
<label for="image-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500">
<span class="">Upload a file</span>
<input id="file-upload" name="file-upload" type="file" class="sr-only">
<input id="image-upload" name="image" type="file" accept="image/*" class="sr-only">
</label>
<p class="pl-1 text-gray-900">or drag and drop</p>
</div>

38
views/deleteitem.html Normal file
View File

@@ -0,0 +1,38 @@
{{ template "header.html" . }}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div class="bg-gray-200 rounded m-4 p-4">
<h3 class="text-lg">{{ .data.shopItem.Name }}</h3>
<i class="text-xs">{{ .data.shopItem.Description }}</i>
<p class="">Price: {{ .data.shopItem.Price }}</p>
{{ if .loggedIn }}
<p class="mt-10 text-center text-sm/6 text-red-500">
Do you really want to delete this item??
</p>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0 px-4 sm:px-8">
<div>
<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>
<form class="space-y-6" action="#" method="POST">
<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">Delete</button>
</div>
</form>
<a href="/shopitems/{{ .data.shopItem.ID }}" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-red-300 hover:bg-gray-700 hover:text-white">Cancel</a>
</div>
{{ end }}
</div>
</div>
{{ template "footer.html" . }}

105
views/edititem.html Normal file
View File

@@ -0,0 +1,105 @@
{{ template "header.html" . }}
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
<img class="mx-auto h-10 w-auto" src="/static/img/circlea.png" alt="Your Company">
<h2 class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">Edit Item</h2>
</div>
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form class="space-y-6" action="#" method="POST">
<div>
<label for="name" class="block text-sm/6 font-medium text-gray-900">Name</label>
<div class="mt-2">
<input type="text" name="name" id="name" value="{{ .data.shopItem.Name }}" 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>
<label for="abstract" class="block text-sm/6 font-medium text-gray-900">Abstract</label>
<div class="mt-2">
<input type="text" name="abstract" id="abstract" value="{{ .data.shopItem.Abstract }}" 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>
<label for="description" class="block text-sm/6 font-medium text-gray-900">Description</label>
<textarea id="description" name="description" type="textarea" class="block w-full px-4 py-2 mt-2 text-gray-900 bg-white border border-gray-300 rounded-md dark:bg-gray-800 dark:text-gray-900 dark:border-gray-600 focus:border-blue-500 dark:focus:border-blue-500 focus:outline-none focus:ring">{{ .data.shopItem.Description}}</textarea>
</div>
<div class="mb-4">
<label class="block text-sm/6 text-gray-900">Select Categories</label>
<div class="space-y-2">
{{ range .data.tags }}
<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[]">
<span class="ml-2 text-sm/6 text-gray-900">{{ .Name }}</span>
</label>
{{ end }}
</div>
</div>
<!--
<div class="mb-4">
<label for="tags" class="block text-gray-700">Select Tags</label>
<select id="tags" name="tags" multiple class="mt-1 block w-full border border-gray-300 rounded-md p-2 focus:outline-none focus:ring focus:ring-blue-500">
<option value="tag1">Tag 1</option>
<option value="tag2">Tag 2</option>
<option value="tag3">Tag 3</option>
<option value="tag4">Tag 4</option>
<option value="tag5">Tag 5</option>
</select>
<p class="mt-1 text-gray-500 text-sm">Hold down the Ctrl (Windows) or Command (Mac) button to select multiple options.</p>
</div>
-->
<div>
<div class="flex items-center justify-between">
<label for="price" class="block text-sm/6 font-medium text-gray-900">Price</label>
</div>
<div class="mt-2">
<input type="number" name="price" id="price" value="{{ .data.shopItem.Price }}" 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>
<label class="block text-sm font-medium text-gray-900">
Image
</label>
<div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
<div class="space-y-1 text-center">
<svg class="mx-auto h-12 w-12 text-gray-900" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true">
<path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500">
<span class="">Upload a file</span>
<input id="file-upload" name="file-upload" type="file" class="sr-only">
</label>
<p class="pl-1 text-gray-900">or drag and drop</p>
</div>
<p class="text-xs text-gray-900">
PNG, JPG, GIF up to 10MB
</p>
</div>
</div>
</div>
<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>
<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>
</form>
</div>
</div>
{{ template "footer.html" . }}

View File

@@ -27,13 +27,13 @@
</div>
{{ if .loggedIn }}
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0 px-4 sm:px-8">
<a href="additem" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Add Item</a>
<a href="logout" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-red-300 hover:bg-gray-700 hover:text-white">Logout</a>
<a href="/additem" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Add Item</a>
<a href="/logout" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-red-300 hover:bg-gray-700 hover:text-white">Logout</a>
</div>
{{ else }}
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0 px-4 sm:px-8">
<a href="login" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Login</a>
<a href="register" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Register</a>
<a href="/login" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Login</a>
<a href="/register" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Register</a>
</div>
{{ end }}
</div>

View File

@@ -2,10 +2,22 @@
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div class="bg-gray-200 rounded m-4 p-4">
<div class="relative w-full md:w-48 mb-4 flex rounded-lg justify-center items-center">
<img src="/{{ .data.shopItem.Image }}" alt="shopping image"
class="object-cover w-full h-48 md:h-full rounded">
</div>
<h3 class="text-lg">{{ .data.shopItem.Name }}</h3>
<i class="text-xs">{{ .data.shopItem.Description }}</i>
<i class="text-xs">{{ .data.shopItem.Abstract }}</i>
<p class="text-xs">{{ .data.shopItem.Description }}</p>
<p class="">Price: {{ .data.shopItem.Price }}</p>
{{ if .loggedIn }}
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0 px-4 sm:px-8">
<a href="{{ .data.shopItem.ID }}/edit" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Edit</a>
<a href="{{ .data.shopItem.ID }}/delete" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-red-300 hover:bg-gray-700 hover:text-white">Delete</a>
</div>
{{ end }}
</div>
</div>
{{ template "footer.html" . }}

View File

@@ -1,11 +1,16 @@
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{{ range .data.shopItems }}
<a href="shopitems/{{ .ID }}">
<a href="/shopitems/{{ .ID }}">
<div class="bg-gray-200 rounded m-4 p-4">
<div class="relative w-full md:w-48 mb-4 flex rounded-lg justify-center items-center">
<img src="/{{ .Image }}" alt="shopping image"
class="object-cover w-full h-48 md:h-full rounded">
</div>
<h3 class="text-lg">{{ .Name }}</h3>
<i class="text-xs">{{ .Description }}</i>
<p class="">Price: {{ .Price }}</p>
<i class="text-xs">{{ .Abstract }}</i>
<p class="">Price: {{ .Price }}</pÖ>
</div>
</a>
{{ end }}