allow finalizing orders
This commit is contained in:
@@ -34,14 +34,6 @@ func NewCartItemController() CartItemController {
|
|||||||
return &cartItemController{}
|
return &cartItemController{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetShippingMethods() []models.Shipping {
|
|
||||||
return []models.Shipping{
|
|
||||||
{Id: "germany", Name: "Germany (DHL)", Price: 3.99},
|
|
||||||
{Id: "international", Name: "International (DHL)", Price: 5.99},
|
|
||||||
{Id: "pickup", Name: "Pickup", Price: 0.00},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateSessionId(length int) string {
|
func generateSessionId(length int) string {
|
||||||
bytes := make([]byte, length) // 16 bytes = 128 bits
|
bytes := make([]byte, length) // 16 bytes = 128 bits
|
||||||
_, err := rand.Read(bytes)
|
_, err := rand.Read(bytes)
|
||||||
@@ -63,7 +55,7 @@ func GetSessionId(ctx *gin.Context) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GenerateToken() string {
|
func GenerateToken() string {
|
||||||
return generateSessionId(8)
|
return generateSessionId(16)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *cartItemController) NewCartItemFromForm(ctx *gin.Context) (models.CartItem, error) {
|
func (rc *cartItemController) NewCartItemFromForm(ctx *gin.Context) (models.CartItem, error) {
|
||||||
@@ -143,7 +135,7 @@ func (rc *cartItemController) NewAddressFromForm(ctx *gin.Context) (models.Addre
|
|||||||
|
|
||||||
func (rc *cartItemController) NewOrderFromForm(ctx *gin.Context) (models.Order, error) {
|
func (rc *cartItemController) NewOrderFromForm(ctx *gin.Context) (models.Order, error) {
|
||||||
sessionId := GetSessionId(ctx)
|
sessionId := GetSessionId(ctx)
|
||||||
status := models.OrderStatus("Received")
|
status := models.OrderStatus("AwaitingConfirmation")
|
||||||
token := GenerateToken()
|
token := GenerateToken()
|
||||||
email := ctx.PostForm("email")
|
email := ctx.PostForm("email")
|
||||||
comment := ctx.PostForm("comment")
|
comment := ctx.PostForm("comment")
|
||||||
@@ -162,14 +154,9 @@ func (rc *cartItemController) NewOrderFromForm(ctx *gin.Context) (models.Order,
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
var shipping models.Shipping
|
shipping, err := models.GetShippingMethod(shippingStr)
|
||||||
for _, shippingMethod := range GetShippingMethods() {
|
|
||||||
if shippingMethod.Id == shippingStr {
|
|
||||||
shipping = shippingMethod
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if shipping == (models.Shipping{}) {
|
if err != nil {
|
||||||
return models.Order{}, fmt.Errorf("Invalid shipping method.")
|
return models.Order{}, fmt.Errorf("Invalid shipping method.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +254,7 @@ func (rc *cartItemController) CartItemView(c *gin.Context) {
|
|||||||
data := CreateSessionData(c, gin.H{
|
data := CreateSessionData(c, gin.H{
|
||||||
"cartItems": cartItems,
|
"cartItems": cartItems,
|
||||||
"priceTotal": fmt.Sprintf("%.2f", priceTotal), //round 2 decimals
|
"priceTotal": fmt.Sprintf("%.2f", priceTotal), //round 2 decimals
|
||||||
"shipping": GetShippingMethods(),
|
"shipping": models.GetShippingMethods(),
|
||||||
})
|
})
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "cart.html", data)
|
c.HTML(http.StatusOK, "cart.html", data)
|
||||||
@@ -348,6 +335,11 @@ func (rc *cartItemController) EditItemHandler(c *gin.Context) {
|
|||||||
func (rc *cartItemController) CheckoutView(c *gin.Context) {
|
func (rc *cartItemController) CheckoutView(c *gin.Context) {
|
||||||
shippingMethod := c.Query("shippingMethod")
|
shippingMethod := c.Query("shippingMethod")
|
||||||
|
|
||||||
|
if shippingMethod == "" {
|
||||||
|
rc.CartItemView(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "checkout.html", gin.H{
|
c.HTML(http.StatusOK, "checkout.html", gin.H{
|
||||||
"askAddress": (shippingMethod != "pickup"),
|
"askAddress": (shippingMethod != "pickup"),
|
||||||
"shippingMethod": shippingMethod,
|
"shippingMethod": shippingMethod,
|
||||||
@@ -366,10 +358,8 @@ func (rc *cartItemController) CheckoutHandler(c *gin.Context) {
|
|||||||
existingOrder, err := repositories.Orders.GetBySession(order.SessionId)
|
existingOrder, err := repositories.Orders.GetBySession(order.SessionId)
|
||||||
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
fmt.Println("CREATE")
|
|
||||||
_, err = repositories.Orders.Create(order)
|
_, err = repositories.Orders.Create(order)
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
fmt.Println("UPDATE")
|
|
||||||
order.ID = existingOrder.ID
|
order.ID = existingOrder.ID
|
||||||
order.CreatedAt = existingOrder.CreatedAt
|
order.CreatedAt = existingOrder.CreatedAt
|
||||||
repositories.Orders.Update(order)
|
repositories.Orders.Update(order)
|
||||||
@@ -385,19 +375,27 @@ func (rc *cartItemController) CheckoutHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var shipping models.Shipping
|
shipping, err := models.GetShippingMethod(order.Shipping)
|
||||||
for _, shippingMethod := range GetShippingMethods() {
|
if err != nil {
|
||||||
if shippingMethod.Id == order.Shipping {
|
data := CreateSessionData(c, gin.H{
|
||||||
shipping = shippingMethod
|
"error": err,
|
||||||
}
|
"success": "",
|
||||||
|
})
|
||||||
|
|
||||||
|
c.HTML(http.StatusOK, "cart.html", data)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
priceProducts := 0.0
|
priceProducts, priceTotal, err := order.CalculatePrices()
|
||||||
for _, cartItem := range order.CartItems {
|
if err != nil {
|
||||||
priceProducts += (float64(cartItem.Quantity) * cartItem.ItemVariant.Price)
|
data := CreateSessionData(c, gin.H{
|
||||||
}
|
"error": err,
|
||||||
|
"success": "",
|
||||||
|
})
|
||||||
|
|
||||||
priceTotal := priceProducts + shipping.Price
|
c.HTML(http.StatusOK, "cart.html", data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data := CreateSessionData(c, gin.H{
|
data := CreateSessionData(c, gin.H{
|
||||||
"error": "",
|
"error": "",
|
||||||
@@ -411,19 +409,85 @@ func (rc *cartItemController) CheckoutHandler(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
fmt.Println(order)
|
fmt.Println(order)
|
||||||
c.HTML(http.StatusOK, "order.html", data)
|
c.HTML(http.StatusOK, "orderpreview.html", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *cartItemController) OrderView(c *gin.Context) {
|
func (rc *cartItemController) OrderView(c *gin.Context) {
|
||||||
shippingMethod := c.Query("shippingMethod")
|
orderToken := c.Param("token")
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "checkout.html", gin.H{
|
order, err := repositories.Orders.GetByToken(orderToken)
|
||||||
"askAddress": (shippingMethod != "pickup"),
|
if err != nil {
|
||||||
"shippingMethod": shippingMethod,
|
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Order does not exist."})
|
||||||
})
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
shipping, err := models.GetShippingMethod(order.Shipping)
|
||||||
|
if err != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Could not get shipping method"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
priceProducts, priceTotal, err := order.CalculatePrices()
|
||||||
|
if err != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Could not calculate final prices"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Order: %v\n", order)
|
||||||
|
fmt.Printf("PriceTotal: %v\n", priceTotal)
|
||||||
|
fmt.Printf("Amount Items: %v\n", len(order.CartItems))
|
||||||
|
|
||||||
|
for _, item := range order.CartItems {
|
||||||
|
fmt.Printf("Cartitem: %v", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.HTML(http.StatusOK, "order.html", CreateSessionData(c, gin.H{
|
||||||
|
"error": "",
|
||||||
|
"success": "",
|
||||||
|
"order": order,
|
||||||
|
"shipping": shipping,
|
||||||
|
"priceProducts": fmt.Sprintf("%.2f", priceProducts), //round 2 decimals
|
||||||
|
"priceTotal": fmt.Sprintf("%.2f", priceTotal), //round 2 decimals
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *cartItemController) OrderHandler(c *gin.Context) {
|
func (rc *cartItemController) OrderHandler(c *gin.Context) {
|
||||||
//get order by session id
|
confirmation := c.PostForm("confirm-order")
|
||||||
//generate token, preview payment info
|
|
||||||
|
if confirmation == "" {
|
||||||
|
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Something went wrong, try again later"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if confirmation != "true" {
|
||||||
|
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Order was not confirmed."})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionId := GetSessionId(c)
|
||||||
|
order, err := repositories.Orders.GetBySession(sessionId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Something went wrong, try again later"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
order.Status = models.AwaitingPayment
|
||||||
|
|
||||||
|
err = order.Validate()
|
||||||
|
if err != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = repositories.Orders.Update(order)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: cartItemRepository delete all by session - otherwise items stay in cart after completing order..
|
||||||
|
|
||||||
|
c.Redirect(http.StatusFound, fmt.Sprintf("/order/%s", order.Token))
|
||||||
}
|
}
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -90,7 +90,7 @@ func main() {
|
|||||||
viewRoutes.POST("/cart/edit", cartItemController.EditItemHandler)
|
viewRoutes.POST("/cart/edit", cartItemController.EditItemHandler)
|
||||||
viewRoutes.GET("/checkout", cartItemController.CheckoutView)
|
viewRoutes.GET("/checkout", cartItemController.CheckoutView)
|
||||||
viewRoutes.POST("/checkout", cartItemController.CheckoutHandler)
|
viewRoutes.POST("/checkout", cartItemController.CheckoutHandler)
|
||||||
viewRoutes.GET("/order", cartItemController.OrderView)
|
viewRoutes.GET("/order/:token", cartItemController.OrderView)
|
||||||
viewRoutes.POST("/order", cartItemController.OrderHandler)
|
viewRoutes.POST("/order", cartItemController.OrderHandler)
|
||||||
|
|
||||||
//write middleware that redirects to homescreen on register/login/reset for logged in users
|
//write middleware that redirects to homescreen on register/login/reset for logged in users
|
||||||
|
|||||||
166
models/cart.go
166
models/cart.go
@@ -1,59 +1,161 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OrderStatus string
|
type OrderStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Received OrderStatus = "Received"
|
AwaitingConfirmation OrderStatus = "AwaitingConfirmation"
|
||||||
AwaitingPayment OrderStatus = "AwaitingPayment"
|
Received OrderStatus = "Received"
|
||||||
Payed OrderStatus = "Payed"
|
AwaitingPayment OrderStatus = "AwaitingPayment"
|
||||||
ReadyForPickup OrderStatus = "ReadyForPickup"
|
Payed OrderStatus = "Payed"
|
||||||
Shipped OrderStatus = "Shipped"
|
ReadyForPickup OrderStatus = "ReadyForPickup"
|
||||||
Cancelled OrderStatus = "Cancelled"
|
Shipped OrderStatus = "Shipped"
|
||||||
|
Cancelled OrderStatus = "Cancelled"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AddressInfo struct {
|
type AddressInfo struct {
|
||||||
FirstName string `json:"firstname"`
|
FirstName string `json:"firstname"`
|
||||||
LastName string `json:"lastname"`
|
LastName string `json:"lastname"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
PostalCode string `json:"postalcode"`
|
PostalCode string `json:"postalcode"`
|
||||||
City string `json:"city"`
|
City string `json:"city"`
|
||||||
Country string `json:"country"`
|
Country string `json:"country"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Shipping struct {
|
type Shipping struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Price float64 `json:"price"`
|
Price float64 `json:"price"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetShippingMethods() []Shipping {
|
||||||
|
return []Shipping{
|
||||||
|
{Id: "germany", Name: "Germany (DHL)", Price: 3.99},
|
||||||
|
{Id: "international", Name: "International (DHL)", Price: 5.99},
|
||||||
|
{Id: "pickup", Name: "Pickup", Price: 0.00},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetShippingMethod(id string) (Shipping, error) {
|
||||||
|
var shipping Shipping
|
||||||
|
found := false
|
||||||
|
for _, shippingMethod := range GetShippingMethods() {
|
||||||
|
if shippingMethod.Id == id {
|
||||||
|
shipping = shippingMethod
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return Shipping{}, fmt.Errorf("Shipping method does not exist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return shipping, nil
|
||||||
|
}
|
||||||
|
|
||||||
type Order struct {
|
type Order struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
SessionId string `json:"sessionid" binding:"required" gorm:"not null"`
|
SessionId string `json:"sessionid" binding:"required" gorm:"not null"`
|
||||||
Status OrderStatus `json:"status"`
|
Status OrderStatus `json:"status"`
|
||||||
Token string `json:"token" binding:"required" gorm:"not null"`
|
Token string `json:"token" binding:"required" gorm:"not null"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
FirstName string `json:"firstname"`
|
FirstName string `json:"firstname"`
|
||||||
LastName string `json:"lastname"`
|
LastName string `json:"lastname"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
PostalCode string `json:"postalcode"`
|
PostalCode string `json:"postalcode"`
|
||||||
City string `json:"city"`
|
City string `json:"city"`
|
||||||
Country string `json:"country"`
|
Country string `json:"country"`
|
||||||
Shipping string `json:"shipping"`
|
Shipping string `json:"shipping"`
|
||||||
CartItems []CartItem `json:"cartitems" gorm:"foreignKey:OrderID"`
|
CartItems []CartItem `json:"cartitems" gorm:"foreignKey:OrderID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Order) Validate() error {
|
||||||
|
//TODO: validate sessionId
|
||||||
|
if o.SessionId == "" {
|
||||||
|
return fmt.Errorf("Invalid SessionId")
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: validate token
|
||||||
|
if o.Token == "" {
|
||||||
|
return fmt.Errorf("Invalid Token")
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.Status == AwaitingConfirmation {
|
||||||
|
return fmt.Errorf("Order still awaiting confirmation.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(o.CartItems) == 0 {
|
||||||
|
return fmt.Errorf("Order is empty.")
|
||||||
|
}
|
||||||
|
|
||||||
|
shipping, err := GetShippingMethod(o.Shipping)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//for pickup no address validation is necessary
|
||||||
|
if shipping.Id == "pickup" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.ValidateAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Order) ValidateAddress() error {
|
||||||
|
if o.FirstName == "" {
|
||||||
|
return fmt.Errorf("Firstname missing")
|
||||||
|
}
|
||||||
|
if o.LastName == "" {
|
||||||
|
return fmt.Errorf("Lastname missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.Address == "" {
|
||||||
|
return fmt.Errorf("Address missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.PostalCode == "" {
|
||||||
|
return fmt.Errorf("Postalcode missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.City == "" {
|
||||||
|
return fmt.Errorf("City missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.Country == "" {
|
||||||
|
return fmt.Errorf("Country missing")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Order) CalculatePrices() (float64, float64, error) {
|
||||||
|
shipping, err := GetShippingMethod(o.Shipping)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
priceProducts := 0.0
|
||||||
|
for _, cartItem := range o.CartItems {
|
||||||
|
priceProducts += (float64(cartItem.Quantity) * cartItem.ItemVariant.Price)
|
||||||
|
}
|
||||||
|
|
||||||
|
priceTotal := priceProducts + shipping.Price
|
||||||
|
|
||||||
|
return priceProducts, priceTotal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CartItem struct {
|
type CartItem struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
SessionId string `json:"sessionid" binding:"required" gorm:"not null"`
|
SessionId string `json:"sessionid" binding:"required" gorm:"not null"`
|
||||||
ShopItemId uint
|
ShopItemId uint
|
||||||
ShopItem ShopItem `json:"shopitem" gorm:"foreignKey:ShopItemId"` //gorm one2one
|
ShopItem ShopItem `json:"shopitem" gorm:"foreignKey:ShopItemId"` //gorm one2one
|
||||||
ItemVariantId uint
|
ItemVariantId uint
|
||||||
ItemVariant ItemVariant `json:"itemvariant" gorm:"foreignKey:ItemVariantId"` //gorm one2one
|
ItemVariant ItemVariant `json:"itemvariant" gorm:"foreignKey:ItemVariantId"` //gorm one2one
|
||||||
Quantity int `json:"quantity" binding:"required"`
|
Quantity int `json:"quantity" binding:"required"`
|
||||||
OrderID uint
|
OrderID uint
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type OrderRepository interface {
|
|||||||
GetAll() ([]models.Order, error)
|
GetAll() ([]models.Order, error)
|
||||||
GetById(string) (models.Order, error)
|
GetById(string) (models.Order, error)
|
||||||
GetBySession(string) (models.Order, error)
|
GetBySession(string) (models.Order, error)
|
||||||
|
GetByToken(string) (models.Order, error)
|
||||||
Update(models.Order) (models.Order, error)
|
Update(models.Order) (models.Order, error)
|
||||||
DeleteById(string) error
|
DeleteById(string) error
|
||||||
}
|
}
|
||||||
@@ -62,12 +63,19 @@ func (t *GORMOrderRepository) GetById(id string) (models.Order, error) {
|
|||||||
|
|
||||||
func (r *GORMOrderRepository) GetBySession(sessionId string) (models.Order, error) {
|
func (r *GORMOrderRepository) GetBySession(sessionId string) (models.Order, error) {
|
||||||
var orders models.Order
|
var orders models.Order
|
||||||
result := r.DB.Preload("CartItems").Where("session_id = ?", sessionId).First(&orders)
|
result := r.DB.Preload("CartItems").Preload("CartItems.ShopItem").Preload("CartItems.ItemVariant").Where("session_id = ?", sessionId).First(&orders)
|
||||||
|
|
||||||
return orders, result.Error
|
return orders, result.Error
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *GORMOrderRepository) GetByToken(token string) (models.Order, error) {
|
||||||
|
var orders models.Order
|
||||||
|
result := r.DB.Preload("CartItems").Preload("CartItems.ShopItem").Preload("CartItems.ItemVariant").Where("token = ?", token).First(&orders)
|
||||||
|
|
||||||
|
return orders, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
func (r *GORMOrderRepository) Update(order models.Order) (models.Order, error) {
|
func (r *GORMOrderRepository) Update(order models.Order) (models.Order, error) {
|
||||||
result := r.DB.Save(&order)
|
result := r.DB.Save(&order)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
|
|||||||
@@ -587,6 +587,22 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -698,6 +714,16 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -798,6 +824,11 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -810,6 +841,10 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -822,6 +857,14 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -1024,6 +1067,11 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -1301,6 +1349,11 @@ video {
|
|||||||
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
|
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-blue-600 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
.underline {
|
.underline {
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
}
|
}
|
||||||
@@ -1357,6 +1410,37 @@ 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;
|
||||||
|
|||||||
@@ -1,9 +1,37 @@
|
|||||||
{{ template "header.html" . }}
|
{{ template "header.html" . }}
|
||||||
|
|
||||||
<section class="bg-white py-8 antialiased dark:bg-gray-900 md:py-16">
|
<section class="bg-white py-8 antialiased dark:bg-gray-900 md:py-16">
|
||||||
<form action="/checkout" method="POST" class="mx-auto max-w-screen-xl px-4 2xl:px-0">
|
<div class="mx-auto max-w-screen-xl px-4 2xl:px-0">
|
||||||
<div class="mx-auto max-w-3xl">
|
<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>
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white sm:text-2xl">Order summary</h2>
|
||||||
|
<dd class="mt-1 text-base font-normal text-gray-500 dark:text-gray-400">
|
||||||
|
Thanks for your order! As soon as your payment arrived we will print your Order.
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<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">Order status: {{ .data.order.Status }}</h4>
|
||||||
|
<dl>
|
||||||
|
<dd class="mt-1 text-base font-normal text-gray-500 dark:text-gray-400">
|
||||||
|
Order Code: {{ .data.order.Token }}
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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">Payment information</h4>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dd class="mt-1 text-base font-normal text-gray-500 dark:text-gray-400">
|
||||||
|
Either you transfer money to our bank account, or you come by and pay in cash.<br><br>
|
||||||
|
|
||||||
|
Miteinander Dresden e.V.*<br>
|
||||||
|
IBAN: DE66500310001076201001<br>
|
||||||
|
BIC: TRODDEF1 (Triodos Bank)<br>
|
||||||
|
Subject: {{ .data.order.Token }}<br>
|
||||||
|
Amount: {{ .data.priceTotal }}€
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-6 space-y-4 border-b border-t border-gray-200 py-8 dark:border-gray-700 sm:mt-8">
|
<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>
|
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Delivery information</h4>
|
||||||
@@ -23,8 +51,6 @@
|
|||||||
<p><b>Comment:</b> {{ .data.order.Comment }}</p>
|
<p><b>Comment:</b> {{ .data.order.Comment }}</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</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>
|
||||||
|
|
||||||
<div class="mt-6 sm:mt-8">
|
<div class="mt-6 sm:mt-8">
|
||||||
@@ -51,7 +77,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 space-y-6">
|
<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-4">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
@@ -72,21 +97,10 @@
|
|||||||
<dd class="text-lg font-bold text-gray-900 dark:text-white">{{ .data.priceTotal }}€</dd>
|
<dd class="text-lg font-bold text-gray-900 dark:text-white">{{ .data.priceTotal }}€</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{{ template "footer.html" . }}
|
{{ template "footer.html" . }}
|
||||||
|
|||||||
Reference in New Issue
Block a user