Files
terminalShop/pkg/tui/confirmation.go
marcusDenslow 6559c4dd8f fix and feature: made checkout page cleaner, and added card and address modification to account page
the checkout flow was a bit wonky because it would display the same info twice which looked weird and confusing. this is now changed to only show once and update one of the pages independantly.

Now added the possibility to modify card and address list in the account page and not in the checkout flow
2026-04-12 12:11:04 +02:00

234 lines
5.8 KiB
Go

package tui
import (
"fmt"
"strings"
"terminalShop/pkg/models"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
// RenderConfirmation renders the order confirmation view
func (m Model) RenderConfirmation() string {
titleStyle := m.theme.TextAccent().Bold(true).Padding(0, 0, 1, 0)
sectionStyle := m.theme.TextBody().Padding(0, 0, 1, 2)
labelStyle := m.theme.TextDim()
valueStyle := m.theme.TextAccent()
successStyle := m.theme.TextSuccess().Bold(true).Padding(1, 0)
title := titleStyle.Render("Order Confirmation")
// Shipping summary
shippingSection := ""
if m.ConfirmShipping != nil {
shippingSection = sectionStyle.Render(
labelStyle.Render("Ship to: ") +
valueStyle.Render(m.ConfirmShipping.Name) + "\n" +
labelStyle.Render(" ") +
valueStyle.Render(m.ConfirmShipping.Street) + "\n" +
labelStyle.Render(" ") +
valueStyle.Render(fmt.Sprintf("%s, %s %s, %s", m.ConfirmShipping.City, m.ConfirmShipping.State, m.ConfirmShipping.Zip, m.ConfirmShipping.Country)),
)
}
// Items summary
cartSection := ""
for _, item := range m.ConfirmItems {
line := fmt.Sprintf(" %s x%d $%.2f", item.Coffee.Name, item.Quantity, float64(item.Coffee.Price*item.Quantity)/100)
cartSection += sectionStyle.Render(line) + "\n"
}
cartSection += sectionStyle.Render(
labelStyle.Render("Shipping: ")+valueStyle.Render("$0.00"),
) + "\n"
cartSection += sectionStyle.Render(
labelStyle.Render("Total: ") + valueStyle.Render(fmt.Sprintf("$%.2f", float64(m.ConfirmTotal)/100)),
)
success := successStyle.Render("Order placed successfully!")
return lipgloss.JoinVertical(
lipgloss.Left,
title,
"",
shippingSection,
"",
cartSection,
"",
success,
)
}
func (m Model) updateConfirmViewport() Model {
headerH := lipgloss.Height(m.BuildHeader())
breadH := lipgloss.Height(m.BuildBreadcrumbs())
footerH := lipgloss.Height(m.BuildFooter())
availH := m.heightContainer - headerH - footerH - breadH
if availH < 1 {
availH = 1
}
if !m.confirmVPReady {
m.confirmVP = viewport.New(m.widthContent, availH)
m.confirmVP.KeyMap = viewport.DefaultKeyMap()
m.confirmVPReady = true
} else {
m.confirmVP.Width = m.widthContent
m.confirmVP.Height = availH
}
return m
}
func (m Model) ConfirmView() string {
if !m.confirmVPReady {
m = m.updateConfirmViewport()
}
m.confirmVP.SetContent(m.RenderConfirmation())
return lipgloss.Place(
m.widthContainer,
lipgloss.Height(m.confirmVP.View()),
lipgloss.Center, lipgloss.Center,
m.confirmVP.View(),
)
}
func (m Model) ConfirmUpdate(msg tea.Msg) (Model, tea.Cmd) {
var cmd tea.Cmd
if m.confirmVPReady {
m.confirmVP, cmd = m.confirmVP.Update(msg)
}
keyMsg, ok := msg.(tea.KeyMsg)
if !ok {
return m, cmd
}
if keyMsg.String() != "esc" && keyMsg.String() != "s" {
return m, cmd
}
m = m.SwitchPage(shopPage)
m.CheckingOut = false
m.ConfirmTotal = 0
m.ConfirmItems = nil
m.ConfirmShipping = nil
m = m.resetPageState()
return m, func() tea.Msg {
if m.APIClient != nil {
_ = m.APIClient.ClearCart()
}
return nil
}
}
func formatUSD(cents int) string {
return fmt.Sprintf("$%d.%02d", cents/100, cents%100)
}
func (m Model) generateReviewContent() string {
view := strings.Builder{}
if m.CardJustAdded {
view.WriteString(m.theme.TextSuccess().Bold(true).Render("card added successfully") + "\n\n")
}
if m.ShippingInfo != nil {
view.WriteString(m.ShippingInfo.Name + "\n")
view.WriteString(m.ShippingInfo.Street + "\n")
if m.ShippingInfo.Street2 != "" {
view.WriteString(m.ShippingInfo.Street2 + "\n")
}
view.WriteString(fmt.Sprintf("%s, %s %s, %s\n", m.ShippingInfo.City, m.ShippingInfo.State, m.ShippingInfo.Zip, m.ShippingInfo.Country))
}
view.WriteString("\n")
if m.SelectedCard != nil {
view.WriteString(fmt.Sprintf("cc: **** **** **** %s\n", m.SelectedCard.Last4))
}
view.WriteString("\n")
subtotal := 0
for _, item := range m.GetCartItemsSlice() {
subtotal += item.Coffee.Price * item.Quantity
}
view.WriteString(fmt.Sprintf("subtotal: %s\n", formatUSD(subtotal)))
view.WriteString(m.theme.TextAccent().Render(fmt.Sprintf("total: %s", formatUSD(subtotal))) + "\n")
view.WriteString("\n")
view.WriteString(m.theme.TextDim().Render("press enter to confirm") + "\n")
return m.theme.Base().Padding(0, 1).Render(view.String())
}
func (m Model) ReviewView() string {
if m.CheckingOut {
return m.theme.TextAccent().Bold(true).Padding(1).Render(" submitting order...")
}
if m.ReviewSuccess {
content := m.theme.TextSuccess().Bold(true).Padding(1).Render("order placed successfully!")
return lipgloss.Place(
m.widthContainer,
lipgloss.Height(content),
lipgloss.Center, lipgloss.Center,
content,
)
}
content := m.generateReviewContent()
return lipgloss.Place(
m.widthContainer,
lipgloss.Height(content),
lipgloss.Center, lipgloss.Center,
content,
)
}
func (m Model) ReviewUpdate(msg tea.Msg) (Model, tea.Cmd) {
switch msg := msg.(type) {
case CheckoutResultMsg:
m.CheckingOut = false
if msg.Err != nil {
m.ErrorMsg = fmt.Sprintf("checkout failed: %v", msg.Err)
m = m.SwitchPage(paymentPage)
m.PaymentView = 0
m.PaymentForm = nil
return m, nil
}
m.Cart = make(map[uint]*models.CartItem)
m.CartCursor = 0
m.ShippingInfo = nil
m.OrdersLoaded = false
m.ReviewSuccess = true
return m, nil
case tea.KeyMsg:
if m.CheckingOut {
return m, nil
}
if m.ReviewSuccess {
m = m.SwitchPage(shopPage)
m.ReviewSuccess = false
m.CardJustAdded = false
m = m.resetPageState()
return m, func() tea.Msg {
if m.APIClient != nil {
_ = m.APIClient.ClearCart()
}
return nil
}
}
switch msg.String() {
case "esc":
m = m.SwitchPage(paymentPage)
return m, nil
case "enter":
m.CheckingOut = true
return m, m.checkoutWithSavedCard()
}
}
return m, nil
}