Producte show

Producte werden nun angezeigt .
This commit is contained in:
2023-11-14 20:27:23 +01:00
parent 8d81958da3
commit 54fdfe30a9
11 changed files with 458 additions and 29 deletions

View File

@@ -160,6 +160,9 @@ func (server *Server) routeInit() {
server.Router.HandleFunc("/register", server.DoRegister).Methods("POST")
server.Router.HandleFunc("/logout", server.Logout).Methods("GET")
server.Router.HandleFunc("/products", server.Products).Methods("GET")
server.Router.HandleFunc("/products/{slug}", server.GetProductByID).Methods("GET")
staticFileDirectory := http.Dir("./assets/")
staticFileHandler := http.StripPrefix("/public/", http.FileServer(staticFileDirectory))
server.Router.PathPrefix("/public/").Handler(staticFileHandler).Methods("GET")

View File

@@ -0,0 +1,62 @@
package controllers
import (
"github.com/gorilla/mux"
"github.com/unrolled/render"
"moretcgshop/app/models"
"net/http"
"strconv"
)
func (server *Server) Products(w http.ResponseWriter, r *http.Request) {
renderer := render.New(render.Options{
Layout: "layout",
Extensions: []string{".html", ".tmpl"},
})
q := r.URL.Query()
page, _ := strconv.Atoi(q.Get("page"))
if page <= 0 {
page = 1
}
perPage := 9
productModel := models.Product{}
products, totalRows, err := productModel.GetProducts(server.DB, perPage, page)
if err != nil {
return
}
_ = totalRows
_ = renderer.HTML(w, http.StatusOK, "products", map[string]interface{}{
"products": products,
})
}
func (server *Server) GetProductByID(w http.ResponseWriter, r *http.Request) {
renderer := render.New(render.Options{
Layout: "layout",
Extensions: []string{".html", ".tmpl"},
})
vars := mux.Vars(r)
if vars["slug"] == "" {
return
}
productModel := models.Product{}
product, err := productModel.FindByID(server.DB, vars["slug"])
if err != nil {
println(err.Error())
return
}
_ = renderer.HTML(w, http.StatusOK, "product", map[string]interface{}{
"product": product,
"success": GetFlash(w, r, "success"),
"error": GetFlash(w, r, "error"),
})
}

View File

@@ -0,0 +1,16 @@
package models
import "time"
type ProductImage struct {
ID string `gorm:"size:36;not null;uniqueIndex;primary_key"`
Product Product
ProductID string `gorm:"size:36;index"`
Path string `gorm:"type:text"`
ExtraLarge string `gorm:"type:text"`
Large string `gorm:"type:text"`
Medium string `gorm:"type:text"`
Small string `gorm:"type:text"`
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@@ -10,5 +10,6 @@ func RegisterModels() []Model {
{Model: Product{}},
{Model: Category{}},
{Model: Section{}},
{Model: ProductImage{}},
}
}

View File

@@ -1,16 +1,55 @@
package models
import (
"github.com/shopspring/decimal"
"gorm.io/gorm"
"time"
)
type Product struct {
ID string
ParentID string
Name string
Categories []Category `gorm:"many2many:product_categories;"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt
ID string `gorm:"size:36;not null;uniqueIndex;primary_key"`
ParentID string `gorm:"size:36;index"`
Name string `gorm:"size:255"`
Slug string `gorm:"size:255"`
ProductImages []ProductImage
Price decimal.Decimal `gorm:"type:decimal(16,2);"`
Categories []Category `gorm:"many2many:product_categories;"`
ShortDescription string `gorm:"type:text"`
Description string `gorm:"type:text"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt
}
func (p *Product) GetProducts(db *gorm.DB, perPage int, page int) (*[]Product, int64, error) {
var count int64
var products []Product
err := db.Debug().Model(&Product{}).Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * perPage
err = db.Debug().Model(&Product{}).Order("created_at desc").Limit(perPage).Offset(offset).Find(&products).Error
if err != nil {
return nil, 0, err
}
return &products, count, nil
}
func (p *Product) FindByID(db *gorm.DB, productID string) (*Product, error) {
var err error
var product Product
err = db.Debug().Preload("ProductImages").Model(&Product{}).Where("id = ?", productID).First(&product).Error
//err = db.Debug().Model(&Product{}).Where("id = ?", productID).First(&product).Error
if err != nil {
return nil, err
}
return &product, nil
}

View File

@@ -31,29 +31,17 @@ services:
labels:
# Frontend
- "traefik.enable=true"
- "traefik.http.routers.hochzeit.entrypoints=http"
- "traefik.http.routers.hochzeit.rule=Host(`drone.cosysda.de`)"
- "traefik.http.middlewares.hochzeit-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.hochzeit.middlewares=hochzeit-https-redirect"
- "traefik.http.routers.hochzeit-secure.entrypoints=https"
- "traefik.http.routers.hochzeit-secure.rule=Host(`drone.cosysda.de`)"
- "traefik.http.routers.hochzeit-secure.tls=true"
- "traefik.http.routers.hochzeit-secure.tls.certresolver=http"
- "traefik.http.routers.hochzeit-secure.service=hochzeit"
- "traefik.http.services.hochzeit.loadbalancer.server.port=9000"
- "traefik.http.routers.moretcg-shop.entrypoints=http"
- "traefik.http.routers.moretcg-shop.rule=Host(`drone.cosysda.de`)"
- "traefik.http.middlewares.moretcg-shop-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.moretcg-shop.middlewares=moretcg-shop-https-redirect"
- "traefik.http.routers.moretcg-shop-secure.entrypoints=https"
- "traefik.http.routers.moretcg-shop-secure.rule=Host(`drone.cosysda.de`)"
- "traefik.http.routers.moretcg-shop-secure.tls=true"
- "traefik.http.routers.moretcg-shop-secure.tls.certresolver=http"
- "traefik.http.routers.moretcg-shop-secure.service=moretcg-shop"
- "traefik.http.services.moretcg-shop.loadbalancer.server.port=9000"
- "traefik.docker.network=http_network"
#- "traefik.http.routers.frontend.entrypoints=websecure"
#- "traefik.http.services.frontend.loadbalancer.server.port=9000"
#- "traefik.http.routers.frontend.service=frontend"
#- "traefik.http.routers.frontend.tls.certresolver=leresolver"
# Edge
#- "traefik.http.routers.edge.rule=Host:edge.cosysda.de"
#- "traefik.http.routers.edge.entrypoints=websecure"
#- "traefik.http.services.edge.loadbalancer.server.port=8000"
#- "traefik.http.routers.edge.service=edge"
#- "traefik.http.routers.edge.tls.certresolver=leresolver"
networks:
- http_network
- moretcg
#volumes:
#portainer_data:

1
go.mod
View File

@@ -7,6 +7,7 @@ require (
github.com/gorilla/mux v1.8.1
github.com/gorilla/sessions v1.2.2
github.com/joho/godotenv v1.5.1
github.com/shopspring/decimal v1.3.1
github.com/unrolled/render v1.6.1
golang.org/x/crypto v0.14.0
gorm.io/driver/postgres v1.5.4

2
go.sum
View File

@@ -25,6 +25,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

View File

@@ -0,0 +1,27 @@
{{ define "pagination" }}
<div class="row">
<div class="col-12">
<ul class="pagination">
<li class="page-item">
<a class="page-link" href="{{ .pagination.PrevPage }}" tabindex="-1">
<i class="fa fa-angle-left"></i>
<span class="sr-only">Previous</span>
</a>
</li>
{{ range $i, $link := .pagination.Links }}
{{ $active := "" }}
{{ if $link.IsCurrentPage }}
{{ $active = "active" }}
{{ end }}
<li class="page-item {{ $active }}"><a class="page-link" href="{{ $link.Url }}">{{ $link.Page }}</a></li>
{{ end }}
<li class="page-item">
<a class="page-link" href="{{ .pagination.NextPage }}">
<i class="fa fa-angle-right"></i>
<span class="sr-only">Next</span>
</a>
</li>
</ul>
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,170 @@
{{ define "product" }}
<section class="breadcrumb-section pb-3 pt-3">
<div class="container">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item"><a href="/products">Products</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ .product.Name}}</li>
</ol>
</div>
</section>
<section class="product-page pb-4 pt-4">
<div class="container">
<div class="row product-detail-inner">
<div class="col-lg-6 col-md-6 col-12">
<div id="product-images" class="carousel slide" data-ride="carousel">
<!-- slides -->
<div class="carousel-inner">
{{ range $i, $productImage := .product.ProductImages }}
{{ if eq $i 0 }}
<div class="carousel-item active"><img src="{{ $productImage.Path }}" alt="Product 1">
</div>
{{ else }}
<<div class="carousel-item"><img src="{{ $productImage.Path }}" alt="Product 2"></div>
{{ end }}
{{ end }}
</div> <!-- Left right -->
<a class="carousel-control-prev" href="#product-images" data-slide="prev"> <span
class="carousel-control-prev-icon"></span> </a> <a class="carousel-control-next"
href="#product-images" data-slide="next">
<span class="carousel-control-next-icon"></span> </a><!-- Thumbnails -->
<ol class="carousel-indicators list-inline">
{{ range $i, $productImage := .product.ProductImages }}
{{ if eq $i 0 }}
<li class="list-inline-item active"><a id="carousel-selector-{{ $i }}" class="selected"
data-slide-to="{{ $i }}" data-target="#product-images">
<img src="{{ $productImage.Path }}" class="img-fluid"> </a></li>
{{ else }}
<li class="list-inline-item"><a id="carousel-selector-{{ $i }}" data-slide-to="{{ $i }}"
data-target="#product-images"> <img
src="{{ $productImage.Path }}" class="img-fluid"> </a></li>
{{ end }}
{{ end }}
</ol>
</div>
</div>
<div class="col-lg-6 col-md-6 col-12">
<div class="product-detail">
<h2 class="product-name">{{ .product.Name }}</h2>
<div class="product-price">
<span class="price">IDR {{ .product.Price }}</span>
</div>
{{ if .success }}
<div class="alert alert-success">
{{ range $i, $msg := .success }}
{{ $msg }}<br/>
{{ end }}
</div>
{{ end }}
{{ if .error }}
<div class="alert alert-danger">
{{ range $i, $msg := .error }}
{{ $msg }}<br/>
{{ end }}
</div>
{{ end }}
<div class="product-short-desc">
<p>{{ .product.ShortDescription }}</p>
</div>
<div class="product-select">
<form method="POST" action="/carts">
<input type="hidden" name="product_id" value="{{ .product.ID }}"/>
<div class="row">
<div class="col-md-3">
<input type="number" name="qty" class="form-control" value="1"/>
</div>
<div class="col-md-5">
<button type="submit" class="btn btn-primary btn-block">Add to Cart</button>
</div>
<div class="col-md-4">
<a href="#" class="btn btn-secondary"><i class="fa fa-heart-o"></i></a>
</div>
</div>
</form>
</div>
<!--
<div class="product-categories">
<ul>
<li class="categories-title">Categories :</li>
<li><a href="#">fashion</a></li>
<li><a href="#">electronics</a></li>
<li><a href="#">toys</a></li>
<li><a href="#">food</a></li>
<li><a href="#">jewellery</a></li>
</ul>
</div>
-->
<!--<div class="product-tags">
<ul>
<li class="categories-title">Tags :</li>
<li><a href="#">fashion</a></li>
<li><a href="#">electronics</a></li>
<li><a href="#">toys</a></li>
<li><a href="#">food</a></li>
<li><a href="#">jewellery</a></li>
</ul>
</div>
-->
<!--
<div class="product-share">
<ul>
<li class="categories-title">Share :</li>
<li><a href="#"><i class="fa fa-facebook"></i></a></li>
<li><a href="#"><i class="fa fa-twitter"></i></a></li>
<li><a href="#"><i class="fa fa-pinterest"></i></a></li>
</ul>
</div>
-->
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="product-details">
<div class="nav-wrapper">
<ul class="nav nav-pills nav-fill flex-column flex-md-row" id="tabs-icons-text" role="tablist">
<li class="nav-item">
<a class="nav-link mb-sm-3 mb-md-0 active" id="tabs-icons-text-1-tab" data-toggle="tab"
href="#tabs-icons-text-1" role="tab" aria-controls="tabs-icons-text-1"
aria-selected="true">Beschreibung</a>
</li>
<li class="nav-item">
<a class="nav-link mb-sm-3 mb-md-0" id="tabs-icons-text-2-tab" data-toggle="tab"
href="#tabs-icons-text-2" role="tab" aria-controls="tabs-icons-text-2"
aria-selected="false">Reviews</a>
</li>
</ul>
</div>
<div class="card">
<div class="card-body">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="tabs-icons-text-1" role="tabpanel"
aria-labelledby="tabs-icons-text-1-tab">
<p>{{ .product.Description }}</p>
</div>
<div class="tab-pane fade" id="tabs-icons-text-2" role="tabpanel"
aria-labelledby="tabs-icons-text-2-tab">
<div class="review-form">
<h3>Write a review</h3>
<form>
<div class="form-group">
<label>Your Name</label>
<input type="text" class="form-control"/>
</div>
<div class="form-group">
<label>Your Review</label>
<textarea cols="4" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{{ end }}

View File

@@ -0,0 +1,120 @@
{{ define "products" }}
<section class="breadcrumb-section pb-3 pt-3">
<div class="container">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Products</li>
</ol>
</div>
</section>
<section class="products-grid pb-4 pt-4">
<div class="container">
<div class="row">
<div class="col-lg-3 col-md-4 col-12">
<div class="sidebar">
<!--<div class="sidebar-widget">
<div class="widget-title">
<h3>Shop by Price</h3>
</div>
<div class="widget-content shop-by-price">
<form method="get" action="/tesas">
<div class="price-filter">
<div class="price-filter-inner">
<div id="slider-range"></div>
<div class="price_slider_amount">
<div class="label-input">
<input type="text" id="amount" name="price"
placeholder="Add Your Price" />
<button type="submit">Filter</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>-->
<div class="sidebar-widget">
<div class="widget-title">
<h3>Categories</h3>
</div>
<div class="widget-content widget-categories">
<ul>
<li><a href="#">Fashions</a></li>
<li><a href="#">Electronics</a>
<ul>
<li><a href="#">Hand Phone</a></li>
<li><a href="#">Laptops</a></li>
</ul>
</li>
<li><a href="#">Home and Kitchen</a></li>
<li><a href="#">Baby and Toys</a></li>
<li><a href="#">Sports</a></li>
<li><a href="#">Digital Goods</a></li>
</ul>
</div>
</div>
<!--
<div class="sidebar-widget">
<div class="widget-title">
<h3>Brands</h3>
</div>
<div class="widget-content widget-brands">
<ul>
<li><a href="#">Apple</a></li>
<li><a href="#">Samsung</a></li>
<li><a href="#">Lenovo</a></li>
<li><a href="#">Asus</a></li>
<li><a href="#">Xiaomi</a></li>
</ul>
</div>
</div>
-->
</div>
</div>
<div class="col-lg-9 col-md-8 col-12">
<div class="row">
<div class="col-12">
<div class="products-top">
<div class="products-top-inner">
<div class="products-found">
<p><span>25</span> products found of <span>1.342</span></p>
</div>
<!--
<div class="products-sort">
<span>Sort By : </span>
<select>
<option>Default</option>
<option>Price</option>
<option>Recent</option>
</select>
</div>
-->
</div>
</div>
</div>
</div>
<div class="row">
{{ range $i, $product := .products }}
<div class="col-lg-4 col-md-6 col-12">
<div class="single-product">
<div class="product-img">
<a href="/products/{{ $product.Slug }}">
<img src="https://placehold.jp/300x400.png" class="img-fluid" />
</a>
</div>
<div class="product-content">
<h3><a href="/products/{{ $product.Slug }}">{{ $product.Name }}</a></h3>
<div class="product-price">
<span>{{ $product.Price }}</span>
</div>
</div>
</div>
</div>
{{ end }}
</div>
{{ template "pagination" . }}
</div>
</div>
</div>
</section>
{{ end }}