b6af682381
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 23s
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m49s
The wallet storefront listed products in creation order — its purchases and its chip-exchange values were unsorted, unlike the admin console and the public offer. Extract the ordering (chip packs first by rouble price, then chip-priced values grouped hints → no-ads → combo → tournament and by chip price) into one comparator, compareCatalogRank over a small rank key, and apply it at all three sites (projectCatalog, projectOfferPricing, SortAdminCatalog) — so the subgroup ranking (valueGroup) and the full order now live in one place. The rewarded "watch for chips" CTA is a wallet-driven element rendered above the packs, so it is unaffected and stays on top. Also add the admin catalog active/all toggle: AdminCatalog takes includeInactive, the console shows active products by default and ?all=1 lists archived ones too (the detail and grant forms keep loading all products). Tests: a storefront canonical-order unit test (reproduced the unsorted bug first); an integration test for the active/all toggle. Existing offer/admin order tests unchanged — behaviour preserved.
62 lines
2.3 KiB
Go
62 lines
2.3 KiB
Go
package payments
|
|
|
|
import (
|
|
"cmp"
|
|
"slices"
|
|
)
|
|
|
|
// catalogRank is the canonical listing key for a catalog product: whether it is a chip pack, its
|
|
// value subgroup (see [valueGroup]; unused for a pack) and the minor-unit amount its section sorts by
|
|
// (a pack's direct rouble price, a value's chip price; math.MaxInt64 when absent, so a misconfigured
|
|
// row sorts last rather than leading).
|
|
type catalogRank struct {
|
|
pack bool
|
|
group int
|
|
amount int64
|
|
}
|
|
|
|
// compareCatalogRank is the single canonical product order, shared by the storefront ([projectCatalog]),
|
|
// the public offer ([projectOfferPricing]) and the admin catalog ([SortAdminCatalog]): chip packs
|
|
// first, ascending by rouble price; then chip-priced values grouped hints → no-ads → combo →
|
|
// tournament and, within a group, ascending by chip price. A pack's group is unused (all packs share
|
|
// one section). Callers use it through [entryRank] / [adminRank], which build the key from each
|
|
// product shape, so the subgroup and ordering rules live in exactly one place.
|
|
func compareCatalogRank(a, b catalogRank) int {
|
|
if a.pack != b.pack {
|
|
if a.pack {
|
|
return -1 // packs (sales) before values (chip exchange)
|
|
}
|
|
return 1
|
|
}
|
|
if !a.pack {
|
|
if d := cmp.Compare(a.group, b.group); d != 0 {
|
|
return d
|
|
}
|
|
}
|
|
return cmp.Compare(a.amount, b.amount)
|
|
}
|
|
|
|
// entryRank builds the canonical rank of a storefront / offer catalog entry.
|
|
func entryRank(e catalogEntry) catalogRank {
|
|
if isPackEntry(e) {
|
|
return catalogRank{pack: true, amount: offerSortAmount(e, string(SourceDirect), CurrencyRUB)}
|
|
}
|
|
return catalogRank{group: offerValueGroup(e), amount: offerSortAmount(e, "", CurrencyChip)}
|
|
}
|
|
|
|
// adminRank builds the canonical rank of an admin catalog product.
|
|
func adminRank(p AdminProduct) catalogRank {
|
|
if adminIsPack(p) {
|
|
return catalogRank{pack: true, amount: adminPriceAmount(p, string(SourceDirect), CurrencyRUB)}
|
|
}
|
|
return catalogRank{group: adminValueGroup(p), amount: adminPriceAmount(p, "", CurrencyChip)}
|
|
}
|
|
|
|
// sortCatalogEntries orders storefront / offer entries in place into the canonical listing order
|
|
// ([compareCatalogRank]). Stable, so equal-keyed products keep their incoming (creation) order.
|
|
func sortCatalogEntries(entries []catalogEntry) {
|
|
slices.SortStableFunc(entries, func(a, b catalogEntry) int {
|
|
return compareCatalogRank(entryRank(a), entryRank(b))
|
|
})
|
|
}
|