diff --git a/cog.toml b/cog.toml new file mode 100644 index 0000000..0178758 --- /dev/null +++ b/cog.toml @@ -0,0 +1,25 @@ +from_latest_tag = false +ignore_merge_commits = false +ignore_fixup_commits = false +disable_changelog = false +disable_bump_commit = false +generate_mono_repository_global_tag = true +generate_mono_repository_package_tags = true +branch_whitelist = [] +pre = "alpha.*" +skip_ci = "[skip ci]" +skip_untracked = false +pre_bump_hooks = [] +post_bump_hooks = [] +pre_package_bump_hooks = [] +post_package_bump_hooks = [] + +[git_hooks] + +[commit_types] + +[changelog] +path = "CHANGELOG.md" +authors = [] + +[bump_profiles] diff --git a/pkg/models/order_display.go b/pkg/models/order_display.go new file mode 100644 index 0000000..2f68c6b --- /dev/null +++ b/pkg/models/order_display.go @@ -0,0 +1,76 @@ +package models + +// DisplayKind is a ui agnostic color/severity token. +// The TUI (or any other renderer) maps this to a concrete style +// the model stays free of UI deps. +type DisplayKind string + +const ( + DisplayKindNeutral DisplayKind = "neutral" // pending, default + DisplayKindAccent DisplayKind = "accent" // paid, awaiting fulfillment + DisplayKindBrand DisplayKind = "brand" // shipped / in transit + DisplayKindSuccess DisplayKind = "success" // delivered + DisplayKindError DisplayKind = "error" // cancelled, failed, refunded + DisplayKindWarning DisplayKind = "warning" // refund pending, delivery issue +) + +func (order *Order) DisplayState() string { + switch order.Status { + case OrderStatusPending: + return "Awaiting payment" + case OrderStatusPaid: + if order.TrackingNumber == "" { + return "Paid * awaiting label" + } + // !IMPORTANT + // Reachable only if UpdateTracking auto-promote is removed later + if order.TrackingStatus == TrackingStatusPreTransit { + return "Paid * ready to ship" + } + return "Paid" + case OrderStatusShipped: + switch order.TrackingStatus { + case TrackingStatusTransit: + return "Shipped * in transit" + case TrackingStatusFailure: + return "Shipped * delivery issue" + case TrackingStatusReturned: + return "Shipped * returned to sender" + case TrackingStatusDelivered: + // Webhook hasn't promoted Status yet, trust tracking + return "Delivered" + } + return "Shipped" + case OrderStatusDelivered: + return "Delivered" + case OrderStatusCancelled: + return "Cancelled" + case OrderStatusRefunded: + return "Refunded" + case OrderStatusFailed: + return "Payment failed" + } + return string(order.Status) +} + +func (order *Order) DisplayKind() DisplayKind { + switch order.Status { + case OrderStatusPending: + return DisplayKindNeutral + case OrderStatusPaid: + return DisplayKindAccent + case OrderStatusShipped: + switch order.TrackingStatus { + case TrackingStatusFailure, TrackingStatusReturned: + return DisplayKindWarning + case TrackingStatusDelivered: + return DisplayKindSuccess + } + return DisplayKindBrand + case OrderStatusDelivered: + return DisplayKindSuccess + case OrderStatusCancelled, OrderStatusRefunded, OrderStatusFailed: + return DisplayKindError + } + return DisplayKindNeutral +} diff --git a/pkg/models/order_display_test.go b/pkg/models/order_display_test.go new file mode 100644 index 0000000..02a75f6 --- /dev/null +++ b/pkg/models/order_display_test.go @@ -0,0 +1,36 @@ +package models + +import "testing" + +func TestOrderDisplayState(t *testing.T) { + tests := []struct { + name string + order Order + want string + kind DisplayKind + }{ + {"pending", Order{Status: OrderStatusPending}, "Awaiting payment", DisplayKindNeutral}, + {"paid no tracking", Order{Status: OrderStatusPaid}, "Paid * awaiting label", DisplayKindAccent}, + {"paid with tracking, no event", Order{Status: OrderStatusPaid, TrackingNumber: "12999"}, "Paid", DisplayKindAccent}, + {"Paid pre-transit", Order{Status: OrderStatusPaid, TrackingNumber: "12999", TrackingStatus: TrackingStatusPreTransit}, "Paid * ready to ship", DisplayKindAccent}, + {"shipped no tracking event", Order{Status: OrderStatusShipped}, "Shipped", DisplayKindBrand}, + {"shipped in transit", Order{Status: OrderStatusShipped, TrackingStatus: TrackingStatusTransit}, "Shipped * in transit", DisplayKindBrand}, + {"shipped failure", Order{Status: OrderStatusShipped, TrackingStatus: TrackingStatusFailure}, "Shipped * delivery issue", DisplayKindWarning}, + {"shipped returned", Order{Status: OrderStatusShipped, TrackingStatus: TrackingStatusReturned}, "Shipped * returned to sender", DisplayKindWarning}, + {"shipped tracking-delivered (webhook lag)", Order{Status: OrderStatusShipped, TrackingStatus: TrackingStatusDelivered}, "Delivered", DisplayKindSuccess}, + {"delivered", Order{Status: OrderStatusDelivered}, "Delivered", DisplayKindSuccess}, + {"cancelled", Order{Status: OrderStatusCancelled}, "Cancelled", DisplayKindError}, + {"refunded", Order{Status: OrderStatusRefunded}, "Refunded", DisplayKindError}, + {"failed", Order{Status: OrderStatusFailed}, "Payment failed", DisplayKindError}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := tc.order.DisplayState(); got != tc.want { + t.Errorf("DisplayState() = %q, want %q", got, tc.want) + } + if got := tc.order.DisplayKind(); got != tc.kind { + t.Errorf("DisplayKind() = %q, want %q", got, tc.kind) + } + }) + } +} diff --git a/pkg/tui/account_orders.go b/pkg/tui/account_orders.go index 1b09547..98b8beb 100644 --- a/pkg/tui/account_orders.go +++ b/pkg/tui/account_orders.go @@ -74,9 +74,9 @@ func (m Model) buildOrderCard(order models.Order, boxWidth int, isSelected bool) } line1 := orderLabel + lipgloss.NewStyle().Width(spacing).Render("") + total - // Line 2: date + status + // Line 2: date + derived status date := dimStyle.Render(order.CreatedAt.Format("Jan 02 2006")) - status := m.statusStyle(order.Status).Bold(true).Render(strings.ToUpper(string(order.Status))) + status := m.displayStyle(order.DisplayKind()).Bold(true).Render(order.DisplayState()) line2 := date + " " + status boxContent := line1 + "\n" + line2 @@ -102,7 +102,7 @@ func (m Model) buildOrderDetailView(order models.Order, _ int) string { // Status and date b.WriteString(labelStyle.Render("Status: ")) - b.WriteString(m.statusStyle(order.Status).Render(strings.ToUpper(string(order.Status)))) + b.WriteString(m.displayStyle(order.DisplayKind()).Render(order.DisplayState())) b.WriteString("\n") b.WriteString(labelStyle.Render("Date: ")) b.WriteString(valueStyle.Render(order.CreatedAt.Format("Jan 02 2006 3:04 PM"))) @@ -183,15 +183,23 @@ func (m Model) buildOrderDetailView(order models.Order, _ int) string { return b.String() } -func (m Model) statusStyle(s models.OrderStatus) lipgloss.Style { - switch s { - case models.OrderStatusDelivered: +// displayStyle maps a model-layer DisplayKind token to a themed lipgloss style. +// Keeps pkg/models free of UI deps. +func (m Model) displayStyle(k models.DisplayKind) lipgloss.Style { + switch k { + case models.DisplayKindSuccess: return m.theme.TextSuccess() - case models.OrderStatusShipped: + case models.DisplayKindBrand: return m.theme.TextBrand() - case models.OrderStatusFailed, models.OrderStatusCancelled, models.OrderStatusRefunded: + case models.DisplayKindAccent: + return m.theme.TextAccent() + case models.DisplayKindError: return m.theme.TextError() - + case models.DisplayKindWarning: + // !TODO add refunds and change this + // No warning color in theme yet, reuse loading pink as visual flag + // for now + return m.theme.TextLoading() } return m.theme.TextHighlight() }