almost all ready
This commit is contained in:
+285
-20
@@ -1,26 +1,31 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"fmt"
|
||||
"html"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
pscart "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cart"
|
||||
pscatalog "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/catalog"
|
||||
pscookie "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cookie"
|
||||
"git.ma-al.com/goc_marek/ps_shop/internal/viewmodel"
|
||||
)
|
||||
|
||||
var htmlTagPattern = regexp.MustCompile(`<[^>]+>`)
|
||||
|
||||
func menuListClass(depth int) string {
|
||||
if depth == 0 {
|
||||
return "flex min-w-0 flex-col gap-2 text-sm"
|
||||
}
|
||||
return "mt-2 space-y-2 border-l border-stone-200 pl-4 text-sm"
|
||||
return "mt-2 space-y-2 border-l border-stone-200/80 pl-4 text-sm"
|
||||
}
|
||||
|
||||
func menuLinkClass(depth int) string {
|
||||
if depth == 0 {
|
||||
return "inline-flex items-center gap-2 px-2 py-2 text-[1.02rem] font-medium text-stone-900 transition hover:text-amber-600"
|
||||
return "flex items-center justify-between gap-3 rounded-[1.1rem] border border-stone-200/80 bg-stone-50/90 px-4 py-3 text-[1rem] font-medium text-stone-900 shadow-[0_10px_24px_rgba(20,33,61,0.05)] transition hover:border-amber-300/60 hover:bg-white hover:text-amber-700"
|
||||
}
|
||||
return "inline-flex items-center gap-2 text-stone-700 transition hover:text-amber-600"
|
||||
return "inline-flex items-center gap-2 py-1 text-stone-700 transition hover:text-amber-600"
|
||||
}
|
||||
|
||||
func menuItemClass(depth int, hasChildren bool) string {
|
||||
@@ -79,20 +84,280 @@ func menuPanelID(id int64) string {
|
||||
return "mega-menu-panel-" + strconv.FormatInt(id, 10)
|
||||
}
|
||||
|
||||
func sessionCookieLines(session *pscookie.SessionContext) []string {
|
||||
if session == nil || len(session.Values) == 0 {
|
||||
return nil
|
||||
func moneyWithCurrency(amount float64, sign, code string) string {
|
||||
formatted := fmt.Sprintf("%.2f", amount)
|
||||
sign = strings.TrimSpace(sign)
|
||||
switch {
|
||||
case sign != "":
|
||||
return formatted + " " + sign
|
||||
default:
|
||||
return formatted
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(session.Values))
|
||||
for key := range session.Values {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
lines := make([]string, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
lines = append(lines, key+"="+session.Values[key])
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func taxLabel(rate float64) string {
|
||||
return fmt.Sprintf("Tax %.2f%%", rate)
|
||||
}
|
||||
|
||||
func conversionRateLabel(rate float64, code string) string {
|
||||
code = strings.TrimSpace(code)
|
||||
if code == "" {
|
||||
return fmt.Sprintf("Rate %.6f", rate)
|
||||
}
|
||||
return fmt.Sprintf("Rate %.6f %s", rate, code)
|
||||
}
|
||||
|
||||
func localizedCartPath(locale pscatalog.HeaderLocaleData) string {
|
||||
code := strings.ToLower(strings.TrimSpace(locale.CurrentLanguage.Code))
|
||||
if code == "" {
|
||||
return "/cart"
|
||||
}
|
||||
return "/" + code + "/cart"
|
||||
}
|
||||
|
||||
func plainTextHTML(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
value = htmlTagPattern.ReplaceAllString(value, " ")
|
||||
value = html.UnescapeString(value)
|
||||
return strings.Join(strings.Fields(value), " ")
|
||||
}
|
||||
|
||||
func truncatedPlainTextHTML(value string, maxChars int) string {
|
||||
value = plainTextHTML(value)
|
||||
if value == "" || maxChars <= 0 {
|
||||
return value
|
||||
}
|
||||
runes := []rune(value)
|
||||
if len(runes) <= maxChars {
|
||||
return value
|
||||
}
|
||||
cut := strings.TrimSpace(string(runes[:maxChars]))
|
||||
if idx := strings.LastIndex(cut, " "); idx >= maxChars/2 {
|
||||
cut = strings.TrimSpace(cut[:idx])
|
||||
}
|
||||
if cut == "" {
|
||||
return value
|
||||
}
|
||||
return cut + "..."
|
||||
}
|
||||
|
||||
func combinationAttributeLabel(publicName, group string) string {
|
||||
publicName = strings.TrimSpace(publicName)
|
||||
if publicName != "" {
|
||||
return publicName
|
||||
}
|
||||
return strings.TrimSpace(group)
|
||||
}
|
||||
|
||||
func isHexColor(value string) bool {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return false
|
||||
}
|
||||
if !strings.HasPrefix(value, "#") {
|
||||
value = "#" + value
|
||||
}
|
||||
if len(value) != 7 {
|
||||
return false
|
||||
}
|
||||
for _, r := range value[1:] {
|
||||
if (r < '0' || r > '9') && (r < 'a' || r > 'f') && (r < 'A' || r > 'F') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func cartCurrencyCode(data viewmodel.CartPageData) string {
|
||||
if code := cartCurrencyField(data.Cart.Items, func(item pscart.Item) string { return item.CurrencyCode }); code != "" {
|
||||
return code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cartCurrencySign(data viewmodel.CartPageData) string {
|
||||
if sign := cartCurrencyField(data.Cart.Items, func(item pscart.Item) string { return item.CurrencySign }); sign != "" {
|
||||
return sign
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cartCurrencyField(items []pscart.Item, pick func(pscart.Item) string) string {
|
||||
for _, item := range items {
|
||||
value := strings.TrimSpace(pick(item))
|
||||
if value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cartItemAttributeLabel(item pscart.Item) string {
|
||||
if len(item.Attributes) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
parts := make([]string, 0, len(item.Attributes))
|
||||
for _, attribute := range item.Attributes {
|
||||
group := strings.TrimSpace(attribute.Group)
|
||||
value := strings.TrimSpace(attribute.Value)
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
if group != "" {
|
||||
parts = append(parts, group+": "+value)
|
||||
continue
|
||||
}
|
||||
parts = append(parts, value)
|
||||
}
|
||||
|
||||
return strings.Join(parts, " • ")
|
||||
}
|
||||
|
||||
func layoutCartItems(summary *pscart.Summary) int64 {
|
||||
if summary == nil || summary.TotalItems <= 0 {
|
||||
return 0
|
||||
}
|
||||
return summary.TotalItems
|
||||
}
|
||||
|
||||
func categoryPageStart(pagination viewmodel.CategoryPagination) int64 {
|
||||
if pagination.TotalItems <= 0 || pagination.Page <= 0 || pagination.PerPage <= 0 {
|
||||
return 0
|
||||
}
|
||||
return int64((pagination.Page-1)*pagination.PerPage) + 1
|
||||
}
|
||||
|
||||
func categoryPageEnd(pagination viewmodel.CategoryPagination, loaded int) int64 {
|
||||
if pagination.TotalItems <= 0 || loaded <= 0 {
|
||||
return 0
|
||||
}
|
||||
end := categoryPageStart(pagination) + int64(loaded) - 1
|
||||
if end > pagination.TotalItems {
|
||||
return pagination.TotalItems
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
type productVariantGroupView struct {
|
||||
Key string
|
||||
Label string
|
||||
GroupType string
|
||||
Options []productVariantOptionView
|
||||
}
|
||||
|
||||
type productVariantOptionView struct {
|
||||
Value string
|
||||
ColorStyle string
|
||||
CombinationIDs string
|
||||
Selected bool
|
||||
}
|
||||
|
||||
func productVariantGroups(combinations []pscatalog.ProductCombination, defaultID int64) []productVariantGroupView {
|
||||
groups := make([]productVariantGroupView, 0)
|
||||
groupIndex := make(map[string]int)
|
||||
optionIndex := make(map[string]map[string]int)
|
||||
|
||||
for _, combination := range combinations {
|
||||
for _, attribute := range combination.Attributes {
|
||||
label := combinationAttributeLabel(attribute.PublicName, attribute.Group)
|
||||
if label == "" {
|
||||
continue
|
||||
}
|
||||
key := strings.ToLower(strings.TrimSpace(label))
|
||||
idx, exists := groupIndex[key]
|
||||
if !exists {
|
||||
groups = append(groups, productVariantGroupView{
|
||||
Key: "variant-group-" + strconv.Itoa(len(groups)),
|
||||
Label: label,
|
||||
GroupType: normalizedGroupType(attribute.GroupType),
|
||||
Options: make([]productVariantOptionView, 0),
|
||||
})
|
||||
idx = len(groups) - 1
|
||||
groupIndex[key] = idx
|
||||
optionIndex[key] = make(map[string]int)
|
||||
}
|
||||
|
||||
optionKey := strings.ToLower(strings.TrimSpace(attribute.Value)) + "|" + normalizedColorStyle(attribute.Color)
|
||||
optIdx, exists := optionIndex[key][optionKey]
|
||||
if !exists {
|
||||
groups[idx].Options = append(groups[idx].Options, productVariantOptionView{
|
||||
Value: attribute.Value,
|
||||
ColorStyle: normalizedColorStyle(attribute.Color),
|
||||
CombinationIDs: strconv.FormatInt(combination.ID, 10),
|
||||
Selected: combination.ID == defaultID,
|
||||
})
|
||||
optionIndex[key][optionKey] = len(groups[idx].Options) - 1
|
||||
continue
|
||||
}
|
||||
|
||||
option := &groups[idx].Options[optIdx]
|
||||
option.CombinationIDs += "," + strconv.FormatInt(combination.ID, 10)
|
||||
if combination.ID == defaultID {
|
||||
option.Selected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
func normalizedGroupType(value string) string {
|
||||
value = strings.ToLower(strings.TrimSpace(value))
|
||||
switch value {
|
||||
case "color", "radio", "select":
|
||||
return value
|
||||
default:
|
||||
return "select"
|
||||
}
|
||||
}
|
||||
|
||||
func normalizedColorStyle(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
if !strings.HasPrefix(value, "#") {
|
||||
value = "#" + value
|
||||
}
|
||||
if !isHexColor(value) {
|
||||
return ""
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func variantColorOptionClass(selected bool) string {
|
||||
if selected {
|
||||
return "inline-flex min-h-10 min-w-10 items-center justify-center border border-stone-900 p-0.5 ring-1 ring-stone-900 transition"
|
||||
}
|
||||
return "inline-flex min-h-10 min-w-10 items-center justify-center border border-stone-300 p-0.5 transition hover:border-stone-700"
|
||||
}
|
||||
|
||||
func variantRadioOptionClass(selected bool) string {
|
||||
if selected {
|
||||
return "rounded-full border border-stone-900 bg-stone-900 px-4 py-2 text-sm font-medium text-stone-50 transition"
|
||||
}
|
||||
return "rounded-full border border-stone-300 bg-white px-4 py-2 text-sm font-medium text-stone-900 transition hover:border-stone-700"
|
||||
}
|
||||
|
||||
func variantSelectOptionClass(selected bool) string {
|
||||
if selected {
|
||||
return "w-full rounded-2xl bg-stone-900 px-4 py-3 text-left text-sm font-medium text-stone-50 transition"
|
||||
}
|
||||
return "w-full rounded-2xl px-4 py-3 text-left text-sm font-medium text-stone-700 transition hover:bg-stone-100 hover:text-stone-950"
|
||||
}
|
||||
|
||||
func selectedVariantOptionValue(options []productVariantOptionView) string {
|
||||
for _, option := range options {
|
||||
if option.Selected {
|
||||
return option.Value
|
||||
}
|
||||
}
|
||||
if len(options) == 0 {
|
||||
return ""
|
||||
}
|
||||
return options[0].Value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user