Files
terminalShop/pkg/tui/confirmation.go
marcusDenslow 6006f9617d refactor: add dedicated *Switch() methods
added dedicated *Switch() methods (ShopSwitch, CartSwitc, AccountSwitch, ...) for page transitions and remove the catch-all resetPageState(). Each switch owns its pages zero-init, viewport setup, and async data fetching
2026-04-24 09:25:31 +02:00

232 lines
5.9 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.confirm.shipping != nil {
shippingSection = sectionStyle.Render(
labelStyle.Render("Ship to: ") +
valueStyle.Render(m.confirm.shipping.Name) + "\n" +
labelStyle.Render(" ") +
valueStyle.Render(m.confirm.shipping.Street) + "\n" +
labelStyle.Render(" ") +
valueStyle.Render(fmt.Sprintf("%s, %s %s, %s", m.confirm.shipping.City, m.confirm.shipping.State, m.confirm.shipping.Zip, m.confirm.shipping.Country)),
)
}
// Items summary
cartSection := ""
for _, item := range m.confirm.items {
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.confirm.total)/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.confirm.viewportReady {
m.confirm.viewport = viewport.New(m.widthContent, availH)
m.confirm.viewport.KeyMap = viewport.DefaultKeyMap()
m.confirm.viewportReady = true
} else {
m.confirm.viewport.Width = m.widthContent
m.confirm.viewport.Height = availH
}
return m
}
func (m Model) ConfirmView() string {
if !m.confirm.viewportReady {
m = m.updateConfirmViewport()
}
m.confirm.viewport.SetContent(m.RenderConfirmation())
return lipgloss.Place(
m.widthContainer,
lipgloss.Height(m.confirm.viewport.View()),
lipgloss.Center, lipgloss.Center,
m.confirm.viewport.View(),
)
}
func (m Model) ConfirmUpdate(msg tea.Msg) (Model, tea.Cmd) {
var cmd tea.Cmd
if m.confirm.viewportReady {
m.confirm.viewport, cmd = m.confirm.viewport.Update(msg)
}
keyMsg, ok := msg.(tea.KeyMsg)
if !ok {
return m, cmd
}
if keyMsg.String() != "esc" && keyMsg.String() != "s" {
return m, cmd
}
m.CheckingOut = false
m.confirm.total = 0
m.confirm.items = nil
m.confirm.shipping = nil
m, _ = m.ShopSwitch()
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.review.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.review.success {
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.payment.view = 0
m.payment.form = nil
return m, nil
}
m.Cart = make(map[uint]*models.CartItem)
m.cart.cursor = 0
m.ShippingInfo = nil
m.OrdersLoaded = false
m.review.success = true
return m, nil
case tea.KeyMsg:
if m.CheckingOut {
return m, nil
}
if m.review.success {
m.review.success = false
m.review.cardJustAdded = false
m, _ = m.ShopSwitch()
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
}